26
"github.com/cilium/tetragon/api/v1/tetragon"
27
"github.com/cilium/tetragon/pkg/defaults"
28
"github.com/cilium/tetragon/pkg/logger"
29
"github.com/cilium/tetragon/pkg/policyfilter"
30
gopssignal "github.com/google/gops/signal"
31
"go.uber.org/multierr"
32
"google.golang.org/grpc"
33
"google.golang.org/grpc/credentials/insecure"
35
"github.com/sirupsen/logrus"
36
"github.com/vishvananda/netlink"
41
ExportFname string `json:"export_fname"`
42
LibDir string `json:"lib_dir"`
43
BtfFname string `json:"btf_fname"`
44
ServerAddr string `json:"server_address"`
45
MetricsAddr string `json:"metrics_address"`
46
GopsAddr string `json:"gops_address"`
47
MapDir string `json:"map_dir"`
48
BpfToolPath string `json:"bpftool_path"`
49
GopsPath string `json:"gops_path"`
53
func LoadInitInfo() (*InitInfo, error) {
54
return doLoadInitInfo(defaults.InitInfoFile)
58
func SaveInitInfo(info *InitInfo) error {
59
return doSaveInitInfo(defaults.InitInfoFile, info)
62
func doLoadInitInfo(fname string) (*InitInfo, error) {
63
f, err := os.Open(fname)
65
logger.GetLogger().WithField("infoFile", fname).Warn("failed to open file")
71
if err := json.NewDecoder(f).Decode(&info); err != nil {
72
logger.GetLogger().WithField("infoFile", fname).Warn("failed to read information from file")
79
func doSaveInitInfo(fname string, info *InitInfo) error {
81
bpftool, err := exec.LookPath("bpftool")
83
logger.GetLogger().Warn("failed to locate bpftool binary, on bugtool debugging ensure you have bpftool installed")
85
info.BpfToolPath = bpftool
86
logger.GetLogger().WithField("bpftool", info.BpfToolPath).Info("Successfully detected bpftool path")
89
gops, err := exec.LookPath("gops")
91
logger.GetLogger().Warn("failed to locate gops binary, on bugtool debugging ensure you have gops installed")
94
logger.GetLogger().WithField("gops", info.GopsPath).Info("Successfully detected gops path")
98
if err := os.MkdirAll(defaults.DefaultRunDir, 0755); err != nil {
99
logger.GetLogger().WithField("infoFile", fname).Warn("failed to directory exists")
102
f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, 0744)
104
logger.GetLogger().WithField("infoFile", fname).Warn("failed to create file")
109
if err := f.Truncate(0); err != nil {
110
logger.GetLogger().WithField("infoFile", fname).Warn("failed to truncate file")
114
if err := json.NewEncoder(f).Encode(info); err != nil {
115
logger.GetLogger().WithField("infoFile", fname).Warn("failed to write information to file")
122
type bugtoolInfo struct {
128
func doTarAddBuff(tarWriter *tar.Writer, fname string, buff *bytes.Buffer) error {
129
logHdr := tar.Header{
130
Typeflag: tar.TypeReg,
132
Size: int64(buff.Len()),
136
if err := tarWriter.WriteHeader(&logHdr); err != nil {
137
logger.GetLogger().Error("failed to write log buffer tar header")
140
_, err := io.Copy(tarWriter, buff)
142
logger.GetLogger().Error("failed to copy log buffer")
147
func (s *bugtoolInfo) tarAddBuff(tarWriter *tar.Writer, fname string, buff *bytes.Buffer) error {
148
name := filepath.Join(s.prefixDir, fname)
149
return doTarAddBuff(tarWriter, name, buff)
152
func (s *bugtoolInfo) tarAddJson(tarWriter *tar.Writer, fname string, obj interface{}) error {
153
b, err := json.Marshal(obj)
157
return s.tarAddBuff(tarWriter, fname, bytes.NewBuffer(b))
160
func (s *bugtoolInfo) tarAddFile(tarWriter *tar.Writer, fnameSrc string, fnameDst string) error {
161
fileSrc, err := os.Open(fnameSrc)
163
s.multiLog.WithField("path", fnameSrc).Warn("failed to open file")
166
defer fileSrc.Close()
168
fileSrcInfo, err := fileSrc.Stat()
170
s.multiLog.WithField("path", fnameSrc).Warn("failed to stat file")
174
hdr, err := tar.FileInfoHeader(fileSrcInfo, "" )
176
s.multiLog.Warn("error creating tar header")
179
hdr.Name = filepath.Join(s.prefixDir, fnameDst)
181
if err := tarWriter.WriteHeader(hdr); err != nil {
182
s.multiLog.Warn("failed to write tar header")
186
_, err = io.Copy(tarWriter, fileSrc)
188
s.multiLog.WithField("fnameSrc", fnameSrc).Warn("error copying data from source file")
196
func Bugtool(outFname string, bpftool string, gops string) error {
197
info, err := LoadInitInfo()
203
info.BpfToolPath = bpftool
210
return doBugtool(info, outFname)
213
func doBugtool(info *InitInfo, outFname string) error {
216
bugtoolLogger := logrus.New()
217
logBuff := new(bytes.Buffer)
218
bugtoolLogger.Out = logBuff
219
logrus.SetLevel(logrus.InfoLevel)
220
multiLog := MultiLog{
221
Logs: []logrus.FieldLogger{
226
prefixDir := fmt.Sprintf("tetragon-bugtool-%s", time.Now().Format("20060102150405"))
228
outFile, err := os.Create(outFname)
230
multiLog.WithError(err).WithField("tarFile", outFname).Warn("failed to create bugtool tarfile")
233
defer outFile.Close()
237
prefixDir: prefixDir,
241
gzWriter := gzip.NewWriter(outFile)
242
defer gzWriter.Close()
244
tarWriter := tar.NewWriter(gzWriter)
246
defer tarWriter.Close()
247
si.tarAddBuff(tarWriter, "tetragon-bugtool.log", logBuff)
250
si.addInitInfo(tarWriter)
251
si.addLibFiles(tarWriter)
252
si.addBtfFile(tarWriter)
253
si.addTetragonLog(tarWriter)
254
si.addMetrics(tarWriter)
255
si.execCmd(tarWriter, "dmesg.out", "dmesg")
256
si.addTcInfo(tarWriter)
257
si.addBpftoolInfo(tarWriter)
258
si.addGopsInfo(tarWriter)
259
si.dumpPolicyFilterMap(tarWriter)
260
si.addGrpcInfo(tarWriter)
264
func (s *bugtoolInfo) addInitInfo(tarWriter *tar.Writer) error {
265
s.multiLog.Info("saving init info")
266
buff := new(bytes.Buffer)
267
if err := json.NewEncoder(buff).Encode(s.info); err != nil {
268
s.multiLog.Warn("failed to serialze init info")
271
return s.tarAddBuff(tarWriter, "tetragon-info.json", buff)
279
func (s *bugtoolInfo) addLibFiles(tarWriter *tar.Writer) error {
280
s.multiLog.WithField("libDir", s.info.LibDir).Info("retrieving lib directory")
281
return filepath.Walk(
286
func(path string, info os.FileInfo, err error) error {
288
s.multiLog.WithField("path", path).Warn("error walking path.")
292
if info.IsDir() && info.Name() == "metadata" {
293
s.multiLog.WithField("path", path).Info("skipping metadata directory")
294
return filepath.SkipDir
302
if !(mode.IsRegular() || mode.IsDir()) {
303
s.multiLog.WithField("path", path).Warn("not a regular file, ignoring")
307
if !strings.HasSuffix(info.Name(), ".o") {
308
s.multiLog.WithField("path", path).Warn("not an object file, ignoring")
312
hdr, err := tar.FileInfoHeader(info, "" )
314
s.multiLog.WithField("path", path).Warn("error creating tar header")
318
hdr.Name = filepath.Join(s.prefixDir, "lib", strings.TrimPrefix(path, s.info.LibDir))
320
if err := tarWriter.WriteHeader(hdr); err != nil {
321
s.multiLog.WithField("path", path).Warn("failed to write tar header")
330
file, err := os.Open(path)
332
s.multiLog.WithField("path", path).Warn("error opening file")
336
_, err = io.Copy(tarWriter, file)
338
s.multiLog.WithField("path", path).Warn("error copying data from file")
346
func (s *bugtoolInfo) addBtfFile(tarWriter *tar.Writer) error {
347
btfFname, err := filepath.EvalSymlinks(s.info.BtfFname)
348
if err != nil && s.info.BtfFname != "" {
349
s.multiLog.WithField("btfFname", s.info.BtfFname).Warnf("error resolving btf file: %s", err)
353
if s.info.BtfFname == "" {
354
s.multiLog.Warnf("no btf filename in tetragon config, attempting to fall back to /sys/kernel/btf/vmlinux")
355
btfFname = "/sys/kernel/btf/vmlinux"
358
if rel, err := filepath.Rel(s.info.LibDir, btfFname); err == nil && !strings.HasPrefix(rel, "..") {
359
s.multiLog.WithField("btfFname", btfFname).Infof("btf file already in lib dir: %s", rel)
363
err = s.tarAddFile(tarWriter, btfFname, "btf")
365
s.multiLog.WithField("btfFname", btfFname).Info("btf file added")
371
func (s *bugtoolInfo) addTetragonLog(tarWriter *tar.Writer) error {
372
if s.info.ExportFname == "" {
373
s.multiLog.Info("no export file specified")
377
err := s.tarAddFile(tarWriter, s.info.ExportFname, "tetragon.log")
379
s.multiLog.WithField("exportFname", s.info.ExportFname).Info("tetragon log file added")
385
func (s *bugtoolInfo) addMetrics(tarWriter *tar.Writer) error {
387
if s.info.MetricsAddr == "" {
392
slice := strings.Split(s.info.MetricsAddr, ":")
394
s.multiLog.WithField("metricsAddr", s.info.MetricsAddr).Warn("could not determine metrics port")
395
return errors.New("failed to determine metrics port")
397
port := slice[len(slice)-1]
400
metricsAddr := fmt.Sprintf("http://localhost:%s/metrics", port)
401
s.multiLog.WithField("metricsAddr", metricsAddr).Info("contacting metrics server")
402
resp, err := http.Get(metricsAddr)
404
s.multiLog.WithField("metricsAddr", metricsAddr).WithField("err", err).Warn("failed to contact metrics server")
407
defer resp.Body.Close()
409
buff := new(bytes.Buffer)
410
if _, err = buff.ReadFrom(resp.Body); err != nil {
411
s.multiLog.Warn("error in reading metrics server response: %s", err)
413
return s.tarAddBuff(tarWriter, "metrics", buff)
417
func (s *bugtoolInfo) execCmd(tarWriter *tar.Writer, dstFname string, cmdName string, cmdArgs ...string) error {
418
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
420
cmd := exec.CommandContext(ctx, cmdName, cmdArgs...)
422
stdin, err := cmd.StdinPipe()
424
s.multiLog.Warnf("StdinPipe() failed: %s", err)
429
stdout, err := cmd.StdoutPipe()
431
s.multiLog.Warnf("StdoutPipe() failed: %v", err)
435
stderr, err := cmd.StderrPipe()
437
s.multiLog.Warnf("StderrPipe() failed: %v", err)
443
s.multiLog.WithField("cmd", cmd).WithError(err).Warnf("failed to execute command")
451
outbuff := new(bytes.Buffer)
452
if _, err = outbuff.ReadFrom(stdout); err != nil {
453
s.multiLog.WithField("cmd", cmd).WithError(err).Warnf("error reading stdout")
456
errbuff := new(bytes.Buffer)
457
if _, err = errbuff.ReadFrom(stderr); err != nil {
458
s.multiLog.WithField("cmd", cmd).WithError(err).Warnf("error reading stderr")
466
s.multiLog.WithField("cmd", cmd).WithField("ret", errStr).WithField("dstFname", dstFname).Info("executed command")
468
ret := s.tarAddBuff(tarWriter, dstFname, outbuff)
469
if errbuff.Len() > 0 {
470
errstderr := s.tarAddBuff(tarWriter, dstFname+".err", errbuff)
471
ret = multierr.Append(ret, errstderr)
477
func (s *bugtoolInfo) addTcInfo(tarWriter *tar.Writer) error {
478
links, err := netlink.LinkList()
480
s.multiLog.WithError(err).Warn("listing devices failed")
488
for _, link := range links {
489
linkName := link.Attrs().Name
490
s.execCmd(tarWriter, fmt.Sprintf("tc-info.%s.ingress", linkName), "tc", "filter", "show", "dev", linkName, "ingress")
491
s.execCmd(tarWriter, fmt.Sprintf("tc-info.%s.egress", linkName), "tc", "filter", "show", "dev", linkName, "egress")
498
func (s *bugtoolInfo) addBpftoolInfo(tarWriter *tar.Writer) {
499
if s.info.BpfToolPath == "" {
500
s.multiLog.Warn("Failed to locate bpftool, please install it and specify its path")
504
_, err := os.Stat(s.info.BpfToolPath)
506
s.multiLog.WithError(err).Warn("Failed to locate bpftool. Please install it or specify its path, see 'bugtool --help'")
509
s.execCmd(tarWriter, "bpftool-maps.json", s.info.BpfToolPath, "map", "show", "-j")
510
s.execCmd(tarWriter, "bpftool-progs.json", s.info.BpfToolPath, "prog", "show", "-j")
511
s.execCmd(tarWriter, "bpftool-cgroups.json", s.info.BpfToolPath, "cgroup", "tree", "-j")
514
func (s *bugtoolInfo) getPProf(tarWriter *tar.Writer, file string) error {
515
if s.info.GopsAddr == "" {
516
s.multiLog.Info("Skipping gops dump info as daemon is running without gops, use --gops-address to enable gops")
520
s.multiLog.WithField("gops-address", s.info.GopsAddr).Info("Contacting gops server for pprof dump")
522
conn, err := net.Dial("tcp", s.info.GopsAddr)
524
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithError(err).Warn("Failed to contact gops server")
528
buf := []byte{gopssignal.HeapProfile}
529
if _, err := conn.Write(buf); err != nil {
530
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithError(err).Warn("Failed to send gops pprof-heap command")
534
buff := new(bytes.Buffer)
535
if _, err = buff.ReadFrom(conn); err != nil {
536
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithError(err).Warn("Failed reading gops pprof-heap response")
538
return s.tarAddBuff(tarWriter, file, buff)
541
func (s *bugtoolInfo) addGopsInfo(tarWriter *tar.Writer) {
542
if s.info.GopsAddr == "" {
543
s.multiLog.Info("Skipping gops dump info as daemon is running without gops, use --gops-address to enable gops")
547
if s.info.GopsPath == "" {
548
s.multiLog.WithField("gops-address", s.info.GopsAddr).Warn("Failed to locate gops. Please install it or specify its path, see 'bugtool --help'")
552
_, err := os.Stat(s.info.GopsPath)
554
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithError(err).Warn("Failed to locate gops, please install it")
558
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithField("gops-path", s.info.GopsPath).Info("Dumping gops information")
560
s.execCmd(tarWriter, "gops.stack", s.info.GopsPath, "stack", s.info.GopsAddr)
561
s.execCmd(tarWriter, "gops.stats", s.info.GopsPath, "stats", s.info.GopsAddr)
562
s.execCmd(tarWriter, "gops.memstats", s.info.GopsPath, "memstats", s.info.GopsAddr)
563
err = s.getPProf(tarWriter, "gops.pprof-heap")
565
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithField("gops-path", s.info.GopsPath).WithError(err).Warn("Failed to dump gops pprof-heap")
567
s.multiLog.WithField("gops-address", s.info.GopsAddr).WithField("gops-path", s.info.GopsPath).Info("Successfully dumped gops pprof-heap")
571
func (s *bugtoolInfo) dumpPolicyFilterMap(tarWriter *tar.Writer) error {
572
fname := path.Join(s.info.MapDir, policyfilter.MapName)
573
m, err := policyfilter.OpenMap(fname)
575
s.multiLog.WithError(err).Warnf("failed to open policyfilter map")
581
s.multiLog.WithError(err).Warnf("failed to dump policyfilter map")
584
return s.tarAddJson(tarWriter, policyfilter.MapName+".json", obj)
587
func (s *bugtoolInfo) addGrpcInfo(tarWriter *tar.Writer) {
588
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
590
conn, err := grpc.DialContext(
593
grpc.WithTransportCredentials(insecure.NewCredentials()),
597
s.multiLog.Warnf("failed to connect to %s: %v", s.info.ServerAddr, err)
601
client := tetragon.NewFineGuidanceSensorsClient(conn)
603
res, err := client.ListTracingPolicies(ctx, &tetragon.ListTracingPoliciesRequest{})
604
if err != nil || res == nil {
605
s.multiLog.Warnf("failed to list tracing policies: %v", err)
609
fname := "tracing-policies.json"
610
err = s.tarAddJson(tarWriter, fname, res)
612
s.multiLog.Warnf("failed to dump tracing policies: %v", err)
616
s.multiLog.Infof("dumped tracing policies in %s", fname)