From 3b5dc98a06b50ab0fa41eec41874fb0886838e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Sat, 19 Jul 2025 10:35:49 +0300 Subject: [PATCH 1/3] drop use of CGO in profiler core (#628) --- Makefile | 7 +- interpreter/customlabels/customlabels.go | 12 +- interpreter/golabels/golabels.go | 8 +- interpreter/golabels/runtime_data.go | 34 +-- interpreter/hotspot/instance.go | 61 ++-- interpreter/luajit/luajit.go | 22 +- interpreter/nodev8/v8.go | 133 ++++----- interpreter/perl/data.go | 5 +- interpreter/perl/instance.go | 66 ++--- interpreter/python/python.go | 48 ++- metrics/metrics.go | 18 +- processmanager/ebpf/ebpf.go | 162 +++++------ reporter/iface.go | 4 - support/ebpf/tracer.ebpf.amd64 | Bin 3059320 -> 3059336 bytes support/ebpf/tracer.ebpf.arm64 | Bin 3036520 -> 3036536 bytes support/ebpf/types.h | 14 +- support/ebpf/v8_tracer.ebpf.c | 12 +- support/ebpf/v8_tracer.h | 12 +- support/types.go | 353 +++++++++++++++++++++++ support/types_def.go | 181 ++++++++++++ tracer/events.go | 12 +- tracer/systemconfig.go | 50 ++-- tracer/tracer.go | 213 ++++---------- 23 files changed, 903 insertions(+), 524 deletions(-) diff --git a/Makefile b/Makefile index cb2223a06..42c87ad25 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ $(error Unsupported architecture: $(TARGET_ARCH)) endif export TARGET_ARCH -export CGO_ENABLED = 1 +export CGO_ENABLED = 0 export GOARCH = $(TARGET_ARCH) export CC = $(ARCH_PREFIX)-linux-gnu-gcc export OBJCOPY = $(ARCH_PREFIX)-linux-gnu-objcopy @@ -103,7 +103,8 @@ vanity-import-fix: $(PORTO) @porto --include-internal -w . test: generate ebpf test-deps - go test $(GO_FLAGS) -tags $(GO_TAGS) ./... + # tools/coredump tests build ebpf C-code using CGO to test it against coredumps + CGO_ENABLED=1 go test $(GO_FLAGS) -tags $(GO_TAGS) ./... # This target isn't called from CI, it doesn't work for cross compile (ie TARGET_ARCH=arm64 on # amd64) and the CI kernel tests run them already. Useful for local testing. @@ -130,7 +131,7 @@ support/golbls_1_24.test: ./interpreter/golabels/test/main.go CGO_ENABLED=0 GOTOOLCHAIN=go1.24.1 go build -tags $(GO_TAGS),nocgo -o $@ $< support/golbls_cgo.test: ./interpreter/golabels/test/main-cgo.go - GOTOOLCHAIN=go1.24.1 go build -ldflags '-extldflags "-static"' -tags $(GO_TAGS),usecgo -o $@ $< + CGO_ENABLED=1 GOTOOLCHAIN=go1.24.1 go build -ldflags '-extldflags "-static"' -tags $(GO_TAGS),usecgo -o $@ $< integration-test-binaries: generate ebpf rust-components support/golbls_1_23.test support/golbls_1_24.test support/golbls_cgo.test $(foreach test_name, $(TEST_INTEGRATION_BINARY_DIRS), \ diff --git a/interpreter/customlabels/customlabels.go b/interpreter/customlabels/customlabels.go index 7f57069b7..29f7e457b 100644 --- a/interpreter/customlabels/customlabels.go +++ b/interpreter/customlabels/customlabels.go @@ -1,8 +1,5 @@ package customlabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/customlabels" -// #include -// #include "../../support/ebpf/types.h" -import "C" import ( "errors" "fmt" @@ -13,6 +10,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/remotememory" + "go.opentelemetry.io/ebpf-profiler/support" ) const ( @@ -127,10 +125,10 @@ func (d data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, currentHmTlsOffset = rm.Uint64(bias + d.currentHmTlsAddr + 8) } - procInfo := C.NativeCustomLabelsProcInfo{ - current_set_tls_offset: C.u64(currentSetTlsOffset), - has_current_hm: C.bool(d.hasCurrentHm), - current_hm_tls_offset: C.u64(currentHmTlsOffset), + procInfo := support.NativeCustomLabelsProcInfo{ + Current_set_tls_offset: currentSetTlsOffset, + Has_current_hm: d.hasCurrentHm, + Current_hm_tls_offset: currentHmTlsOffset, } if err := ebpf.UpdateProcData(libpf.CustomLabels, pid, unsafe.Pointer(&procInfo)); err != nil { return nil, err diff --git a/interpreter/golabels/golabels.go b/interpreter/golabels/golabels.go index 6a8583add..ae8ef1c52 100644 --- a/interpreter/golabels/golabels.go +++ b/interpreter/golabels/golabels.go @@ -13,14 +13,12 @@ import ( "go.opentelemetry.io/ebpf-profiler/interpreter" "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/remotememory" + "go.opentelemetry.io/ebpf-profiler/support" ) -// #include "../../support/ebpf/types.h" -import "C" - type data struct { goVersion string - offsets C.GoLabelsOffsets + offsets support.GoLabelsOffsets interpreter.InstanceStubs } @@ -64,7 +62,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete if err != nil { return nil, fmt.Errorf("failed to extract TLS offset: %w", err) } - offsets.tls_offset = C.s32(tlsOffset) + offsets.Tls_offset = tlsOffset return &data{ goVersion: goVersion, diff --git a/interpreter/golabels/runtime_data.go b/interpreter/golabels/runtime_data.go index fd6b2ae3f..a36b8e4cc 100644 --- a/interpreter/golabels/runtime_data.go +++ b/interpreter/golabels/runtime_data.go @@ -3,49 +3,49 @@ package golabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels" -// #include "../../support/ebpf/types.h" -import "C" import ( "go/version" + + "go.opentelemetry.io/ebpf-profiler/support" ) // Offsets come from DWARF debug information, use tools/gooffsets to extract them. // However since DWARF information can be stripped we record them here. // TODO: Should we look for DWARF information to support new versions // automatically when available? -func getOffsets(vers string) C.GoLabelsOffsets { - offsets := C.GoLabelsOffsets{ +func getOffsets(vers string) support.GoLabelsOffsets { + offsets := support.GoLabelsOffsets{ // https://github.com/golang/go/blob/80e2e474b8d9124d03b744f/src/runtime/runtime2.go#L410 - m_offset: 48, + M_offset: 48, // https://github.com/golang/go/blob/80e2e474b8d9124d03b744f/src/runtime/runtime2.go#L541 - curg: 192, + Curg: 192, // https://github.com/golang/go/blob/80e2e474b8d9124d03b744f/src/runtime/runtime2.go#L483 - labels: 0, + Labels: 0, // https://github.com/golang/go/blob/6885bad7dd86880be6929c0/src/runtime/map.go#L112 - hmap_count: 0, + Hmap_count: 0, // https://github.com/golang/go/blob/6885bad7dd86880be6929c0/src/runtime/map.go#L114 - hmap_log2_bucket_count: 0, + Hmap_log2_bucket_count: 0, // https://github.com/golang/go/blob/6885bad7dd86880be6929c0/src/runtime/map.go#L118 - hmap_buckets: 0, + Hmap_buckets: 0, } // Version enforcement takes place in the Loader function. if version.Compare(vers, "go1.24") >= 0 { - offsets.labels = 352 + offsets.Labels = 352 return offsets } // These are the same for all versions but we have to leave them zero for 1.24+ detection. - offsets.hmap_log2_bucket_count = 9 - offsets.hmap_buckets = 16 + offsets.Hmap_log2_bucket_count = 9 + offsets.Hmap_buckets = 16 if version.Compare(vers, "go1.23") >= 0 { - offsets.labels = 352 + offsets.Labels = 352 } else if version.Compare(vers, "go1.21") >= 0 { - offsets.labels = 344 + offsets.Labels = 344 } else if version.Compare(vers, "go1.17") >= 0 { - offsets.labels = 360 + offsets.Labels = 360 } else { - offsets.labels = 344 + offsets.Labels = 344 } return offsets } diff --git a/interpreter/hotspot/instance.go b/interpreter/hotspot/instance.go index 44a8c4c15..f68f0b83e 100644 --- a/interpreter/hotspot/instance.go +++ b/interpreter/hotspot/instance.go @@ -31,10 +31,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/util" ) -// #include "../../support/ebpf/types.h" -// #include "../../support/ebpf/frametypes.h" -import "C" - // heapRange contains info for an individual heap. type heapRange struct { codeStart, codeEnd libpf.Address @@ -237,9 +233,9 @@ func (d *hotspotInstance) getPoolSymbol(addr libpf.Address, ndx uint16) libpf.St // getStubNameID read the stub name from the code blob at given address and generates a ID. func (d *hotspotInstance) getStubNameID(symbolReporter reporter.SymbolReporter, ripOrBci uint32, - addr libpf.Address, _ uint32) (libpf.AddressOrLineno, error) { + addr libpf.Address, _ uint32) libpf.AddressOrLineno { if value, ok := d.addrToStubNameID.Get(addr); ok { - return value, nil + return value } vms := &d.d.Get().vmStructs constStubNameAddr := d.rm.Ptr(addr + libpf.Address(vms.CodeBlob.Name)) @@ -263,7 +259,7 @@ func (d *hotspotInstance) getStubNameID(symbolReporter reporter.SymbolReporter, }) d.addrToStubNameID.Add(addr, stubID) - return stubID, nil + return stubID } // getMethod reads and returns the interesting data from "class Method" at given address @@ -745,31 +741,31 @@ func (d *hotspotInstance) populateMainMappings(vmd *hotspotVMData, // Set up the main eBPF info structure. vms := &vmd.vmStructs - procInfo := C.HotspotProcInfo{ - nmethod_deopt_offset: C.u16(vms.Nmethod.DeoptimizeOffset), - nmethod_compileid: C.u16(vms.Nmethod.CompileID), - nmethod_orig_pc_offset: C.u16(vms.Nmethod.OrigPcOffset), - codeblob_name: C.u8(vms.CodeBlob.Name), - codeblob_codestart: C.u8(vms.CodeBlob.CodeBegin), - codeblob_codeend: C.u8(vms.CodeBlob.CodeEnd), - codeblob_framecomplete: C.u8(vms.CodeBlob.FrameCompleteOffset), - codeblob_framesize: C.u8(vms.CodeBlob.FrameSize), - cmethod_size: C.u8(vms.ConstMethod.Sizeof), - heapblock_size: C.u8(vms.HeapBlock.Sizeof), - method_constmethod: C.u8(vms.Method.ConstMethod), - jvm_version: C.u8(vmd.version >> 24), - segment_shift: C.u8(heap.segmentShift), - nmethod_uses_offsets: C.u8(vmd.nmethodUsesOffsets), + procInfo := support.HotspotProcInfo{ + Nmethod_deopt_offset: uint16(vms.Nmethod.DeoptimizeOffset), + Nmethod_compileid: uint16(vms.Nmethod.CompileID), + Nmethod_orig_pc_offset: uint16(vms.Nmethod.OrigPcOffset), + Codeblob_name: uint8(vms.CodeBlob.Name), + Codeblob_codestart: uint8(vms.CodeBlob.CodeBegin), + Codeblob_codeend: uint8(vms.CodeBlob.CodeEnd), + Codeblob_framecomplete: uint8(vms.CodeBlob.FrameCompleteOffset), + Codeblob_framesize: uint8(vms.CodeBlob.FrameSize), + Cmethod_size: uint8(vms.ConstMethod.Sizeof), + Heapblock_size: uint8(vms.HeapBlock.Sizeof), + Method_constmethod: uint8(vms.Method.ConstMethod), + Jvm_version: uint8(vmd.version >> 24), + Segment_shift: uint8(heap.segmentShift), + Nmethod_uses_offsets: vmd.nmethodUsesOffsets, } if vms.CodeCache.LowBound == 0 { // JDK-8 has only one heap, use its bounds - procInfo.codecache_start = C.u64(heap.ranges[0].codeStart) - procInfo.codecache_end = C.u64(heap.ranges[0].codeEnd) + procInfo.Codecache_start = uint64(heap.ranges[0].codeStart) + procInfo.Codecache_end = uint64(heap.ranges[0].codeEnd) } else { // JDK9+ the VM tracks it separately - procInfo.codecache_start = C.u64(d.rm.Ptr(vms.CodeCache.LowBound + d.bias)) - procInfo.codecache_end = C.u64(d.rm.Ptr(vms.CodeCache.HighBound + d.bias)) + procInfo.Codecache_start = uint64(d.rm.Ptr(vms.CodeCache.LowBound + d.bias)) + procInfo.Codecache_end = uint64(d.rm.Ptr(vms.CodeCache.HighBound + d.bias)) } if err = ebpf.UpdateProcData(libpf.HotSpot, pid, unsafe.Pointer(&procInfo)); err != nil { @@ -875,22 +871,19 @@ func (d *hotspotInstance) Symbolize(symbolReporter reporter.SymbolReporter, sfCounter := successfailurecounter.New(&d.successCount, &d.failCount) defer sfCounter.DefaultToFailure() - switch subtype { - case C.FRAME_HOTSPOT_STUB, C.FRAME_HOTSPOT_VTABLE: + switch uint8(subtype) { + case support.FrameHotspotStub, support.FrameHotspotVtable: // These are stub frames that may or may not be interesting // to be seen in the trace. - stubID, err1 := d.getStubNameID(symbolReporter, ripOrBci, ptr, ptrCheck) - if err1 != nil { - return err - } + stubID := d.getStubNameID(symbolReporter, ripOrBci, ptr, ptrCheck) trace.AppendFrame(libpf.HotSpotFrame, hotspotStubsFileID, stubID) - case C.FRAME_HOTSPOT_INTERPRETER: + case support.FrameHotspotInterpreter: method, err1 := d.getMethod(ptr, ptrCheck) if err1 != nil { return err1 } method.symbolize(symbolReporter, ripOrBci, d, trace) - case C.FRAME_HOTSPOT_NATIVE: + case support.FrameHotspotNative: jitinfo, err1 := d.getJITInfo(ptr, ptrCheck) if err1 != nil { return err1 diff --git a/interpreter/luajit/luajit.go b/interpreter/luajit/luajit.go index 0d1572c25..35cc41c5b 100644 --- a/interpreter/luajit/luajit.go +++ b/interpreter/luajit/luajit.go @@ -34,12 +34,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/util" ) -// #include "../../support/ebpf/types.h" -// #include "../../support/ebpf/luajit.h" -import "C" - -const LuaJITFFIFunc = C.LUAJIT_FFI_FUNC - // Records all the "global" pointers we've seen. type vmMap map[libpf.Address]struct{} @@ -92,10 +86,10 @@ var ( func (d *luajitData) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, _ libpf.Address, rm remotememory.RemoteMemory) (interpreter.Instance, error) { - cdata := C.LuaJITProcInfo{ - g2dispatch: C.u16(d.g2Dispatch), - cur_L_offset: C.u16(d.currentLOffset), - cframe_size_jit: C.u16(cframeSizeJIT), + cdata := support.LuaJITProcInfo{ + G2dispatch: d.g2Dispatch, + Cur_L_offset: d.currentLOffset, + Cframe_size_jit: uint16(cframeSizeJIT), } if err := ebpf.UpdateProcData(libpf.LuaJIT, pid, unsafe.Pointer(&cdata)); err != nil { return nil, err @@ -210,7 +204,7 @@ func (l *luajitInstance) addJITRegion(ebpf interpreter.EbpfHandler, pid libpf.PI logf("lj: add JIT region pid(%v) %#x:%#x", pid, start, end) for _, prefix := range prefixes { // TODO: fix these: WARN[0267] Failed to lookup file ID 0x2a00000000 - fileID := uint64(C.LUAJIT_JIT_FILE_ID) << 32 + fileID := support.LJFileId << 32 if err := ebpf.UpdatePidInterpreterMapping(pid, prefix, support.ProgUnwindLuaJIT, host.FileID(fileID), 0); err != nil { return err @@ -231,7 +225,7 @@ func (l *luajitInstance) addTrace(ebpf interpreter.EbpfHandler, pid libpf.PID, t } logf("lj: add trace mapping for pid(%v) %x:%x", pid, start, end) for _, prefix := range prefixes { - fileID := uint64(C.LUAJIT_JIT_FILE_ID)<<32 | spadjust + fileID := support.LJFileId<<32 | spadjust if err := ebpf.UpdatePidInterpreterMapping(pid, prefix, support.ProgUnwindLuaJIT, host.FileID(fileID), g); err != nil { return nil, err @@ -383,7 +377,7 @@ func (l *luajitInstance) symbolizeFrame(symbolReporter reporter.SymbolReporter, frameID libpf.FrameID) error { var line uint32 var fileName string - if ptAddr != C.LUAJIT_FFI_FUNC { + if ptAddr != support.LJFFIFunc { pt, err := l.getGCproto(ptAddr) if err != nil { return err @@ -438,7 +432,7 @@ func (l *luajitInstance) Symbolize(symbolReporter reporter.SymbolReporter, frame } var funcName string - if frame.File == C.LUAJIT_FFI_FUNC { + if frame.File == support.LJFFIFunc { switch frame.Lineno & 7 { case 0: funcName = "lua-frame" diff --git a/interpreter/nodev8/v8.go b/interpreter/nodev8/v8.go index 332e713e5..3468082aa 100644 --- a/interpreter/nodev8/v8.go +++ b/interpreter/nodev8/v8.go @@ -187,19 +187,15 @@ import ( "go.opentelemetry.io/ebpf-profiler/util" ) -// #include "../../support/ebpf/types.h" -// #include "../../support/ebpf/v8_tracer.h" -import "C" - const ( // Use build-time constants for the HeapObject/SMI Tags for code size and speed. // They are unlikely to change, and likely require larger modifications on change. - SmiTag = C.SmiTag - SmiTagMask = C.SmiTagMask - SmiTagShift = C.SmiTagShift - SmiValueShift = C.SmiValueShift - HeapObjectTag = C.HeapObjectTag - HeapObjectTagMask = C.HeapObjectTagMask + SmiTag = support.V8SmiTag + SmiTagMask = support.V8SmiTagMask + SmiTagShift = support.V8SmiTagShift + SmiValueShift = support.V8SmiValueShift + HeapObjectTag = support.V8HeapObjectTag + HeapObjectTagMask = support.V8HeapObjectTagMask // The largest possible identifier for V8 frame type (marker) MaxFrameType = 64 @@ -952,7 +948,7 @@ func (i *v8Instance) getStringPtr(ptr libpf.Address) (libpf.String, error) { // analyzeScopeInfo reads and heuristically analyzes V8 ScopeInfo data. It tries to // extract the function name, and its start and end line. func (i *v8Instance) analyzeScopeInfo(ptr libpf.Address) (name libpf.String, - startPos, endPos int, err error) { + startPos, endPos int) { vms := &i.d.vmStructs var data libpf.Address if vms.ScopeInfo.HeapObject { @@ -972,8 +968,8 @@ func (i *v8Instance) analyzeScopeInfo(ptr libpf.Address) (name libpf.String, const numSlots = 16 const slotSize = pointerSize slotData := make([]byte, numSlots*slotSize) - if err = i.rm.Read(data, slotData); err != nil { - return libpf.NullString, 0, 0, nil + if err := i.rm.Read(data, slotData); err != nil { + return libpf.NullString, 0, 0 } // Skip reserved slots and the context locals @@ -997,12 +993,12 @@ func (i *v8Instance) analyzeScopeInfo(ptr libpf.Address) (name libpf.String, startPos = int(decodeSMI(prev)) endPos = int(decodeSMI(cur)) if startPos < endPos { - return name, startPos, endPos, nil + return name, startPos, endPos } } prev = cur } - return name, 0, 0, nil + return name, 0, 0 } // readFixedTable reads the data of a FixedArray object. @@ -1039,9 +1035,9 @@ func (i *v8Instance) readFixedTablePtr(taggedPtr libpf.Address, tag uint16, } // getSource reads and caches needed V8 Source object data. -func (i *v8Instance) getSource(addr libpf.Address) (*v8Source, error) { +func (i *v8Instance) getSource(addr libpf.Address) *v8Source { if value, ok := i.addrToSource.Get(addr); ok { - return value, nil + return value } vms := &i.d.vmStructs @@ -1089,7 +1085,7 @@ func (i *v8Instance) getSource(addr libpf.Address) (*v8Source, error) { } i.addrToSource.Add(addr, src) - return src, nil + return src } // getSFI reads and caches needed V8 SharedFunctionInfo object data. @@ -1116,7 +1112,7 @@ func (i *v8Instance) getSFI(taggedPtr libpf.Address) (*v8SFI, error) { } switch { case nosType == vms.Type.ScopeInfo: - sfi.funcName, sfi.funcStartPos, sfi.funcEndPos, err = i.analyzeScopeInfo(nosAddr) + sfi.funcName, sfi.funcStartPos, sfi.funcEndPos = i.analyzeScopeInfo(nosAddr) case nosType < vms.Fixed.FirstNonstringType: sfi.funcName, err = i.getString(nosAddr, nosType) } @@ -1161,7 +1157,7 @@ func (i *v8Instance) getSFI(taggedPtr libpf.Address) (*v8SFI, error) { sodiAddr, sodiType, _ := i.readObjectPtr(addr + libpf.Address(vms.SharedFunctionInfo.ScriptOrDebugInfo)) if sodiType == vms.Type.Script { - sfi.source, _ = i.getSource(sodiAddr) + sfi.source = i.getSource(sodiAddr) if sfi.funcStartPos != sfi.funcEndPos { sfi.funcStartLine = mapPositionToLine(sfi.source.lineTable, int32(sfi.funcStartPos)) @@ -1591,7 +1587,7 @@ func (i *v8Instance) symbolizeSFI(symbolReporter reporter.SymbolReporter, pointe // Adjust the bytecode pointer as needed //nolint:lll // https://chromium.googlesource.com/v8/v8.git/+/refs/tags/9.2.230.1/src/execution/frames.cc#1793 - bytecodeDelta := int64(delta & C.V8_LINE_DELTA_MASK) + bytecodeDelta := int64(delta & support.V8LineDeltaMask) bytecodeDelta -= int64(vms.BytecodeArray.Data) - HeapObjectTag if bytecodeDelta < 0 { // Should not be happening @@ -1707,7 +1703,7 @@ func (i *v8Instance) symbolizeCode(symbolReporter reporter.SymbolReporter, code delta uint64, trace *libpf.Trace) error { var err error sfi := code.sfi - delta &= C.V8_LINE_DELTA_MASK + delta &= support.V8LineDeltaMask // This is a native PC delta and points to the instruction after // the call function. Adjust to get the CALL instruction. @@ -1775,22 +1771,22 @@ func (i *v8Instance) Symbolize(symbolReporter reporter.SymbolReporter, pointerAndType := libpf.Address(frame.File) deltaOrMarker := uint64(frame.Lineno) - frameType := pointerAndType & C.V8_FILE_TYPE_MASK - pointer := pointerAndType&^C.V8_FILE_TYPE_MASK | HeapObjectTag + frameType := pointerAndType & support.V8FileTypeMask + pointer := pointerAndType&^support.V8FileTypeMask | HeapObjectTag var err error switch frameType { - case C.V8_FILE_TYPE_MARKER: + case support.V8FileTypeMarker: // This is a stub V8 frame, with deltaOrMarker containing the marker. // Convert the V8 build specific marker ID to a static ID and symbolize // that if needed. err = i.symbolizeMarkerFrame(symbolReporter, deltaOrMarker, trace) - case C.V8_FILE_TYPE_BYTECODE, C.V8_FILE_TYPE_NATIVE_SFI: + case support.V8FileTypeByteCode, support.V8FileTypeNativeSFI: err = i.symbolizeSFI(symbolReporter, pointer, deltaOrMarker, trace) - case C.V8_FILE_TYPE_NATIVE_CODE, C.V8_FILE_TYPE_NATIVE_JSFUNC: + case support.V8FileTypeNativeCode, support.V8FileTypeNativeJSFunc: var code *v8Code - codeCookie := uint32(deltaOrMarker & C.V8_LINE_COOKIE_MASK >> C.V8_LINE_COOKIE_SHIFT) - if frameType == C.V8_FILE_TYPE_NATIVE_CODE { + codeCookie := uint32(deltaOrMarker & support.V8LineCookieMask >> support.V8LineCookieShift) + if frameType == support.V8FileTypeNativeCode { code, err = i.getCode(pointer, codeCookie) } else { code, err = i.getCodeFromJSFunc(pointer, codeCookie) @@ -1817,49 +1813,48 @@ func (d *v8Data) String() string { // mapFramePointerOffset converts the frame pointer offset in bytes to eBPF used // word offset relative to the number of slots read -func mapFramePointerOffset(relBytes uint8) C.u8 { - slotOffset := int(C.V8_FP_CONTEXT_SIZE) + int(int8(relBytes)) - if slotOffset < 0 || slotOffset > C.V8_FP_CONTEXT_SIZE-pointerSize { - return C.V8_FP_CONTEXT_SIZE +func mapFramePointerOffset(relBytes uint8) uint8 { + slotOffset := int(support.V8FpContextSize) + int(int8(relBytes)) + if slotOffset < 0 || slotOffset > support.V8FpContextSize-pointerSize { + return support.V8FpContextSize } - return C.u8(slotOffset) + return uint8(slotOffset) } func (d *v8Data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, _ libpf.Address, rm remotememory.RemoteMemory) (interpreter.Instance, error) { vms := &d.vmStructs - data := C.V8ProcInfo{ - version: C.uint(d.version), - - context_handle_offset: C.uint(d.contextHandleOffset), - native_context_offset: C.uint(d.nativeContextOffset), - embedder_data_offset: C.uint(d.embedderDataOffset), - environment_pointer_offset: C.uint(d.environmentPointerOffset), - execution_async_id_offset: C.uint(d.executionAsyncIdOffset), - - fp_marker: mapFramePointerOffset(vms.FramePointer.Context), - fp_function: mapFramePointerOffset(vms.FramePointer.Function), - fp_bytecode_offset: mapFramePointerOffset(vms.FramePointer.BytecodeOffset), - - type_JSFunction_first: C.u16(vms.Fixed.FirstJSFunctionType), - type_JSFunction_last: C.u16(vms.Fixed.LastJSFunctionType), - type_Code: C.u16(vms.Type.Code), - type_SharedFunctionInfo: C.u16(vms.Type.SharedFunctionInfo), - - off_HeapObject_map: C.u8(vms.HeapObject.Map), - off_Map_instancetype: C.u8(vms.Map.InstanceType), - off_JSFunction_code: C.u8(vms.JSFunction.Code), - off_JSFunction_shared: C.u8(vms.JSFunction.SharedFunctionInfo), - - off_Code_instruction_start: C.u8(vms.Code.InstructionStart), - off_Code_instruction_size: C.u8(vms.Code.InstructionSize), - off_Code_flags: C.u8(vms.Code.Flags), - - codekind_shift: C.u8(vms.CodeKind.FieldShift), - codekind_mask: C.u8(vms.CodeKind.FieldMask), - codekind_baseline: C.u8(vms.CodeKind.Baseline), - - isolate_sym: C.u64(d.isolateSym), + data := support.V8ProcInfo{ + Version: d.version, + + Context_handle_offset: d.contextHandleOffset, + Native_context_offset: d.nativeContextOffset, + Embedder_data_offset: d.embedderDataOffset, + Environment_pointer_offset: d.environmentPointerOffset, + Execution_async_id_offset: d.executionAsyncIdOffset, + + Fp_marker: mapFramePointerOffset(vms.FramePointer.Context), + Fp_function: mapFramePointerOffset(vms.FramePointer.Function), + Fp_bytecode_offset: mapFramePointerOffset(vms.FramePointer.BytecodeOffset), + + Type_JSFunction_first: vms.Fixed.FirstJSFunctionType, + Type_JSFunction_last: vms.Fixed.LastJSFunctionType, + Type_Code: vms.Type.Code, + Type_SharedFunctionInfo: vms.Type.SharedFunctionInfo, + + Off_HeapObject_map: uint8(vms.HeapObject.Map), + Off_Map_instancetype: uint8(vms.Map.InstanceType), + Off_JSFunction_code: uint8(vms.JSFunction.Code), + Off_JSFunction_shared: uint8(vms.JSFunction.SharedFunctionInfo), + + Off_Code_instruction_start: uint8(vms.Code.InstructionStart), + Off_Code_instruction_size: uint8(vms.Code.InstructionSize), + Off_Code_flags: uint8(vms.Code.Flags), + + Codekind_shift: vms.CodeKind.FieldShift, + Codekind_mask: uint8(vms.CodeKind.FieldMask), + Codekind_baseline: vms.CodeKind.Baseline, + Isolate_sym: uint64(d.isolateSym), } if err := ebpf.UpdateProcData(libpf.V8, pid, unsafe.Pointer(&data)); err != nil { return nil, err @@ -2292,9 +2287,9 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr return nil, errors.New("incompatible tagging scheme") } - if mapFramePointerOffset(vms.FramePointer.Context) >= C.V8_FP_CONTEXT_SIZE || - mapFramePointerOffset(vms.FramePointer.Function) >= C.V8_FP_CONTEXT_SIZE || - mapFramePointerOffset(vms.FramePointer.BytecodeOffset) >= C.V8_FP_CONTEXT_SIZE { + if mapFramePointerOffset(vms.FramePointer.Context) >= support.V8FpContextSize || + mapFramePointerOffset(vms.FramePointer.Function) >= support.V8FpContextSize || + mapFramePointerOffset(vms.FramePointer.BytecodeOffset) >= support.V8FpContextSize { return nil, fmt.Errorf("incompatible framepointer offsets (%d/%d/%d)", vms.FramePointer.Context, vms.FramePointer.Function, vms.FramePointer.BytecodeOffset) diff --git a/interpreter/perl/data.go b/interpreter/perl/data.go index d77ed5fcc..af30cc882 100644 --- a/interpreter/perl/data.go +++ b/interpreter/perl/data.go @@ -17,9 +17,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/support" ) -// #include "../../support/ebpf/types.h" -import "C" - type perlData struct { // vmStructs reflects the Perl internal class names and the offsets of named field // The struct names are based on the Perl C "struct name", the alternate typedef seen @@ -146,7 +143,7 @@ func (d *perlData) Attach(_ interpreter.EbpfHandler, _ libpf.PID, bias libpf.Add return &perlInstance{ d: d, rm: rm, - bias: C.u64(bias), + bias: bias, addrToHEK: addrToHEK, addrToCOP: addrToCOP, addrToGV: addrToGV, diff --git a/interpreter/perl/instance.go b/interpreter/perl/instance.go index 9e92bc3e2..11cd6aa3f 100644 --- a/interpreter/perl/instance.go +++ b/interpreter/perl/instance.go @@ -23,13 +23,11 @@ import ( "go.opentelemetry.io/ebpf-profiler/remotememory" "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/successfailurecounter" + "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/tpbase" "go.opentelemetry.io/ebpf-profiler/util" ) -// #include "../../support/ebpf/types.h" -import "C" - type perlInstance struct { interpreter.InstanceStubs @@ -39,7 +37,7 @@ type perlInstance struct { d *perlData rm remotememory.RemoteMemory - bias C.u64 + bias libpf.Address // addrToHEK maps a PERL Hash Element Key (string with hash) to a Go string addrToHEK *freelru.LRU[libpf.Address, string] @@ -84,42 +82,42 @@ func hashCOPKey(k copKey) uint32 { func (i *perlInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID, tsdInfo tpbase.TSDInfo) error { d := i.d - stateInTSD := C.u8(0) + stateInTSD := uint8(0) if d.stateInTSD { stateInTSD = 1 } vms := &d.vmStructs - data := C.PerlProcInfo{ - version: C.uint(d.version), - stateAddr: C.u64(d.stateAddr) + i.bias, - stateInTSD: stateInTSD, - - tsdInfo: C.TSDInfo{ - offset: C.s16(tsdInfo.Offset), - multiplier: C.u8(tsdInfo.Multiplier), - indirect: C.u8(tsdInfo.Indirect), + data := support.PerlProcInfo{ + Version: d.version, + StateAddr: uint64(d.stateAddr) + uint64(i.bias), + StateInTSD: stateInTSD, + + TsdInfo: support.TSDInfo{ + Offset: tsdInfo.Offset, + Multiplier: tsdInfo.Multiplier, + Indirect: tsdInfo.Indirect, }, - interpreter_curcop: C.u16(vms.interpreter.curcop), - interpreter_curstackinfo: C.u16(vms.interpreter.curstackinfo), - - si_cxstack: C.u8(vms.stackinfo.si_cxstack), - si_next: C.u8(vms.stackinfo.si_next), - si_cxix: C.u8(vms.stackinfo.si_cxix), - si_type: C.u8(vms.stackinfo.si_type), - - context_type: C.u8(vms.context.cx_type), - context_blk_oldcop: C.u8(vms.context.blk_oldcop), - context_blk_sub_retop: C.u8(vms.context.blk_sub_retop), - context_blk_sub_cv: C.u8(vms.context.blk_sub_cv), - context_sizeof: C.u8(vms.context.sizeof), - - sv_flags: C.u8(vms.sv.sv_flags), - sv_any: C.u8(vms.sv.sv_any), - svu_gp: C.u8(vms.sv.svu_gp), - xcv_flags: C.u8(vms.xpvcv.xcv_flags), - xcv_gv: C.u8(vms.xpvcv.xcv_gv), - gp_egv: C.u8(vms.gp.gp_egv), + Interpreter_curcop: uint16(vms.interpreter.curcop), + Interpreter_curstackinfo: uint16(vms.interpreter.curstackinfo), + + Si_cxstack: uint8(vms.stackinfo.si_cxstack), + Si_next: uint8(vms.stackinfo.si_next), + Si_cxix: uint8(vms.stackinfo.si_cxix), + Si_type: uint8(vms.stackinfo.si_type), + + Context_type: uint8(vms.context.cx_type), + Context_blk_oldcop: uint8(vms.context.blk_oldcop), + Context_blk_sub_retop: uint8(vms.context.blk_sub_retop), + Context_blk_sub_cv: uint8(vms.context.blk_sub_cv), + Context_sizeof: uint8(vms.context.sizeof), + + Sv_flags: uint8(vms.sv.sv_flags), + Sv_any: uint8(vms.sv.sv_any), + Svu_gp: uint8(vms.sv.svu_gp), + Xcv_flags: uint8(vms.xpvcv.xcv_flags), + Xcv_gv: uint8(vms.xpvcv.xcv_gv), + Gp_egv: uint8(vms.gp.gp_egv), } err := ebpf.UpdateProcData(libpf.Perl, pid, unsafe.Pointer(&data)) diff --git a/interpreter/python/python.go b/interpreter/python/python.go index 6bea8fa42..3b2cace3f 100644 --- a/interpreter/python/python.go +++ b/interpreter/python/python.go @@ -38,10 +38,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/util" ) -// #include -// #include "../../support/ebpf/types.h" -import "C" - // The following regexs are intended to match either a path to a Python binary or // library. var ( @@ -135,7 +131,7 @@ func (d *pythonData) Attach(_ interpreter.EbpfHandler, _ libpf.PID, bias libpf.A i := &pythonInstance{ d: d, rm: rm, - bias: C.u64(bias), + bias: bias, addrToCodeObject: addrToCodeObject, } @@ -349,7 +345,7 @@ type pythonInstance struct { d *pythonData rm remotememory.RemoteMemory - bias C.u64 + bias libpf.Address // addrToCodeObject maps a Python Code object to a pythonCodeObject which caches // the needed data from it. @@ -400,28 +396,28 @@ func (p *pythonInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.P tsdInfo tpbase.TSDInfo) error { d := p.d vm := &d.vmStructs - cdata := C.PyProcInfo{ - autoTLSKeyAddr: C.u64(d.autoTLSKey) + p.bias, - version: C.u16(d.version), - - tsdInfo: C.TSDInfo{ - offset: C.s16(tsdInfo.Offset), - multiplier: C.u8(tsdInfo.Multiplier), - indirect: C.u8(tsdInfo.Indirect), + cdata := support.PyProcInfo{ + AutoTLSKeyAddr: uint64(d.autoTLSKey) + uint64(p.bias), + Version: d.version, + + TsdInfo: support.TSDInfo{ + Offset: tsdInfo.Offset, + Multiplier: tsdInfo.Multiplier, + Indirect: tsdInfo.Indirect, }, - PyThreadState_frame: C.u8(vm.PyThreadState.Frame), - PyCFrame_current_frame: C.u8(vm.PyCFrame.CurrentFrame), - PyFrameObject_f_back: C.u8(vm.PyFrameObject.Back), - PyFrameObject_f_code: C.u8(vm.PyFrameObject.Code), - PyFrameObject_f_lasti: C.u8(vm.PyFrameObject.LastI), - PyFrameObject_entry_member: C.u8(vm.PyFrameObject.EntryMember), - PyFrameObject_entry_val: C.u8(vm.PyFrameObject.EntryVal), - PyCodeObject_co_argcount: C.u8(vm.PyCodeObject.ArgCount), - PyCodeObject_co_kwonlyargcount: C.u8(vm.PyCodeObject.KwOnlyArgCount), - PyCodeObject_co_flags: C.u8(vm.PyCodeObject.Flags), - PyCodeObject_co_firstlineno: C.u8(vm.PyCodeObject.FirstLineno), - PyCodeObject_sizeof: C.u8(vm.PyCodeObject.Sizeof), + PyThreadState_frame: uint8(vm.PyThreadState.Frame), + PyCFrame_current_frame: uint8(vm.PyCFrame.CurrentFrame), + PyFrameObject_f_back: uint8(vm.PyFrameObject.Back), + PyFrameObject_f_code: uint8(vm.PyFrameObject.Code), + PyFrameObject_f_lasti: uint8(vm.PyFrameObject.LastI), + PyFrameObject_entry_member: uint8(vm.PyFrameObject.EntryMember), + PyFrameObject_entry_val: uint8(vm.PyFrameObject.EntryVal), + PyCodeObject_co_argcount: uint8(vm.PyCodeObject.ArgCount), + PyCodeObject_co_kwonlyargcount: uint8(vm.PyCodeObject.KwOnlyArgCount), + PyCodeObject_co_flags: uint8(vm.PyCodeObject.Flags), + PyCodeObject_co_firstlineno: uint8(vm.PyCodeObject.FirstLineno), + PyCodeObject_sizeof: uint8(vm.PyCodeObject.Sizeof), } err := ebpf.UpdateProcData(libpf.Python, pid, unsafe.Pointer(&cdata)) diff --git a/metrics/metrics.go b/metrics/metrics.go index 31678a5b6..4cb2a4b80 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -10,20 +10,19 @@ import ( "encoding/json" "fmt" "sync" + "time" log "github.com/sirupsen/logrus" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/ebpf-profiler/libpf" - "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/vc" ) var ( // prevTimestamp holds the timestamp of the buffered metrics - prevTimestamp libpf.UnixTime32 + prevTimestamp uint32 // metricsBuffer buffers the metricsBuffer for the timestamp assigned to prevTimestamp metricsBuffer = make([]Metric, IDMax) @@ -50,10 +49,15 @@ var ( counters = map[MetricID]metric.Int64Counter{} gauges = map[MetricID]metric.Int64Gauge{} - reporterImpl reporter.MetricsReporter + reporterImpl MetricsReporter ) -func SetReporter(r reporter.MetricsReporter) { +// MetricsReporter is the interface for reporting metrics +type MetricsReporter interface { + ReportMetrics(timestamp uint32, ids []uint32, values []int64) +} + +func SetReporter(r MetricsReporter) { reporterImpl = r } @@ -102,7 +106,7 @@ var report = func() { ids[i] = uint32(metricsBuffer[i].ID) values[i] = int64(metricsBuffer[i].Value) } - reporterImpl.ReportMetrics(uint32(prevTimestamp), ids, values) + reporterImpl.ReportMetrics(prevTimestamp, ids, values) } for i := range nMetrics { metric := metricsBuffer[i] @@ -140,7 +144,7 @@ var report = func() { // This ensures that the buffered metrics from the previous timestamp are sent // with the correctly assigned TSMetric.Timestamp. func AddSlice(newMetrics []Metric) { - now := libpf.UnixTime32(libpf.NowAsUInt32()) + now := uint32(time.Now().Unix()) mutex.Lock() defer mutex.Unlock() diff --git a/processmanager/ebpf/ebpf.go b/processmanager/ebpf/ebpf.go index ec7dc3f88..c32b07e9c 100644 --- a/processmanager/ebpf/ebpf.go +++ b/processmanager/ebpf/ebpf.go @@ -26,12 +26,6 @@ import ( "golang.org/x/exp/constraints" ) -/* -#include -#include "../../support/ebpf/types.h" -*/ -import "C" - const ( // updatePoolWorkers decides how many background workers we spawn to // process map-in-map updates. @@ -295,10 +289,10 @@ func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID } func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, - offsetRanges []util.Range) (key uint64, value C.OffsetRange, err error) { + offsetRanges []util.Range) (key uint64, value support.OffsetRange, err error) { rLen := len(offsetRanges) if rLen < 1 || rLen > 2 { - return 0, C.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) + return 0, support.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) } // The keys of this map are executable-id-and-offset-into-text entries, and // the offset_range associated with them gives the precise area in that page @@ -306,10 +300,10 @@ func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, // nicely from native code into interpreted code. key = uint64(fileID) first := offsetRanges[0] - value = C.OffsetRange{ - lower_offset1: C.u64(first.Start), - upper_offset1: C.u64(first.End), - program_index: C.u16(ebpfProgIndex), + value = support.OffsetRange{ + Lower_offset1: first.Start, + Upper_offset1: first.End, + Program_index: ebpfProgIndex, } if len(offsetRanges) == 2 { // Fields {lower,upper}_offset2 may be used to specify an optional second range @@ -317,8 +311,8 @@ func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, // consists of two non-contiguous memory ranges, which may happen due to Hot/Cold // split compiler optimization second := offsetRanges[1] - value.lower_offset2 = C.u64(second.Start) - value.upper_offset2 = C.u64(second.End) + value.Lower_offset2 = second.Start + value.Upper_offset2 = second.End } return key, value, nil } @@ -398,19 +392,19 @@ func (impl *ebpfMapsImpl) UpdatePidInterpreterMapping(pid libpf.PID, prefix lpm. bePid := bits.ReverseBytes32(uint32(pid)) bePage := bits.ReverseBytes64(prefix.Key) - cKey := C.PIDPage{ - prefixLen: C.u32(support.BitWidthPID + prefix.Length), - pid: C.u32(bePid), - page: C.u64(bePage), + cKey := support.PIDPage{ + PrefixLen: support.BitWidthPID + prefix.Length, + Pid: bePid, + Page: bePage, } biasAndUnwindProgram, err := support.EncodeBiasAndUnwindProgram(bias, interpreterProgram) if err != nil { return err } - cValue := C.PIDPageMappingInfo{ - file_id: C.u64(fileID), - bias_and_unwind_program: C.u64(biasAndUnwindProgram), + cValue := support.PIDPageMappingInfo{ + File_id: uint64(fileID), + Bias_and_unwind_program: biasAndUnwindProgram, } return impl.pidPageToMappingInfo.Update(unsafe.Pointer(&cKey), unsafe.Pointer(&cValue), @@ -427,10 +421,10 @@ func (impl *ebpfMapsImpl) DeletePidInterpreterMapping(pid libpf.PID, prefix lpm. bePid := bits.ReverseBytes32(uint32(pid)) bePage := bits.ReverseBytes64(prefix.Key) - cKey := C.PIDPage{ - prefixLen: C.u32(support.BitWidthPID + prefix.Length), - pid: C.u32(bePid), - page: C.u64(bePage), + cKey := support.PIDPage{ + PrefixLen: support.BitWidthPID + prefix.Length, + Pid: bePid, + Page: bePage, } return impl.pidPageToMappingInfo.Delete(unsafe.Pointer(&cKey)) } @@ -464,29 +458,29 @@ func (impl *ebpfMapsImpl) CollectMetrics() []metrics.Metric { return counts } -// poolPIDPage caches reusable heap-allocated C.PIDPage instances +// poolPIDPage caches reusable heap-allocated support.PIDPage instances // to avoid excessive heap allocations. var poolPIDPage = sync.Pool{ New: func() any { - return new(C.PIDPage) + return new(support.PIDPage) }, } -// getPIDPage initializes a C.PIDPage instance. -func getPIDPage(pid libpf.PID, prefix lpm.Prefix) C.PIDPage { +// getPIDPage initializes a support.PIDPage instance. +func getPIDPage(pid libpf.PID, prefix lpm.Prefix) support.PIDPage { // pid_page_to_mapping_info is an LPM trie and expects the pid and page // to be in big endian format. - return C.PIDPage{ - pid: C.u32(bits.ReverseBytes32(uint32(pid))), - page: C.u64(bits.ReverseBytes64(prefix.Key)), - prefixLen: C.u32(support.BitWidthPID + prefix.Length), + return support.PIDPage{ + Pid: bits.ReverseBytes32(uint32(pid)), + Page: bits.ReverseBytes64(prefix.Key), + PrefixLen: support.BitWidthPID + prefix.Length, } } -// getPIDPagePooled returns a heap-allocated and initialized C.PIDPage instance. +// getPIDPagePooled returns a heap-allocated and initialized support.PIDPage instance. // After usage, put the instance back into the pool with poolPIDPage.Put(). -func getPIDPagePooled(pid libpf.PID, prefix lpm.Prefix) *C.PIDPage { - cPIDPage := poolPIDPage.Get().(*C.PIDPage) +func getPIDPagePooled(pid libpf.PID, prefix lpm.Prefix) *support.PIDPage { + cPIDPage := poolPIDPage.Get().(*support.PIDPage) *cPIDPage = getPIDPage(pid, prefix) return cPIDPage } @@ -495,16 +489,16 @@ func getPIDPagePooled(pid libpf.PID, prefix lpm.Prefix) *C.PIDPage { // to avoid excessive heap allocations. var poolPIDPageMappingInfo = sync.Pool{ New: func() any { - return new(C.PIDPageMappingInfo) + return new(support.PIDPageMappingInfo) }, } -// getPIDPageMappingInfo returns a heap-allocated and initialized C.PIDPageMappingInfo instance. +// getPIDPageMappingInfo returns a heap-allocated and initialized PIDPageMappingInfo instance. // After usage, put the instance back into the pool with poolPIDPageMappingInfo.Put(). -func getPIDPageMappingInfo(fileID, biasAndUnwindProgram uint64) *C.PIDPageMappingInfo { - cInfo := poolPIDPageMappingInfo.Get().(*C.PIDPageMappingInfo) - cInfo.file_id = C.u64(fileID) - cInfo.bias_and_unwind_program = C.u64(biasAndUnwindProgram) +func getPIDPageMappingInfo(fileID, biasAndUnwindProgram uint64) *support.PIDPageMappingInfo { + cInfo := poolPIDPageMappingInfo.Get().(*support.PIDPageMappingInfo) + cInfo.File_id = fileID + cInfo.Bias_and_unwind_program = biasAndUnwindProgram return cInfo } @@ -609,13 +603,13 @@ func (impl *ebpfMapsImpl) UpdateUnwindInfo(index uint16, info sdtypes.UnwindInfo index, impl.unwindInfoArray.MaxEntries()) } - key := C.u32(index) - value := C.UnwindInfo{ - opcode: C.u8(info.Opcode), - fpOpcode: C.u8(info.FPOpcode), - mergeOpcode: C.u8(info.MergeOpcode), - param: C.s32(info.Param), - fpParam: C.s32(info.FPParam), + key := uint32(index) + value := support.UnwindInfo{ + Opcode: info.Opcode, + FpOpcode: info.FPOpcode, + MergeOpcode: info.MergeOpcode, + Param: info.Param, + FpParam: info.FPParam, } return impl.trackMapError(metrics.IDUnwindInfoArrayUpdate, impl.unwindInfoArray.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), @@ -633,9 +627,6 @@ func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas [] } outerMap := impl.getOuterMap(mapID) - keySize := uint32(C.sizeof_uint32_t) - valueSize := uint32(C.sizeof_StackDelta) - restoreRlimit, err := rlimit.MaximizeMemlock() if err != nil { return 0, fmt.Errorf("failed to increase rlimit: %v", err) @@ -643,8 +634,8 @@ func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas [] defer restoreRlimit() innerMap, err := cebpf.NewMap(&cebpf.MapSpec{ Type: cebpf.Array, - KeySize: keySize, - ValueSize: valueSize, + KeySize: 4, + ValueSize: support.Sizeof_StackDelta, MaxEntries: 1 << mapID, }) if err != nil { @@ -671,18 +662,18 @@ func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas [] if impl.hasGenericBatchOperations { innerKeys := make([]uint32, numDeltas) - stackDeltas := make([]C.StackDelta, numDeltas) + stackDeltas := make([]support.StackDelta, numDeltas) // Prepare values for batch update. for index, delta := range deltas { innerKeys[index] = uint32(index) - stackDeltas[index].addrLow = C.uint16_t(delta.AddressLow) - stackDeltas[index].unwindInfo = C.uint16_t(delta.UnwindInfo) + stackDeltas[index].AddrLow = delta.AddressLow + stackDeltas[index].UnwindInfo = delta.UnwindInfo } _, err := innerMap.BatchUpdate( ptrCastMarshaler[uint32](innerKeys), - ptrCastMarshaler[C.StackDelta](stackDeltas), + ptrCastMarshaler[support.StackDelta](stackDeltas), &cebpf.BatchOptions{Flags: uint64(cebpf.UpdateAny)}) if err != nil { return 0, impl.trackMapError(metrics.IDExeIDToStackDeltasBatchUpdate, @@ -694,10 +685,10 @@ func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas [] } innerKey := uint32(0) - stackDelta := C.StackDelta{} + stackDelta := support.StackDelta{} for index, delta := range deltas { - stackDelta.addrLow = C.uint16_t(delta.AddressLow) - stackDelta.unwindInfo = C.uint16_t(delta.UnwindInfo) + stackDelta.AddrLow = delta.AddressLow + stackDelta.UnwindInfo = delta.UnwindInfo innerKey = uint32(index) if err := innerMap.Update(unsafe.Pointer(&innerKey), unsafe.Pointer(&stackDelta), cebpf.UpdateAny); err != nil { @@ -729,28 +720,28 @@ func (impl *ebpfMapsImpl) DeleteExeIDToStackDeltas(fileID host.FileID, mapID uin func (impl *ebpfMapsImpl) UpdateStackDeltaPages(fileID host.FileID, numDeltasPerPage []uint16, mapID uint16, firstPageAddr uint64) error { firstDelta := uint32(0) - keys := make([]C.StackDeltaPageKey, len(numDeltasPerPage)) - values := make([]C.StackDeltaPageInfo, len(numDeltasPerPage)) + keys := make([]support.StackDeltaPageKey, len(numDeltasPerPage)) + values := make([]support.StackDeltaPageInfo, len(numDeltasPerPage)) // Prepare the key/value combinations that will be loaded. for pageNumber, numDeltas := range numDeltasPerPage { pageAddr := firstPageAddr + uint64(pageNumber)<M_5HdsTkCepa_v=wjzoI zkxf|aH9C%@5xiNjF(|qbXl!@Yj>WYS=yJ-SbV( z+^YQ#{@O>?o0reMC-2^OPVW7l+`QWipZa#Fa_HNkYGNY%xm#xzXF`iJ6N@vGi!)P; zGaD9Xj#`}AxHuDDoY}NE^OD7xqZen6S)4g`apt(inU^lkL>6bJ7iTtqV`j_muMb_j zcKQdm{CK*Q|E2;0Duc0&lw=A#Y^!m&4Iz_L)Ca-h! zdY`;5)9c6Nb)8=Sm|oL=p(*+&PRCQf^-6T|j(Z>d6QTnhxK>`j&>i@d?Z9#GAGPPV z?}c~2NDcm%GfqFyi23yk;MCF)VZB5eEIJK-^o48ii1yy~oQ>LXH%o&zNC&{5cDUj2qYgji@P9fS z8BjwMHC%tDC)is z)Bum%a~wYZ|LD+NFGzde7QxZr^hEGcUU=dBt0HzyGaQUUTu4 zAN39VlC#~Ig+sWs??t(8o<9Qb)CcJvs^P*3C9`hN*4dLgB zH%^7TrsCs?+hB9?TVvZw7ydJul|x=%iZ3NT`%@ursQ66c$R^ti>Z8J+<@diTH zGvv)Hej{;8@#}~)KGR2v-@pc0W$;qsg5r(D72#*eKKh{vuc~-6ab58oaYONi#H)%o z5;vJk6uH;^i|3tNpYU2@z&uObRm}B0VYqzY@K6{o_l~w6fy*Pp@OXmw5BQ^VJ2iOb z{S#hN446+NPAT3ev zUrqdKZ_-OEo+HjF=K8!aZog>x91jI$F~I#-gmM2h)SJC;k8Qj3tJLns@TAv~V&*7u zPchf`g?~!*8%~<^ykqTsfFodY`0cSvLbK-LY;5OfToq{Q;Je1SDJ$l2Ra4C4t}cw5 zT6MT7jGJr;<0d#c!|Vo%kynYag5RqPD>2t;F-f@1*)ODwAGP@p;5);V<+1%QQL^ZzawP z|BmWUtxkGn;qAZnyvaK$zCM>c4+Ra!Ah*?e_8?}=Rz`qtV$K8*y~JJt3F zf*cm+Pb3R+R52@I0&I3&r35)G#TY4B#XM39ig~0Ig)vep4p)USP-?;$C=JUKUf&$P zibe`_c=a@UJJ8{_!(E39r(1ityQrAmT~W;5t|{jFy5*7|4t94_8Sqf+2t$Xvidl#I zidlyTidlz;idl!fGi-+;!(pFGR+m|a7@kf#oDzno)57p{R@HOnQxJv>mzhs{ z+Uhdva77ug4%dYbnnPFnI((Q;BOUHZEp&L`aAAkNU+8dI`08I+9j+>79d0OQ9bOg2 z?YAtCr{uO6;Ql+pxc?sN&4Fu_4o4HVU(n%%V%Fh#Vd!q!;VjrJKQfyBs>~ooTO!n+yx%CZm?eI-C##=x`crcE4Zsvn9poXGbyjv#*%@ zIS@uay;s?Ofy2V+XG9qNj9cbmKYMNLtWiEt6g%VtWntt471W!(*T%MOM?O%OV&nr2 z;U#n6S|z{jS=&pqDGU_zoDiNHzn|xXgfQecshH(AV|gsUIWb^4X8A1& zLw?JOS$?a+kl%)4mfuxj$ZuO1YTNUfCKVJ69D~ML_KqOGEr;6uFyuF`nB{j~G0SgKG0SgCG0SgS81kF(S;=qSF(?Sb(`AP%!tiue7@n@H zdY0d&Fyyz*tmU_(3|M~q!UxUCH~R9sK&O%XCN8x8g8U{OZVE$w+rp6Fu40zofnt{5 zp)hVg{KtNKWBHBP0{;sjue?NsasLU_n`4VgesfX``7J1B`7H`Veya}G!RD5tFTV$h zO4hvBTd!a^MHKUJN+{;xG%t+1OgWquMvybYxXGO5vHTXq0Pd-O*Oe?&t`^9X-qAp!dZ9{ul_u zA70wtSKl0av(n+nMYb3^99PVQ9F3u?C$)O+&9WEu^I1pu;U; z=x|#x>u^^XIy_L!Iy@AH4oCjP-XLT*;j_}=q+>7;h7O0{XfFVcINTP74iAK(!{ImC z_k-iiT8Gqgscvm)SQ$hYN~XhbzL+;hJLB;Z?<~!(CwrLr*b>c3^p|!`|C% z2k_YlE9Th8!RF9k_&U6Bo7Um#<@W8+;hHdiB3XwUidl!7U~}kWN{72rjFHk;%p=8n zhiwM+VPTAvsKaq#43va021?R0>+mg$N{3VLwCzBLvkvDS_TDx29xq3S6|=jeig~0Z z6m$K&<*^Q@l>ra6oG^4aub6eXsF-!QteADUqL_8KDhwU2`K)w!)iG!a!_#etJHqgE zR~Vk|t9sVq_%7=)%#jIZULQwjlVZTkq0cDhNir*p4&@y#2%|$qVRWdX>bXM=;u5dj zV#utD0Y*eiF~_i@m}A&g%p+!?m_rzTx4mOz9#O^oy+8hL?|6Ulm<>4dNQ%Khb8*Gb zJa*G*lz9}T7MVxI;qVoxpA!Q(I4=wb7gaqw zxFw8;w}lb#u3`@OKrsh==yS;%bs(IxcZeQFgwdg>Fglb__1vMfFan-Y%mK|Q=71NN zIaS0bqNofw;#IJj{iMpZT2hQ$tD~56t*$U~t%1YdmG(Y=YL-74yJQ+UP5M2yc#k>! z$=J@(SNWzCBd2KzBd2Mj-t69^zRLHc7&%Q}c*(5niCs~G#Lr)48)asRPYYwAJFA$# zX%~c{@I}R}@D*Vwd|fdse8Y0dgT%MQ021GIxbJZNeYV3;^rpithl^KRdnkO}=X1vk z*-bHk#CIGnUSlsXySA{G6#hyqbca7+%c1ab#jNm2VJLiBF)MsdF)MsQc-E}n5!+Ae z(kq|Qg50^mHU~lO3iDuKK^`b(LH0gu?VmRn-=ze3UWzeDQ;Kn791`L zV~m!CF-EJF$3GM|!~hG_O<^c=$KjsC)f?@7VUX7p^B`|1=0V={nPwRb@|H5-LEcr& z)A~U8*`HHGq^v{5tjuA0dW&U+>sgs2idmVXidmWC%=(Aogcz_?^8$5BG5=7U7KY2S z4(Ej7^1LuyUR3owEv^##LWqgFDF*lqv=#Gbpr@EW1ASq1Xy~wall2HX6c$E@qBqgg zi|jHs;2tH#05MD{<``xaa}0BexjwI$Ls(YKA*=~QnHx9lus!4gmgZGuz{=bfK4|vV zd}Ust(@2@aAF=O-GDjV*2}7A1!cgX>VpiskVpis^F#OxMJXYp`7{I|pVK_K^^Ed$P z-;^){o)$*Hvx+(31;rfjqR%vD(4&eNfUCmjP)!&eYN&edP+J%Q?9{H%frUYN%K z3vyX83vv}~_I^nTa!ZOaHam)WZ1xrN*c=FBD0+9;Zh*tW7@84b49&RZu^=bK0D_zr zh9Ku0E;tV#?kHwK?h3=jeTN6aaQRReE)UbAwp#vD#778nS`6?R$SU^dXT|>f zEQ}6S9IgtZLp5P^sBxzw$Zat|3_FVb`B||)KP%??p<;h}zH9uM;1DK+A;`(Q96?Sg z0~X}0@IiB+;S2IQokoIOlUfLJ!{NkdZSxT1q%Z_It(XNlrR;L zEhy%ImzlL7SCj!qybd-i4=6$IN-+evub2gSAPhkc*X)ghqhNFLgG!Kde`U?~m|GrH zg4~f}2y#~#g4{#BS$|Lo@=%H)$ll%d>Pu$!AtlHeDTW~DgdxZU#Vp8WVF+?XF$;1{ z7=pa2m<73Mc`V2sF@PZV9UeNodXK#i2y)xuj>DDDS$hcbs?Vp61-LB+_^R9kn_E_t zAUE!{*MT6n6tf`rgdxa%#Vp9-&)fHNJqvO|c-G85>DGX<&9nJ{DSy^E?D{pz6y;Q^i6IoR;Ph@q)Jdv#` z=K7{$p2s?h{nY?rBp*Yc&$VCw{pEnau^t#L2PpQJ1HfkGTd`6J>+{9GwQobtQB}-2 zM@<-xYB;j4(Qs6-I~h zK9hv80r#jP2BXL+_9Lg*kDOvZa*F-PDdx!ag^_a%m|y$!__;jq8#V-Nz&S@)_?6Gn z0Bq30jl=U`v;G~Lh7Ow9NBq>}Vmggdlk7L`MUk519qtJuH5mvaH5n@A)FiTIuZQ}m zFv5_qJpL(pUJMY2q%h);M!h+CpAzb#6ho*hidm?u!Vu<$!%eW+yU%}q;LyI5#rzp;Ddx{$TNpRrb+{*td+!V5-iMZXs$JNxo|elzV!MaD zAuEi$A%}Xiyg#<>0?rfo>G`4*BX1}RV{N^v*#G%l_?_nPe)0mIE?C`XTSp$zQp|Zo zR~UIjPci2a17YM5;qTh(F>@Xf5yp!9yyep9VYajwAg{@Pj<&DYV>eZ)_A=ARyoJ-Yoe za@M9ctxo>H+JJjtv+w&PVDS0sW7dqBecn~fG3hDhdarHmKf%pRO?gp=6Ap(TxAw?$ zqLxb@5}kQ50H+)t2*de9#q9j>4{ZZn&%Tc;=K8o|_I=W4`qc6p*!dYTKx&Xx%$Y%6 zF|SJ$6mw=!RLtuVWnrWU6~+8JL7iEzTQtM~(_z!$mN2HnwlJo{uBvB6jyz#Kf)pVt zjOjT+?5|sJ0ng7V#~>|?4rLwA38O=KVRWdd>bXNTVT7>$gsfX|0mo=n8E_0+in+e6 zm}A&e%pn{KBS#27X+6NqzY|0hbBd67a-%gkXcivx#eWx_M&h3rbBKS@;h`|ZKYYMm zZ6xlBS^Vb}^SVS*82(LL9HX%!1tYnZ^Wq z)E5KrKo}hw3Zp~eA6XA@J$EP}jEK)G=76RYbHFpqdR-!`3^?KiusQj0mF_g880k(^ zF{e8%VWc}UdqEA_kDbI@qi~q1Gkx|7foRc`qwwd9MpY-W!Tp z-rI_KU7{yEYnGq%*Ch@;8T+5m_TQ}2_lL@q*Z-Mq4m<>}9sW`5xlN&One}e$x$P(Z zmb_No;8h3K99##l9sXHt2kkCjTfZc>ZEExlz8<#rSnddRjt4u(?PmR8=eQji5Sd`- zc(8LE2}Q7TJlHudUmkLCf0`ISGGDL-qaUV=iaD38f`gso^cP)%o#VmIaek~}y!0IG z9H$@3f}P|3Uy=!Sj@$3T!On4HUct_B|1YTpJI8tXoR+)O7uauUSndvXj$425VlXfM z20O?34-*DE$Nfh~v7R369RHR518Tv}aja4YJIDRMcpB^+Ctc;Aor9g@!On4;N#JMb zVCQ%=XFY;n1QNpd**PtY?=di6k9 zuydRq&J1>r2Rp|LciNGI2YQRbSWgUgj$8lWt8(JAmLbT&&hcR9I16&HbNmQF4t9?F z?sz`fIZl5&Yt>Gfytc;M?1P=-c6-?1Yt}E=9v18zr#(Kr@DuDD4|a~z-dk*~3U-du zpNdgC#rgj~7VI3i|5vhb%g&*}&T-ku%|B4_4^qL-anJh%?X%;Z^=U#~F0&pd&%Y zE9sb_<4ihUMZ3;t-|}dvU@raK-2MsgS+jY0?$1MIv$Q-{n(!vg=6ea}&C}mP| z?!hzWjt8g*aWnlORYum99-MnD6nem1zB2cR6W;UYj+MFdLywrBtjz5UMa=ZWbV`~_ IAD-LszhKo^?f?J) delta 34389 zcmeI5f3TZXnaAJUd+QBCA<&9ZG0km(hKs!f@ke&GHn@vMr8YaV64Xkn0)eG7QBYHD zO(@yyy8Q zPtV`**S^fWee*fz$$QTGJUQq4alN!Mo)h`R7*^a4&5@pI3HS^pIV%sUYws< zoL{#%|DwhD*~R(r;`}L#^DkbUKXq~bw8i=Li}R;1&Y!V3A6c9~b8-HxZ_RJ`<3~d8 zT|4il4L>=veAVwf{_nI8(mx06>+rSm`bC~M6Q$RGL*>`eng9D+c^#+MMqa1s^}X^s zPp==6*A;r*lGhD--KN*{UucH@iPLfPcV3xJ-s#uSKOs8Mfg9xY^WA};+YX$5{kT29 zeJ{NC1!{004c;gX{*S}c$>*qsJOG2!96rb4^UU&JZ`fL%m*Sh{x|diEbCg~$bsv*D zICUQ}zkVK^T0SYPX=$+JG`RoyYw)D@c0OmLcKRo!!E2=h;IhMCa`+n#A9eWG4u^)+ z5CsX>e?{3Tc&)>`9KPP+TOD31&VPvhfe0P~kHV-@1{G zm(X!G9WSLLO2^CScsU)fpyQmihi`r5C6`_Ld#4qyp%9^i@TArT=v+&BZIsc%>K&rHeWI26@*j7>vB_G#o?O6i5J@r?j!rt?hJWJ z#qS_aE53|4>oa|d_zi53QwC=c7ZuMESA~y}ee~ujucr7c;)db{;-=y^6R#?sC2ld7 zDRQs-56`=#G3B+zfO#8nPchf`h2ip{!y{q1+&k5J1TK#V!{Z6!*YZcFLJeMc-;|dU z1Lkvy(~4(_bHYc=%DZEmLPyL|I+cG(*6VsxUR`RzO@|Yw+3sxlPtRL;npzXSlK3^= zw3ku5K%7_1^#x(ve#!EyJrq>L0QX-N#{Jh(Z}zW%^vJyjGKyL9#?h6JnkC8xT#f#Tf(@> zwlHq8YkBi{T=m6Z|F7wGUqjuG*bePC+pmgUeeqE$KIQUhuP(*RZzFC9M+a1&o%Wij zH%G6cpy#Og)DKO2Z7F7+CGH3xBz|dW+6$j)Tf;OH5r#YBirF10#q5r>Fx-)~JPCSE z4B(HvF#J(Oy;*t>g$1)iim%T!-JziA800rvk6g`fs84$(hbs=JUSjPZA^Vp%s6)cPBAz{W#w$5o zW8O$30S2kF?FC|Md*2t^wXS@@v-HL{&Ul?owwyUi+*i!?BjLZK`j_Qryl~vwGry5I zu6QePMi`;bD(2AUEx+0q0*-yrF(@hK*w?^j?+0R6hwfiH%BQg)ds}ROAjn~1{zS4M zM-{UYCctLzhm;^^q!=S5rsxn|5ZU`SS$FKEuc>Q%shx<|s9UeMd+-mO^I$RNk4%Za34mTCE4zCL1_S=>x zQ*uWPaQ|Il+N9c{mM(ahD^9y$kL2aFbzS++@`9M28b%03FVN&4KGxKig7_es&deKL?7rpF?5v z(|eWe7dR}8eny1R&$wkC_S@9P)vN6eAyK3LiAb zZcy^u*=BnQ`Rys@NnxOvCxxM6o)f|gllSwSkPwFarWCXMW-U+TH!lV($1J}kVaRVq zG0SgF81max%<{V`4EgN{Lv8y$)1-oep<~dz$lej;x9xDp;Y!llLw=jWP}jD@J!UPx zm5c2KVrwfmYWa=4!IndQ6N*`W)54J7tYVhmf?}56nlSXBu9!pHv^VC!NM$gA;GI{(}6b9Bv6iemlaD-=1QY-=Sib-;pqG zKm2EYdlUJM*aH6xAg{bcg>nB0)SFk9l>FwU81h?G%<@|jhWyqXZh*}lC0~Azm6WV` zZ?Rs%aEd7A;gnF!!zn3@yG%Qr5k`=+!nn!2<%#?j#Q^eK1DnJ9)Yss|TWvofdr4u) zUJCVQ=O-xWkiD!FL-umQ_!?Xn#@FD6Fx;`KnBCD)%tdbMLlqgbo)Kvkq5ti!8{S%-VV5Qe^D4(-tLM2EdAZ3pn# z2rK5;$HC_KL0^Y=-l}!Dc9nfQbhs|epGel>refCN7T6sBw9?_86l0_e6!S>&-ea3V zeOMSHCF*co7y~6CjDeD}%sPC>qSE2?du=<=;he(-hrRbryvNJYVa4q3sA3*z3B_EW zv^>$_j56S%mKTN&7ZkG&mlU%OR}`}jR~54k*MyFFMPYQPB#aJKRXum8NnGZ&TMU_1 zF~EpuE9Mw>6>|)Gih0Bg6>|u~@3(i1%pK@=MH6r5%8>H4rpF62fWD4sUkiR zC1t=7uYt|>+f=UAmSW^uUB#Sh^@NdY4ITFO*!%o}S-CB?>rCV{nGe|F{bv2`u^r>D z@+~PwPSX}fPSZiXIdHrBD&Lo4Og zzNDBHzA6lbZzyJkZ(1&UkodM3K;nB24;*e>V>=8*Z#mp{xOAUv%(jJW9G!?V~^3g^x-dP zLGFIkHU~lO3G-lJK^`h*LH0go?MG(!T}qIXQj9^GR?LGmrAJtj_hU%n`+`%u&Ux%yDM@Lvcb3SgLt}I<1&jurtDNdCuXyFkD^` zhRaK;o~OlCVqXX`QMbeZpMj2I{tWaL^Jicnj1G+)_V!tiphIC{bSS!y9$;jbu>toe zB?gFLS~15ktC(Y$SIqSV#T>$lVh&+l7|Pt-x7GHL3s{<0l>sYrNBD?Ys{6`(m`-D5 z4u8VF8_FDYxGoH3ZVE%0TZ&nkyNX$vd&2PV!16?yhhhK+kA&gi@Fyn$VE?9t5%7#K z0-jUM0WT`%fR}uxF@qje#QKV^%d%o$-Qa~AdH z@FAtl1u2Fy7ljX+6Nh3Kjg`4A#Zcz1FqFBkn3Z`b3}qfEW@QdjJb4g8nd6FCnG=>L z%A6JhD09x?g2VBOZ2-!gayaeqNEpf-zlGnwWunX}zhJ^yu-Um3+cg&A=&iP02ys#| z3vpH$LYz~~LR?bJLR=RnAzq5@2*uX6-xGUm9SQP@uWCWg|DA0Xf?N>hF~EXcQOtr| z1DmCNOeZh0ceDKUT`XM`chd54P* z$G>1ZfU%uW%ws#Hn8$X;XZq|wkh98w$96$6&*l|j2y#_13vyjC3vxp-3vyF23-T(n z7UY%~K#;qNS&)0eaPh$5p)g!N5{Ape^t7#(zclelf}9ZpdMlo+)5?GaIVXI? z9BcZ5e1cA6L9R;IJhbd2iH|S z>t9zGg4`2Ezz2#s;GVHJfcmh{G-lAFs2G6b!st*!7#&KfdhSq87y-{K=71IzbHFRi zT9B*CfFs@jo5S}jLGDR01bLvC1$ih8K@QjLjf10Lv+V&T$oVf>v;F3d2b3Upr5J+T z6NVu7QEyH>pagj&#SmogZhQ5EX8VImkh4+@LCy<9kc*00kSoFvv8nlOfDT^QYHTIMC|1CPXZ%>9h+dfgu= z#eK+j7aRwhV~@n1xfpWY`A2I8x$ZkW1e@b)u^r>b+}f)rJF6oMXY~|w_A&sQy)EUe z@Wb{pa8^_?J1eP}os|-Xvoa26h2gB6Fq~DeJjq_FVt|RPrkE$PhGL$`Ruyx7OEJ%5 zUB&)tfH0DekL(WlC%sEG07>;T> zyebSwwS?iQj;iMg-}|QDUfF{Lg>Aujc|NMxkDOvZa>D3P#^J0mI+PPehYCKEgs}nl zs452I$SL+Cr`V62Vn1?<{m3ci$PI*%a}1gP{26cjT%PwW8v-`qoFgp!^8ca%*rbIU zhm&A);yW}A9WmP<^;47GbQ-57xkv0pk(v}7?h7L|844pc87bz}B(i3&hx({6!jP~$ z`6)Rm28csS7;(s;-fTOpgt{cf5bCO87V4TXgt_T(3v8AS`_B&?KOFnqcxgNF9eWp~ z*WXo-71X5|vqw`ge+Ju%`7_uN#?AK}?hE7I2g11bk!AlWoyXMEa@j|1_mDT_gpoJo zQEyfri*34;^8|i+z9hxS8!EzBTdyhhe?Aw!+^lbt7w~k!>S5bD@`$!#&Leul$RqlS zIgc0$BaaAw*ItjA^N5HrR@{@8%j1XHGGc(dBIj`AF?&Je5ebKr4iAKpM}*t<{a4b! zdg-I|@Vw7u&*Oqqs30x|W#a8Wpr_{*pGur{I4AsLvR^--{9W;8;+pX6aT?j@%y>S z0XXgOP#Df1DQ4$~e`FirdiH%(G1tcxv+q+r)2Ei-z|PN#0aAmUV$KW-ig{h4sF*W@ zl44$$s0brPs4C{)2^!3L-J&T5m=0SGw}mksc7!n<_EbG9a^y+t5u^xFVNA~nVt?I& z3wVA`I|dnHbSUR=UKkxJ2%|$KRnHx&3nPS$CuQA&3phrr%7A0oR?PJs#T>)FVh-U* z7&$`tDeD1d{+%GAm{WwrQ?u6Kh}ro=U;OvdX)OK)F^Bk<93BZn{KH4>)yCqkn8iP- znAasz!tig#@?>2iD+X|IP8bd@sCssALl_Zn3M1kz#T@XiVix3{&om~`qk$NJhr;O4 zNEjUo|JZtf>$yV-VMIKsm;;(t%mL3b>vf5oGT?|8!DibND&1*HG18ruVorD3!bo>| z4iCU)=?S$ik?h*r*>6rfq1GkpQjGkfA&mT@iF&j5M`~T7Eyc($I>HCd%8%5#MD(Y& zQLIZOgpp6A6mve25k@|dRm}NBUKsgAQ8DKeCCig_iJBN7e`q?~a=7>}w!_FLst(s2 z&it#jM?O*XdCO#7qACWE!3Nlzcv7uP6#mU#=YnVHOzRL8#Vqd)VaR(^G0S^LF|SMX zg=1#rDSut!_*1d}4b44$oW4I)XS~7BY;)idcy0YpV$Yoty5F4W#h#lx^>?&mueQ#s z4Xru20bX1G^Vrr2gPr5@=Q{$aYLm~+V*IM_K(x*O~q4|b08V-1s~=V0eJ z{ZJO{9QXf{Ot5p@{^6Ej=QuL2VCT61>Lkvj@wKEKSKvQ z$D?`c5&R;M5XR5W8DV@^$tvcbo%4$MXXhfb{@J;t4E&`+aIkZne}?9tor9g@{JS&% z&K&F<=gdCXIUejB4|a}QUBUxl!Ornu=Q#hl_F(6@zv_Vp#L^WzOu!>yJP;PYh3nM= zVZqLEdN?!KIUejBFWzZK4j$+&31dAm*g0|Y>>Q`RowaJGOkP{#ZT7*=al1Wi_^|a0wuc2f$LaBOUib-ijt4u(Y40tzRs}o9 z=}*O|o#Ooe9}9Ml+y5(BxNYaqVCT4O>QVgQXU=* zc8&)-$0^4Oc8=R&fgcL~pY0s~4DB4JV-p>5I_BuuOve^F&ZXl#I?ktKD;)_sE}&zc zjtl8{7416T_O8c6d9&xs3y)0=pEaA87Csd!n&qX1^3-t3Y`%vuZkF#^D2K9U?kjX^ zm}NRm4L6z14XVtV%O;`g=M>%rc*fX6_&8)HTa|q6;O z2H}wm^({hmAd2-w>R?)Jo8@#g>Z1wEKF9-XW9?v2tXay8;isXN5`WN@wYt#mefQqU zp0k#J75=-k*3Hf5oPF;(cb|LCcjw%D=JmgPUtkDZITlFlon4*{EYD6X&rUASPA$)_ zTb?~;d3OEsY;bw@*yY(*EzcgeJbV1|?1tsp6P9OBT%HXr&z`hAd-A=r8&BL4*wlE# zmW}H-6yiTK`0x1Z;NuGV9{eDGKgO7;FnrHI`m11%|E}flQTToXe^0{q8~J+%zAy9l zB7DD-zgOYAh41k1gb%<+05&MEfC<6t3Czn~;Fq|-55f7lK(D;qsJz0Z4p;Ts*k%i)6#|Et4KXvVwre{}-zDbK?Pc!X+b z%yx$_b@*zBmmIbZe@$@#UH*_t&`t1Z$a^K4;BhYRQwDk|7Wmva!Q&TyONH#qULo** z6;8VDzu^O1{#p3Y9=IF$mGD4_%ljO*zbTuH3-5I@WzhMDSEzE=aCv`El`EJJAD!T}IphToyh0VYmCO6GR{^*QK5`hH(%L+pYU%Aw&P#0GjG^yG4BChw>V)+4p$sbLBNC+Tmk#zKNB!%#g_qR6u$*H=W&4!K!dz8 zI1#w4cs+29IsAk%;g3w1y5f_8TZ-p^+lns%?kHXl+(j-xjsFZzcwu$I^w|lnBR^0=}FTa<=Z2-K&#&c>Bn6&X$G8*ydHSS zyb63;e$pgPqU)EDCdn)#Nm?=bBdZwwkz*Er6p6>JUSb1rNSRq2Qk8srumJ6{!UIJ8 zWU3c3_Wzb*93VQ1F~6%A$A^Jp93Mg(>3lE3b#Kp2nyBKFffK|9^VVmeyM27pq}Tv? z8aTsz8}K=$Nt0E45pbUQdw6^ix`X0r;0p81v&L*LPnsI@4&cepOq!O*Fycu<$1x~P z(-qu}2UI3a)!~}MnN!IA=U{(Eb&-y9c30tO2#o;>gG;}mESUi- zWM1i!X0#MzlXr+O@I(dgiJoK7SB&?B*+dmwu?KI9><;X0Y$(BQq}932lr37FXU01f zwYsDjMY1An58ke{y2;7XgW8I*2lW(V59%{Z4;ngbwotXEd!*GtX6Zm-;t4Ztcl;&v zAd%?UYv?$U=%mAGhs`@)8DJO8FpETI6{AGw z6{AEK6{AF#6r)6!nMI*19zzcjiLN^aO=j_Q+u;tgc)H6hp6;uBlxTAl9k3rsbdK{yqKgij7!@cI9cC7ZjwwcoPAW!;PBDuE7Kz7$ zb%qVZ16gMAKwk1~WS^4gCMSzTw-uvAcbG+@`wkC|a*q?lK2 zq^y)VJ4dHqfpG%+eO@t+3nj&vUsjC6LQOFa3vFgm=B{E?<^l0onay^pfGBgAS(G`d z7?nBBEXtfxjLMv5o_Us(Im0YMocCBMbICDC#;HQ0%!>|Z91g#h>_wRq%p$yLhqK69 znL`)Q33fG}|CCnd^o5jupt0>Pt;~ZrQo1N}@S-uJGDn$3nd6F4nNx~UnRCo|3!qfw z6=Rc^h{wuYaSW=8@t$Z3+epPz<|W*XlsPs}^%Z4~Gh;f zD07Edl)0xEm3gQbmD#+R&MyuK5s#HQ%m(6tD6@DVF8Oxfex=MgP8MY@Dn?~4F^e)+ z9j*)8m-c(gyyG6Fc%ipZt))Xn6=R1=D8>$zWR|N;JG{s&7nxy}i_8&^mAS|UqRdrc zyX_0=$8#b@^%O-+GK(UnM)~&sFQ^~S8BP~P%reW5=Nhy8c&;;xKU#{>A6>=hj~=u5 zV?aD^^&uOGL(HXgA>xn_^R(SpRT3S2JDIIO|Hc6zt{4Z1q+-lZDaPSAqZr4BBJ+z+ z(~sw}VwC6_@mQjpY#C2NdfNdd(Um`<>WM_x9d0?CyOQk1-Fe06?vi5cffdD=UnL$(bW<5%7wj;LM0XXV zME4b=L=P0BL=P3CM4R_fg+-!+9xI8CItDRj@pQuBB(r!r#Vnp)RQV{;Wo8*WE66zE zE=^x!17vLamSSxBHnUWy>u`@*D%59|3Jq00Rw%q1p6u#p6j4YJ#x{y8_C`^~n4ePY zjiQRN3G>Vnl#~?XY(aUqIn65&4KOIFu>k}nzdlGo$;?+&P}1jo2}*_z=QDHx5|osg zB`B#X#-OCB7=w})vv{~eTo}#hciBK(++!9O4^%$7IB^wKNZLHfEKQzPj7^?Zj7^^N zxL`&VD6)anu*57CDlQ6^11_S;DZiVhqC;nI#O%Ib0OBJL-|0CrP-|zM4vF*n{=R{AdCoaSbJp!X4&O zxHHPPGhb5^02xjng*(iv_R6nCb{9nb>(|ohkx~8I%qaFabKX^qGv|F~5&wZ=6o2!_ zbUqRPuwoSdC~?7v`X|^x#6InC#^LaFRDg(m+~I`717;Eba2Aiha6B^}_Y%gOa(KWD zzt6v}#XtT*stEi(UjgyIN@mX6AEtB>|E^*b{{gdzzu80f$SD3{#h&;x@3M!Qkq2O+ zd*6Lpt4klH!$qsh%-Bm%t80o;tDC}h+c%X~_c>X5?@%%J-cX*3k^C^Tbk>-|apr00 zTi!!yX6d{sV)am(4d&qh@1Zoah<3^0io>ZJse&uq`_qcCb7vG|@6UM*!w(*S26<(G zy}zs&NBkPIh<07E_fT4~_fT4~_fT4~_fQ)7sE5*QAT!=W#oj~dkI@y1%R>%_nZ@N% zW^s93<>Ppn1@<&jM(`XPNE;Uw<2_JOj3aoJSt?X_xXCOPYB5WNIv<0#uTYfH0Bba0 z18fqEbBBttiGnwc+Zfw0tQhm7im?e3im?gP%p%&En__fAOhDDnDgzYlA~T5g4fm61 z-vRs4OhSwEMYKB(r*Ee7i)d$&gJzye({Zw3KL@Tj)YXv_p!$7Y~?4wBrsZh3#rfiFOHGfp>7jM%I*Q4>?&x+Z4%6 zL_5eFeA?czrbIi+=_1-O=2d%WEwX(i+IdbE(JnEIXjc@YXxErUwCjpdv|G#~+Fivc z+CAd2Xb;&yL_4&Xu1`4XaFi&M;?#Sca11G^d<9@Zg2?KV2yfyRz+MZP)( zTK&?uwN@|QMfsxD8D{JpsMUGJsMRH5yZ<{%tDBrGowltQJ8e%fcG^C(^wFWi<}*~S z=^j6nW|lr1CLU{boDD>)lgxYI1o#5kqQhB-!+%8uu7Ev`^HIgv>EnvA(hcR+kl{R#z0GR#z3HR@acVR@d1;w7RVrwYtMBF7G+qXBL+a zn8oD=-UHUkm;gRXtCMUX_dr@P-UC_1-mfiYsZi1360=mO%q$hEe%8_I78^(#wiRO& zbroY9_7!9PKruF9(2nm3Y{DqBXm#8=TAfe^sMTp^(CQbG~SG)!}G` zDj-@NXBMqaDn_kdRE%1kVHOYPh{sx;X9IC@ky%_^R{1E6ZD!Hx4zo0QPcb(6P%$>S z`5ayl^ctyA$V(V=m{}?mWtIxXRX$cI%`8p6s2H0xs~DR+kF2%2s0^^pE5dfQqqMri z$)eRg#i-SNX3=VMH?^E_NZ4+BRB83%Uz1tG?t4^eb(@n#t2@l1)!k9P{q>_ts|TDe zT0LZ5wHJR+X?5!JRH|t8BC}|9RxxUIo>{cIs2H`n%q&`6Q;b?&Cmw5cn+-&pdkzmA zuI;A^i&nQBZaZAOhwMeGYaT1DZn1%AbywK#`@YiZYL(6-THRFaO`0={R`(QpljeUz z$74Qfb(ndVz4$RttCt?rTAlwQnZt{3KOn8X{zqD?qhF`fP_;`fQIF=fDp>9+}@%2XCzV6)0w%E=PD# z*lsu+`N?bNd-SW@e4Wfh$-@rEgl*(VWPbF(V93ehEb|RIjW{c$7@ZXrw)>ALXC*mV zoRwDWiJD?h)R@ItMTbkw({Ob-2~lPiXH|*EflZ4IR^R{(OxlV)QB&-Rnqp7X6nmn! zLVj3*^P#B4m?bWWdwe0hV)+D2zd@jpR0bHBq!eRdk`}hl|0Gf<$b^6WUMgo4jwtrR z5oU2z&*47vG&^d*ERHh&MCZqRRDuL?!H6~`*$@?mdG(nOB5yFUip;L@H!`phPM=>hPRnTvwIE?gzcfHymu8MPe<+?&6Q_X z>8cxc^=b7ALx+>)F6=4xA|%CLgmgchMlL?&aF|)HJ<2TC9w)}ZcIg@Q(q4(}B%~-a zOGr@}<=Z3AL}nH+guwUv>zpnjMUz=3>)VQPvcAK-*$vcsu0s(^$T8HckDM;|162{Dq) zbI-xLhku5f*kc$CFyUB8SY(6u0$=>*=;5PFS$X|cv zl<7GJ)D+_|*HVlE)nk@eZRl|5TU24mZ!zzB%9wY2Xv(yKPYKvFo{K!VY34tn zG1~CHN1Kij4h!3to&yaNpZC8_X2|IC(7%lt+a#tv#pwK`V)T7lG3GBSM&IW=hPxI|h|VvufkX#o#TXn^ z6=QHvQ;fkuT`|sDG?^tzXeq{3Z(U?PtI=Zv84m{z51C~=G>6C|G9HGQF(1Q(6thGL zX=WLpGr-=g1}5MDop%h1%u=DU!xd(!P?cFKR9E>}p$;>89HWHpA)eL11Z<C$oL+>HViAqCX6#nn2=PAQ9?>FMhTfmf^46w~>!uHA+RK(Nc zWQlkNiZS9DGE2l0dXz3)I4W!py`W|-vfm@KhTS$$vlbmrmJp-MEFng3lyC1Ds9B34 zr%Q-ozE7uLwTB04)*{WxGHa1xmXIQ+7(Fz2`O3*w;eA2fb1otsCm43{Dh&!2BLIbVY}@Yde-8HbRJRorebf_f>{*4 zr`Vgd=+g0{SqtV}_V6#fS&PUokD9gk5t$#bSNS%$!k1dJRotDd@q?hlz&_t(Sw>*4WXvcDdl`h`s7 z`|IJKfuDXb_wBET_t(Q4{EuJ#_3#i<;ar8k9^PLM@2`iy>S(?6*Tehk;mhz^le6C4 zUk|^B`iab($C&-~@ZJhBd0Wk24-zaHLS5AUys_x?ByuVa@t%l-B66g-UHEZ=`L`|IKT_3&no zzaAdf&h)=QeFNWW#J3m+igDsUxH8W7*TX{~E)Pp{y!>3|F8uZI{(5+CdQVn|`Rn1~ zr9FQ=JpHvee?5Gk{*NDjJ-jipUfy31@2`jV*TctodAYwHUe=)b>*4WF>iX;9ommTi zJv{vJ+yBpc_}gy2V+OXfV2i@G3AVFg+YH+l*j@wMIk24zTMV{Y*v^CPeAu=&ZoXsN z)V+a4yZ23V4+O$?;mvb}K*g@%4(KhgOW8Htxdv#rbKf;M&8TprTbE`#*8UAP=}a47D8cEGM;*Kjwq z3-5s4qFsaCMDPZC$ delta 34796 zcmeI5f3#awmB&x|UTOHzHqZhQNlBqXp70)#I{F-`jfi6?VDlK5dGn)^VjUx6os8(1 z5!yuMEiy9{!ie4n_F)t{lciFlf-kI@Vi29lsD(V#WtKxRfOO&yYWy)y{DU>K_dWZY z?043h|H^;2Yu(;_&e^&5+`aEP-<@;sX$PNsB-Ep=k_aU}GBGz1nwwZLH!(IhF+Ml3 za&F>;xrtSC6XCgu6XzyQnwvOzZsL@=iPduxYvv}_&P_z-CQhB3IPKwyb!#_;PFsA# zrgf`U7dQUQ@SjuOO@F=Beh%L#pHDDmJW8KusQe__^PfMG&vE*Ey?jp5=Nsj7mOlTD zd@j-Fl6&nK=4Z~BojtHwj7q4*5q7TDYKx3P1I zH?UbLWO`Ek*Tk>6J!A%oFC>nTzxgz%j|zX4zkgB4Bpgl(hr7m{JsmRB!rUxwM%r@G zuz?|itQc%2KKI%cCNI2;c;)O0Q+9aX;WQ;m+`t25f6CnejU5)3v|K@A6Q{##ejJeaaQpv;)3umpY@)& zIksU1?T^vExb`^;QD=o|NIkgaaQYN`0o%!b>S;Q@@W+T>YsO4o@f2}cG1pgw;ee{; zOAHEXVgL`+h2eoF>b*m^#Lk)d4xM*Zc+8m9wq49o;)r6dj|%sxe&yL?Ch2er>`m^8 zZ4Yhp_Uwsm9rmp1T6=W#oVsG}SuMrf^V-6=sIJ33;V|9k+_U<^xJVQ6&v)*yXGLs* zsXb4Z`&w!~*xRu;cKs#4qv8{<8Z#{^X1;>BEqwWpsXjAiI>UPJ*j|eItyF&Ut})Y> za^_XU1L1?jXBNiH^r`mxG18=j;g5`B_D4=J`y($5f0QhbqFxpQIHV#Bhg4DT9V${> z4#)seKh3s_nfrfJF%J-J#a!P}%;Q5}F^>AEk@kD0jQ(}<@n7tQNmpze0- zm`RHP^LpZ}aDn*z@|ejfzLdBi{5XHVMBPF0dg6KEwMUIPuQFz8!fzrTyL-$ueWno) z1#QQmyxv~HKkygk$4u4Xn#0-Ct^IRke^zzOl!PPyMR&rcaWn65oq0VCI53z!!yaJU z;=Yf>E)Jb~9ewfQFVGjywB`F34}Ubab-egXKH#O}W;$-mnWMxR#ay2gj`fUrMRwd2 z6kkeQQG5Y$Lm0P2Q!$6UZTS*kRQR6gItD$(d{3B-wt)w{L$}4Q550deR;JxhtMljC zYG`#qnD129>at=M$$7AM=4OnsHK>DP**Ycpq?;#(7<7{$u5ecw-Ba}}(dGi{ zF(`C|`Bfs>EYVSAz!IGlCTV`t?N*}SLHnUZ=cOJJU2@nY>gY0_wfVeM+JmQVfZ1DQ1ao3qzuN4iCWI!aiT3U)-l8diFwl z)#y+;#oVDvin&9Tg>jYh4p)V7ku_mlWW(}EqFZ7BiSB{D!*{55W-nUa&b(r7XIU|~ zvm%UkRvoShqn&kOw6kfMJN~{qV;2uoh3KSh2U3N&Fj9rYu--d%XKcepqzWl1N2-t( zKIlzWlrrb0?BSuz1;sorloj*1P*KdoLQOFb3oT(Nb4M{NbKmkvna#zv0Z`_sFqAp2 zn3Xvx3}sF$W@XL@Lz%O}Z#rs~x!|)>=CWguO43dH*UH>~qb-Lrhc6v5D|1{J%A8cp%A8ir%A6O5G8Ytc z$jg>T$~^BFR2B0*(Exjs^S(0gnGIvwT#UW%d2CB>}FWnn0D z)!{nWTYaCe%scK=iWhmCZ8bVnTrqd3X~o>3Qo^{(jKj0SxX7$9E;4UA5F#VkB(yYM^_mB z=vy8|eIN#Kh`GXE2pkd-4ton#CDHM1*6aZFZyo@Wig|!YDdzgLVjhmOig|n}2}7bQ zidmv-mPZoZ5CcebTNo1EQOpwE6NW^aEA0g^vqXo5A<e_woF8-QlLg`5o3C?k*^1cb64&51d!b^;OFwiEbzZ?t*P$NOVUr zOLR{$OLSi`OY}f7OSE~1Z7?J{?6Z>SxMPqIhNq_;P6@-)X<>MJR@JjaSA;Qk&NK6b zJ3?O*17;3=Q!$6WC5#4j9PSFEK|NtKXrSu3LDB2!iLQP|5vKw$$0(`TA4L^&eOj?U ziYn$17KD+Mloj)ALFIaLrhh^<;H0D`29%VnIcSrTDcTQ{lAhEfDH%9i$l41)Qc@8{ zQc_jSNl8O7CnZf`c(`r3IGoY%hyh&O6^4uZs-9guz0)=bF;592~p;7ucY$3^?ZD8|)Px@Lv3) zO2bl8j5I8xnA5OXVWeSshf82@M?JRvRHQqtciOYnyhHWa*5L%e^e$UGOm~Eb>CUj; zoBEQP0LV)DFx?S8=&2j&&?2woLNgX#`9gwdd;FdEeUFuirfqRa-|qP`d)h6BYM zqVUb57;_Axin%_nm_s?=N0>p;T8Lj;f3Mhs^yVr*TeuWt_#D(O;yhx9tb1kW{+(k zLLO1fAx|jgkSBdsqMZ^0v@k7<24#fNpscFr29<;n^0HzM>AYeNd5u|%c3l~8%v)e@ zM^lNm*=sKpq8(A}zjz=F(M~#?0(*y>O0>)53ciDD-sEFSvek$E6&ioe)0g9eFHv@ldo2QVh{93q!Q$6|-p9gdy5>#Vp!QVTg7|F^hKB@<_A? zVgS*Oe8gTKIPP#q7^2;Gc;ImTqxN_Z?T*jqjYPUH1`zS^$Ltp(-sI!4?L#eY-)f7Y z#XZHW#o;+y4=s);W-U%CW-U$&lNLW7+Zx)o82g4!32L|5!|h+(@y*y5$4RSKe@koi z?8j|Av^p!yorATypqRC~4E7%Uw$kc`6rWnaH^|2?cR~>Fq19D~~yi;Np@nTwGE0ER8K;Xmwi{A@3^YkPj4d$jv=`LDXx|qKIEG;;1kh z6cn;x-AT??hNa_HBTw6?n^nedLVqzyZR}m)#?5AtkCLN zVQ6(uF>7@}7+PIY%vxO$hE~@UvsTwFkF>fa2GHiN!+nQq3%0?~>ZZdjhfDWaduVmd zXQkCmF@RQgz}~`ll~z})_Bhb$hGKuxTo_v2RqRiif5v{F>shO#!rQ#7zvpZ9zVB(R zF5GX;>BYD2Tdm&nL#@^EFWP!&bwZdshyR#bvHzGF?8Tl|T3wQ2blQqy?zA<<+-d8= z=(A0STf*V4eN5dJMxX6k<~i^~Psg@y{0w-KWMvtTcFB=)1%K*`OQtr?U& z>Tm+=O+FLbI(%R-kYYH?e8nCH&Wb2zXT`zZgU={urKA|n$|&|lO|dU(!f;l};j%EC zRS|}>s+LEIO;Zdo!nG9pqNdmvHO0QDDfUI}A?pV|9*bH+72JF4`qgb(s3IF=T_B_LMM6sWa2*XibhkL?sR9_g5GGDdF=XzFxY0E_e zZAyv3Fmj6h$SL+CCyWM_94-r^K^0*%sOmFm8(P#9gJI+p`;k-ZM^3RHImLeD7MDjZ zE{t>}!OR&NrVgf+0jDD=;peym*659YhjUmdTN~$14JS(j7XGF@9lnGX?R_Vq2W!%tl=$T zXm;1(KG-|*y#KDkm`3TDONZNyu?LCakf zpG;hKxFY;XvR~b!_xlx}OWYEElImBTJ#M-V_nF6OG_sC zd~3uUlc-{@pBBE1Pcu#{6dcYvocgvs-e&S0uX@W^E*j)IIWYhi98P@49stfyDrV=W z6tnL$in)GPG5bF6Gu^d(Kz4pv43HgE6mxP=Rm{mjO))12b;Ueu(GW(K&{WK;-a5>B zR--Eh7!Ugn4}>uun#0y37!M=D5aqNmvV@E<#^)@tKdZq7JU|y5gOV^BRB?D-7!9fl zqd|35&v{E*7$NK&mRSuh;28Ck0mpElnCs0GqkDj37*)(6ObR1SNGawA3~9xjC1jrn z+XIsR-|?c=|AVw2>VH+f0R68!oP5%LANrpXhW=+1v;OB4v;OCW;o*|y(X2&T48r6g zelNBn3>Q~bJ-fIgjF@+Y5%a!c4tcn3F97uspQ%@%MR73zCxpY&prkMwlv4HFpsX-r zo>R;rEhy%YmznjfMMW8K%xhq8_b*h&)0JXmJblHS@eG8K@kEyFg@fZ@@5nFItVQlA zYgY5N^wq3ITZ)llbcB&&bcglc!M>Wc7)Uu%4D(%k_=DbyeKl*5kz&kRWQCDZ-pt^@YA{7TPS{J6Wp|I$&uOGg>2 ztTdTl+iGwY{P2StE(oni{%mo>2V)z?hrjxR_3-x9g!D<)Zx~@Rig|$J^XNnAXpC{U#=NV#jF`^%cEENyN*FmF=wU0dU&3V#c%guJ$$epKH>MU(M)!* z9-f}O1?%Byu5B01VNcreH&_qPexT_V{Ll{8!(X~wnS=H4!FqU3EqGR*XVue}+4}>t z>cM*WU_CrPOb*t=+kSzG{9rx&3$&n%=DvgV@WFa`!~gJWupYik|JpCjRRrtdgZ1#i zdib5o^)grwAFPM3&}&W3diP*G{7tr>;0bv`I9LzwuMoqGcd#BlBCEymqEN6NK3ESQ ztcMTQ!_(VmB`YEP_FAwWp5`*~wpy?rp8rFYU_E@W9zIwPAFPKD*2DY%InAf6SmMp{ zU_HF`$MDVah2$J$zD@mj~;vLajX*caH@h<1h`~mHv-V*Ox z-pt!*m++Q&N0w>Y^}HqCrM;OeXjk)=cvtXdw$X0hTjCuZ<;tm|m$`B(>~*f3Duf!| z%vDrQU*#Rq)wC;nOT6oOGjFF|+FPRCiotI0n(b42`MBE|Z}G0l5K?W%H=3EDUCvwL xUD=zthIWO;rE8{c3x(e2{mHdcucykpuASN%y4zdgUBNr`I@*=Ind_!5_&-Xx<&6LU diff --git a/support/ebpf/types.h b/support/ebpf/types.h index 0be2d3fed..27b0f08da 100644 --- a/support/ebpf/types.h +++ b/support/ebpf/types.h @@ -8,10 +8,10 @@ // ID values used as index to maps/metrics array. // If you add enums below please update the following places too: -// - The host agent ebpf metricID to DB IDMetric translation table in: -// tracer/tracer.go/(StartMapMonitors). -// - The ebpf userland test code metricID stringification table in: -// support/ebpf/tests/tostring.c +// - The actual metric knob in: +// metrics/metrics.json +// - The mapping of this enum to the metric in Go: +// support/types_def.go enum { // number of calls to interpreter unwinding in get_next_interpreter() metricID_UnwindCallInterpreter = 0, @@ -610,8 +610,8 @@ typedef struct __attribute__((packed)) ApmCorrelationBuf { #define CUSTOM_LABEL_MAX_VAL_LEN 48 typedef struct CustomLabel { - char key[CUSTOM_LABEL_MAX_KEY_LEN]; - char val[CUSTOM_LABEL_MAX_VAL_LEN]; + u8 key[CUSTOM_LABEL_MAX_KEY_LEN]; + u8 val[CUSTOM_LABEL_MAX_VAL_LEN]; } CustomLabel; typedef struct NativeCustomLabelsString { @@ -658,7 +658,7 @@ typedef struct Trace { // Monotonic kernel time in nanosecond precision. u64 ktime; // The current COMM of the thread of this Trace. - char comm[COMM_LEN]; + u8 comm[COMM_LEN]; // APM transaction ID or all-zero if not present. ApmSpanID apm_transaction_id; // APM trace ID or all-zero if not present. diff --git a/support/ebpf/v8_tracer.ebpf.c b/support/ebpf/v8_tracer.ebpf.c index be05ea6e3..e84d79991 100644 --- a/support/ebpf/v8_tracer.ebpf.c +++ b/support/ebpf/v8_tracer.ebpf.c @@ -62,10 +62,10 @@ static EBPF_INLINE ErrorCode push_v8( // Verify a V8 tagged pointer static EBPF_INLINE uintptr_t v8_verify_pointer(uintptr_t maybe_pointer) { - if ((maybe_pointer & HeapObjectTagMask) != HeapObjectTag) { + if ((maybe_pointer & V8_HeapObjectTagMask) != V8_HeapObjectTag) { return 0; } - return maybe_pointer & ~HeapObjectTagMask; + return maybe_pointer & ~V8_HeapObjectTagMask; } // Read and verify a V8 tagged pointer from given memory location. @@ -83,10 +83,10 @@ static EBPF_INLINE uintptr_t v8_read_object_ptr(uintptr_t addr) // Returns the SMI value, or def_value in case of errors. static EBPF_INLINE uintptr_t v8_parse_smi(uintptr_t maybe_smi, uintptr_t def_value) { - if ((maybe_smi & SmiTagMask) != SmiTag) { + if ((maybe_smi & V8_SmiTagMask) != V8_SmiTag) { return def_value; } - return maybe_smi >> SmiValueShift; + return maybe_smi >> V8_SmiValueShift; } // Read the type tag of a Heap Object at given memory location. @@ -143,10 +143,10 @@ static EBPF_INLINE ErrorCode unwind_one_v8_frame(PerCPURecord *record, V8ProcInf // Before V8 5.8.261 the frame marker was a SMI. Now it has the tag, but it's not shifted fully. // The special coding was done to reduce the frame marker push to . - if ((fp_marker & SmiTagMask) == SmiTag) { + if ((fp_marker & V8_SmiTagMask) == V8_SmiTag) { // Shift with the tag length only (shift on normal SMI is different). pointer_and_type = V8_FILE_TYPE_MARKER; - delta_or_marker = fp_marker >> SmiTagShift; + delta_or_marker = fp_marker >> V8_SmiTagShift; DEBUG_PRINT("v8: -> stub frame, tag %ld", delta_or_marker); goto frame_done; } diff --git a/support/ebpf/v8_tracer.h b/support/ebpf/v8_tracer.h index b88b49593..54093beb7 100644 --- a/support/ebpf/v8_tracer.h +++ b/support/ebpf/v8_tracer.h @@ -4,17 +4,17 @@ // They are unlikely to change, and likely require larger modifications on change. // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#52 -#define SmiTag 0x0 +#define V8_SmiTag 0x0 // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#54 -#define SmiTagMask 0x1 +#define V8_SmiTagMask 0x1 // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#91 -#define SmiTagShift 1 +#define V8_SmiTagShift 1 // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#98 -#define SmiValueShift 32 +#define V8_SmiValueShift 32 // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#39 -#define HeapObjectTag 0x1 +#define V8_HeapObjectTag 0x1 // https://chromium.googlesource.com/v8/v8.git/+/refs/heads/9.2.230/include/v8-internal.h#42 -#define HeapObjectTagMask 0x3 +#define V8_HeapObjectTagMask 0x3 // The Trace 'file' field is split to object pointer (aligned to 8 bytes), // and the zero bits due to alignment are re-used as the following flags. diff --git a/support/types.go b/support/types.go index fe9967ea8..774fd412b 100644 --- a/support/types.go +++ b/support/types.go @@ -6,6 +6,10 @@ package support // import "go.opentelemetry.io/ebpf-profiler/support" +import ( + "go.opentelemetry.io/ebpf-profiler/metrics" +) + const ( FrameMarkerUnknown = 0x0 FrameMarkerErrorBit = 0x80 @@ -88,12 +92,135 @@ const ( TraceOriginMemory = 0x3 ) +type ApmSpanID [8]byte +type ApmTraceID [16]byte +type CustomLabel struct { + Key [16]uint8 + Val [48]uint8 +} +type CustomLabelsArray struct { + Len uint32 + Labels [10]CustomLabel +} +type Event struct { + Type uint32 +} +type Frame struct { + File_id uint64 + Addr_or_line uint64 + Kind uint8 + Return_address uint8 + Callee_pc_hi uint8 + Caller_pc_hi uint8 + Callee_pc_lo uint16 + Caller_pc_lo uint16 +} +type OffsetRange struct { + Lower_offset1 uint64 + Upper_offset1 uint64 + Lower_offset2 uint64 + Upper_offset2 uint64 + Program_index uint16 + Pad_cgo_0 [6]byte +} +type PIDPage struct { + PrefixLen uint32 + Pid uint32 + Page uint64 +} +type PIDPageMappingInfo struct { + File_id uint64 + Bias_and_unwind_program uint64 +} +type StackDelta struct { + AddrLow uint16 + UnwindInfo uint16 +} +type StackDeltaPageInfo struct { + FirstDelta uint32 + NumDeltas uint16 + MapID uint16 +} +type StackDeltaPageKey struct { + FileID uint64 + Page uint64 +} +type SystemAnalysis struct { + Address uint64 + Pid uint32 + Code [128]uint8 + Pad_cgo_0 [4]byte +} +type SystemConfig struct { + Inverse_pac_mask uint64 + Tpbase_offset uint64 + Task_stack_offset uint32 + Stack_ptregs_offset uint32 + Off_cpu_threshold uint32 + Drop_error_only_traces bool + Pad_cgo_0 [3]byte +} +type TSDInfo struct { + Offset int16 + Multiplier uint8 + Indirect uint8 +} +type Trace struct { + Pid uint32 + Tid uint32 + Ktime uint64 + Comm [16]uint8 + Apm_transaction_id [8]byte + Apm_trace_id [16]byte + Custom_labels CustomLabelsArray + Kernel_stack_id int32 + Stack_len uint32 + Origin uint32 + Offtime uint64 + Frames [256]Frame +} +type UnwindInfo struct { + Opcode uint8 + FpOpcode uint8 + MergeOpcode uint8 + Param int32 + FpParam int32 +} + type ApmIntProcInfo struct { Offset uint64 } type DotnetProcInfo struct { Version uint32 } +type GoLabelsOffsets struct { + M_offset uint32 + Curg uint32 + Labels uint32 + Hmap_count uint32 + Hmap_log2_bucket_count uint32 + Hmap_buckets uint32 + Tls_offset int32 +} +type HotspotProcInfo struct { + Codecache_start uint64 + Codecache_end uint64 + Nmethod_deopt_offset uint16 + Nmethod_compileid uint16 + Nmethod_orig_pc_offset uint16 + Codeblob_name uint8 + Codeblob_codestart uint8 + Codeblob_codeend uint8 + Codeblob_framecomplete uint8 + Codeblob_framesize uint8 + Heapblock_size uint8 + Method_constmethod uint8 + Cmethod_size uint8 + Jvm_version uint8 + Segment_shift uint8 + Nmethod_uses_offsets uint8 + Pad_cgo_0 [7]byte +} type PHPProcInfo struct { Current_execute_data uint64 Jit_return_address uint64 @@ -105,6 +232,48 @@ type PHPProcInfo struct { Zend_op_lineno uint8 Pad_cgo_0 [2]byte } +type PerlProcInfo struct { + StateAddr uint64 + Version uint32 + TsdInfo TSDInfo + Interpreter_curcop uint16 + Interpreter_curstackinfo uint16 + StateInTSD uint8 + Si_cxstack uint8 + Si_next uint8 + Si_cxix uint8 + Si_type uint8 + Context_type uint8 + Context_blk_oldcop uint8 + Context_blk_sub_retop uint8 + Context_blk_sub_cv uint8 + Context_sizeof uint8 + Sv_flags uint8 + Sv_any uint8 + Svu_gp uint8 + Xcv_flags uint8 + Xcv_gv uint8 + Gp_egv uint8 + Pad_cgo_0 [4]byte +} +type PyProcInfo struct { + AutoTLSKeyAddr uint64 + Version uint16 + TsdInfo TSDInfo + PyThreadState_frame uint8 + PyCFrame_current_frame uint8 + PyFrameObject_f_back uint8 + PyFrameObject_f_code uint8 + PyFrameObject_f_lasti uint8 + PyFrameObject_entry_member uint8 + PyFrameObject_entry_val uint8 + PyCodeObject_co_argcount uint8 + PyCodeObject_co_kwonlyargcount uint8 + PyCodeObject_co_flags uint8 + PyCodeObject_co_firstlineno uint8 + PyCodeObject_sizeof uint8 + Pad_cgo_0 [6]byte +} type RubyProcInfo struct { Version uint32 Current_ctx_ptr uint64 @@ -123,8 +292,48 @@ type RubyProcInfo struct { Running_ec uint16 Pad_cgo_0 [2]byte } +type V8ProcInfo struct { + Version uint32 + Context_handle_offset uint32 + Native_context_offset uint32 + Embedder_data_offset uint32 + Environment_pointer_offset uint32 + Execution_async_id_offset uint32 + Type_JSFunction_first uint16 + Type_JSFunction_last uint16 + Type_Code uint16 + Type_SharedFunctionInfo uint16 + Off_HeapObject_map uint8 + Off_Map_instancetype uint8 + Off_JSFunction_code uint8 + Off_JSFunction_shared uint8 + Off_Code_instruction_start uint8 + Off_Code_instruction_size uint8 + Off_Code_flags uint8 + Fp_marker uint8 + Fp_function uint8 + Fp_bytecode_offset uint8 + Codekind_shift uint8 + Codekind_mask uint8 + Codekind_baseline uint8 + Isolate_sym uint64 +} +type NativeCustomLabelsProcInfo struct { + Current_set_tls_offset uint64 + Has_current_hm bool + Current_hm_tls_offset uint64 +} +type LuaJITProcInfo struct { + G2dispatch uint16 + Cur_L_offset uint16 + Cframe_size_jit uint16 +} const ( + Sizeof_Frame = 0x18 + Sizeof_StackDelta = 0x4 + Sizeof_Trace = 0x1ad0 + sizeof_ApmIntProcInfo = 0x8 sizeof_DotnetProcInfo = 0x4 sizeof_PHPProcInfo = 0x18 @@ -154,3 +363,147 @@ const ( UnwindDerefMask int32 = 0x7 UnwindDerefMultiplier int32 = 0x8 ) + +const ( + FrameHotspotStub = 0x0 + FrameHotspotVtable = 0x1 + FrameHotspotInterpreter = 0x2 + FrameHotspotNative = 0x3 + + V8SmiTag = 0x0 + V8SmiTagMask = 0x1 + V8SmiTagShift = 0x1 + V8SmiValueShift = 0x20 + V8HeapObjectTag = 0x1 + V8HeapObjectTagMask = 0x3 + + V8FpContextSize = 0x40 + + V8FileTypeMarker = 0x0 + V8FileTypeByteCode = 0x1 + V8FileTypeNativeSFI = 0x2 + V8FileTypeNativeCode = 0x3 + V8FileTypeNativeJSFunc = 0x4 + V8FileTypeMask = 0x7 + + V8LineCookieShift = 0x20 + V8LineCookieMask = 0xffffffff00000000 + V8LineDeltaMask = 0xffffffff +) + +const ( + LJFFIFunc = 0xff1 + LJFileId = 0x2a +) + +var MetricsTranslation = []metrics.MetricID{ + 0x0: metrics.IDUnwindCallInterpreter, + 0x1: metrics.IDUnwindErrZeroPC, + 0x2: metrics.IDUnwindErrStackLengthExceeded, + 0x3: metrics.IDUnwindErrBadTLSAddr, + 0x4: metrics.IDUnwindErrBadTPBaseAddr, + 0x5: metrics.IDUnwindNativeAttempts, + 0x6: metrics.IDUnwindNativeFrames, + 0x7: metrics.IDUnwindNativeStackDeltaStop, + 0x8: metrics.IDUnwindNativeErrLookupTextSection, + 0x9: metrics.IDUnwindNativeErrLookupIterations, + 0xa: metrics.IDUnwindNativeErrLookupRange, + 0xb: metrics.IDUnwindNativeErrKernelAddress, + 0xc: metrics.IDUnwindNativeErrWrongTextSection, + 0xe: metrics.IDUnwindNativeErrPCRead, + 0x15: metrics.IDUnwindPythonAttempts, + 0x16: metrics.IDUnwindPythonFrames, + 0x17: metrics.IDUnwindPythonErrBadPyThreadStateCurrentAddr, + 0x18: metrics.IDUnwindPythonErrZeroThreadState, + 0x19: metrics.IDUnwindPythonErrBadThreadStateFrameAddr, + 0x1c: metrics.IDUnwindPythonZeroFrameCodeObject, + 0x1e: metrics.IDUnwindPythonErrBadCodeObjectArgCountAddr, + 0xd: metrics.IDUnwindNativeErrStackDeltaInvalid, + 0x28: metrics.IDErrEmptyStack, + 0x29: metrics.IDUnwindHotspotAttempts, + 0x2a: metrics.IDUnwindHotspotFrames, + 0x2b: metrics.IDUnwindHotspotErrNoCodeblob, + 0x2c: metrics.IDUnwindHotspotErrInvalidCodeblob, + 0x2d: metrics.IDUnwindHotspotErrInterpreterFP, + 0x2f: metrics.IDUnwindHotspotErrLrUnwindingMidTrace, + 0x30: metrics.IDHotspotUnsupportedFrameSize, + 0x31: metrics.IDUnwindNativeSmallPC, + 0x32: metrics.IDUnwindNativeErrLookupStackDeltaInnerMap, + 0x33: metrics.IDUnwindNativeErrLookupStackDeltaOuterMap, + 0x34: metrics.IDErrBPFCurrentComm, + 0x22: metrics.IDUnwindPHPAttempts, + 0x23: metrics.IDUnwindPHPFrames, + 0x24: metrics.IDUnwindPHPErrBadCurrentExecuteData, + 0x25: metrics.IDUnwindPHPErrBadZendExecuteData, + 0x26: metrics.IDUnwindPHPErrBadZendFunction, + 0x27: metrics.IDUnwindPHPErrBadZendOpline, + 0x35: metrics.IDUnwindRubyAttempts, + 0x36: metrics.IDUnwindRubyFrames, + 0xf: metrics.IDUnwindPerlAttempts, + 0x10: metrics.IDUnwindPerlFrames, + 0x11: metrics.IDUnwindPerlTLS, + 0x12: metrics.IDUnwindPerlReadStackInfo, + 0x13: metrics.IDUnwindPerlReadContextStackEntry, + 0x14: metrics.IDUnwindPerlResolveEGV, + 0x2e: metrics.IDUnwindHotspotErrInvalidRA, + 0x37: metrics.IDUnwindV8Attempts, + 0x38: metrics.IDUnwindV8Frames, + 0x39: metrics.IDUnwindV8ErrBadFP, + 0x3a: metrics.IDUnwindV8ErrBadJSFunc, + 0x3b: metrics.IDUnwindV8ErrBadCode, + 0x3d: metrics.IDReportedPIDsErr, + 0x3e: metrics.IDPIDEventsErr, + 0x3c: metrics.IDUnwindNativeLr0, + 0x3f: metrics.IDNumProcNew, + 0x40: metrics.IDNumProcExit, + 0x41: metrics.IDNumUnknownPC, + 0x42: metrics.IDNumGenericPID, + 0x43: metrics.IDUnwindPythonErrBadCFrameFrameAddr, + 0x44: metrics.IDMaxTailCalls, + 0x45: metrics.IDUnwindPythonErrNoProcInfo, + 0x46: metrics.IDUnwindPythonErrBadAutoTlsKeyAddr, + 0x47: metrics.IDUnwindPythonErrReadThreadStateAddr, + 0x48: metrics.IDUnwindPythonErrReadTsdBase, + 0x49: metrics.IDUnwindRubyErrNoProcInfo, + 0x4a: metrics.IDUnwindRubyErrReadStackPtr, + 0x4b: metrics.IDUnwindRubyErrReadStackSize, + 0x4c: metrics.IDUnwindRubyErrReadCfp, + 0x4d: metrics.IDUnwindRubyErrReadEp, + 0x4e: metrics.IDUnwindRubyErrReadIseqBody, + 0x4f: metrics.IDUnwindRubyErrReadIseqEncoded, + 0x50: metrics.IDUnwindRubyErrReadIseqSize, + 0x51: metrics.IDUnwindNativeErrLrUnwindingMidTrace, + 0x52: metrics.IDUnwindNativeErrReadKernelModeRegs, + 0x53: metrics.IDUnwindNativeErrChaseIrqStackLink, + 0x54: metrics.IDUnwindV8ErrNoProcInfo, + 0x55: metrics.IDUnwindNativeErrBadUnwindInfoIndex, + 0x56: metrics.IDUnwindApmIntErrReadTsdBase, + 0x57: metrics.IDUnwindApmIntErrReadCorrBufPtr, + 0x58: metrics.IDUnwindApmIntErrReadCorrBuf, + 0x59: metrics.IDUnwindApmIntReadSuccesses, + 0x5a: metrics.IDUnwindDotnetAttempts, + 0x5b: metrics.IDUnwindDotnetFrames, + 0x5c: metrics.IDUnwindDotnetErrNoProcInfo, + 0x5d: metrics.IDUnwindDotnetErrBadFP, + 0x5e: metrics.IDUnwindDotnetErrCodeHeader, + 0x5f: metrics.IDUnwindDotnetErrCodeTooLarge, + 0x69: metrics.IDUnwindLuaJITAttempts, + 0x6a: metrics.IDUnwindLuaJITErrNoProcInfo, + 0x6f: metrics.IDUnwindNodeClFailedReadHmPointer, + 0x70: metrics.IDUnwindNodeClFailedNoLsInHm, + 0x71: metrics.IDUnwindNodeClFailedReadHmStruct, + 0x72: metrics.IDUnwindNodeClFailedReadBucket, + 0x73: metrics.IDUnwindNodeClFailedReadLsAddr, + 0x74: metrics.IDUnwindNodeClFailedTooManyBuckets, + 0x75: metrics.IDUnwindNodeClFailedGettingId, + 0x76: metrics.IDUnwindNodeClWarnIdZero, + 0x77: metrics.IDUnwindNodeAsyncIdErrGetTlsSymbol, + 0x78: metrics.IDUnwindNodeAsyncIdErrReadIsolate, + 0x79: metrics.IDUnwindNodeAsyncIdErrReadContextHandle, + 0x7a: metrics.IDUnwindNodeAsyncIdErrReadRealContextHandle, + 0x7b: metrics.IDUnwindNodeAsyncIdErrReadNativeContext, + 0x7c: metrics.IDUnwindNodeAsyncIdErrReadEmbedderData, + 0x7d: metrics.IDUnwindNodeAsyncIdErrReadEnvPtr, + 0x7e: metrics.IDUnwindNodeAsyncIdErrReadIdField, + 0x7f: metrics.IDUnwindNodeAsyncIdErrReadIdDouble, +} diff --git a/support/types_def.go b/support/types_def.go index dee0ea1c5..5be2dd3e3 100644 --- a/support/types_def.go +++ b/support/types_def.go @@ -5,10 +5,16 @@ package support // import "go.opentelemetry.io/ebpf-profiler/support" +import ( + "go.opentelemetry.io/ebpf-profiler/metrics" +) + /* #include "./ebpf/types.h" #include "./ebpf/frametypes.h" #include "./ebpf/stackdeltatypes.h" +#include "./ebpf/v8_tracer.h" +#include "./ebpf/luajit.h" */ import "C" @@ -98,12 +104,41 @@ const ( TraceOriginMemory = C.TRACE_MEMORY ) +type ApmSpanID C.ApmSpanID +type ApmTraceID C.ApmTraceID +type CustomLabel C.CustomLabel +type CustomLabelsArray C.CustomLabelsArray +type Event C.Event +type Frame C.Frame +type OffsetRange C.OffsetRange +type PIDPage C.PIDPage +type PIDPageMappingInfo C.PIDPageMappingInfo +type StackDelta C.StackDelta +type StackDeltaPageInfo C.StackDeltaPageInfo +type StackDeltaPageKey C.StackDeltaPageKey +type SystemAnalysis C.SystemAnalysis +type SystemConfig C.SystemConfig +type TSDInfo C.TSDInfo +type Trace C.Trace +type UnwindInfo C.UnwindInfo + type ApmIntProcInfo C.ApmIntProcInfo type DotnetProcInfo C.DotnetProcInfo +type GoLabelsOffsets C.GoLabelsOffsets +type HotspotProcInfo C.HotspotProcInfo type PHPProcInfo C.PHPProcInfo +type PerlProcInfo C.PerlProcInfo +type PyProcInfo C.PyProcInfo type RubyProcInfo C.RubyProcInfo +type V8ProcInfo C.V8ProcInfo +type NativeCustomLabelsProcInfo C.NativeCustomLabelsProcInfo +type LuaJITProcInfo C.LuaJITProcInfo const ( + Sizeof_Frame = C.sizeof_Frame + Sizeof_StackDelta = C.sizeof_StackDelta + Sizeof_Trace = C.sizeof_Trace + sizeof_ApmIntProcInfo = C.sizeof_ApmIntProcInfo sizeof_DotnetProcInfo = C.sizeof_DotnetProcInfo sizeof_PHPProcInfo = C.sizeof_PHPProcInfo @@ -136,3 +171,149 @@ const ( UnwindDerefMask int32 = C.UNWIND_DEREF_MASK UnwindDerefMultiplier int32 = C.UNWIND_DEREF_MULTIPLIER ) + +const ( + // Hotspot specific + FrameHotspotStub = C.FRAME_HOTSPOT_STUB + FrameHotspotVtable = C.FRAME_HOTSPOT_VTABLE + FrameHotspotInterpreter = C.FRAME_HOTSPOT_INTERPRETER + FrameHotspotNative = C.FRAME_HOTSPOT_NATIVE + + // V8 specific + V8SmiTag = C.V8_SmiTag + V8SmiTagMask = C.V8_SmiTagMask + V8SmiTagShift = C.V8_SmiTagShift + V8SmiValueShift = C.V8_SmiValueShift + V8HeapObjectTag = C.V8_HeapObjectTag + V8HeapObjectTagMask = C.V8_HeapObjectTagMask + + V8FpContextSize = C.V8_FP_CONTEXT_SIZE + + V8FileTypeMarker = C.V8_FILE_TYPE_MARKER + V8FileTypeByteCode = C.V8_FILE_TYPE_BYTECODE + V8FileTypeNativeSFI = C.V8_FILE_TYPE_NATIVE_SFI + V8FileTypeNativeCode = C.V8_FILE_TYPE_NATIVE_CODE + V8FileTypeNativeJSFunc = C.V8_FILE_TYPE_NATIVE_JSFUNC + V8FileTypeMask = C.V8_FILE_TYPE_MASK + + V8LineCookieShift = C.V8_LINE_COOKIE_SHIFT + V8LineCookieMask = C.V8_LINE_COOKIE_MASK + V8LineDeltaMask = C.V8_LINE_DELTA_MASK +) + +const ( + LJFFIFunc = C.LUAJIT_FFI_FUNC + LJFileId = C.LUAJIT_JIT_FILE_ID +) + +var MetricsTranslation = []metrics.MetricID{ + C.metricID_UnwindCallInterpreter: metrics.IDUnwindCallInterpreter, + C.metricID_UnwindErrZeroPC: metrics.IDUnwindErrZeroPC, + C.metricID_UnwindErrStackLengthExceeded: metrics.IDUnwindErrStackLengthExceeded, + C.metricID_UnwindErrBadTSDAddr: metrics.IDUnwindErrBadTLSAddr, + C.metricID_UnwindErrBadTPBaseAddr: metrics.IDUnwindErrBadTPBaseAddr, + C.metricID_UnwindNativeAttempts: metrics.IDUnwindNativeAttempts, + C.metricID_UnwindNativeFrames: metrics.IDUnwindNativeFrames, + C.metricID_UnwindNativeStackDeltaStop: metrics.IDUnwindNativeStackDeltaStop, + C.metricID_UnwindNativeErrLookupTextSection: metrics.IDUnwindNativeErrLookupTextSection, + C.metricID_UnwindNativeErrLookupIterations: metrics.IDUnwindNativeErrLookupIterations, + C.metricID_UnwindNativeErrLookupRange: metrics.IDUnwindNativeErrLookupRange, + C.metricID_UnwindNativeErrKernelAddress: metrics.IDUnwindNativeErrKernelAddress, + C.metricID_UnwindNativeErrWrongTextSection: metrics.IDUnwindNativeErrWrongTextSection, + C.metricID_UnwindNativeErrPCRead: metrics.IDUnwindNativeErrPCRead, + C.metricID_UnwindPythonAttempts: metrics.IDUnwindPythonAttempts, + C.metricID_UnwindPythonFrames: metrics.IDUnwindPythonFrames, + C.metricID_UnwindPythonErrBadPyThreadStateCurrentAddr: metrics.IDUnwindPythonErrBadPyThreadStateCurrentAddr, + C.metricID_UnwindPythonErrZeroThreadState: metrics.IDUnwindPythonErrZeroThreadState, + C.metricID_UnwindPythonErrBadThreadStateFrameAddr: metrics.IDUnwindPythonErrBadThreadStateFrameAddr, + C.metricID_UnwindPythonZeroFrameCodeObject: metrics.IDUnwindPythonZeroFrameCodeObject, + C.metricID_UnwindPythonErrBadCodeObjectArgCountAddr: metrics.IDUnwindPythonErrBadCodeObjectArgCountAddr, + C.metricID_UnwindNativeErrStackDeltaInvalid: metrics.IDUnwindNativeErrStackDeltaInvalid, + C.metricID_ErrEmptyStack: metrics.IDErrEmptyStack, + C.metricID_UnwindHotspotAttempts: metrics.IDUnwindHotspotAttempts, + C.metricID_UnwindHotspotFrames: metrics.IDUnwindHotspotFrames, + C.metricID_UnwindHotspotErrNoCodeblob: metrics.IDUnwindHotspotErrNoCodeblob, + C.metricID_UnwindHotspotErrInvalidCodeblob: metrics.IDUnwindHotspotErrInvalidCodeblob, + C.metricID_UnwindHotspotErrInterpreterFP: metrics.IDUnwindHotspotErrInterpreterFP, + C.metricID_UnwindHotspotErrLrUnwindingMidTrace: metrics.IDUnwindHotspotErrLrUnwindingMidTrace, + C.metricID_UnwindHotspotUnsupportedFrameSize: metrics.IDHotspotUnsupportedFrameSize, + C.metricID_UnwindNativeSmallPC: metrics.IDUnwindNativeSmallPC, + C.metricID_UnwindNativeErrLookupStackDeltaInnerMap: metrics.IDUnwindNativeErrLookupStackDeltaInnerMap, + C.metricID_UnwindNativeErrLookupStackDeltaOuterMap: metrics.IDUnwindNativeErrLookupStackDeltaOuterMap, + C.metricID_ErrBPFCurrentComm: metrics.IDErrBPFCurrentComm, + C.metricID_UnwindPHPAttempts: metrics.IDUnwindPHPAttempts, + C.metricID_UnwindPHPFrames: metrics.IDUnwindPHPFrames, + C.metricID_UnwindPHPErrBadCurrentExecuteData: metrics.IDUnwindPHPErrBadCurrentExecuteData, + C.metricID_UnwindPHPErrBadZendExecuteData: metrics.IDUnwindPHPErrBadZendExecuteData, + C.metricID_UnwindPHPErrBadZendFunction: metrics.IDUnwindPHPErrBadZendFunction, + C.metricID_UnwindPHPErrBadZendOpline: metrics.IDUnwindPHPErrBadZendOpline, + C.metricID_UnwindRubyAttempts: metrics.IDUnwindRubyAttempts, + C.metricID_UnwindRubyFrames: metrics.IDUnwindRubyFrames, + C.metricID_UnwindPerlAttempts: metrics.IDUnwindPerlAttempts, + C.metricID_UnwindPerlFrames: metrics.IDUnwindPerlFrames, + C.metricID_UnwindPerlTSD: metrics.IDUnwindPerlTLS, + C.metricID_UnwindPerlReadStackInfo: metrics.IDUnwindPerlReadStackInfo, + C.metricID_UnwindPerlReadContextStackEntry: metrics.IDUnwindPerlReadContextStackEntry, + C.metricID_UnwindPerlResolveEGV: metrics.IDUnwindPerlResolveEGV, + C.metricID_UnwindHotspotErrInvalidRA: metrics.IDUnwindHotspotErrInvalidRA, + C.metricID_UnwindV8Attempts: metrics.IDUnwindV8Attempts, + C.metricID_UnwindV8Frames: metrics.IDUnwindV8Frames, + C.metricID_UnwindV8ErrBadFP: metrics.IDUnwindV8ErrBadFP, + C.metricID_UnwindV8ErrBadJSFunc: metrics.IDUnwindV8ErrBadJSFunc, + C.metricID_UnwindV8ErrBadCode: metrics.IDUnwindV8ErrBadCode, + C.metricID_ReportedPIDsErr: metrics.IDReportedPIDsErr, + C.metricID_PIDEventsErr: metrics.IDPIDEventsErr, + C.metricID_UnwindNativeLr0: metrics.IDUnwindNativeLr0, + C.metricID_NumProcNew: metrics.IDNumProcNew, + C.metricID_NumProcExit: metrics.IDNumProcExit, + C.metricID_NumUnknownPC: metrics.IDNumUnknownPC, + C.metricID_NumGenericPID: metrics.IDNumGenericPID, + C.metricID_UnwindPythonErrBadCFrameFrameAddr: metrics.IDUnwindPythonErrBadCFrameFrameAddr, + C.metricID_MaxTailCalls: metrics.IDMaxTailCalls, + C.metricID_UnwindPythonErrNoProcInfo: metrics.IDUnwindPythonErrNoProcInfo, + C.metricID_UnwindPythonErrBadAutoTlsKeyAddr: metrics.IDUnwindPythonErrBadAutoTlsKeyAddr, + C.metricID_UnwindPythonErrReadThreadStateAddr: metrics.IDUnwindPythonErrReadThreadStateAddr, + C.metricID_UnwindPythonErrReadTsdBase: metrics.IDUnwindPythonErrReadTsdBase, + C.metricID_UnwindRubyErrNoProcInfo: metrics.IDUnwindRubyErrNoProcInfo, + C.metricID_UnwindRubyErrReadStackPtr: metrics.IDUnwindRubyErrReadStackPtr, + C.metricID_UnwindRubyErrReadStackSize: metrics.IDUnwindRubyErrReadStackSize, + C.metricID_UnwindRubyErrReadCfp: metrics.IDUnwindRubyErrReadCfp, + C.metricID_UnwindRubyErrReadEp: metrics.IDUnwindRubyErrReadEp, + C.metricID_UnwindRubyErrReadIseqBody: metrics.IDUnwindRubyErrReadIseqBody, + C.metricID_UnwindRubyErrReadIseqEncoded: metrics.IDUnwindRubyErrReadIseqEncoded, + C.metricID_UnwindRubyErrReadIseqSize: metrics.IDUnwindRubyErrReadIseqSize, + C.metricID_UnwindNativeErrLrUnwindingMidTrace: metrics.IDUnwindNativeErrLrUnwindingMidTrace, + C.metricID_UnwindNativeErrReadKernelModeRegs: metrics.IDUnwindNativeErrReadKernelModeRegs, + C.metricID_UnwindNativeErrChaseIrqStackLink: metrics.IDUnwindNativeErrChaseIrqStackLink, + C.metricID_UnwindV8ErrNoProcInfo: metrics.IDUnwindV8ErrNoProcInfo, + C.metricID_UnwindNativeErrBadUnwindInfoIndex: metrics.IDUnwindNativeErrBadUnwindInfoIndex, + C.metricID_UnwindApmIntErrReadTsdBase: metrics.IDUnwindApmIntErrReadTsdBase, + C.metricID_UnwindApmIntErrReadCorrBufPtr: metrics.IDUnwindApmIntErrReadCorrBufPtr, + C.metricID_UnwindApmIntErrReadCorrBuf: metrics.IDUnwindApmIntErrReadCorrBuf, + C.metricID_UnwindApmIntReadSuccesses: metrics.IDUnwindApmIntReadSuccesses, + C.metricID_UnwindDotnetAttempts: metrics.IDUnwindDotnetAttempts, + C.metricID_UnwindDotnetFrames: metrics.IDUnwindDotnetFrames, + C.metricID_UnwindDotnetErrNoProcInfo: metrics.IDUnwindDotnetErrNoProcInfo, + C.metricID_UnwindDotnetErrBadFP: metrics.IDUnwindDotnetErrBadFP, + C.metricID_UnwindDotnetErrCodeHeader: metrics.IDUnwindDotnetErrCodeHeader, + C.metricID_UnwindDotnetErrCodeTooLarge: metrics.IDUnwindDotnetErrCodeTooLarge, + C.metricID_UnwindLuaJITAttempts: metrics.IDUnwindLuaJITAttempts, + C.metricID_UnwindLuaJITErrNoProcInfo: metrics.IDUnwindLuaJITErrNoProcInfo, + C.metricID_UnwindNodeClFailedReadHmPointer: metrics.IDUnwindNodeClFailedReadHmPointer, + C.metricID_UnwindNodeClFailedNoLsInHm: metrics.IDUnwindNodeClFailedNoLsInHm, + C.metricID_UnwindNodeClFailedReadHmStruct: metrics.IDUnwindNodeClFailedReadHmStruct, + C.metricID_UnwindNodeClFailedReadBucket: metrics.IDUnwindNodeClFailedReadBucket, + C.metricID_UnwindNodeClFailedReadLsAddr: metrics.IDUnwindNodeClFailedReadLsAddr, + C.metricID_UnwindNodeClFailedTooManyBuckets: metrics.IDUnwindNodeClFailedTooManyBuckets, + C.metricID_UnwindNodeClFailedGettingId: metrics.IDUnwindNodeClFailedGettingId, + C.metricID_UnwindNodeClWarnIdZero: metrics.IDUnwindNodeClWarnIdZero, + C.metricID_UnwindNodeAsyncIdErrGetTlsSymbol: metrics.IDUnwindNodeAsyncIdErrGetTlsSymbol, + C.metricID_UnwindNodeAsyncIdErrReadIsolate: metrics.IDUnwindNodeAsyncIdErrReadIsolate, + C.metricID_UnwindNodeAsyncIdErrReadContextHandle: metrics.IDUnwindNodeAsyncIdErrReadContextHandle, + C.metricID_UnwindNodeAsyncIdErrReadRealContextHandle: metrics.IDUnwindNodeAsyncIdErrReadRealContextHandle, + C.metricID_UnwindNodeAsyncIdErrReadNativeContext: metrics.IDUnwindNodeAsyncIdErrReadNativeContext, + C.metricID_UnwindNodeAsyncIdErrReadEmbedderData: metrics.IDUnwindNodeAsyncIdErrReadEmbedderData, + C.metricID_UnwindNodeAsyncIdErrReadEnvPtr: metrics.IDUnwindNodeAsyncIdErrReadEnvPtr, + C.metricID_UnwindNodeAsyncIdErrReadIdField: metrics.IDUnwindNodeAsyncIdErrReadIdField, + C.metricID_UnwindNodeAsyncIdErrReadIdDouble: metrics.IDUnwindNodeAsyncIdErrReadIdDouble, +} diff --git a/tracer/events.go b/tracer/events.go index 14b878f5b..5fdeab0ba 100644 --- a/tracer/events.go +++ b/tracer/events.go @@ -22,12 +22,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/times" ) -/* -#include -#include "../support/ebpf/types.h" -*/ -import "C" - const ( // Length of the pidEvents channel. It must be large enough so the // consuming goroutine doesn't go idle due to scheduling, but small enough @@ -79,8 +73,8 @@ func (t *Tracer) handleGenericPID() { // C structure in the received data is transformed to a Go structure and the event // handler is invoked. func (t *Tracer) triggerPidEvent(data []byte) { - event := (*C.Event)(unsafe.Pointer(&data[0])) - if event.event_type == support.EventTypeGenericPID { + event := (*support.Event)(unsafe.Pointer(&data[0])) + if event.Type == support.EventTypeGenericPID { t.handleGenericPID() } } @@ -143,7 +137,7 @@ func (t *Tracer) startTraceEventMonitor(ctx context.Context, traceOutChan chan<- *host.Trace) func() []metrics.Metric { eventsMap := t.ebpfMaps["trace_events"] eventReader, err := perf.NewReader(eventsMap, - t.samplesPerSecond*int(unsafe.Sizeof(C.Trace{}))) + t.samplesPerSecond*support.Sizeof_Trace) if err != nil { log.Fatalf("Failed to setup perf reporting via %s: %v", eventsMap, err) } diff --git a/tracer/systemconfig.go b/tracer/systemconfig.go index d2d63a2d8..0075d0812 100644 --- a/tracer/systemconfig.go +++ b/tracer/systemconfig.go @@ -16,6 +16,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/pacmask" "go.opentelemetry.io/ebpf-profiler/rlimit" + "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/tracer/types" cebpf "github.com/cilium/ebpf" @@ -24,9 +25,6 @@ import ( log "github.com/sirupsen/logrus" ) -// #include "../support/ebpf/types.h" -import "C" - // memberByName resolves btf Member from a Struct with given name func memberByName(t *btf.Struct, field string) (*btf.Member, error) { for i, m := range t.Members { @@ -72,7 +70,7 @@ func getTSDBaseFieldSpec() string { } // parseBTF resolves the SystemConfig data from kernel BTF -func parseBTF(syscfg *C.SystemConfig) error { +func parseBTF(syscfg *support.SystemConfig) error { fh, err := os.Open("/sys/kernel/btf/vmlinux") if err != nil { return err @@ -94,13 +92,13 @@ func parseBTF(syscfg *C.SystemConfig) error { if err != nil { return err } - syscfg.task_stack_offset = C.u32(stackOffset) + syscfg.Task_stack_offset = uint32(stackOffset) tpbaseOffset, err := calculateFieldOffset(taskStruct, getTSDBaseFieldSpec()) if err != nil { return err } - syscfg.tpbase_offset = C.u64(tpbaseOffset) + syscfg.Tpbase_offset = uint64(tpbaseOffset) return nil } @@ -111,9 +109,9 @@ func executeSystemAnalysisBpfCode(progSpec *cebpf.ProgramSpec, maps map[string]* systemAnalysis := maps["system_analysis"] key0 := uint32(0) - data := C.SystemAnalysis{ - pid: C.uint(os.Getpid()), - address: C.u64(address), + data := support.SystemAnalysis{ + Pid: uint32(os.Getpid()), + Address: uint64(address), } if err = systemAnalysis.Update(unsafe.Pointer(&key0), unsafe.Pointer(&data), @@ -152,14 +150,12 @@ func executeSystemAnalysisBpfCode(progSpec *cebpf.ProgramSpec, maps map[string]* return nil, 0, fmt.Errorf("failed to configure tracepoint: %v", err) } err = systemAnalysis.Lookup(unsafe.Pointer(&key0), unsafe.Pointer(&data)) - progLink.Close() + _ = progLink.Close() if err != nil { return nil, 0, fmt.Errorf("failed to get analysis data: %v", err) } - //nolint:gocritic - return C.GoBytes(unsafe.Pointer(&data.code[0]), C.int(len(data.code))), - uint64(data.address), nil + return data.Code[:], data.Address, nil } // loadKernelCode will request the ebpf code to read the first X bytes from given address. @@ -183,20 +179,20 @@ func readTaskStruct(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, // determineStackPtregs determines the offset of `struct pt_regs` within the entry stack // when the `stack` field offset within `task_struct` is already known. func determineStackPtregs(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, - syscfg *C.SystemConfig) error { - data, ptregs, err := readTaskStruct(coll, maps, libpf.SymbolValue(syscfg.task_stack_offset)) + syscfg *support.SystemConfig) error { + data, ptregs, err := readTaskStruct(coll, maps, libpf.SymbolValue(syscfg.Task_stack_offset)) if err != nil { return err } stackBase := binary.LittleEndian.Uint64(data) - syscfg.stack_ptregs_offset = C.u32(ptregs - stackBase) + syscfg.Stack_ptregs_offset = uint32(ptregs - stackBase) return nil } // determineStackLayout scans `task_struct` for offset of the `stack` field, and using // its value determines the offset of `struct pt_regs` within the entry stack. func determineStackLayout(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, - syscfg *C.SystemConfig) error { + syscfg *support.SystemConfig) error { const maxTaskStructSize = 8 * 1024 const maxStackSize = 64 * 1024 @@ -215,8 +211,8 @@ func determineStackLayout(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map continue } if ptregs > stackBase && ptregs < stackBase+maxStackSize { - syscfg.task_stack_offset = C.u32(offs + i) - syscfg.stack_ptregs_offset = C.u32(ptregs - stackBase) + syscfg.Task_stack_offset = uint32(offs + i) + syscfg.Stack_ptregs_offset = uint32(ptregs - stackBase) return nil } } @@ -234,10 +230,10 @@ func loadSystemConfig(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, } else { log.Debug("PAC is not enabled on the system.") } - syscfg := C.SystemConfig{ - inverse_pac_mask: ^C.u64(pacMask), - drop_error_only_traces: C.bool(filterErrorFrames), - off_cpu_threshold: C.u32(offCPUThreshold), + syscfg := support.SystemConfig{ + Inverse_pac_mask: ^pacMask, + Drop_error_only_traces: filterErrorFrames, + Off_cpu_threshold: offCPUThreshold, } if err := parseBTF(&syscfg); err != nil { @@ -254,7 +250,7 @@ func loadSystemConfig(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, if err != nil { return err } - syscfg.tpbase_offset = C.u64(tpbaseOffset) + syscfg.Tpbase_offset = tpbaseOffset } } else { // Sadly BTF does not currently include THREAD_SIZE which is needed @@ -267,9 +263,9 @@ func loadSystemConfig(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, } log.Infof("Found offsets: task stack %#x, pt_regs %#x, tpbase %#x", - syscfg.task_stack_offset, - syscfg.stack_ptregs_offset, - syscfg.tpbase_offset) + syscfg.Task_stack_offset, + syscfg.Stack_ptregs_offset, + syscfg.Tpbase_offset) key0 := uint32(0) return maps["system_config"].Update(unsafe.Pointer(&key0), unsafe.Pointer(&syscfg), diff --git a/tracer/tracer.go b/tracer/tracer.go index d960a1622..7196e129a 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -6,6 +6,7 @@ package tracer // import "go.opentelemetry.io/ebpf-profiler/tracer" import ( "bufio" + "bytes" "context" "errors" "fmt" @@ -40,12 +41,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/tracer/types" ) -/* -#include -#include "../support/ebpf/types.h" -*/ -import "C" - // Compile time check to make sure config.Times satisfies the interfaces. var _ Intervals = (*times.Times)(nil) @@ -172,6 +167,15 @@ type progLoaderHelper struct { noTailCallTarget bool } +// Convert a C-string to Go string. +func goString(cstr []byte) string { + index := bytes.IndexByte(cstr, byte(0)) + if index < 0 { + index = len(cstr) + } + return strings.Clone(unsafe.String(unsafe.SliceData(cstr), index)) +} + // NewTracer loads eBPF code and map definitions from the ELF module at the configured path. func NewTracer(ctx context.Context, cfg *Config) (*Tracer, error) { kernelSymbolizer, err := kallsyms.NewSymbolizer() @@ -204,8 +208,6 @@ func NewTracer(ctx context.Context, cfg *Config) (*Tracer, error) { return nil, fmt.Errorf("failed to create processManager: %v", err) } - const fallbackSymbolsCacheSize = 16384 - perfEventList := []*perf.Event{} tracer := &Tracer{ @@ -265,8 +267,8 @@ func buildStackDeltaTemplates(coll *cebpf.CollectionSpec) error { } def.InnerMap = &cebpf.MapSpec{ Type: cebpf.Array, - KeySize: uint32(C.sizeof_uint32_t), - ValueSize: uint32(C.sizeof_StackDelta), + KeySize: 4, + ValueSize: support.Sizeof_StackDelta, MaxEntries: 1 << i, } } @@ -674,8 +676,8 @@ func loadProgram(ebpfProgs map[string]*cebpf.Program, tailcallMap *cebpf.Map, // It returns the number of kernel frames for kstackID or an error. func (t *Tracer) insertKernelFrames(trace *host.Trace, ustackLen uint32, kstackID int32) (uint32, error) { - cKstackID := C.s32(kstackID) - kstackVal := make([]C.uint64_t, support.PerfMaxStackDepth) + cKstackID := kstackID + kstackVal := make([]uint64, support.PerfMaxStackDepth) if err := t.ebpfMaps["kernel_stackmap"].Lookup(unsafe.Pointer(&cKstackID), unsafe.Pointer(&kstackVal[0])); err != nil { @@ -882,34 +884,34 @@ func (t *Tracer) eBPFMetricsCollector( // If the raw trace contains a kernel stack ID, the kernel stack is also // retrieved and inserted at the appropriate position. func (t *Tracer) loadBpfTrace(raw []byte, cpu int) *host.Trace { - frameListOffs := int(unsafe.Offsetof(C.Trace{}.frames)) + frameListOffs := int(unsafe.Offsetof(support.Trace{}.Frames)) if len(raw) < frameListOffs { panic("trace record too small") } - frameSize := int(unsafe.Sizeof(C.Frame{})) - ptr := (*C.Trace)(unsafe.Pointer(unsafe.SliceData(raw))) + frameSize := support.Sizeof_Frame + ptr := (*support.Trace)(unsafe.Pointer(unsafe.SliceData(raw))) // NOTE: can't do exact check here: kernel adds a few padding bytes to messages. - if len(raw) < frameListOffs+int(ptr.stack_len)*frameSize { + if len(raw) < frameListOffs+int(ptr.Stack_len)*frameSize { panic("unexpected record size") } - pid := libpf.PID(ptr.pid) + pid := libpf.PID(ptr.Pid) procMeta := t.processManager.MetaForPID(pid) trace := &host.Trace{ - Comm: C.GoString((*C.char)(unsafe.Pointer(&ptr.comm))), + Comm: goString(ptr.Comm[:]), ExecutablePath: procMeta.Executable, ContainerID: procMeta.ContainerID, ProcessName: procMeta.Name, - APMTraceID: *(*libpf.APMTraceID)(unsafe.Pointer(&ptr.apm_trace_id)), - APMTransactionID: *(*libpf.APMTransactionID)(unsafe.Pointer(&ptr.apm_transaction_id)), + APMTraceID: *(*libpf.APMTraceID)(unsafe.Pointer(&ptr.Apm_trace_id)), + APMTransactionID: *(*libpf.APMTransactionID)(unsafe.Pointer(&ptr.Apm_transaction_id)), PID: pid, - TID: libpf.PID(ptr.tid), - Origin: libpf.Origin(ptr.origin), - OffTime: int64(ptr.offtime), - KTime: times.KTime(ptr.ktime), + TID: libpf.PID(ptr.Tid), + Origin: libpf.Origin(ptr.Origin), + OffTime: int64(ptr.Offtime), + KTime: times.KTime(ptr.Ktime), CPU: cpu, EnvVars: procMeta.EnvVariables, } @@ -919,12 +921,13 @@ func (t *Tracer) loadBpfTrace(raw []byte, cpu int) *host.Trace { return nil } - if ptr.custom_labels.len > 0 { - trace.CustomLabels = make(map[string]string, int(ptr.custom_labels.len)) - for i := 0; i < int(ptr.custom_labels.len); i++ { - lbl := ptr.custom_labels.labels[i] - key := C.GoString((*C.char)(unsafe.Pointer(&lbl.key))) - val := C.GoString((*C.char)(unsafe.Pointer(&lbl.val))) + clLen := int(ptr.Custom_labels.Len) + if clLen > 0 { + trace.CustomLabels = make(map[string]string, clLen) + for i := 0; i < clLen; i++ { + lbl := ptr.Custom_labels.Labels[i] + key := goString(lbl.Key[:]) + val := goString(lbl.Val[:]) trace.CustomLabels[key] = val } } @@ -933,19 +936,19 @@ func (t *Tracer) loadBpfTrace(raw []byte, cpu int) *host.Trace { // - PID, kernel stack ID, length & frame array // Intentionally excluded: // - ktime, COMM, APM trace, APM transaction ID, Origin and Off Time - ptr.comm = [16]C.char{} - ptr.apm_trace_id = C.ApmTraceID{} - ptr.apm_transaction_id = C.ApmSpanID{} - ptr.ktime = 0 - ptr.origin = 0 - ptr.offtime = 0 - ptr.custom_labels = C.CustomLabelsArray{} + ptr.Comm = [16]byte{} + ptr.Apm_trace_id = support.ApmTraceID{} + ptr.Apm_transaction_id = support.ApmSpanID{} + ptr.Ktime = 0 + ptr.Origin = 0 + ptr.Offtime = 0 + ptr.Custom_labels = support.CustomLabelsArray{} trace.Hash = host.TraceHash(xxh3.Hash128(raw).Lo) userFrameOffs := 0 - if ptr.kernel_stack_id >= 0 { + if ptr.Kernel_stack_id >= 0 { kstackLen, err := t.insertKernelFrames( - trace, uint32(ptr.stack_len), int32(ptr.kernel_stack_id)) + trace, ptr.Stack_len, ptr.Kernel_stack_id) if err != nil { log.Errorf("Failed to get kernel stack frames for 0x%x: %v", trace.Hash, err) @@ -957,18 +960,18 @@ func (t *Tracer) loadBpfTrace(raw []byte, cpu int) *host.Trace { // If there are no kernel frames, or reading them failed, we are responsible // for allocating the columnar frame array. if len(trace.Frames) == 0 { - trace.Frames = make([]host.Frame, ptr.stack_len) + trace.Frames = make([]host.Frame, ptr.Stack_len) } - for i := 0; i < int(ptr.stack_len); i++ { - rawFrame := &ptr.frames[i] + for i := 0; i < int(ptr.Stack_len); i++ { + rawFrame := &ptr.Frames[i] trace.Frames[userFrameOffs+i] = host.Frame{ - File: host.FileID(rawFrame.file_id), - Lineno: libpf.AddressOrLineno(rawFrame.addr_or_line), - Type: libpf.FrameType(rawFrame.kind), - ReturnAddress: rawFrame.return_address != 0, - LJCalleePC: uint32(rawFrame.callee_pc_lo) + (uint32(rawFrame.callee_pc_hi) << 16), - LJCallerPC: uint32(rawFrame.caller_pc_lo) + (uint32(rawFrame.caller_pc_hi) << 16), + File: host.FileID(rawFrame.File_id), + Lineno: libpf.AddressOrLineno(rawFrame.Addr_or_line), + Type: libpf.FrameType(rawFrame.Kind), + ReturnAddress: rawFrame.Return_address != 0, + LJCalleePC: uint32(rawFrame.Callee_pc_lo) + (uint32(rawFrame.Callee_pc_hi) << 16), + LJCallerPC: uint32(rawFrame.Caller_pc_lo) + (uint32(rawFrame.Caller_pc_hi) << 16), } } return trace @@ -1000,118 +1003,7 @@ func (t *Tracer) StartMapMonitors(ctx context.Context, traceOutChan chan<- *host // translateIDs is a translation table for eBPF IDs into Metric IDs. // Index is the ebpfID, value is the corresponding metricID. - //nolint:lll - translateIDs := []metrics.MetricID{ - C.metricID_UnwindCallInterpreter: metrics.IDUnwindCallInterpreter, - C.metricID_UnwindErrZeroPC: metrics.IDUnwindErrZeroPC, - C.metricID_UnwindErrStackLengthExceeded: metrics.IDUnwindErrStackLengthExceeded, - C.metricID_UnwindErrBadTSDAddr: metrics.IDUnwindErrBadTLSAddr, - C.metricID_UnwindErrBadTPBaseAddr: metrics.IDUnwindErrBadTPBaseAddr, - C.metricID_UnwindNativeAttempts: metrics.IDUnwindNativeAttempts, - C.metricID_UnwindNativeFrames: metrics.IDUnwindNativeFrames, - C.metricID_UnwindNativeStackDeltaStop: metrics.IDUnwindNativeStackDeltaStop, - C.metricID_UnwindNativeErrLookupTextSection: metrics.IDUnwindNativeErrLookupTextSection, - C.metricID_UnwindNativeErrLookupIterations: metrics.IDUnwindNativeErrLookupIterations, - C.metricID_UnwindNativeErrLookupRange: metrics.IDUnwindNativeErrLookupRange, - C.metricID_UnwindNativeErrKernelAddress: metrics.IDUnwindNativeErrKernelAddress, - C.metricID_UnwindNativeErrWrongTextSection: metrics.IDUnwindNativeErrWrongTextSection, - C.metricID_UnwindNativeErrPCRead: metrics.IDUnwindNativeErrPCRead, - C.metricID_UnwindPythonAttempts: metrics.IDUnwindPythonAttempts, - C.metricID_UnwindPythonFrames: metrics.IDUnwindPythonFrames, - C.metricID_UnwindPythonErrBadPyThreadStateCurrentAddr: metrics.IDUnwindPythonErrBadPyThreadStateCurrentAddr, - C.metricID_UnwindPythonErrZeroThreadState: metrics.IDUnwindPythonErrZeroThreadState, - C.metricID_UnwindPythonErrBadThreadStateFrameAddr: metrics.IDUnwindPythonErrBadThreadStateFrameAddr, - C.metricID_UnwindPythonZeroFrameCodeObject: metrics.IDUnwindPythonZeroFrameCodeObject, - C.metricID_UnwindPythonErrBadCodeObjectArgCountAddr: metrics.IDUnwindPythonErrBadCodeObjectArgCountAddr, - C.metricID_UnwindNativeErrStackDeltaInvalid: metrics.IDUnwindNativeErrStackDeltaInvalid, - C.metricID_ErrEmptyStack: metrics.IDErrEmptyStack, - C.metricID_UnwindHotspotAttempts: metrics.IDUnwindHotspotAttempts, - C.metricID_UnwindHotspotFrames: metrics.IDUnwindHotspotFrames, - C.metricID_UnwindHotspotErrNoCodeblob: metrics.IDUnwindHotspotErrNoCodeblob, - C.metricID_UnwindHotspotErrInvalidCodeblob: metrics.IDUnwindHotspotErrInvalidCodeblob, - C.metricID_UnwindHotspotErrInterpreterFP: metrics.IDUnwindHotspotErrInterpreterFP, - C.metricID_UnwindHotspotErrLrUnwindingMidTrace: metrics.IDUnwindHotspotErrLrUnwindingMidTrace, - C.metricID_UnwindHotspotUnsupportedFrameSize: metrics.IDHotspotUnsupportedFrameSize, - C.metricID_UnwindNativeSmallPC: metrics.IDUnwindNativeSmallPC, - C.metricID_UnwindNativeErrLookupStackDeltaInnerMap: metrics.IDUnwindNativeErrLookupStackDeltaInnerMap, - C.metricID_UnwindNativeErrLookupStackDeltaOuterMap: metrics.IDUnwindNativeErrLookupStackDeltaOuterMap, - C.metricID_ErrBPFCurrentComm: metrics.IDErrBPFCurrentComm, - C.metricID_UnwindPHPAttempts: metrics.IDUnwindPHPAttempts, - C.metricID_UnwindPHPFrames: metrics.IDUnwindPHPFrames, - C.metricID_UnwindPHPErrBadCurrentExecuteData: metrics.IDUnwindPHPErrBadCurrentExecuteData, - C.metricID_UnwindPHPErrBadZendExecuteData: metrics.IDUnwindPHPErrBadZendExecuteData, - C.metricID_UnwindPHPErrBadZendFunction: metrics.IDUnwindPHPErrBadZendFunction, - C.metricID_UnwindPHPErrBadZendOpline: metrics.IDUnwindPHPErrBadZendOpline, - C.metricID_UnwindRubyAttempts: metrics.IDUnwindRubyAttempts, - C.metricID_UnwindRubyFrames: metrics.IDUnwindRubyFrames, - C.metricID_UnwindPerlAttempts: metrics.IDUnwindPerlAttempts, - C.metricID_UnwindPerlFrames: metrics.IDUnwindPerlFrames, - C.metricID_UnwindPerlTSD: metrics.IDUnwindPerlTLS, - C.metricID_UnwindPerlReadStackInfo: metrics.IDUnwindPerlReadStackInfo, - C.metricID_UnwindPerlReadContextStackEntry: metrics.IDUnwindPerlReadContextStackEntry, - C.metricID_UnwindPerlResolveEGV: metrics.IDUnwindPerlResolveEGV, - C.metricID_UnwindHotspotErrInvalidRA: metrics.IDUnwindHotspotErrInvalidRA, - C.metricID_UnwindV8Attempts: metrics.IDUnwindV8Attempts, - C.metricID_UnwindV8Frames: metrics.IDUnwindV8Frames, - C.metricID_UnwindV8ErrBadFP: metrics.IDUnwindV8ErrBadFP, - C.metricID_UnwindV8ErrBadJSFunc: metrics.IDUnwindV8ErrBadJSFunc, - C.metricID_UnwindV8ErrBadCode: metrics.IDUnwindV8ErrBadCode, - C.metricID_ReportedPIDsErr: metrics.IDReportedPIDsErr, - C.metricID_PIDEventsErr: metrics.IDPIDEventsErr, - C.metricID_UnwindNativeLr0: metrics.IDUnwindNativeLr0, - C.metricID_NumProcNew: metrics.IDNumProcNew, - C.metricID_NumProcExit: metrics.IDNumProcExit, - C.metricID_NumUnknownPC: metrics.IDNumUnknownPC, - C.metricID_NumGenericPID: metrics.IDNumGenericPID, - C.metricID_UnwindPythonErrBadCFrameFrameAddr: metrics.IDUnwindPythonErrBadCFrameFrameAddr, - C.metricID_MaxTailCalls: metrics.IDMaxTailCalls, - C.metricID_UnwindPythonErrNoProcInfo: metrics.IDUnwindPythonErrNoProcInfo, - C.metricID_UnwindPythonErrBadAutoTlsKeyAddr: metrics.IDUnwindPythonErrBadAutoTlsKeyAddr, - C.metricID_UnwindPythonErrReadThreadStateAddr: metrics.IDUnwindPythonErrReadThreadStateAddr, - C.metricID_UnwindPythonErrReadTsdBase: metrics.IDUnwindPythonErrReadTsdBase, - C.metricID_UnwindRubyErrNoProcInfo: metrics.IDUnwindRubyErrNoProcInfo, - C.metricID_UnwindRubyErrReadStackPtr: metrics.IDUnwindRubyErrReadStackPtr, - C.metricID_UnwindRubyErrReadStackSize: metrics.IDUnwindRubyErrReadStackSize, - C.metricID_UnwindRubyErrReadCfp: metrics.IDUnwindRubyErrReadCfp, - C.metricID_UnwindRubyErrReadEp: metrics.IDUnwindRubyErrReadEp, - C.metricID_UnwindRubyErrReadIseqBody: metrics.IDUnwindRubyErrReadIseqBody, - C.metricID_UnwindRubyErrReadIseqEncoded: metrics.IDUnwindRubyErrReadIseqEncoded, - C.metricID_UnwindRubyErrReadIseqSize: metrics.IDUnwindRubyErrReadIseqSize, - C.metricID_UnwindNativeErrLrUnwindingMidTrace: metrics.IDUnwindNativeErrLrUnwindingMidTrace, - C.metricID_UnwindNativeErrReadKernelModeRegs: metrics.IDUnwindNativeErrReadKernelModeRegs, - C.metricID_UnwindNativeErrChaseIrqStackLink: metrics.IDUnwindNativeErrChaseIrqStackLink, - C.metricID_UnwindV8ErrNoProcInfo: metrics.IDUnwindV8ErrNoProcInfo, - C.metricID_UnwindNativeErrBadUnwindInfoIndex: metrics.IDUnwindNativeErrBadUnwindInfoIndex, - C.metricID_UnwindApmIntErrReadTsdBase: metrics.IDUnwindApmIntErrReadTsdBase, - C.metricID_UnwindApmIntErrReadCorrBufPtr: metrics.IDUnwindApmIntErrReadCorrBufPtr, - C.metricID_UnwindApmIntErrReadCorrBuf: metrics.IDUnwindApmIntErrReadCorrBuf, - C.metricID_UnwindApmIntReadSuccesses: metrics.IDUnwindApmIntReadSuccesses, - C.metricID_UnwindDotnetAttempts: metrics.IDUnwindDotnetAttempts, - C.metricID_UnwindDotnetFrames: metrics.IDUnwindDotnetFrames, - C.metricID_UnwindDotnetErrNoProcInfo: metrics.IDUnwindDotnetErrNoProcInfo, - C.metricID_UnwindDotnetErrBadFP: metrics.IDUnwindDotnetErrBadFP, - C.metricID_UnwindDotnetErrCodeHeader: metrics.IDUnwindDotnetErrCodeHeader, - C.metricID_UnwindDotnetErrCodeTooLarge: metrics.IDUnwindDotnetErrCodeTooLarge, - C.metricID_UnwindLuaJITAttempts: metrics.IDUnwindLuaJITAttempts, - C.metricID_UnwindLuaJITErrNoProcInfo: metrics.IDUnwindLuaJITErrNoProcInfo, - C.metricID_UnwindNodeClFailedReadHmPointer: metrics.IDUnwindNodeClFailedReadHmPointer, - C.metricID_UnwindNodeClFailedNoLsInHm: metrics.IDUnwindNodeClFailedNoLsInHm, - C.metricID_UnwindNodeClFailedReadHmStruct: metrics.IDUnwindNodeClFailedReadHmStruct, - C.metricID_UnwindNodeClFailedReadBucket: metrics.IDUnwindNodeClFailedReadBucket, - C.metricID_UnwindNodeClFailedReadLsAddr: metrics.IDUnwindNodeClFailedReadLsAddr, - C.metricID_UnwindNodeClFailedTooManyBuckets: metrics.IDUnwindNodeClFailedTooManyBuckets, - C.metricID_UnwindNodeClFailedGettingId: metrics.IDUnwindNodeClFailedGettingId, - C.metricID_UnwindNodeClWarnIdZero: metrics.IDUnwindNodeClWarnIdZero, - C.metricID_UnwindNodeAsyncIdErrGetTlsSymbol: metrics.IDUnwindNodeAsyncIdErrGetTlsSymbol, - C.metricID_UnwindNodeAsyncIdErrReadIsolate: metrics.IDUnwindNodeAsyncIdErrReadIsolate, - C.metricID_UnwindNodeAsyncIdErrReadContextHandle: metrics.IDUnwindNodeAsyncIdErrReadContextHandle, - C.metricID_UnwindNodeAsyncIdErrReadRealContextHandle: metrics.IDUnwindNodeAsyncIdErrReadRealContextHandle, - C.metricID_UnwindNodeAsyncIdErrReadNativeContext: metrics.IDUnwindNodeAsyncIdErrReadNativeContext, - C.metricID_UnwindNodeAsyncIdErrReadEmbedderData: metrics.IDUnwindNodeAsyncIdErrReadEmbedderData, - C.metricID_UnwindNodeAsyncIdErrReadEnvPtr: metrics.IDUnwindNodeAsyncIdErrReadEnvPtr, - C.metricID_UnwindNodeAsyncIdErrReadIdField: metrics.IDUnwindNodeAsyncIdErrReadIdField, - C.metricID_UnwindNodeAsyncIdErrReadIdDouble: metrics.IDUnwindNodeAsyncIdErrReadIdDouble, - } + translateIDs := support.MetricsTranslation // previousMetricValue stores the previously retrieved metric values to // calculate and store delta values. @@ -1195,6 +1087,7 @@ func (t *Tracer) probabilisticProfile(interval time.Duration, threshold uint) { enableSampling := false var probProfilingStatus = probProfilingDisable + //nolint:gosec if rand.UintN(ProbabilisticThresholdMax) < threshold { enableSampling = true probProfilingStatus = probProfilingEnable From d310831ad529a4bb0826409d36ce4f04bdfb0bfd Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Thu, 11 Sep 2025 07:56:39 -0400 Subject: [PATCH 2/3] Attempt to make node test less flaky --- interpreter/customlabels/integrationtests/node_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/customlabels/integrationtests/node_test.go b/interpreter/customlabels/integrationtests/node_test.go index 503295ac9..11ee1300b 100644 --- a/interpreter/customlabels/integrationtests/node_test.go +++ b/interpreter/customlabels/integrationtests/node_test.go @@ -176,11 +176,11 @@ func TestIntegration(t *testing.T) { // Really, there should be zero frames in the // `marked` workload that aren't under labels, - // but accept a 1% slop because the unwinder + // but accept a 5% slop because the unwinder // isn't perfect (e.g. it might interrupt the // process when the Node environment is in an // undefined state) - require.Less(t, 100*unlabeledWorkloadFrames, totalWorkloadFrames) + require.Less(t, 20*unlabeledWorkloadFrames, totalWorkloadFrames) }) } } From ef7836c18abf77cfcbf65bb8ac70be225091582c Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Thu, 11 Sep 2025 12:19:45 -0400 Subject: [PATCH 3/3] Support node 24.8.0 --- interpreter/nodev8/node_offsets_generated.go | 1 + tools/complete_offsets.csv | 1 + 2 files changed, 2 insertions(+) diff --git a/interpreter/nodev8/node_offsets_generated.go b/interpreter/nodev8/node_offsets_generated.go index 36a1a3aa6..93a01255e 100644 --- a/interpreter/nodev8/node_offsets_generated.go +++ b/interpreter/nodev8/node_offsets_generated.go @@ -114,4 +114,5 @@ var nodeOffsetTable = map[string]nodeOffsets{ "v24.5.0": { 336, 31, 47, 271, 1160 }, "v24.6.0": { 336, 31, 47, 271, 1160 }, "v24.7.0": { 336, 31, 47, 271, 1160 }, + "v24.8.0": { 336, 31, 47, 271, 1160 }, } diff --git a/tools/complete_offsets.csv b/tools/complete_offsets.csv index e718b22a3..87a3b75b3 100644 --- a/tools/complete_offsets.csv +++ b/tools/complete_offsets.csv @@ -99,3 +99,4 @@ v24.4.1,336,31,47,271,1160 v24.5.0,336,31,47,271,1160 v24.6.0,336,31,47,271,1160 v24.7.0,336,31,47,271,1160 +v24.8.0,336,31,47,271,1160