@@ -10,6 +10,7 @@ package beam // import "go.opentelemetry.io/ebpf-profiler/interpreter/beam"
10
10
11
11
import (
12
12
"fmt"
13
+ "os"
13
14
"regexp"
14
15
"strconv"
15
16
@@ -19,6 +20,8 @@ import (
19
20
"go.opentelemetry.io/ebpf-profiler/interpreter"
20
21
"go.opentelemetry.io/ebpf-profiler/libpf"
21
22
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
23
+ "go.opentelemetry.io/ebpf-profiler/lpm"
24
+ "go.opentelemetry.io/ebpf-profiler/process"
22
25
"go.opentelemetry.io/ebpf-profiler/remotememory"
23
26
"go.opentelemetry.io/ebpf-profiler/reporter"
24
27
"go.opentelemetry.io/ebpf-profiler/support"
@@ -40,6 +43,12 @@ type beamInstance struct {
40
43
41
44
data * beamData
42
45
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
43
52
}
44
53
45
54
func readSymbolValue (ef * pfelf.File , name libpf.SymbolName ) ([]byte , error ) {
@@ -116,11 +125,126 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
116
125
func (d * beamData ) Attach (ebpf interpreter.EbpfHandler , pid libpf.PID , bias libpf.Address , rm remotememory.RemoteMemory ) (interpreter.Instance , error ) {
117
126
log .Infof ("BEAM interpreter attaching" )
118
127
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 ),
121
132
}, nil
122
133
}
123
134
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
+
124
248
func (i * beamInstance ) Detach (interpreter.EbpfHandler , libpf.PID ) error {
125
249
log .Infof ("BEAM interpreter detaching" )
126
250
return nil
0 commit comments