Skip to content

Commit

Permalink
Merge branch 'use_generic_tracer' of github.com:grafana/beyla into us…
Browse files Browse the repository at this point in the history
…e_generic_tracer
  • Loading branch information
marctc committed Oct 10, 2024
2 parents 71e2ae9 + 76f44fd commit 17ca01a
Show file tree
Hide file tree
Showing 40 changed files with 466 additions and 946 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ test/integration/components/goredis/vendor/*
test/integration/components/goredis/goredis
test/integration/components/go_otel/rolldice
test/integration/components/go_otel_grpc/rolldice
test/integration/components/pythonserver/lib/
test/integration/components/pythonserver/lib64
test/integration/components/pythonserver/pyvenv.cfg
1 change: 1 addition & 0 deletions bpf/generic_tracer.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "k_tracer.h"
#include "http_ssl.h"
#include "nodejs.h"

char __license[] SEC("license") = "Dual MIT/GPL";
93 changes: 0 additions & 93 deletions bpf/nodejs.c

This file was deleted.

90 changes: 89 additions & 1 deletion bpf/nodejs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "vmlinux.h"
#include "bpf_helpers.h"
#include "bpf_dbg.h"
#include "pid.h"
#include "ringbuf.h"
#include "map_sizing.h"

struct {
Expand All @@ -21,4 +24,89 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} nodejs_parent_map SEC(".maps");

#endif
volatile const s32 async_wrap_async_id_off = 0;
volatile const s32 async_wrap_trigger_async_id_off = 0;

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u64); // the pid_tid
__type(value, u64); // the last AsyncWrap *
__uint(max_entries, 1000); // 1000 nodejs services, small number, nodejs is single threaded
__uint(pinning, LIBBPF_PIN_BY_NAME);
} async_reset_args SEC(".maps");

SEC("uprobe/node:AsyncReset")
int async_reset(struct pt_regs *ctx) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

u64 wrap = (u64)PT_REGS_PARM1(ctx);

bpf_dbg_printk("=== uprobe AsyncReset id=%d wrap=%llx ===", id, wrap);
bpf_map_update_elem(&async_reset_args, &id, &wrap, BPF_ANY);

return 0;
}

SEC("uretprobe/node:AsyncReset")
int async_reset_ret(struct pt_regs *ctx) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uprobe AsyncReset returns id=%d ===", id);
bpf_map_delete_elem(&async_reset_args, &id);

return 0;
}

SEC("uprobe/node:EmitAsyncInit")
int emit_async_init(struct pt_regs *ctx) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uprobe EmitAsyncInit id=%d ===", id);

u64 *wrap_val = bpf_map_lookup_elem(&async_reset_args, &id);
bpf_dbg_printk("wrap_val = %llx", wrap_val);
if (wrap_val) {
u64 wrap = *wrap_val;
bpf_dbg_printk("wrap = %llx", wrap);

if (wrap) {
u64 async_id = 0;
u64 trigger_async_id = 0;

bpf_probe_read_user(&async_id, sizeof(u64), ((void *)wrap) + async_wrap_async_id_off);
bpf_probe_read_user(
&trigger_async_id, sizeof(u64), ((void *)wrap) + async_wrap_trigger_async_id_off);

if (async_id) {
bpf_map_update_elem(&active_nodejs_ids, &id, &async_id, BPF_ANY);
if (trigger_async_id) {
bpf_map_update_elem(&nodejs_parent_map, &async_id, &trigger_async_id, BPF_ANY);
bpf_dbg_printk(
"async_id = %llx, trigger_async_id = %llx", async_id, trigger_async_id);
} else {
bpf_dbg_printk("No trigger async id");
}
} else {
bpf_dbg_printk("No async id");
}
}
} else {
bpf_dbg_printk("No wrap value found");
}

return 0;
}

#endif // NODE_JS_H
111 changes: 51 additions & 60 deletions pkg/internal/discover/attacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/grafana/beyla/pkg/beyla"
"github.com/grafana/beyla/pkg/internal/ebpf"
"github.com/grafana/beyla/pkg/internal/goexec"
"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/internal/imetrics"
"github.com/grafana/beyla/pkg/internal/request"
Expand Down Expand Up @@ -107,11 +106,15 @@ func (ta *TraceAttacher) getTracer(ie *ebpf.Instrumentable) bool {
ta.monitorPIDs(tracer, ie)
ta.Metrics.InstrumentProcess(ie.FileInfo.ExecutableName())
if tracer.Type == ebpf.Generic {
ta.monitorPIDs(ta.reusableTracer, ie)
// We need to do this because generic tracers have shared libraries. For example,
// a python executable can run an SSL and non-SSL application, so it's not enough
// to look at the executable, we must ensure this process doesn't have different
// libraries attached
ok = ta.updateTracerProbes(tracer, ie)
} else {
ta.monitorPIDs(ta.reusableGoTracer, ie)
}
ta.log.Debug(".done")
ta.log.Debug(".done", "success", ok)
return ok
}

Expand All @@ -135,39 +138,24 @@ func (ta *TraceAttacher) getTracer(ie *ebpf.Instrumentable) bool {
ta.log.Warn("Unsupported Go program detected, using generic instrumentation", "error", ie.InstrumentationError)
}
if ta.reusableTracer != nil {
programs = newNonGoTracersGroupUProbes(ta.Cfg, ta.Metrics)
// We need to do more than monitor PIDs. It's possible that this new
// instance of the executable has different DLLs loaded, e.g. libssl.so.
return ta.reuseTracer(ta.reusableTracer, ie)
} else {
programs = newNonGoTracersGroup(ta.Cfg, ta.Metrics)
programs = newGenericTracersGroup(ta.Cfg, ta.Metrics)
}
} else {
if ta.reusableGoTracer != nil {
exe, ok := ta.loadExecutable(ie)
if !ok {
return false
}

if err := ta.reusableGoTracer.NewExecutable(exe, ie); err != nil {
return false
}

ta.log.Debug("reusing Go tracer for",
"pid", ie.FileInfo.Pid,
"child", ie.ChildPids,
"exec", ie.FileInfo.CmdExePath)

ta.monitorPIDs(ta.reusableGoTracer, ie)
ta.existingTracers[ie.FileInfo.Ino] = ta.reusableGoTracer

return true
return ta.reuseTracer(ta.reusableGoTracer, ie)
}
tracerType = ebpf.Go
programs = filterNotFoundPrograms(newGoTracersGroup(ta.Cfg, ta.Metrics), ie.Offsets)
programs = newGoTracersGroup(ta.Cfg, ta.Metrics)
}
case svc.InstrumentableNodejs, svc.InstrumentableJava, svc.InstrumentableRuby, svc.InstrumentablePython, svc.InstrumentableDotnet, svc.InstrumentableGeneric, svc.InstrumentableRust, svc.InstrumentablePHP:
if ta.reusableTracer != nil {
return ta.reuseTracer(ta.reusableTracer, ie)
}
case svc.InstrumentableNodejs:
programs = ta.genericTracers()
programs = append(programs, newNodeJSTracersGroup(ta.Cfg, ta.Metrics)...)
case svc.InstrumentableJava, svc.InstrumentableRuby, svc.InstrumentablePython, svc.InstrumentableDotnet, svc.InstrumentableGeneric, svc.InstrumentableRust, svc.InstrumentablePHP:
programs = ta.genericTracers()
programs = newGenericTracersGroup(ta.Cfg, ta.Metrics)
default:
ta.log.Warn("unexpected instrumentable type. This is basically a bug", "type", ie.Type)
}
Expand Down Expand Up @@ -231,12 +219,40 @@ func (ta *TraceAttacher) loadExecutable(ie *ebpf.Instrumentable) (*link.Executab
return exe, true
}

func (ta *TraceAttacher) genericTracers() []ebpf.Tracer {
if ta.reusableTracer != nil {
return newNonGoTracersGroupUProbes(ta.Cfg, ta.Metrics)
func (ta *TraceAttacher) reuseTracer(tracer *ebpf.ProcessTracer, ie *ebpf.Instrumentable) bool {
exe, ok := ta.loadExecutable(ie)
if !ok {
return false
}

if err := tracer.NewExecutable(exe, ie); err != nil {
return false
}

ta.log.Debug("reusing Generic tracer for",
"pid", ie.FileInfo.Pid,
"child", ie.ChildPids,
"exec", ie.FileInfo.CmdExePath)

ta.monitorPIDs(tracer, ie)
ta.existingTracers[ie.FileInfo.Ino] = tracer

return true
}

func (ta *TraceAttacher) updateTracerProbes(tracer *ebpf.ProcessTracer, ie *ebpf.Instrumentable) bool {
if err := tracer.NewExecutableInstance(ie); err != nil {
return false
}

return newNonGoTracersGroup(ta.Cfg, ta.Metrics)
ta.log.Debug("reusing Generic tracer for",
"pid", ie.FileInfo.Pid,
"child", ie.ChildPids,
"exec", ie.FileInfo.CmdExePath)

ta.monitorPIDs(tracer, ie)

return true
}

func (ta *TraceAttacher) monitorPIDs(tracer *ebpf.ProcessTracer, ie *ebpf.Instrumentable) {
Expand Down Expand Up @@ -290,38 +306,13 @@ func (ta *TraceAttacher) notifyProcessDeletion(ie *ebpf.Instrumentable) {
ta.Metrics.UninstrumentProcess(ie.FileInfo.ExecutableName())
tracer.BlockPID(uint32(ie.FileInfo.Pid), ie.FileInfo.Ns)

// if there are no more trace instances for a Go program, we need to notify that
// if there are no more trace instances for a program, we need to notify that
// the tracer needs to be stopped and deleted.
// We don't remove kernel-based traces as there is only one tracer per host
if tracer.Type != ebpf.Generic && ta.processInstances.Dec(ie.FileInfo.Ino) == 0 {
if ta.processInstances.Dec(ie.FileInfo.Ino) == 0 {
delete(ta.existingTracers, ie.FileInfo.Ino)
ie.Tracer = tracer
ta.DeleteTracers <- ie
}
}
}

// filterNotFoundPrograms will filter these programs whose required functions (as
// returned in the Offsets method) haven't been found in the offsets
func filterNotFoundPrograms(programs []ebpf.Tracer, offsets *goexec.Offsets) []ebpf.Tracer {
if offsets == nil {
return nil
}
var filtered []ebpf.Tracer
funcs := offsets.Funcs
programs:
for _, p := range programs {
for funcName, funcPrograms := range p.GoProbes() {
for _, fp := range funcPrograms {
if !fp.Required {
continue
}
if _, ok := funcs[funcName]; !ok {
continue programs
}
}
}
filtered = append(filtered, p)
}
return filtered
}
11 changes: 1 addition & 10 deletions pkg/internal/discover/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/grafana/beyla/pkg/internal/ebpf"
"github.com/grafana/beyla/pkg/internal/ebpf/generictracer"
"github.com/grafana/beyla/pkg/internal/ebpf/gotracer"
"github.com/grafana/beyla/pkg/internal/ebpf/nodejs"
"github.com/grafana/beyla/pkg/internal/imetrics"
"github.com/grafana/beyla/pkg/internal/pipe/global"
"github.com/grafana/beyla/pkg/internal/request"
Expand Down Expand Up @@ -98,14 +97,6 @@ func newGoTracersGroup(cfg *beyla.Config, metrics imetrics.Reporter) []ebpf.Trac
return []ebpf.Tracer{gotracer.New(cfg, metrics)}
}

func newNonGoTracersGroup(cfg *beyla.Config, metrics imetrics.Reporter) []ebpf.Tracer {
func newGenericTracersGroup(cfg *beyla.Config, metrics imetrics.Reporter) []ebpf.Tracer {
return []ebpf.Tracer{generictracer.New(cfg, metrics)}
}

func newNonGoTracersGroupUProbes(cfg *beyla.Config, metrics imetrics.Reporter) []ebpf.Tracer {
return []ebpf.Tracer{generictracer.New(cfg, metrics)}
}

func newNodeJSTracersGroup(cfg *beyla.Config, metrics imetrics.Reporter) []ebpf.Tracer {
return []ebpf.Tracer{nodejs.New(cfg, metrics)}
}
Loading

0 comments on commit 17ca01a

Please sign in to comment.