Skip to content

Commit b5172f9

Browse files
committed
Refactor libpf.Frame mappings
- use unique FrameMappingData to reduce the Frame size - construct FrameMappingData directly from the cached ELF info - remove the FileID LRU cache as unneeded - replace processmnanager.processInfo.mappings map with a sorted slice of the []Mappings - atomically do batch updates of the []Mappings - remove processmanager.processInfo.mappingsByFileID as redudant TODO: - eim should probably get libpf.FrameMappingFile instead of host.FileID - eventually move processmanager.Mapping to interpreters and pass it as the mapping type
1 parent f48ba1f commit b5172f9

File tree

15 files changed

+395
-1038
lines changed

15 files changed

+395
-1038
lines changed

interpreter/dotnet/instance.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ func (i *dotnetInstance) SynchronizeMappings(ebpf interpreter.EbpfHandler,
603603
log.Debugf("%v -> %v guid %v", m.Path, info.simpleName, info.guid)
604604

605605
exeReporter.ReportExecutable(&reporter.ExecutableMetadata{
606-
MappingFile: info.file,
606+
MappingFile: info.mapping.Value().File,
607607
Process: pr,
608608
Mapping: m,
609609
})
@@ -736,7 +736,7 @@ func (i *dotnetInstance) Symbolize(frame *host.Frame, frames *libpf.Frames) erro
736736
AddressOrLineno: libpf.AddressOrLineno(pcOffset),
737737
FunctionName: module.resolveR2RMethodName(pcOffset),
738738
SourceFile: module.simpleName,
739-
MappingFile: module.file,
739+
Mapping: module.mapping,
740740
})
741741
case codeJIT:
742742
// JITted frame in anonymous mapping
@@ -765,7 +765,7 @@ func (i *dotnetInstance) Symbolize(frame *host.Frame, frames *libpf.Frames) erro
765765
SourceFile: method.module.simpleName,
766766
FunctionName: methodName,
767767
FunctionOffset: ilOffset,
768-
MappingFile: method.module.file,
768+
Mapping: method.module.mapping,
769769
})
770770
default:
771771
// Stub code

interpreter/dotnet/pe.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ const (
258258
type peInfo struct {
259259
err error
260260
lastModified int64
261-
file libpf.FrameMappingFile
261+
mapping libpf.FrameMapping
262262
simpleName libpf.String
263263
guid string
264264
typeSpecs []peTypeSpec
@@ -1251,11 +1251,14 @@ func (pc *peCache) Get(pr process.Process, mapping *process.Mapping) *peInfo {
12511251
}
12521252
info.err = info.parse(file)
12531253
if info.err == nil {
1254-
info.file = libpf.NewFrameMappingFile(libpf.FrameMappingFileData{
1254+
mf := libpf.NewFrameMappingFile(libpf.FrameMappingFileData{
12551255
FileID: fileID,
12561256
FileName: libpf.Intern(path.Base(mapping.Path.String())),
12571257
GnuBuildID: info.guid,
12581258
})
1259+
info.mapping = libpf.NewFrameMapping(libpf.FrameMappingData{
1260+
File: mf,
1261+
})
12591262
}
12601263
pc.peInfoCache.Add(key, info)
12611264
return info

kallsyms/kallsyms.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ type Module struct {
6565
mtime int64
6666
stub bool
6767

68-
mappingFile libpf.FrameMappingFile
68+
mapping libpf.FrameMapping
6969

7070
names []byte
7171
symbols []symbol
@@ -181,10 +181,12 @@ var loadModuleMetadata = func(m *Module, name string, oldMtime int64) bool {
181181
if err == nil && len(buildID) >= 16 {
182182
fileID = libpf.FileIDFromKernelBuildID(buildID)
183183
}
184-
m.mappingFile = libpf.NewFrameMappingFile(libpf.FrameMappingFileData{
185-
FileID: fileID,
186-
FileName: libpf.Intern(name),
187-
GnuBuildID: buildID,
184+
m.mapping = libpf.NewFrameMapping(libpf.FrameMappingData{
185+
File: libpf.NewFrameMappingFile(libpf.FrameMappingFileData{
186+
FileID: fileID,
187+
FileName: libpf.Intern(name),
188+
GnuBuildID: buildID,
189+
}),
188190
})
189191
return true
190192
}
@@ -216,8 +218,8 @@ func (m *Module) End() libpf.Address {
216218
return m.end
217219
}
218220

219-
func (m *Module) MappingFile() libpf.FrameMappingFile {
220-
return m.mappingFile
221+
func (m *Module) Mapping() libpf.FrameMapping {
222+
return m.mapping
221223
}
222224

223225
// LookupSymbolByAddress resolves the `pc` address to the function and offset from it.

libpf/trace.go

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,44 @@ func NewFrameMappingFile(data FrameMappingFileData) FrameMappingFile {
2929
return FrameMappingFile{value: unique.Make(data)}
3030
}
3131

32-
// Valid determines if the FrameMappingFile is valid.
33-
func (fmf FrameMappingFile) Valid() bool {
34-
return fmf != FrameMappingFile{}
35-
}
36-
3732
// Value returns the dereferences FrameMappingFileData.
38-
// This can be done only if it the FrameMappingFile is Valid.
3933
func (fmf FrameMappingFile) Value() FrameMappingFileData {
4034
return fmf.value.Value()
4135
}
4236

37+
// FrameMappingData contains data regarding file backed mapping.
38+
type FrameMappingData struct {
39+
// File is a reference to data about the backing file.
40+
File FrameMappingFile
41+
// Start contains the mapping start address (file virtual address).
42+
Start Address
43+
// End contains the mapping end address (file virtual address).
44+
End Address
45+
// FileOffset is the offset within the file for this mapping.
46+
FileOffset uint64
47+
}
48+
49+
// FrameMapping is an interned FrameMappingData reference.
50+
type FrameMapping struct {
51+
value unique.Handle[FrameMappingData]
52+
}
53+
54+
// NewFrameMapping interns given FrameMappingData.
55+
func NewFrameMapping(data FrameMappingData) FrameMapping {
56+
return FrameMapping{value: unique.Make(data)}
57+
}
58+
59+
// Valid determines if the FrameMapping is valid.
60+
func (fmf FrameMapping) Valid() bool {
61+
return fmf != FrameMapping{}
62+
}
63+
64+
// Value returns the dereferences FrameMappingData.
65+
// This can be done only if it the FrameMapping is Valid.
66+
func (fmf FrameMapping) Value() FrameMappingData {
67+
return fmf.value.Value()
68+
}
69+
4370
// Frame represents one frame in a stack trace.
4471
type Frame struct {
4572
// Type is the frame type.
@@ -52,16 +79,11 @@ type Frame struct {
5279
SourceFile String
5380
// SourceLine is the source code level line number of this frame.
5481
SourceLine SourceLineno
55-
5682
// An address in ELF VA space (native frame) or line number (interpreted frame).
5783
AddressOrLineno AddressOrLineno
58-
59-
// File metadata for the backing file of the mapping.
60-
MappingFile FrameMappingFile
61-
62-
MappingStart Address
63-
MappingEnd Address
64-
MappingFileOffset uint64
84+
// Mapping is a reference to the mapping data to which this Frame coresspends to.
85+
// Available only for frames executing on a file backed memory mapping.
86+
Mapping FrameMapping
6587
}
6688

6789
// Frames is a list of interned frames.

processmanager/helpers.go

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,9 @@
44
package processmanager // import "go.opentelemetry.io/ebpf-profiler/processmanager"
55

66
import (
7-
lru "github.com/elastic/go-freelru"
8-
log "github.com/sirupsen/logrus"
9-
10-
"go.opentelemetry.io/ebpf-profiler/host"
11-
"go.opentelemetry.io/ebpf-profiler/libpf"
127
"go.opentelemetry.io/ebpf-profiler/reporter"
138
)
149

15-
type lruFileIDMapper struct {
16-
cache *lru.SyncedLRU[host.FileID, libpf.FrameMappingFile]
17-
}
18-
19-
// identityHash maps the host.FileID to a 32bit value.
20-
// No need to explicitly hash the FileID, since it's already a hash value.
21-
func identityHash(key host.FileID) uint32 {
22-
return uint32(key)
23-
}
24-
25-
func newFileIDMapper(size int) (*lruFileIDMapper, error) {
26-
cache, err := lru.NewSynced[host.FileID, libpf.FrameMappingFile](uint32(size), identityHash)
27-
if err != nil {
28-
return nil, err
29-
}
30-
return &lruFileIDMapper{cache}, nil
31-
}
32-
33-
func (fm *lruFileIDMapper) Get(key host.FileID) (libpf.FrameMappingFile, bool) {
34-
if mappingFile, ok := fm.cache.Get(key); ok {
35-
return mappingFile, true
36-
}
37-
38-
log.Warnf("Failed to lookup file ID %#x", key)
39-
return libpf.FrameMappingFile{}, false
40-
}
41-
42-
func (fm *lruFileIDMapper) Set(key host.FileID, val libpf.FrameMappingFile) {
43-
fm.cache.Add(key, val)
44-
log.Debugf("Stored file ID mapping %#x -> %#x", key, val.Value())
45-
}
46-
47-
var _ FileIDMapper = (*lruFileIDMapper)(nil)
48-
49-
// MapFileIDMapper implements the FileIDMApper using a map (for testing)
50-
type MapFileIDMapper struct {
51-
fileMap map[host.FileID]libpf.FrameMappingFile
52-
}
53-
54-
func NewMapFileIDMapper() *MapFileIDMapper {
55-
return &MapFileIDMapper{
56-
fileMap: make(map[host.FileID]libpf.FrameMappingFile),
57-
}
58-
}
59-
60-
func (fm *MapFileIDMapper) Get(key host.FileID) (libpf.FrameMappingFile, bool) {
61-
if value, ok := fm.fileMap[key]; ok {
62-
return value, true
63-
}
64-
return libpf.FrameMappingFile{}, true
65-
}
66-
67-
func (fm *MapFileIDMapper) Set(key host.FileID, value libpf.FrameMappingFile) {
68-
fm.fileMap[key] = value
69-
}
70-
71-
var _ FileIDMapper = (*MapFileIDMapper)(nil)
72-
73-
// FileIDMapper is responsible for mapping between 64-bit file IDs to the frame mapping metadata.
74-
type FileIDMapper interface {
75-
// Retrieve the metadata for given 64-bit file ID.
76-
Get(fileID host.FileID) (libpf.FrameMappingFile, bool)
77-
// Associate the metadata for given 64-bit file ID.
78-
Set(fileID host.FileID, metadata libpf.FrameMappingFile)
79-
}
80-
8110
// executableReporterStub is a stub to implement reporter.ExecutableReporter which is used
8211
// as the reporter by default. This can be overridden on at processmanager creation time.
8312
type executableReporterStub struct {

processmanager/manager.go

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,9 @@ var (
7373
// fileIDMapper and symbolReporter. Specify nil for fileIDMapper to use the default
7474
// implementation.
7575
func New(ctx context.Context, includeTracers types.IncludedTracers, monitorInterval time.Duration,
76-
ebpf pmebpf.EbpfHandler, fileIDMapper FileIDMapper, traceReporter reporter.TraceReporter,
76+
ebpf pmebpf.EbpfHandler, traceReporter reporter.TraceReporter,
7777
exeReporter reporter.ExecutableReporter, sdp nativeunwind.StackDeltaProvider,
7878
filterErrorFrames bool, includeEnvVars libpf.Set[string]) (*ProcessManager, error) {
79-
if fileIDMapper == nil {
80-
var err error
81-
fileIDMapper, err = newFileIDMapper(lruFileIDCacheSize)
82-
if err != nil {
83-
return nil, fmt.Errorf("failed to initialize file ID mapping: %v", err)
84-
}
85-
}
8679
if exeReporter == nil {
8780
exeReporter = executableReporterStub{}
8881
}
@@ -114,7 +107,6 @@ func New(ctx context.Context, includeTracers types.IncludedTracers, monitorInter
114107
exitEvents: make(map[libpf.PID]times.KTime),
115108
pidToProcessInfo: make(map[libpf.PID]*processInfo),
116109
ebpf: ebpf,
117-
FileIDMapper: fileIDMapper,
118110
elfInfoCache: elfInfoCache,
119111
frameCache: frameCache,
120112
traceReporter: traceReporter,
@@ -240,10 +232,16 @@ func (pm *ProcessManager) symbolizeFrame(pid libpf.PID, bpfFrame *host.Frame, fr
240232
// if non-trivial cacheable conversion was done.
241233
func (pm *ProcessManager) convertFrame(pid libpf.PID, frame *host.Frame, dst *libpf.Frames) bool {
242234
switch frame.Type.Interpreter() {
243-
case libpf.UnknownInterp:
244-
log.Errorf("Unexpected frame type 0x%02X (neither error nor interpreter frame)",
235+
case libpf.UnknownInterp, libpf.Kernel:
236+
log.Errorf("Unexpected frame type 0x%02X (neither error nor usermode frame)",
245237
uint8(frame.Type))
246-
case libpf.Native, libpf.Kernel:
238+
case libpf.Native:
239+
// Attempt symbolization of native frames. It is best effort and
240+
// provides non-symbolized frames if no native symbolizer is active.
241+
if err := pm.symbolizeFrame(pid, frame, dst); err == nil {
242+
return true
243+
}
244+
247245
// The BPF code classifies whether an address is a return address or not.
248246
// Return addresses are where execution resumes when returning to the stack
249247
// frame and point to the **next instruction** after the call instruction
@@ -264,43 +262,13 @@ func (pm *ProcessManager) convertFrame(pid libpf.PID, frame *host.Frame, dst *li
264262
}
265263

266264
// Locate mapping info for the frame.
267-
var mappingStart, mappingEnd libpf.Address
268-
var fileOffset uint64
269-
if frame.Type.Interpreter() == libpf.Native {
270-
if mapping, ok := pm.findMappingForTrace(pid, frame.File, frame.Lineno); ok {
271-
mappingStart = mapping.Vaddr - libpf.Address(mapping.Bias)
272-
mappingEnd = mappingStart + libpf.Address(mapping.Length)
273-
fileOffset = mapping.FileOffset
274-
}
275-
}
276-
277-
// Attempt symbolization of native frames. It is best effort and
278-
// provides non-symbolized frames if no native symbolizer is active.
279-
if err := pm.symbolizeFrame(pid, frame, dst); err == nil {
280-
return true
281-
}
282-
283-
if mappingFile, ok := pm.FileIDMapper.Get(frame.File); ok {
284-
dst.Append(&libpf.Frame{
285-
Type: frame.Type,
286-
AddressOrLineno: relativeRIP,
287-
MappingStart: mappingStart,
288-
MappingEnd: mappingEnd,
289-
MappingFileOffset: fileOffset,
290-
MappingFile: mappingFile,
265+
mapping := pm.findMappingForTrace(pid, frame.File,
266+
libpf.Address(frame.Lineno))
267+
dst.Append(&libpf.Frame{
268+
Type: frame.Type,
269+
AddressOrLineno: relativeRIP,
270+
Mapping: mapping,
291271
})
292-
} else {
293-
log.Debugf(
294-
"file ID lookup failed for PID %d, frame type %d",
295-
pid, frame.Type)
296-
297-
dst.Append(&libpf.Frame{
298-
Type: frame.Type,
299-
MappingStart: mappingStart,
300-
MappingEnd: mappingEnd,
301-
MappingFileOffset: fileOffset,
302-
})
303-
}
304272
default:
305273
err := pm.symbolizeFrame(pid, frame, dst)
306274
if err == nil {

0 commit comments

Comments
 (0)