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/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) }) } } 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/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/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)<> 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/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 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