golang與非golang程序探測beyla源碼解讀
beyla中g(shù)olang程序與非golang程序的ebpf探測
beyla中g(shù)olang程序與非golang程序的ebpf采用了不同的探測方式
- golang:使用uprobe監(jiān)聽用戶庫函數(shù);
- 非golang:使用kprobe監(jiān)聽內(nèi)核函數(shù);
程序類型的定義:
// beyla/pkg/internal/svc/svc.go type InstrumentableType int const ( InstrumentableGolang = InstrumentableType(iota) InstrumentableJava InstrumentableDotnet InstrumentablePython InstrumentableRuby InstrumentableNodejs InstrumentableRust InstrumentableGeneric )
一.程序的區(qū)分方法
對golang與非golang程序,其區(qū)分方法是讀elf可執(zhí)行文件,然后查找symbols是否包含golang的function,以go nethttp為例:
- 讀elf文件的symbols,然后查找其中是否有g(shù)o http的function;
http的function:
- "net/http.serverHandler.ServeHTTP"
- "net/http.(*conn).readRequest"
- "net/http.(*response).WriteHeader"
- "net/http.(*Transport).roundTrip"
源碼,重點(diǎn)是inspectOffset(execElf)函數(shù):
- 讀取elf文件,解析其中的symbols;
- 如果探測到golang的用戶函數(shù),則認(rèn)為是Golang程序;
// 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}
}按照注冊監(jiān)聽時的function列表,在elf中查找symbols:
- findGoSymbolTable(elfF)負(fù)責(zé)從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
}重點(diǎn)看一下findGoSymbolTable()函數(shù)的實(shí)現(xiàn):
- 首先,讀取elf文件中section=.gopclntab的內(nèi)容;
- 然后,讀取elf文件中section=.text的內(nèi)容;
- 最后,使用上面讀取的內(nèi)容,構(gòu)造symTab;
golang的這種elf結(jié)構(gòu),保證了:
- 即使elf被stripped,也能通過 debug/gosym 庫,將其中的symbols讀取出來;
- debug/gosym 是Go標(biāo)準(zhǔn)庫中的一個包,用于解析Go程序的符號表信息;
// 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:
- 可以看出,其中包gopclntab和text這兩個section;
# objdump -h example-http
example-http 文件格式 elf64-x86-64
節(jié):
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程序的監(jiān)聽探針
golang與非golang程序使用了不同的ebpf監(jiān)聽方法。
- golang程序:使用newGoTracerGroup()注冊tracer的方法;
- 非golang程序:使用newNonGoTracersGroup()注冊tracer的方法;
// 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監(jiān)聽的用戶庫函數(shù):
// 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)}
}進(jìn)入到httpfltr查看監(jiān)聽的kprobe函數(shù):
// 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,
},
}
}參考:http://m.fzitv.net/article/264383.htm
以上就是golang與非golang程序探測beyla源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于golang beyla程序探測的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深入解析如何基于go-retry構(gòu)建靈活安全和高效的重試邏輯
本文將深入解析 sethvargo/go-retry 這個輕量且強(qiáng)大的 Go 重試庫,帶大家從原理到實(shí)踐,構(gòu)建靈活、安全、高效的重試邏輯,感興趣的小伙伴可以了解下2025-11-11
Go 實(shí)現(xiàn)一次性打包各個平臺的可執(zhí)行程序
這篇文章主要介紹了Go 實(shí)現(xiàn)一次性打包各個平臺的可執(zhí)行程序,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Go實(shí)現(xiàn)跨平臺的藍(lán)牙聊天室示例詳解
這篇文章主要為大家介紹了Go實(shí)現(xiàn)跨平臺的藍(lán)牙聊天室示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
基于Go Int轉(zhuǎn)string幾種方式性能測試
這篇文章主要介紹了Go Int轉(zhuǎn)string幾種方式測試,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04

