@@ -17,12 +17,14 @@ import (
1717 "net/http"
1818 "os"
1919 "os/exec"
20+ "runtime"
2021 "runtime/pprof"
2122 "strconv"
2223 "syscall"
2324 "time"
2425 "unsafe"
2526
27+ "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
2628 manager "github.com/DataDog/ebpf-manager"
2729 "github.com/syndtr/gocapability/capability"
2830 "github.com/vishvananda/netlink"
5557 goSpanSpanID string
5658 goSpanLocalRootSpanID string
5759 goSpanFilePath string
60+ ddtraceSpanTest bool
61+ ddtraceSpanFilePath string
5862)
5963
6064//go:embed ebpf_probe.o
@@ -340,6 +344,96 @@ func RunGoSpanTest(spanID, localRootSpanID, filePath string) error {
340344 return nil
341345}
342346
347+ // RunDDTraceSpanTest uses dd-trace-go to create a real span, which sets pprof
348+ // labels automatically via the profiler code hotspots integration. This tests
349+ // the full dd-trace-go → pprof labels → eBPF Go labels reader pipeline.
350+ func RunDDTraceSpanTest (filePath string ) error {
351+ // Create and seal a tracer-info memfd with tracer_language="go".
352+ type TracerMeta struct {
353+ SchemaVersion uint8 `msgpack:"schema_version"`
354+ TracerLanguage string `msgpack:"tracer_language"`
355+ TracerVersion string `msgpack:"tracer_version"`
356+ Hostname string `msgpack:"hostname"`
357+ ServiceName string `msgpack:"service_name"`
358+ }
359+ meta := TracerMeta {
360+ SchemaVersion : 2 ,
361+ TracerLanguage : "go" ,
362+ TracerVersion : "0.0.1-test" ,
363+ Hostname : "test" ,
364+ ServiceName : "ddtrace-test" ,
365+ }
366+ data , err := msgpack .Marshal (& meta )
367+ if err != nil {
368+ return fmt .Errorf ("msgpack marshal: %w" , err )
369+ }
370+
371+ fd , err := unix .MemfdCreate ("datadog-tracer-info-ddtrace0" , unix .MFD_ALLOW_SEALING )
372+ if err != nil {
373+ return fmt .Errorf ("memfd_create: %w" , err )
374+ }
375+ defer unix .Close (fd )
376+
377+ if _ , err := unix .Write (fd , data ); err != nil {
378+ return fmt .Errorf ("memfd write: %w" , err )
379+ }
380+ const fAddSeals = 1033
381+ const fSealWrite = 0x0008
382+ const fSealShrink = 0x0002
383+ const fSealGrow = 0x0004
384+ if _ , _ , errno := syscall .Syscall (syscall .SYS_FCNTL , uintptr (fd ), fAddSeals , fSealWrite | fSealShrink | fSealGrow ); errno != 0 {
385+ return fmt .Errorf ("memfd seal: %w" , errno )
386+ }
387+
388+ // Wait for the agent to process the memfd seal event and populate the BPF map.
389+ time .Sleep (500 * time .Millisecond )
390+
391+ // Start dd-trace-go with:
392+ // - WithTestDefaults: uses a dummy transport (no real agent needed)
393+ // - WithProfilerCodeHotspots: enables "span id" and "local root span id" pprof labels
394+ // - WithService: set a service name
395+ tracer .Start (
396+ tracer .WithTestDefaults (nil ),
397+ tracer .WithProfilerCodeHotspots (true ),
398+ tracer .WithService ("ddtrace-test" ),
399+ tracer .WithLogStartup (false ),
400+ )
401+ defer tracer .Stop ()
402+
403+ // Create a span. dd-trace-go will automatically set pprof labels
404+ // "span id" and "local root span id" on the current goroutine.
405+ span , ctx := tracer .StartSpanFromContext (context .Background (), "test.operation" )
406+
407+ // The span ID and local root span ID should now be set as pprof labels.
408+ // Print them so the test can verify the values.
409+ spanID := span .Context ().SpanID ()
410+ // For a root span, local root span ID == span ID.
411+ fmt .Printf ("ddtrace_span_id=%d\n " , spanID )
412+
413+ // Activate the labels on this goroutine by using the context.
414+ _ = ctx
415+ // dd-trace-go sets labels in StartSpanFromContext, but we need to ensure
416+ // the goroutine labels are active. Use pprof.SetGoroutineLabels with
417+ // the span's context to make sure.
418+ runtime .LockOSThread ()
419+ defer runtime .UnlockOSThread ()
420+
421+ // Give a moment for the labels to be set
422+ time .Sleep (10 * time .Millisecond )
423+
424+ // Trigger the file open that the CWS rule is watching.
425+ f , err := os .Create (filePath )
426+ if err != nil {
427+ span .Finish ()
428+ return fmt .Errorf ("create file: %w" , err )
429+ }
430+ f .Close ()
431+ os .Remove (filePath )
432+
433+ span .Finish ()
434+ return nil
435+ }
436+
343437func main () {
344438 flag .BoolVar (& bpfLoad , "load-bpf" , false , "load the eBPF programs" )
345439 flag .BoolVar (& bpfClone , "clone-bpf" , false , "clone maps" )
@@ -360,6 +454,8 @@ func main() {
360454 flag .StringVar (& goSpanSpanID , "go-span-span-id" , "" , "span ID for the Go span test (decimal string)" )
361455 flag .StringVar (& goSpanLocalRootSpanID , "go-span-local-root-span-id" , "" , "local root span ID for the Go span test (decimal string)" )
362456 flag .StringVar (& goSpanFilePath , "go-span-file-path" , "" , "file path to open for the Go span test" )
457+ flag .BoolVar (& ddtraceSpanTest , "ddtrace-span-test" , false , "when set, runs the dd-trace-go span test" )
458+ flag .StringVar (& ddtraceSpanFilePath , "ddtrace-span-file-path" , "" , "file path to open for the dd-trace-go span test" )
363459
364460 flag .Parse ()
365461
@@ -426,4 +522,10 @@ func main() {
426522 panic (err )
427523 }
428524 }
525+
526+ if ddtraceSpanTest {
527+ if err := RunDDTraceSpanTest (ddtraceSpanFilePath ); err != nil {
528+ panic (err )
529+ }
530+ }
429531}
0 commit comments