diff --git a/README.md b/README.md index 5f3c1bd1..3922aa8a 100644 --- a/README.md +++ b/README.md @@ -91,9 +91,9 @@ devfiler spins up a local server that listens on `0.0.0.0:11000`. To run it, simply download and unpack the archive from the following URL: -https://upload.elastic.co/d/87e7697991940ec37f0c6e39ac38d213f65e8dc1ef9dbedff6aab9cba0adfaba +https://upload.elastic.co/d/f8aa0c386baa808a616ca29f86b34c726edb5af36f8840a4cf28468ad534a4b5 -Authentication token: `c74dfc4db2212015` +Authentication token: `2635c0750bf8ea69` The archive contains a build for each of the following platforms: diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 12628306..17f796ef 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -56,8 +56,8 @@ func (c *Controller) Start(ctx context.Context) error { traceHandlerCacheSize := traceCacheSize(c.config.MonitorInterval, c.config.SamplesPerSecond, uint16(presentCores)) - intervals := times.New(c.config.MonitorInterval, - c.config.ReporterInterval, c.config.ProbabilisticInterval) + intervals := times.New(c.config.ReporterInterval, c.config.MonitorInterval, + c.config.ProbabilisticInterval) // Start periodic synchronization with the realtime clock times.StartRealtimeSync(ctx, c.config.ClockSyncInterval) diff --git a/libpf/convenience.go b/libpf/convenience.go index 02c7cae1..1abdb604 100644 --- a/libpf/convenience.go +++ b/libpf/convenience.go @@ -5,9 +5,7 @@ package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf" import ( "context" - "fmt" "math/rand/v2" - "os" "reflect" "time" "unsafe" @@ -15,24 +13,6 @@ import ( log "github.com/sirupsen/logrus" ) -// WriteTempFile writes a data buffer to a temporary file on the filesystem. It -// is the callers responsibility to clean up that file again. The function returns -// the filename if successful. -func WriteTempFile(data []byte, directory, prefix string) (string, error) { - file, err := os.CreateTemp(directory, prefix) - if err != nil { - return "", err - } - defer file.Close() - if _, err := file.Write(data); err != nil { - return "", fmt.Errorf("failed to write data to temporary file: %w", err) - } - if err := file.Sync(); err != nil { - return "", fmt.Errorf("failed to synchronize file data: %w", err) - } - return file.Name(), nil -} - // SleepWithJitter sleeps for baseDuration +/- jitter (jitter is [0..1]) func SleepWithJitter(baseDuration time.Duration, jitter float64) { time.Sleep(AddJitter(baseDuration, jitter)) diff --git a/libpf/pfelf/pfelf.go b/libpf/pfelf/pfelf.go index c3c66e08..a0dc8b67 100644 --- a/libpf/pfelf/pfelf.go +++ b/libpf/pfelf/pfelf.go @@ -20,24 +20,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/libpf" ) -// SafeOpenELF opens the given ELF file in a safely way in that -// it recovers from panics inside elf.Open(). -// Under circumstances we see fatal errors from inside the runtime, which -// are not recoverable, e.g. "fatal error: runtime: out of memory". -func SafeOpenELF(name string) (elfFile *elf.File, err error) { - defer func() { - // debug/elf has issues with malformed ELF files - if r := recover(); r != nil { - if elfFile != nil { - elfFile.Close() - elfFile = nil - } - err = fmt.Errorf("failed to open ELF file (recovered from panic): %s", name) - } - }() - return elf.Open(name) -} - // HasDWARFData returns true if the provided ELF file contains actionable DWARF debugging // information. // This function does not call `elfFile.DWARF()` on purpose, as it can be extremely expensive in @@ -132,41 +114,6 @@ func GetDebugLink(elfFile *elf.File) (linkName string, crc32 int32, err error) { return ParseDebugLink(sectionData) } -// GetDebugAltLink returns the contents of the `.gnu_debugaltlink` section (path and build -// ID). If no link is present, ErrNoDebugLink is returned. -func GetDebugAltLink(elfFile *elf.File) (fileName, buildID string, err error) { - // The .gnu_debugaltlink section is not always present - sectionData, err := getSectionData(elfFile, ".gnu_debugaltlink") - if err != nil { - return "", "", ErrNoDebugLink - } - - // The whole .gnu_debugaltlink section consists of: - // 1) path to target (variable-length string) - // 2) null character separator (1 byte) - // 3) build ID (usually 20 bytes, but can vary) - // - // First, find the position of the null character: - nullCharIdx := bytes.IndexByte(sectionData, 0) - if nullCharIdx == -1 { - return "", "", nil - } - - // The path consists of all the characters before the first null character - path := strings.ToValidUTF8(string(sectionData[:nullCharIdx]), "") - - // Check that we can read a build ID: there should be at least 1 byte after the null character. - if nullCharIdx+1 == len(sectionData) { - return "", "", errors.New("malformed .gnu_debugaltlink section (missing build ID)") - } - - // The build ID consists of all the bytes after the first null character - buildIDBytes := sectionData[nullCharIdx+1:] - buildID = hex.EncodeToString(buildIDBytes) - - return path, buildID, nil -} - var ErrNoBuildID = errors.New("no build ID") var ubuntuKernelSignature = regexp.MustCompile(` \(Ubuntu[^)]*\)\n$`) @@ -303,32 +250,6 @@ func getNoteHexString(sectionBytes []byte, name string, noteType uint32) ( return hex.EncodeToString(sectionBytes[idxDataStart:idxDataEnd]), true, nil } -// GetLinuxBuildSalt extracts the linux kernel build salt from the provided ELF path. -// It is read from the .notes ELF section. -// It should be present in both kernel modules and the kernel image of most distro-vended kernel -// packages, and should be identical across all the files: kernel modules will have the same salt -// as their corresponding vmlinux image if they were built at the same time. -// This can be used to identify the kernel image corresponding to a module. -// See https://lkml.org/lkml/2018/7/3/1156 -func GetLinuxBuildSalt(filePath string) (salt string, found bool, err error) { - elfFile, err := SafeOpenELF(filePath) - if err != nil { - return "", false, fmt.Errorf("could not open %s: %w", filePath, err) - } - defer elfFile.Close() - - sectionData, err := getSectionData(elfFile, ".note.Linux") - if err != nil { - sectionData, err = getSectionData(elfFile, ".notes") - if err != nil { - return "", false, nil - } - } - - // 0x100 is defined as LINUX_ELFNOTE_BUILD_SALT in include/linux/build-salt.h - return getNoteHexString(sectionData, "Linux", 0x100) -} - func symbolMapFromELFSymbols(syms []elf.Symbol) *libpf.SymbolMap { symmap := &libpf.SymbolMap{} for _, sym := range syms { @@ -342,16 +263,6 @@ func symbolMapFromELFSymbols(syms []elf.Symbol) *libpf.SymbolMap { return symmap } -// GetSymbols gets the symbols of elf.File and returns them as libpf.SymbolMap for -// fast lookup by address and name. -func GetSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) { - syms, err := elfFile.Symbols() - if err != nil { - return nil, err - } - return symbolMapFromELFSymbols(syms), nil -} - // GetDynamicSymbols gets the dynamic symbols of elf.File and returns them as libpf.SymbolMap for // fast lookup by address and name. func GetDynamicSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) { @@ -362,43 +273,6 @@ func GetDynamicSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) { return symbolMapFromELFSymbols(syms), nil } -// IsKernelModule returns true if the provided ELF file looks like a kernel module (an ELF with a -// .modinfo and .gnu.linkonce.this_module sections). -func IsKernelModule(file *elf.File) (bool, error) { - sectionFound, err := HasSection(file, ".modinfo") - if err != nil { - return false, err - } - - if !sectionFound { - return false, nil - } - - return HasSection(file, ".gnu.linkonce.this_module") -} - -// IsKernelImage returns true if the provided ELF file looks like a kernel image (an ELF with a -// __modver section). -func IsKernelImage(file *elf.File) (bool, error) { - return HasSection(file, "__modver") -} - -// IsKernelFile returns true if the provided ELF file looks like a kernel file (either a kernel -// image or a kernel module). -func IsKernelFile(file *elf.File) (bool, error) { - isModule, err := IsKernelImage(file) - if err != nil { - return false, err - } - - isImage, err := IsKernelModule(file) - if err != nil { - return false, err - } - - return isModule || isImage, nil -} - // IsGoBinary returns true if the provided file is a Go binary (= an ELF file with // a known Golang section). func IsGoBinary(file *elf.File) (bool, error) { diff --git a/libpf/tracehash.go b/libpf/tracehash.go index 71d1df22..be2bee3b 100644 --- a/libpf/tracehash.go +++ b/libpf/tracehash.go @@ -28,15 +28,6 @@ func TraceHashFromBytes(b []byte) (TraceHash, error) { return TraceHash{h}, nil } -// TraceHashFromString parses a byte slice of a trace hash into the internal data representation. -func TraceHashFromString(s string) (TraceHash, error) { - h, err := basehash.New128FromString(s) - if err != nil { - return TraceHash{}, err - } - return TraceHash{h}, nil -} - func (h TraceHash) Equal(other TraceHash) bool { return h.Hash128.Equal(other.Hash128) } diff --git a/processmanager/ebpf/ebpf.go b/processmanager/ebpf/ebpf.go index 9f9dc90b..bb07da91 100644 --- a/processmanager/ebpf/ebpf.go +++ b/processmanager/ebpf/ebpf.go @@ -135,6 +135,8 @@ var outerMapsName = [...]string{ "exe_id_to_19_stack_deltas", "exe_id_to_20_stack_deltas", "exe_id_to_21_stack_deltas", + "exe_id_to_22_stack_deltas", + "exe_id_to_23_stack_deltas", } // Compile time check to make sure ebpfMapsImpl satisfies the interface . diff --git a/processmanager/ebpf/ebpf_test.go b/processmanager/ebpf/ebpf_test.go index d80a78fd..fec5bf5d 100644 --- a/processmanager/ebpf/ebpf_test.go +++ b/processmanager/ebpf/ebpf_test.go @@ -1,6 +1,3 @@ -//go:build !integration -// +build !integration - // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 @@ -12,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/ebpf-profiler/support" ) func TestMapID(t *testing.T) { @@ -26,6 +24,7 @@ func TestMapID(t *testing.T) { 0x3FF: 10, // 1023 0x400: 11, // 1024 0xFFFFF: 20, // 1048575 (2^20 - 1) + (1 << support.StackDeltaBucketLargest) - 1: support.StackDeltaBucketLargest, } for numStackDeltas, expectedShift := range testCases { numStackDeltas := numStackDeltas @@ -33,11 +32,11 @@ func TestMapID(t *testing.T) { t.Run(fmt.Sprintf("deltas %d", numStackDeltas), func(t *testing.T) { shift, err := getMapID(numStackDeltas) require.NoError(t, err) - assert.Equal(t, expectedShift, shift, - fmt.Sprintf("wrong map name for %d deltas", numStackDeltas)) + assert.Equal(t, expectedShift, shift, "wrong map name for %d deltas", + numStackDeltas) }) } - _, err := getMapID(1 << 22) + _, err := getMapID(1 << (support.StackDeltaBucketLargest + 1)) require.Error(t, err) } diff --git a/reporter/fifo.go b/reporter/fifo.go deleted file mode 100644 index e6c871b7..00000000 --- a/reporter/fifo.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter" - -import ( - "fmt" - "sync" - - log "github.com/sirupsen/logrus" -) - -// FifoRingBuffer implements a first-in-first-out ring buffer that is safe for concurrent access. -type FifoRingBuffer[T any] struct { //nolint:gocritic - sync.Mutex - - // data holds the actual data. - data []T - - // emptyT is variable of type T used for nullifying entries in data[]. - emptyT T - - // name holds a string to uniquely identify the ring buffer in log messages. - name string - - // size is the maximum number of entries in the ring buffer. - size uint32 - - // readPos holds the position of the first element to be read in the data array. - readPos uint32 - - // writePos holds the position where the next element should be - // placed in the data array. - writePos uint32 - - // count holds a count of how many entries are in the array. - count uint32 - - // overwriteCount holds a count of the number of overwritten entries since the last metric - // report interval. - overwriteCount uint32 -} - -func (q *FifoRingBuffer[T]) InitFifo(size uint32, name string) error { - if size == 0 { - return fmt.Errorf("unsupported size of fifo: %d", size) - } - q.Lock() - defer q.Unlock() - q.size = size - q.data = make([]T, size) - q.readPos = 0 - q.writePos = 0 - q.count = 0 - q.overwriteCount = 0 - q.name = name - return nil -} - -// zeroFifo re-initializes the ring buffer and clears the data array, making previously -// stored elements available for GC. -func (q *FifoRingBuffer[T]) zeroFifo() { - if err := q.InitFifo(q.size, q.name); err != nil { - // Should never happen - panic(err) - } -} - -// Append adds element v to the FifoRingBuffer. it overwrites existing elements if there is no -// space left. -func (q *FifoRingBuffer[T]) Append(v T) { - q.Lock() - defer q.Unlock() - - q.data[q.writePos] = v - q.writePos++ - - if q.writePos == q.size { - q.writePos = 0 - } - - if q.count < q.size { - q.count++ - if q.count == q.size { - log.Warnf("About to start overwriting elements in buffer for %s", - q.name) - } - } else { - q.overwriteCount++ - q.readPos = q.writePos - } -} - -// ReadAll returns all elements from the FifoRingBuffer. -func (q *FifoRingBuffer[T]) ReadAll() []T { - q.Lock() - defer q.Unlock() - - data := make([]T, q.count) - readPos := q.readPos - - for i := uint32(0); i < q.count; i++ { - pos := (i + readPos) % q.size - data[i] = q.data[pos] - // Allow for element to be GCed - q.data[pos] = q.emptyT - } - - q.readPos = q.writePos - q.count = 0 - - return data -} - -func (q *FifoRingBuffer[T]) GetOverwriteCount() uint32 { - q.Lock() - defer q.Unlock() - - count := q.overwriteCount - q.overwriteCount = 0 - return count -} diff --git a/reporter/fifo_test.go b/reporter/fifo_test.go deleted file mode 100644 index 7a4c7a80..00000000 --- a/reporter/fifo_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package reporter - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFifo(t *testing.T) { - var integers []int - integers = append(integers, 1, 2, 3, 4, 5) - - var integersShared []int - integersShared = append(integersShared, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) - - var retIntegers []int - retIntegers = append(retIntegers, 3, 4, 5) - - var retIntegersShared []int - retIntegersShared = append(retIntegersShared, 8, 9, 10, 11, 12) - - sharedFifo := &FifoRingBuffer[int]{} - err := sharedFifo.InitFifo(5, t.Name()) - require.NoError(t, err) - - //nolint:lll - tests := map[string]struct { - // size defines the size of the fifo. - size uint32 - // data will be written to and extracted from the fifo. - data []int - // returned reflects the data that is expected from the fifo - // after writing to it. - returned []int - // the number of overwrites that occurred - overwriteCount uint32 - // err indicates if an error is expected for this testcase. - err bool - // sharedFifo indicates if a shared fifo should be used. - // If false, a new fifo is used, specific to the testcase. - sharedFifo bool - // parallel indicates if parallelism should be enabled for this testcase. - parallel bool - }{ - // This testcase simulates a fifo with an invalid size of 0. - "Invalid size": {size: 0, err: true, parallel: true}, - // This testcase simulates a case where the numbers of elements - // written to the fifo represents the size of the fifo. - "Full Fifo": {size: 5, data: integers, returned: integers, overwriteCount: 0, parallel: true}, - // This testcase simulates a case where the number of elements - // written to the fifo exceed the size of the fifo. - "Fifo overflow": {size: 3, data: integers, returned: retIntegers, overwriteCount: 2, parallel: true}, - // This testcase simulates a case where only a few elements are - // written to the fifo and don't exceed the size of the fifo. - "Partial full": {size: 15, data: integers, returned: integers, overwriteCount: 0, parallel: true}, - - // The following test cases share the same fifo - - // This testcase simulates a case where the numbers of elements - // written to the fifo represents the size of the fifo. - "Shared Full Fifo": {data: integers, returned: integers, overwriteCount: 0, sharedFifo: true}, - // This testcase simulates a case where the number of elements - // written to the fifo exceed the size of the fifo. - "Shared Fifo overflow": {data: integersShared, returned: retIntegersShared, overwriteCount: 7, sharedFifo: true}, - } - - for name, testcase := range tests { - name := name - testcase := testcase - var fifo *FifoRingBuffer[int] - - t.Run(name, func(t *testing.T) { - if testcase.parallel { - t.Parallel() - } - - if testcase.sharedFifo { - fifo = sharedFifo - } else { - fifo = &FifoRingBuffer[int]{} - err := fifo.InitFifo(testcase.size, t.Name()) - if testcase.err { - require.Error(t, err) - return - } - require.NoError(t, err) - } - - empty := fifo.ReadAll() - require.Empty(t, empty) - - for _, v := range testcase.data { - fifo.Append(v) - } - - data := fifo.ReadAll() - for i := uint32(0); i < fifo.size; i++ { - assert.Equalf(t, 0, fifo.data[i], "fifo not empty after ReadAll(), idx: %d", i) - } - assert.Equal(t, testcase.returned, data) - assert.Equal(t, testcase.overwriteCount, fifo.GetOverwriteCount(), "overwrite count") - assert.Zero(t, fifo.GetOverwriteCount(), "overwrite count not reset") - }) - } -} - -func TestFifo_isWritableWhenZeroed(t *testing.T) { - fifo := &FifoRingBuffer[int]{} - require.NoError(t, fifo.InitFifo(1, t.Name())) - fifo.zeroFifo() - assert.NotPanics(t, func() { - fifo.Append(123) - }) -} diff --git a/reporter/otlp_reporter.go b/reporter/otlp_reporter.go index 2a5fa6de..cb664305 100644 --- a/reporter/otlp_reporter.go +++ b/reporter/otlp_reporter.go @@ -79,8 +79,10 @@ type traceEvents struct { // attrKeyValue is a helper to populate Profile.attribute_table. type attrKeyValue[T string | int64] struct { - key string - value T + key string + // Set to true for OTel SemConv attributes with requirement level: Required + required bool + value T } // OTLPReporter receives and transforms information to be OTLP/profiles compliant. @@ -599,7 +601,7 @@ func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS u // semantic convention for build_id, replace these hard coded // strings. {key: "process.executable.build_id.gnu", value: execInfo.gnuBuildID}, - {key: "process.executable.build_id.profiling", + {key: "process.executable.build_id.htlhash", value: traceInfo.files[i].StringNoQuotes()}, }, attributeMap) @@ -653,8 +655,8 @@ func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS u loc.Line = append(loc.Line, line) // To be compliant with the protocol, generate a dummy mapping entry. - loc.MappingIndex = getDummyMappingIndex(fileIDtoMapping, stringMap, - profile, traceInfo.files[i]) + loc.MappingIndex = getDummyMappingIndex(fileIDtoMapping, + stringMap, attributeMap, profile, traceInfo.files[i]) } profile.Location = append(profile.Location, loc) } @@ -746,6 +748,9 @@ func addProfileAttributes[T string | int64](profile *profiles.Profile, switch val := any(attr.value).(type) { case string: + if !attr.required && val == "" { + return + } attributeCompositeKey = attr.key + "_" + val attributeValue = common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: val}} case int64: @@ -778,24 +783,23 @@ func addProfileAttributes[T string | int64](profile *profiles.Profile, // getDummyMappingIndex inserts or looks up an entry for interpreted FileIDs. func getDummyMappingIndex(fileIDtoMapping map[libpf.FileID]uint64, - stringMap map[string]uint32, profile *profiles.Profile, - fileID libpf.FileID) uint64 { - var locationMappingIndex uint64 + stringMap map[string]uint32, attributeMap map[string]uint64, + profile *profiles.Profile, fileID libpf.FileID) uint64 { if tmpMappingIndex, exists := fileIDtoMapping[fileID]; exists { - locationMappingIndex = tmpMappingIndex - } else { - idx := uint64(len(fileIDtoMapping)) - fileIDtoMapping[fileID] = idx - locationMappingIndex = idx - - profile.Mapping = append(profile.Mapping, &profiles.Mapping{ - Filename: int64(getStringMapIndex(stringMap, "")), - BuildId: int64(getStringMapIndex(stringMap, - fileID.StringNoQuotes())), - BuildIdKind: *profiles.BuildIdKind_BUILD_ID_BINARY_HASH.Enum(), - }) + return tmpMappingIndex } - return locationMappingIndex + idx := uint64(len(fileIDtoMapping)) + fileIDtoMapping[fileID] = idx + + mappingAttributes := addProfileAttributes(profile, []attrKeyValue[string]{ + {key: "process.executable.build_id.htlhash", + value: fileID.StringNoQuotes()}}, attributeMap) + + profile.Mapping = append(profile.Mapping, &profiles.Mapping{ + Filename: int64(getStringMapIndex(stringMap, "")), + Attributes: mappingAttributes, + }) + return idx } // waitGrpcEndpoint waits until the gRPC connection is established. diff --git a/reporter/otlp_reporter_test.go b/reporter/otlp_reporter_test.go index bdfc22ed..42c64d04 100644 --- a/reporter/otlp_reporter_test.go +++ b/reporter/otlp_reporter_test.go @@ -30,26 +30,8 @@ func TestGetSampleAttributes(t *testing.T) { }, }, attributeMap: make(map[string]uint64), - expectedIndices: [][]uint64{{0, 1, 2, 3}}, + expectedIndices: [][]uint64{{0}}, expectedAttributeTable: []*common.KeyValue{ - { - Key: "container.id", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: ""}, - }, - }, - { - Key: "thread.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: ""}, - }, - }, - { - Key: "service.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: ""}, - }, - }, { Key: "process.pid", Value: &common.AnyValue{ diff --git a/stringutil/stringutil.go b/stringutil/stringutil.go index 3d8b4a88..06a19988 100644 --- a/stringutil/stringutil.go +++ b/stringutil/stringutil.go @@ -84,8 +84,3 @@ func SplitN(s, sep string, f []string) int { func ByteSlice2String(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } - -// StrDataPtr returns the string's underlying Data pointer through reflection. -func StrDataPtr(s string) *byte { - return unsafe.StringData(s) -} diff --git a/support/ebpf/extmaps.h b/support/ebpf/extmaps.h index 76a83f57..5922d9ed 100644 --- a/support/ebpf/extmaps.h +++ b/support/ebpf/extmaps.h @@ -38,6 +38,8 @@ extern bpf_map_def exe_id_to_18_stack_deltas; extern bpf_map_def exe_id_to_19_stack_deltas; extern bpf_map_def exe_id_to_20_stack_deltas; extern bpf_map_def exe_id_to_21_stack_deltas; +extern bpf_map_def exe_id_to_22_stack_deltas; +extern bpf_map_def exe_id_to_23_stack_deltas; extern bpf_map_def hotspot_procs; extern bpf_map_def kernel_stackmap; extern bpf_map_def dotnet_procs; diff --git a/support/ebpf/native_stack_trace.ebpf.c b/support/ebpf/native_stack_trace.ebpf.c index 0012e30e..959099cb 100644 --- a/support/ebpf/native_stack_trace.ebpf.c +++ b/support/ebpf/native_stack_trace.ebpf.c @@ -37,6 +37,8 @@ STACK_DELTA_BUCKET(18); STACK_DELTA_BUCKET(19); STACK_DELTA_BUCKET(20); STACK_DELTA_BUCKET(21); +STACK_DELTA_BUCKET(22); +STACK_DELTA_BUCKET(23); // Unwind info value for invalid stack delta #define STACK_DELTA_INVALID (STACK_DELTA_COMMAND_FLAG | UNWIND_COMMAND_INVALID) @@ -154,6 +156,8 @@ void *get_stack_delta_map(int mapID) { case 19: return &exe_id_to_19_stack_deltas; case 20: return &exe_id_to_20_stack_deltas; case 21: return &exe_id_to_21_stack_deltas; + case 22: return &exe_id_to_22_stack_deltas; + case 23: return &exe_id_to_23_stack_deltas; default: return NULL; } } diff --git a/support/ebpf/tracer.ebpf.release.amd64 b/support/ebpf/tracer.ebpf.release.amd64 index 9ff3eabb..2a8cbb2f 100644 Binary files a/support/ebpf/tracer.ebpf.release.amd64 and b/support/ebpf/tracer.ebpf.release.amd64 differ diff --git a/support/ebpf/tracer.ebpf.release.arm64 b/support/ebpf/tracer.ebpf.release.arm64 index 41971640..ecac9895 100644 Binary files a/support/ebpf/tracer.ebpf.release.arm64 and b/support/ebpf/tracer.ebpf.release.arm64 differ diff --git a/support/ebpf/types.h b/support/ebpf/types.h index bbf6882d..e5592ff8 100644 --- a/support/ebpf/types.h +++ b/support/ebpf/types.h @@ -831,8 +831,8 @@ void decode_bias_and_unwind_program(u64 bias_and_unwind_program, u64* bias, int* // Smallest stack delta bucket that holds up to 2^8 entries #define STACK_DELTA_BUCKET_SMALLEST 8 -// Largest stack delta bucket that holds up to 2^21 entries -#define STACK_DELTA_BUCKET_LARGEST 21 +// Largest stack delta bucket that holds up to 2^23 entries +#define STACK_DELTA_BUCKET_LARGEST 23 // Struct of the `system_config` map. Contains various configuration variables // determined and set by the host agent. diff --git a/tools/coredump/ebpfhelpers.go b/tools/coredump/ebpfhelpers.go index 3f3ef07a..f4088917 100644 --- a/tools/coredump/ebpfhelpers.go +++ b/tools/coredump/ebpfhelpers.go @@ -114,7 +114,8 @@ func __bpf_map_lookup_elem(id C.u64, mapdef *C.bpf_map_def, keyptr unsafe.Pointe &C.exe_id_to_11_stack_deltas, &C.exe_id_to_12_stack_deltas, &C.exe_id_to_13_stack_deltas, &C.exe_id_to_14_stack_deltas, &C.exe_id_to_15_stack_deltas, &C.exe_id_to_16_stack_deltas, &C.exe_id_to_17_stack_deltas, &C.exe_id_to_18_stack_deltas, &C.exe_id_to_19_stack_deltas, - &C.exe_id_to_20_stack_deltas, &C.exe_id_to_21_stack_deltas: + &C.exe_id_to_20_stack_deltas, &C.exe_id_to_21_stack_deltas, &C.exe_id_to_22_stack_deltas, + &C.exe_id_to_23_stack_deltas: ctx.stackDeltaFileID = *(*C.u64)(keyptr) return unsafe.Pointer(stackDeltaInnerMap) case &C.unwind_info_array: