github.com/fafucoder/cilium@v1.6.11/tools/ring-dump/cmd/root.go (about) 1 // Copyright 2017-2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cmd 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "strings" 23 24 "github.com/cilium/cilium/pkg/bpf" 25 "github.com/cilium/cilium/pkg/logging" 26 "github.com/cilium/cilium/pkg/logging/logfields" 27 "github.com/cilium/cilium/pkg/monitor/format" 28 29 "github.com/sirupsen/logrus" 30 "github.com/spf13/cobra" 31 "github.com/spf13/viper" 32 ) 33 34 const targetName = "cilium-ring-dump" 35 36 var ( 37 log = logging.DefaultLogger.WithField(logfields.LogSubsys, targetName) 38 logOpts = make(map[string]string) 39 40 targetFile string 41 ) 42 43 // rootCmd represents the base command when called without any subcommands 44 var rootCmd = &cobra.Command{ 45 Use: targetName, 46 Short: "Cilium Ringbuffer debug tool", 47 Long: `Tool for digging into cilium ringbuffer dumps`, 48 Run: run, 49 } 50 51 // Fatalf prints the Printf formatted message to stderr and exits the program 52 // Note: os.Exit(1) is not recoverable 53 func Fatalf(msg string, args ...interface{}) { 54 fmt.Fprintf(os.Stderr, "Error: %s\n", fmt.Sprintf(msg, args...)) 55 os.Exit(-1) 56 } 57 58 // Execute adds all child commands to the root command sets flags appropriately. 59 // This is called by main.main(). It only needs to happen once to the rootCmd. 60 func Execute() { 61 if err := rootCmd.Execute(); err != nil { 62 fmt.Println(err) 63 os.Exit(-1) 64 } 65 } 66 67 func init() { 68 cobra.OnInitialize(initConfig) 69 flags := rootCmd.PersistentFlags() 70 flags.BoolP("debug", "D", false, "Enable debug messages") 71 viper.BindPFlags(flags) 72 } 73 74 // initConfig reads in config file and ENV variables if set. 75 func initConfig() { 76 if viper.GetBool("debug") { 77 log.Level = logrus.DebugLevel 78 } else { 79 log.Level = logrus.InfoLevel 80 } 81 } 82 83 func isComma(r rune) bool { 84 return r == ',' 85 } 86 87 func isArray(r rune) bool { 88 return r == '[' || r == ']' || r == ' ' 89 } 90 91 func getFields(filename string) (map[string]string, error) { 92 data, err := ioutil.ReadFile(filename) 93 if err != nil { 94 return nil, fmt.Errorf("failed to read %s", filename) 95 } 96 97 // cf. func (e *PerfEvent) DebugDump() 98 fields := strings.FieldsFunc(string(data), isComma) 99 if fields == nil { 100 return nil, fmt.Errorf("couldn't parse %s", filename) 101 } 102 m := make(map[string]string, len(fields)) 103 for _, f := range fields { 104 tuple := strings.Split(f, ":") 105 if len(tuple) != 2 { 106 return nil, fmt.Errorf("malformed field: %s", f) 107 } 108 key := strings.TrimLeft(tuple[0], " ") 109 value := strings.TrimLeft(tuple[1], " ") 110 m[key] = value 111 } 112 113 return m, nil 114 } 115 116 // getBytes converts a string in the format "[0 224 230 ...]" into a byte array 117 func getBytes(raw string) []byte { 118 rawSlice := strings.FieldsFunc(raw, isArray) 119 result := make([]byte, len(rawSlice)) 120 121 i := 0 122 for _, b := range rawSlice { 123 fmt.Sscan(b, &result[i]) 124 i++ 125 } 126 return result 127 } 128 129 func run(cmd *cobra.Command, args []string) { 130 // Logging should always be bootstrapped first. Do not add any code above this! 131 logging.SetupLogging(viper.GetStringSlice("log-driver"), logOpts, targetName, viper.GetBool("debug")) 132 133 if len(args) < 1 { 134 Fatalf("path to ringbuffer dump file must be specified") 135 } 136 137 fields, err := getFields(args[0]) 138 if err != nil { 139 Fatalf(err.Error()) 140 } 141 142 for k, v := range fields { 143 if k == "data" { 144 log.Info("Found data; will decode this later...") 145 continue 146 } 147 log.Infof("%s: %s", k, []byte(v)) 148 } 149 150 stateReader := bytes.NewReader(getBytes(fields["state"])) 151 state := bpf.ReadState{} 152 if err = state.Decode(stateReader); err != nil { 153 Fatalf(err.Error()) 154 } 155 156 rawPage := getBytes(fields["data"]) 157 pageReader := bytes.NewReader(rawPage) 158 page := bpf.PerfEventMmapPage{} 159 if err = page.Decode(pageReader); err != nil { 160 Fatalf(err.Error()) 161 } 162 163 fmt.Printf("decoded state: %+v\n", state) 164 fmt.Printf("decoded page: %+v\n", page) 165 166 fmt.Printf("Buffer Size = %d\n", page.DataSize) 167 fmt.Printf("Head Offset = %d\n", page.DataHead%page.DataSize) 168 fmt.Printf("Tail Offset = %d\n", page.DataTail%page.DataSize) 169 170 debugLevel := format.INFO 171 if viper.GetBool("debug") { 172 debugLevel = format.DEBUG 173 } 174 formatter := format.NewMonitorFormatter(debugLevel) 175 eventReader := bpf.PerfEventFromMemory(&page, rawPage) 176 defer eventReader.Disable() 177 result := 0 178 eventReader.Read( 179 func(msg *bpf.PerfEventSample, cpu int) { 180 data := msg.DataDirect() 181 formatter.FormatSample(data, cpu) 182 }, 183 func(msg *bpf.PerfEventLost, cpu int) { 184 format.LostEvent(msg.Lost, cpu) 185 }, 186 func(msg *bpf.PerfEvent) { 187 fmt.Printf("Error while iterating:\n%s\n", msg.Debug()) 188 result = 1 189 }, 190 ) 191 os.Exit(result) 192 }