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  }