Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > golang beyla程序探测

golang与非golang程序探测beyla源码解读

作者:a朋

这篇文章主要为大家介绍了beyla源码解读之golang与非golang程序的探测实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

beyla中golang程序与非golang程序的ebpf探测

beyla中golang程序与非golang程序的ebpf采用了不同的探测方式

程序类型的定义:

// beyla/pkg/internal/svc/svc.go
type InstrumentableType int
const (
   InstrumentableGolang = InstrumentableType(iota)
   InstrumentableJava
   InstrumentableDotnet
   InstrumentablePython
   InstrumentableRuby
   InstrumentableNodejs
   InstrumentableRust
   InstrumentableGeneric
)

一.程序的区分方法

对golang与非golang程序,其区分方法是读elf可执行文件,然后查找symbols是否包含golang的function,以go nethttp为例:

源码,重点是inspectOffset(execElf)函数:

// beyla/pkg/internal/discover/typer.go
func (t *typer) asInstrumentable(execElf *exec.FileInfo) Instrumentable {
    ...
    // look for suitable Go application first
    offsets, ok := t.inspectOffsets(execElf)
    if ok {
        // we found go offsets, let's see if this application is not a proxy
        if !isGoProxy(offsets) {
            return Instrumentable{Type: svc.InstrumentableGolang, FileInfo: execElf, Offsets: offsets}
        }
    } 
    ...
    detectedType := exec.FindProcLanguage(execElf.Pid, execElf.ELF)
    return Instrumentable{Type: detectedType, FileInfo: execElf, ChildPids: child}
}

按照注册监听时的function列表,在elf中查找symbols:

// beyla/pkg/internal/goexec/instructions.go
func instrumentationPoints(elfF *elf.File, funcNames []string) (map[string]FuncOffsets, error) {
    ...
    symTab, err := findGoSymbolTable(elfF)
    for _, f := range symTab.Funcs {
        ...
        if _, ok := functions[fName]; ok {
            offs, ok, err := findFuncOffset(&f, elfF)
            if ok {
                allOffsets[fName] = offs
            }
        }
    }
    return allOffsets, nil
}

重点看一下findGoSymbolTable()函数的实现:

// beyla/pkg/internal/goexec/instructions.go
func findGoSymbolTable(elfF *elf.File) (*gosym.Table, error) {
    var err error
    var pclndat []byte
    // program counter line table
    if sec := elfF.Section(".gopclntab"); sec != nil {
        if pclndat, err = sec.Data(); err != nil {
            return nil, fmt.Errorf("acquiring .gopclntab data: %w", err)
        }
    }
    txtSection := elfF.Section(".text")
    pcln := gosym.NewLineTable(pclndat, txtSection.Addr)
    symTab, err := gosym.NewTable(nil, pcln)
    ...
    return symTab, nil
}

看下elf中包含的sections:

# objdump -h example-http
example-http     文件格式 elf64-x86-64
节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0020e3e6  0000000000401000  0000000000401000  00001000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .plt          00000260  000000000060f400  000000000060f400  0020f400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       000e2060  0000000000610000  0000000000610000  00210000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .dynsym       000003f0  00000000006f2940  00000000006f2940  002f2940  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
...
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 12 .gosymtab     00000000  00000000006f4a90  00000000006f4a90  002f4a90  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 13 .gopclntab    001440b0  00000000006f4aa0  00000000006f4aa0  002f4aa0  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 14 .go.buildinfo 00000140  0000000000839000  0000000000839000  00439000  2**4
                  CONTENTS, ALLOC, LOAD, DATA
...
 24 .note.go.buildid 00000064  0000000000400f80  0000000000400f80  00000f80  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

二.golang与非golang程序的监听探针

golang与非golang程序使用了不同的ebpf监听方法。

// beyla/pkg/internal/discover/attacher.go
func (ta *TraceAttacher) getTracer(ie *Instrumentable) (*ebpf.ProcessTracer, bool) {
    ...
    var programs []ebpf.Tracer
    switch ie.Type {
    case svc.InstrumentableGolang:
        ...
        tracerType = ebpf.Go
        programs = filterNotFoundPrograms(newGoTracersGroup(ta.Cfg, ta.Metrics), ie.Offsets)
    case svc.InstrumentableJava, svc.InstrumentableNodejs, svc.InstrumentableRuby, svc.InstrumentablePython, svc.InstrumentableDotnet, svc.InstrumentableGeneric, svc.InstrumentableRust:
        ...
        programs = newNonGoTracersGroup(ta.Cfg, ta.Metrics)
    }
    ...
}

1.golang程序的tracer:

// beyla/pkg/internal/discover/finder.go
func newGoTracersGroup(cfg *pipe.Config, metrics imetrics.Reporter) []ebpf.Tracer {
    // Each program is an eBPF source: net/http, grpc...
    return []ebpf.Tracer{
        nethttp.New(&cfg.EBPF, metrics),
        &nethttp.GinTracer{Tracer: *nethttp.New(&cfg.EBPF, metrics)},
        grpc.New(&cfg.EBPF, metrics),
        goruntime.New(&cfg.EBPF, metrics),
        gosql.New(&cfg.EBPF, metrics),
    }
}

以nethttp为例,ebpf监听的用户库函数:

// beyla/pkg/internal/ebpf/nethttp/nethttp.go
func (p *Tracer) GoProbes() map[string]ebpfcommon.FunctionPrograms {
   return map[string]ebpfcommon.FunctionPrograms{
      "net/http.serverHandler.ServeHTTP": {
         Start: p.bpfObjects.UprobeServeHTTP,
      },
      "net/http.(*conn).readRequest": {
         End: p.bpfObjects.UprobeReadRequestReturns,
      },
      "net/http.(*response).WriteHeader": {
         Start: p.bpfObjects.UprobeWriteHeader,
      },
      "net/http.(*Transport).roundTrip": { // HTTP client, works with Client.Do as well as using the RoundTripper directly
         Start: p.bpfObjects.UprobeRoundTrip,
         End:   p.bpfObjects.UprobeRoundTripReturn,
      },
   }
}

2.非golang程序的tracer:

// beyla/pkg/internal/discover/finder.go
func newNonGoTracersGroup(cfg *pipe.Config, metrics imetrics.Reporter) []ebpf.Tracer {
    return []ebpf.Tracer{httpfltr.New(cfg, metrics), httpssl.New(cfg, metrics)}
}

进入到httpfltr查看监听的kprobe函数:

// beyla/pkg/internal/ebpf/httpfltr/httpfltr.go
func (p *Tracer) KProbes() map[string]ebpfcommon.FunctionPrograms {
   return map[string]ebpfcommon.FunctionPrograms{
      // Both sys accept probes use the same kretprobe.
      // We could tap into __sys_accept4, but we might be more prone to
      // issues with the internal kernel code changing.
      "sys_accept": {
         Required: true,
         End:      p.bpfObjects.KretprobeSysAccept4,
      },
      "sys_accept4": {
         Required: true,
         End:      p.bpfObjects.KretprobeSysAccept4,
      },
      "sock_alloc": {
         Required: true,
         End:      p.bpfObjects.KretprobeSockAlloc,
      },
      "tcp_rcv_established": {
         Required: true,
         Start:    p.bpfObjects.KprobeTcpRcvEstablished,
      },
      // Tracking of HTTP client calls, by tapping into connect
      "sys_connect": {
         Required: true,
         End:      p.bpfObjects.KretprobeSysConnect,
      },
      "tcp_connect": {
         Required: true,
         Start:    p.bpfObjects.KprobeTcpConnect,
      },
      "tcp_sendmsg": {
         Required: true,
         Start:    p.bpfObjects.KprobeTcpSendmsg,
      },
      // Reading more than 160 bytes
      "tcp_recvmsg": {
         Required: true,
         Start:    p.bpfObjects.KprobeTcpRecvmsg,
         End:      p.bpfObjects.KretprobeTcpRecvmsg,
      },
   }
}

参考:https://www.jb51.net/article/264383.htm

以上就是golang与非golang程序探测beyla源码解读的详细内容,更多关于golang beyla程序探测的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文