@@ -10,6 +10,7 @@ package beam // import "go.opentelemetry.io/ebpf-profiler/interpreter/beam"
1010
1111import (
1212 "fmt"
13+ "os"
1314 "regexp"
1415 "strconv"
1516
@@ -19,6 +20,8 @@ import (
1920 "go.opentelemetry.io/ebpf-profiler/interpreter"
2021 "go.opentelemetry.io/ebpf-profiler/libpf"
2122 "go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
23+ "go.opentelemetry.io/ebpf-profiler/lpm"
24+ "go.opentelemetry.io/ebpf-profiler/process"
2225 "go.opentelemetry.io/ebpf-profiler/remotememory"
2326 "go.opentelemetry.io/ebpf-profiler/reporter"
2427 "go.opentelemetry.io/ebpf-profiler/support"
@@ -40,6 +43,12 @@ type beamInstance struct {
4043
4144 data * beamData
4245 rm remotememory.RemoteMemory
46+ // mappings is indexed by the Mapping to its generation
47+ mappings map [process.Mapping ]* uint32
48+ // prefixes is indexed by the prefix added to ebpf maps (to be cleaned up) to its generation
49+ prefixes map [lpm.Prefix ]* uint32
50+ // mappingGeneration is the current generation (so old entries can be pruned)
51+ mappingGeneration uint32
4352}
4453
4554func readSymbolValue (ef * pfelf.File , name libpf.SymbolName ) ([]byte , error ) {
@@ -116,11 +125,126 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
116125func (d * beamData ) Attach (ebpf interpreter.EbpfHandler , pid libpf.PID , bias libpf.Address , rm remotememory.RemoteMemory ) (interpreter.Instance , error ) {
117126 log .Infof ("BEAM interpreter attaching" )
118127 return & beamInstance {
119- data : d ,
120- rm : rm ,
128+ data : d ,
129+ rm : rm ,
130+ mappings : make (map [process.Mapping ]* uint32 ),
131+ prefixes : make (map [lpm.Prefix ]* uint32 ),
121132 }, nil
122133}
123134
135+ func (i * beamInstance ) SynchronizeMappings (ebpf interpreter.EbpfHandler ,
136+ _ reporter.SymbolReporter , pr process.Process , mappings []process.Mapping ) error {
137+ pid := pr .PID ()
138+ i .mappingGeneration ++
139+ for idx := range mappings {
140+ m := & mappings [idx ]
141+ if ! m .IsExecutable () || ! m .IsAnonymous () {
142+ continue
143+ }
144+
145+ if _ , exists := i .mappings [* m ]; exists {
146+ * i .mappings [* m ] = i .mappingGeneration
147+ continue
148+ }
149+
150+ // Generate a new uint32 pointer which is shared for mapping and the prefixes it owns
151+ // so updating the mapping above will reflect to prefixes also.
152+ mappingGeneration := i .mappingGeneration
153+ i .mappings [* m ] = & mappingGeneration
154+
155+ // Just assume all anonymous and executable mappings are BEAM for now
156+ log .Infof ("Enabling BEAM for %#x/%#x" , m .Vaddr , m .Length )
157+
158+ prefixes , err := lpm .CalculatePrefixList (m .Vaddr , m .Vaddr + m .Length )
159+ if err != nil {
160+ return fmt .Errorf ("new anonymous mapping lpm failure %#x/%#x" , m .Vaddr , m .Length )
161+ }
162+
163+ for _ , prefix := range prefixes {
164+ _ , exists := i .prefixes [prefix ]
165+ if ! exists {
166+ err := ebpf .UpdatePidInterpreterMapping (pid , prefix , support .ProgUnwindBEAM , 0 , 0 )
167+ if err != nil {
168+ return err
169+ }
170+ }
171+ i .prefixes [prefix ] = & mappingGeneration
172+ }
173+ }
174+
175+ // Remove prefixes not seen
176+ for prefix , generationPtr := range i .prefixes {
177+ if * generationPtr == i .mappingGeneration {
178+ continue
179+ }
180+ log .Infof ("Delete BEAM prefix %#v" , prefix )
181+ _ = ebpf .DeletePidInterpreterMapping (pid , prefix )
182+ delete (i .prefixes , prefix )
183+ }
184+ for m , generationPtr := range i .mappings {
185+ if * generationPtr == i .mappingGeneration {
186+ continue
187+ }
188+ log .Infof ("Disabling BEAM for %#x/%#x" , m .Vaddr , m .Length )
189+ delete (i .mappings , m )
190+ }
191+
192+ return nil
193+ }
194+
195+ func (i * beamInstance ) SynchronizeMappingsFromJITDump (ebpf interpreter.EbpfHandler ,
196+ _ reporter.SymbolReporter , pr process.Process , mappings []process.Mapping ) error {
197+ pid := pr .PID ()
198+ file , err := os .Open (fmt .Sprintf ("/tmp/jit-%d.dump" , uint32 (pid )))
199+ if err != nil {
200+ return err
201+ }
202+ defer file .Close ()
203+
204+ header , err := ReadJITDumpHeader (file )
205+ if err != nil {
206+ return err
207+ }
208+ log .Infof ("Parsed header: %v" , * header )
209+
210+ for recordHeader , err := ReadJITDumpRecordHeader (file ); err == nil ; recordHeader , err = ReadJITDumpRecordHeader (file ) {
211+ switch recordHeader .ID {
212+ case JITCodeLoad :
213+ record , name , err := ReadJITDumpRecordCodeLoad (file , recordHeader )
214+ if err != nil {
215+ return err
216+ }
217+
218+ log .Infof ("JITDump Code Load %s @ 0x%x (%d bytes)" , name , record .CodeAddr , record .CodeSize )
219+
220+ prefixes , err := lpm .CalculatePrefixList (record .CodeAddr , record .CodeAddr + record .CodeSize )
221+ if err != nil {
222+ return fmt .Errorf ("lpm failure %#x/%#x" , record .CodeAddr , record .CodeSize )
223+ }
224+
225+ for _ , prefix := range prefixes {
226+ // TODO: Include FileID
227+ err := ebpf .UpdatePidInterpreterMapping (pid , prefix , support .ProgUnwindBEAM , 0 , 0 )
228+ if err != nil {
229+ return err
230+ }
231+ }
232+
233+ // TODO: remove mappings that have been moved/unloaded
234+
235+ default :
236+ log .Warnf ("Ignoring JITDump record type %d" , recordHeader .ID )
237+ SkipJITDumpRecord (file , recordHeader )
238+ }
239+ }
240+
241+ if err != nil {
242+ return err
243+ }
244+
245+ return nil
246+ }
247+
124248func (i * beamInstance ) Detach (interpreter.EbpfHandler , libpf.PID ) error {
125249 log .Infof ("BEAM interpreter detaching" )
126250 return nil
0 commit comments