diff --git a/interpreter/apmint/apmint.go b/interpreter/apmint/apmint.go index ddfe142a8..5e9967ffa 100644 --- a/interpreter/apmint/apmint.go +++ b/interpreter/apmint/apmint.go @@ -8,10 +8,11 @@ package apmint // import "go.opentelemetry.io/ebpf-profiler/interpreter/apmint" import ( - "encoding/hex" "errors" "fmt" + "hash/fnv" "regexp" + "strconv" "unsafe" log "github.com/sirupsen/logrus" @@ -144,31 +145,46 @@ type Instance struct { var _ interpreter.Instance = &Instance{} +// hashTrace calculates the hash of a trace and returns it. +// Be aware that changes to this calculation will break the ability to +// look backwards for the same TraceHash in our backend. +func hashTrace(trace *libpf.Trace) (hash [16]byte) { + var buf [24]byte + h := fnv.New128a() + for _, uniqueFrame := range trace.Frames { + frame := uniqueFrame.Value() + _, _ = h.Write(frame.FileID.Bytes()) + // Using FormatUint() or putting AppendUint() into a function leads + // to escaping to heap (allocation). + _, _ = h.Write(strconv.AppendUint(buf[:0], uint64(frame.AddressOrLineno), 10)) + } + h.Sum(hash[:0]) + return +} + // Detach implements the interpreter.Instance interface. func (i *Instance) Detach(ebpf interpreter.EbpfHandler, pid libpf.PID) error { return ebpf.DeleteProcData(libpf.APMInt, pid) } // NotifyAPMAgent sends out collected traces to the connected APM agent. -func (i *Instance) NotifyAPMAgent( - pid libpf.PID, rawTrace *host.Trace, umTraceHash libpf.TraceHash, count uint16) { +func (i *Instance) NotifyAPMAgent(pid libpf.PID, rawTrace *host.Trace, umTrace *libpf.Trace) { if rawTrace.APMTransactionID == libpf.InvalidAPMSpanID || i.socket == nil { return } - log.Debugf("Reporting %dx trace hash %s -> TX %s for PID %d", - count, umTraceHash.StringNoQuotes(), - hex.EncodeToString(rawTrace.APMTransactionID[:]), pid) - msg := traceCorrMsg{ MessageType: 1, MinorVersion: 1, APMTraceID: rawTrace.APMTraceID, APMTransactionID: rawTrace.APMTransactionID, - StackTraceID: umTraceHash, - Count: count, + StackTraceID: hashTrace(umTrace), + Count: 1, } + log.Debugf("Reporting a trace hash %x -> TX %x for PID %d", + msg.StackTraceID[:], rawTrace.APMTransactionID[:], pid) + if err := i.socket.SendMessage(msg.Serialize()); err != nil { log.Debugf("Failed to send trace mappings to APM agent: %v", err) } diff --git a/traceutil/traceutil_test.go b/interpreter/apmint/hash_test.go similarity index 68% rename from traceutil/traceutil_test.go rename to interpreter/apmint/hash_test.go index c1bc5d08c..fd3e2a258 100644 --- a/traceutil/traceutil_test.go +++ b/interpreter/apmint/hash_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package traceutil +package apmint // import "go.opentelemetry.io/ebpf-profiler/interpreter/apmint" import ( "testing" @@ -30,19 +30,23 @@ func newPythonTrace() *libpf.Trace { func TestHashTrace(t *testing.T) { tests := map[string]struct { trace *libpf.Trace - result libpf.TraceHash + result [16]byte }{ "empty trace": { - trace: &libpf.Trace{}, - result: libpf.NewTraceHash(0x6c62272e07bb0142, 0x62b821756295c58d)}, + trace: &libpf.Trace{}, + result: [16]uint8{0x6c, 0x62, 0x27, 0x2e, 0x7, 0xbb, 0x1, 0x42, + 0x62, 0xb8, 0x21, 0x75, 0x62, 0x95, 0xc5, 0x8d}, + }, "python trace": { - trace: newPythonTrace(), - result: libpf.NewTraceHash(0x21c6fe4c62868856, 0xcf510596eab68dc8)}, + trace: newPythonTrace(), + result: [16]byte{0x21, 0xc6, 0xfe, 0x4c, 0x62, 0x86, 0x88, 0x56, + 0xcf, 0x51, 0x5, 0x96, 0xea, 0xb6, 0x8d, 0xc8}, + }, } for name, testcase := range tests { t.Run(name, func(t *testing.T) { - assert.Equal(t, testcase.result, HashTrace(testcase.trace)) + assert.Equal(t, testcase.result, hashTrace(testcase.trace)) }) } } diff --git a/interpreter/apmint/socket.go b/interpreter/apmint/socket.go index 6172c4370..8f8c4eb29 100644 --- a/interpreter/apmint/socket.go +++ b/interpreter/apmint/socket.go @@ -103,7 +103,7 @@ type traceCorrMsg struct { MinorVersion uint16 APMTraceID libpf.APMTraceID APMTransactionID libpf.APMTransactionID - StackTraceID libpf.TraceHash + StackTraceID [16]byte Count uint16 } @@ -113,7 +113,7 @@ func (m *traceCorrMsg) Serialize() []byte { _ = binary.Write(&buf, binary.LittleEndian, m.MinorVersion) _, _ = buf.Write(m.APMTraceID[:]) _, _ = buf.Write(m.APMTransactionID[:]) - _, _ = buf.Write(m.StackTraceID.Bytes()) + _, _ = buf.Write(m.StackTraceID[:]) _ = binary.Write(&buf, binary.LittleEndian, m.Count) return buf.Bytes() } diff --git a/libpf/trace.go b/libpf/trace.go index bbae7c8d5..fda4b6221 100644 --- a/libpf/trace.go +++ b/libpf/trace.go @@ -41,7 +41,6 @@ func (frames *Frames) Append(frame *Frame) { // Trace represents a stack trace. type Trace struct { Frames Frames - Hash TraceHash CustomLabels map[string]string } diff --git a/libpf/tracehash.go b/libpf/tracehash.go deleted file mode 100644 index be2bee3b5..000000000 --- a/libpf/tracehash.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf" - -import ( - "encoding" - "encoding/base64" - - "go.opentelemetry.io/ebpf-profiler/libpf/basehash" -) - -// TraceHash represents the unique hash of a trace -type TraceHash struct { - basehash.Hash128 -} - -func NewTraceHash(hi, lo uint64) TraceHash { - return TraceHash{basehash.New128(hi, lo)} -} - -// TraceHashFromBytes parses a byte slice of a trace hash into the internal data representation. -func TraceHashFromBytes(b []byte) (TraceHash, error) { - h, err := basehash.New128FromBytes(b) - if err != nil { - return TraceHash{}, err - } - return TraceHash{h}, nil -} - -func (h TraceHash) Equal(other TraceHash) bool { - return h.Hash128.Equal(other.Hash128) -} - -func (h TraceHash) Less(other TraceHash) bool { - return h.Hash128.Less(other.Hash128) -} - -// EncodeTo encodes the hash into the base64 encoded representation -// and stores it in the provided destination byte array. -// The length of the destination must be at least EncodedLen(). -func (h TraceHash) EncodeTo(dst []byte) { - base64.RawURLEncoding.Encode(dst, h.Bytes()) -} - -// EncodedLen returns the length of the hash's base64 representation. -func (TraceHash) EncodedLen() int { - // TraceHash is 16 bytes long, the base64 representation is one base64 byte per 6 bits. - return ((16)*8)/6 + 1 -} - -// Hash32 returns a 32 bits hash of the input. -// It's main purpose is to be used for LRU caching. -func (h TraceHash) Hash32() uint32 { - return uint32(h.Lo()) -} - -// Compile-time interface checks -var _ encoding.TextUnmarshaler = (*TraceHash)(nil) -var _ encoding.TextMarshaler = (*TraceHash)(nil) diff --git a/libpf/tracehash_test.go b/libpf/tracehash_test.go deleted file mode 100644 index ed26df687..000000000 --- a/libpf/tracehash_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package libpf - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTraceHashSprintf(t *testing.T) { - origHash := NewTraceHash(0x0001C03F8D6B8520, 0xEDEAEEA9460BEEBB) - - marshaled := fmt.Sprintf("%d", origHash) - //nolint:goconst - expected := "{492854164817184 17143777342331285179}" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%s", origHash) - expected = "{%!s(uint64=492854164817184) %!s(uint64=17143777342331285179)}" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%v", origHash) - //nolint:goconst - expected = "{492854164817184 17143777342331285179}" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%#v", origHash) - expected = "0x1c03f8d6b8520edeaeea9460beebb" - assert.Equal(t, expected, marshaled) - - // Values were chosen to test non-zero-padded output - traceHash := NewTraceHash(42, 100) - - marshaled = fmt.Sprintf("%x", traceHash) - expected = "2a0000000000000064" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%X", traceHash) - expected = "2A0000000000000064" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%#x", traceHash) - expected = "0x2a0000000000000064" - assert.Equal(t, expected, marshaled) - - marshaled = fmt.Sprintf("%#X", traceHash) - expected = "0x2A0000000000000064" - assert.Equal(t, expected, marshaled) -} - -func TestTraceHashMarshal(t *testing.T) { - origHash := NewTraceHash(0x600DF00D, 0xF00D600D) - - // Test (Un)MarshalJSON - data, err := origHash.MarshalJSON() - require.NoError(t, err) - - marshaled := string(data) - expected := "\"00000000600df00d00000000f00d600d\"" - assert.Equal(t, expected, marshaled) - - var jsonHash TraceHash - err = jsonHash.UnmarshalJSON(data) - require.NoError(t, err) - assert.Equal(t, origHash, jsonHash) - - // Test (Un)MarshalText - data, err = origHash.MarshalText() - require.NoError(t, err) - - marshaled = string(data) - expected = "00000000600df00d00000000f00d600d" - assert.Equal(t, expected, marshaled) - - var textHash TraceHash - err = textHash.UnmarshalText(data) - require.NoError(t, err) - assert.Equal(t, origHash, textHash) -} diff --git a/processmanager/manager.go b/processmanager/manager.go index a33571ddf..71c9aedf4 100644 --- a/processmanager/manager.go +++ b/processmanager/manager.go @@ -26,7 +26,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/times" "go.opentelemetry.io/ebpf-profiler/tracer/types" - "go.opentelemetry.io/ebpf-profiler/traceutil" "go.opentelemetry.io/ebpf-profiler/util" ) @@ -298,12 +297,10 @@ func (pm *ProcessManager) ConvertTrace(trace *host.Trace) (newTrace *libpf.Trace } } } - newTrace.Hash = traceutil.HashTrace(newTrace) return newTrace } -func (pm *ProcessManager) MaybeNotifyAPMAgent( - rawTrace *host.Trace, umTraceHash libpf.TraceHash, count uint16) string { +func (pm *ProcessManager) MaybeNotifyAPMAgent(rawTrace *host.Trace, umTrace *libpf.Trace) string { pm.mu.RLock() pidInterp, ok := pm.interpreters[rawTrace.PID] pm.mu.RUnlock() @@ -314,7 +311,7 @@ func (pm *ProcessManager) MaybeNotifyAPMAgent( var serviceName string for _, mapping := range pidInterp { if apm, ok := mapping.(*apmint.Instance); ok { - apm.NotifyAPMAgent(rawTrace.PID, rawTrace, umTraceHash, count) + apm.NotifyAPMAgent(rawTrace.PID, rawTrace, umTrace) if serviceName != "" { log.Warnf("Overwriting APM service name from '%s' to '%s' for PID %d", diff --git a/processmanager/manager_test.go b/processmanager/manager_test.go index 51e0fb3c8..7e37a5204 100644 --- a/processmanager/manager_test.go +++ b/processmanager/manager_test.go @@ -29,7 +29,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/support" tracertypes "go.opentelemetry.io/ebpf-profiler/tracer/types" - "go.opentelemetry.io/ebpf-profiler/traceutil" "go.opentelemetry.io/ebpf-profiler/util" "github.com/stretchr/testify/assert" @@ -263,87 +262,6 @@ func (s *symbolReporterMockup) ExecutableMetadata(_ *reporter.ExecutableMetadata var _ reporter.SymbolReporter = (*symbolReporterMockup)(nil) -func TestInterpreterConvertTrace(t *testing.T) { - partialNativeFrameFileID := uint64(0xabcdbeef) - nativeFrameLineno := libpf.AddressOrLineno(0x1234) - - pythonAndNativeTrace := &host.Trace{ - Frames: []host.Frame{{ - // This represents a native frame - File: host.FileID(partialNativeFrameFileID), - Lineno: nativeFrameLineno, - Type: libpf.NativeFrame, - }, { - File: host.FileID(42), - Lineno: libpf.AddressOrLineno(0x13e1bb8e), // same as runForeverTrace - Type: libpf.PythonFrame, - }}, - } - - tests := map[string]struct { - trace *host.Trace - expect *libpf.Trace - }{ - "Convert Trace": { - trace: pythonAndNativeTrace, - expect: getExpectedTrace(pythonAndNativeTrace, - []libpf.AddressOrLineno{0, 1}), - }, - } - - for name, testcase := range tests { - t.Run(name, func(t *testing.T) { - mapper := NewMapFileIDMapper() - for i, f := range testcase.trace.Frames { - mapper.Set(f.File, testcase.expect.Frames[i].Value().FileID) - } - - // For this test do not include interpreters. - noIinterpreters, _ := tracertypes.Parse("") - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // To test ConvertTrace we do not require all parts of processmanager. - manager, err := New(ctx, - noIinterpreters, - 1*time.Second, - nil, - nil, - &symbolReporterMockup{}, - nil, - true, - libpf.Set[string]{}) - require.NoError(t, err) - - newTrace := manager.ConvertTrace(testcase.trace) - - testcase.expect.Hash = traceutil.HashTrace(testcase.expect) - if testcase.expect.Hash == newTrace.Hash { - assert.Equal(t, testcase.expect, newTrace) - } - }) - } -} - -// getExpectedTrace returns a new libpf trace that is based on the provided host trace, but -// with the linenos replaced by the provided values. This function is for generating an expected -// trace for tests below. -func getExpectedTrace(origTrace *host.Trace, linenos []libpf.AddressOrLineno) *libpf.Trace { - newTrace := &libpf.Trace{ - Hash: libpf.NewTraceHash(uint64(origTrace.Hash), uint64(origTrace.Hash)), - } - - for i, frame := range origTrace.Frames { - lineno := frame.Lineno - if linenos != nil { - lineno = linenos[i] - } - newTrace.AppendFrame(frame.Type, libpf.NewFileID(uint64(frame.File), 0), lineno) - } - return newTrace -} - func TestNewMapping(t *testing.T) { tests := map[string]struct { // newMapping holds the arguments that are passed to NewMapping() in the test. diff --git a/reporter/base_reporter.go b/reporter/base_reporter.go index aa380fafc..9bef0268a 100644 --- a/reporter/base_reporter.go +++ b/reporter/base_reporter.go @@ -89,7 +89,6 @@ func (b *baseReporter) ReportTraceEvent(trace *libpf.Trace, meta *samples.TraceE containerID := meta.ContainerID key := samples.TraceAndMetaKey{ - Hash: trace.Hash, Comm: meta.Comm, ProcessName: meta.ProcessName, ExecutablePath: meta.ExecutablePath, diff --git a/reporter/samples/attrmgr_test.go b/reporter/samples/attrmgr_test.go index 5b76ebb59..c02706e1b 100644 --- a/reporter/samples/attrmgr_test.go +++ b/reporter/samples/attrmgr_test.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pprofile" - "go.opentelemetry.io/ebpf-profiler/libpf" semconv "go.opentelemetry.io/otel/semconv/v1.34.0" ) @@ -29,7 +28,6 @@ func TestAttrTableManager(t *testing.T) { "empty": { k: []TraceAndMetaKey{ { - Hash: libpf.TraceHash{}, Comm: "", ApmServiceName: "", ContainerID: "", @@ -44,14 +42,12 @@ func TestAttrTableManager(t *testing.T) { "duplicate": { k: []TraceAndMetaKey{ { - Hash: libpf.TraceHash{}, Comm: "comm1", ApmServiceName: "apmServiceName1", ContainerID: "containerID1", Pid: 1234, }, { - Hash: libpf.TraceHash{}, Comm: "comm1", ApmServiceName: "apmServiceName1", ContainerID: "containerID1", @@ -69,14 +65,12 @@ func TestAttrTableManager(t *testing.T) { "different": { k: []TraceAndMetaKey{ { - Hash: libpf.TraceHash{}, Comm: "comm1", ApmServiceName: "apmServiceName1", ContainerID: "containerID1", Pid: 1234, }, { - Hash: libpf.TraceHash{}, Comm: "comm2", ApmServiceName: "apmServiceName2", ContainerID: "containerID2", diff --git a/reporter/samples/samples.go b/reporter/samples/samples.go index 834b44971..9851986d6 100644 --- a/reporter/samples/samples.go +++ b/reporter/samples/samples.go @@ -33,7 +33,6 @@ type TraceEvents struct { // contain all trace fields that aren't already part of the trace hash to ensure // that we don't accidentally merge traces with different fields. type TraceAndMetaKey struct { - Hash libpf.TraceHash // comm and apmServiceName are provided by the eBPF programs Comm string ApmServiceName string diff --git a/tracehandler/tracehandler.go b/tracehandler/tracehandler.go index e89ae1f54..97230b584 100644 --- a/tracehandler/tracehandler.go +++ b/tracehandler/tracehandler.go @@ -41,7 +41,7 @@ type TraceProcessor interface { // MaybeNotifyAPMAgent notifies a potentially existing connected APM agent // that a stack trace was collected in their process. If an APM agent is // listening, the service name is returned. - MaybeNotifyAPMAgent(rawTrace *host.Trace, umTraceHash libpf.TraceHash, count uint16) string + MaybeNotifyAPMAgent(rawTrace *host.Trace, umTrace *libpf.Trace) string // ConvertTrace converts a trace from eBPF into the form we want to send to // the collection agent. Depending on the frame type it will attempt to symbolize @@ -135,7 +135,7 @@ func (m *traceHandler) HandleTrace(bpfTrace *host.Trace) { traceCacheLifetime); exists { m.traceCacheHit++ // Fast path - meta.APMServiceName = m.traceProcessor.MaybeNotifyAPMAgent(bpfTrace, trace.Hash, 1) + meta.APMServiceName = m.traceProcessor.MaybeNotifyAPMAgent(bpfTrace, &trace) if err := m.reporter.ReportTraceEvent(&trace, meta); err != nil { log.Errorf("Failed to report trace event: %v", err) } @@ -147,7 +147,7 @@ func (m *traceHandler) HandleTrace(bpfTrace *host.Trace) { umTrace := m.traceProcessor.ConvertTrace(bpfTrace) m.traceCache.Add(bpfTrace.Hash, *umTrace) - meta.APMServiceName = m.traceProcessor.MaybeNotifyAPMAgent(bpfTrace, umTrace.Hash, 1) + meta.APMServiceName = m.traceProcessor.MaybeNotifyAPMAgent(bpfTrace, umTrace) if err := m.reporter.ReportTraceEvent(umTrace, meta); err != nil { log.Errorf("Failed to report trace event: %v", err) } diff --git a/tracehandler/tracehandler_test.go b/tracehandler/tracehandler_test.go index 5c726cb3c..71f1887c3 100644 --- a/tracehandler/tracehandler_test.go +++ b/tracehandler/tracehandler_test.go @@ -35,14 +35,14 @@ type fakeTraceProcessor struct{} var _ tracehandler.TraceProcessor = (*fakeTraceProcessor)(nil) func (f *fakeTraceProcessor) ConvertTrace(trace *host.Trace) *libpf.Trace { - var newTrace libpf.Trace - newTrace.Hash = libpf.NewTraceHash(uint64(trace.Hash), uint64(trace.Hash)) - return &newTrace + converted := &libpf.Trace{} + converted.Frames.Append(&libpf.Frame{AddressOrLineno: libpf.AddressOrLineno(trace.Hash)}) + return converted } func (f *fakeTraceProcessor) ProcessedUntil(times.KTime) {} -func (f *fakeTraceProcessor) MaybeNotifyAPMAgent(*host.Trace, libpf.TraceHash, uint16) string { +func (f *fakeTraceProcessor) MaybeNotifyAPMAgent(*host.Trace, *libpf.Trace) string { return "" } @@ -54,15 +54,16 @@ type arguments struct { type mockReporter struct { t *testing.T - reports map[libpf.TraceHash]uint16 + reports map[host.TraceHash]uint16 } func (m *mockReporter) ReportTraceEvent(trace *libpf.Trace, _ *samples.TraceEventMeta) error { - if _, exists := m.reports[trace.Hash]; exists { - m.reports[trace.Hash]++ + hash := host.TraceHash(trace.Frames[0].Value().AddressOrLineno) + if _, exists := m.reports[hash]; exists { + m.reports[hash]++ return nil } - m.reports[trace.Hash] = 1 + m.reports[hash] = 1 return nil } @@ -71,7 +72,7 @@ func TestTraceHandler(t *testing.T) { tests := map[string]struct { input []arguments expireTimeout time.Duration - expectedEvents map[libpf.TraceHash]uint16 + expectedEvents map[host.TraceHash]uint16 }{ // no input simulates a case where no data is provided as input // to the functions of traceHandler. @@ -81,8 +82,8 @@ func TestTraceHandler(t *testing.T) { "single trace": {input: []arguments{ {trace: &host.Trace{Hash: host.TraceHash(0x1234)}}, }, - expectedEvents: map[libpf.TraceHash]uint16{ - libpf.NewTraceHash(0x1234, 0x1234): 1, + expectedEvents: map[host.TraceHash]uint16{ + 0x1234: 1, }, }, @@ -91,8 +92,8 @@ func TestTraceHandler(t *testing.T) { {trace: &host.Trace{Hash: host.TraceHash(4)}}, {trace: &host.Trace{Hash: host.TraceHash(4)}}, }, - expectedEvents: map[libpf.TraceHash]uint16{ - libpf.NewTraceHash(4, 4): 2, + expectedEvents: map[host.TraceHash]uint16{ + 4: 2, }, }, } @@ -101,7 +102,7 @@ func TestTraceHandler(t *testing.T) { t.Run(name, func(t *testing.T) { r := &mockReporter{ t: t, - reports: make(map[libpf.TraceHash]uint16), + reports: make(map[host.TraceHash]uint16), } traceChan := make(chan *host.Trace) diff --git a/traceutil/traceutil.go b/traceutil/traceutil.go deleted file mode 100644 index fead3a987..000000000 --- a/traceutil/traceutil.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package traceutil // import "go.opentelemetry.io/ebpf-profiler/traceutil" - -import ( - "hash/fnv" - "strconv" - - "go.opentelemetry.io/ebpf-profiler/libpf" -) - -// HashTrace calculates the hash of a trace and returns it. -// Be aware that changes to this calculation will break the ability to -// look backwards for the same TraceHash in our backend. -func HashTrace(trace *libpf.Trace) libpf.TraceHash { - var buf [24]byte - h := fnv.New128a() - for _, uniqueFrame := range trace.Frames { - frame := uniqueFrame.Value() - _, _ = h.Write(frame.FileID.Bytes()) - // Using FormatUint() or putting AppendUint() into a function leads - // to escaping to heap (allocation). - _, _ = h.Write(strconv.AppendUint(buf[:0], uint64(frame.AddressOrLineno), 10)) - } - // make instead of nil avoids a heap allocation - traceHash, _ := libpf.TraceHashFromBytes(h.Sum(make([]byte, 0, 16))) - return traceHash -}