@@ -5,34 +5,44 @@ package rtld // import "go.opentelemetry.io/ebpf-profiler/interpreter/rtld"
55
66import (
77 "fmt"
8- "strings"
8+ "path"
9+ "regexp"
910
10- "github.com/cilium/ebpf/link"
1111 log "github.com/sirupsen/logrus"
1212 "go.opentelemetry.io/ebpf-profiler/interpreter"
1313 "go.opentelemetry.io/ebpf-profiler/libpf"
1414 "go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
1515 "go.opentelemetry.io/ebpf-profiler/remotememory"
1616)
1717
18+ // LdsoRegexp matches ld.so filenames across different distributions
19+ var LdsoRegexp = regexp .MustCompile (
20+ `^ld(?:-linux)?(?:-x86-64|-aarch64)?\.so\.\d+$|` +
21+ `^ld\.so\.\d+$|` +
22+ `^ld-\d+\.\d+\.so$|` +
23+ `^ld-musl-[^/]+\.so\.\d+$` )
24+
1825// data holds the Uprobe link to keep it in memory
1926type data struct {
20- probe pfelf.USDTProbe
2127 path string
22- probeLink link.Link
28+ usePoller bool
29+ probe pfelf.USDTProbe
30+ lc interpreter.LinkCloser
2331}
2432
2533// instance represents a per-PID instance of the rtld interpreter
2634type instance struct {
2735 interpreter.InstanceStubs
28- link link.Link
36+ usePoller bool
37+ lc interpreter.LinkCloser
2938}
3039
3140// Loader detects if the ELF file contains the rtld:map_complete USDT probe
32- func Loader (_ interpreter.EbpfHandler , info * interpreter.LoaderInfo ) (interpreter.Data , error ) {
33- // Check if this is ld.so by examining the filename
41+ func Loader (ebpf interpreter.EbpfHandler , info * interpreter.LoaderInfo ) (interpreter.Data , error ) {
42+ // Check if this is ld.so by examining just the basename
3443 fileName := info .FileName ()
35- if ! strings .Contains (fileName , "ld-" ) && ! strings .Contains (fileName , "ld.so" ) {
44+ baseName := path .Base (fileName )
45+ if ! LdsoRegexp .MatchString (baseName ) {
3646 return nil , nil
3747 }
3848
@@ -44,55 +54,87 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
4454 // Look for .note.stapsdt section which contains USDT probes
4555 sec := ef .Section (".note.stapsdt" )
4656 if sec == nil {
47- log .Debugf ("No .note.stapsdt section found in %s" , fileName )
48- return nil , nil
57+ log .Debugf ("No .note.stapsdt section found in %s, will use poller fallback" , fileName )
58+ return & data {
59+ path : fileName ,
60+ usePoller : true ,
61+ }, nil
4962 }
5063
5164 // Parse USDT probes from the section
5265 probes , err := pfelf .ParseUSDTProbes (sec )
5366 if err != nil {
5467 return nil , fmt .Errorf ("failed to parse USDT probes: %w" , err )
5568 }
56-
5769 // Look for rtld:map_complete probe
5870 for _ , probe := range probes {
59- if probe .Provider == "rtld" && probe .Name == "map_complete" {
60- log .Infof ("Found rtld:map_complete USDT probe in %s at 0x%x" ,
61- fileName , probe .Location )
62- return & data {
63- path : fileName ,
64- probe : probe ,
65- }, nil
71+ if probe .Provider != "rtld" || probe .Name != "map_complete" {
72+ continue
6673 }
74+ log .Debugf ("Found rtld:map_complete USDT probe in %s at 0x%x" ,
75+ fileName , probe .Location )
76+
77+ return & data {
78+ probe : probe ,
79+ path : fileName ,
80+ }, nil
6781 }
6882
69- return nil , nil
83+ // No rtld:map_complete probe found, use poller fallback
84+ log .Debugf ("No rtld:map_complete probe found in %s, will use poller fallback" , fileName )
85+ return & data {
86+ path : fileName ,
87+ usePoller : true ,
88+ }, nil
7089}
7190
72- // Attach attaches the uprobe to the rtld:map_complete USDT probe
91+ // Attach attaches the uprobe to the rtld:map_complete USDT probe or registers with poller
7392func (d * data ) Attach (ebpf interpreter.EbpfHandler , pid libpf.PID , _ libpf.Address ,
7493 _ remotememory.RemoteMemory ) (interpreter.Instance , error ) {
75- link , err := ebpf .AttachUSDTProbe (pid , d .path , d .probe , "usdt_rtld_map_complete" )
76- if err != nil {
77- return nil , fmt .Errorf ("failed to attach uprobe to rtld:map_complete usdt: %w" , err )
94+ if d .usePoller {
95+ // Register this PID with the global poller
96+ // Create a trigger function that calls TriggerProcessSync on the ebpf handler
97+ triggerFunc := func (triggerPID libpf.PID ) {
98+ if err := ebpf .TriggerProcessSync (triggerPID ); err != nil {
99+ log .Debugf ("[rtld] TriggerProcessSync failed for PID %d: %v" , triggerPID , err )
100+ }
101+ }
102+ getPoller (triggerFunc ).registerPID (pid )
103+ log .Debugf ("[rtld] Registered PID %d with poller for %s" , pid , d .path )
104+
105+ return & instance {usePoller : true }, nil
106+ } else {
107+ prog := "usdt_rtld_map_complete"
108+ lc , err := ebpf .AttachUSDTProbes (
109+ pid , d .path , prog , []pfelf.USDTProbe {d .probe }, nil , nil , false )
110+ if err != nil {
111+ return nil , fmt .Errorf ("failed to attach uprobe to rtld:map_complete usdt: %w" , err )
112+ }
113+ log .Debugf ("[rtld] Using USDT probe for PID %d on %s" , pid , d .path )
114+ d .lc = lc
115+ return & instance {lc : lc }, nil
78116 }
79- log .Infof ("Attached uprobe to rtld:map_complete usdt in PID %d" , pid )
80- return & instance {link : link }, nil
81117}
82118
83- // Detach removes the uprobe
119+ // Detach removes the uprobe or deregisters from poller
84120func (i * instance ) Detach (_ interpreter.EbpfHandler , pid libpf.PID ) error {
85121 log .Debugf ("[rtld] Detach called for PID %d" , pid )
122+ if i .usePoller {
123+ // Deregister this PID from the global poller
124+ // Pass a nil trigger function since we're just deregistering
125+ getPoller (nil ).deregisterPID (pid )
126+ log .Debugf ("[rtld] Deregistered PID %d from poller" , pid )
127+ }
86128 return nil
87129}
88130
89131// Unload cleans up the uprobe link
90132func (d * data ) Unload (_ interpreter.EbpfHandler ) {
91- if d .probeLink != nil {
92- if err := d .probeLink . Close (); err != nil {
93- log .Errorf ("[rtld] Failed to close uprobe link: %v" , err )
133+ if d .lc != nil {
134+ if err := d .lc . Unload (); err != nil {
135+ log .Errorf ("[rtld] Failed to unload uprobe link: %v" , err )
94136 }
95- d .probeLink = nil
137+ d .lc = nil
96138 }
97139 log .Debugf ("[rtld] Unloaded uprobe for %s" , d .path )
98140}
0 commit comments