Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions interpreter/gpu/cuda.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ import (

const (
GRAPH_LAUNCH_CBID = 311

// eBPF program names for USDT probes
// These correspond to the function names in cuda.ebpf.c, not the SEC() paths
USDTProgCudaCorrelation = "cuda_correlation"
USDTProgCudaKernel = "cuda_kernel_exec"
USDTProgCudaProbe = "cuda_probe"
)

var (
Expand Down Expand Up @@ -82,9 +88,12 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
}
}
if len(parcagpuProbes) != 2 {
log.Warnf("Found %d parcagpu USDT probes in %s, need exactly 2", len(parcagpuProbes), info.FileName())
return nil, nil
}

log.Debugf("Found parcagpu USDT probes in %s: %v", info.FileName(), parcagpuProbes)

d := &data{
path: info.FileName(),
probes: parcagpuProbes,
Expand All @@ -106,18 +115,20 @@ func (d *data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, _ libpf.Addre
for i, probe := range d.probes {
cookies[i] = uint64(probe.Name[0])
// Map probe names to specific program names for single-shot mode
switch probe.Name[0] {
case 'c':
progNames[i] = "usdt_parcagpu_cuda_correlation"
case 'k':
progNames[i] = "usdt_parcagpu_cuda_kernel"
switch probe.Name {
case "cuda_correlation":
progNames[i] = USDTProgCudaCorrelation
case "kernel_executed":
progNames[i] = USDTProgCudaKernel
default:
log.Debugf("unknown parcagpu USDT probe name: %s", probe.Name)
}
}

var lc interpreter.LinkCloser
if d.link == nil {
var err error
lc, err = ebpf.AttachUSDTProbes(pid, d.path, "cuda_probe", d.probes, cookies, progNames)
lc, err = ebpf.AttachUSDTProbes(pid, d.path, USDTProgCudaProbe, d.probes, cookies, progNames)
if err != nil {
return nil, err
}
Expand Down
44 changes: 44 additions & 0 deletions interpreter/gpu/cuda_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package gpu_test

import (
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/ebpf-profiler/interpreter/gpu"
"go.opentelemetry.io/ebpf-profiler/support"
)

// TestProgramNamesExist verifies that the eBPF program names used in cuda.go
// actually exist in the compiled eBPF collection. This catches bugs where
// the program names don't match the SEC() names in the .ebpf.c files.
func TestProgramNamesExist(t *testing.T) {
// Load the eBPF collection
coll, err := support.LoadCollectionSpec()
require.NoError(t, err, "Failed to load eBPF collection spec")

// Verify single-shot program names exist
t.Run("SingleShotPrograms", func(t *testing.T) {
progNames := []string{
gpu.USDTProgCudaCorrelation,
gpu.USDTProgCudaKernel,
}

for _, progName := range progNames {
t.Run(progName, func(t *testing.T) {
prog := coll.Programs[progName]
require.NotNil(t, prog, "eBPF program %q not found in collection", progName)
t.Logf("Found program %q", progName)
})
}
})

// Verify multi-attach program name exists
t.Run("MultiAttachProgram", func(t *testing.T) {
prog := coll.Programs[gpu.USDTProgCudaProbe]
require.NotNil(t, prog, "eBPF program %q not found in collection", gpu.USDTProgCudaProbe)
t.Logf("Found program %q", gpu.USDTProgCudaProbe)
})
}
3 changes: 3 additions & 0 deletions processmanager/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ func (impl *ebpfMapsImpl) AttachUSDTProbes(pid libpf.PID, path, multiProgName st
// loadProgram loads an eBPF program from progSpec and populates the related maps.
func (impl *ebpfMapsImpl) loadUSDTProgram(progName string, useMulti bool) error {
progSpec := impl.coll.Programs[progName]
if progSpec == nil {
return fmt.Errorf("eBPF program %s not found in collection", progName)
}
programOptions := cebpf.ProgramOptions{
// TODO: wire in debug level
}
Expand Down
15 changes: 14 additions & 1 deletion util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"math/bits"
"os"
"strings"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -214,13 +215,25 @@ func probeBpfUprobeMultiLink() bool {
//
// Note: This function requires CAP_BPF or CAP_SYS_ADMIN capabilities to load the probe
// program. The profiler should already have these privileges.
//
// The behavior can be overridden by:
// - Setting PARCA_DISABLE_MULTIPROBE=1 environment variable to force single-shot uprobe mode
// - Using SetTestOnlyMultiUprobeSupport() for testing purposes
func HasMultiUprobeSupport() bool {
// Check for test override first (takes precedence over everything)
if testOnlyMultiUprobeOverride != nil {
return *testOnlyMultiUprobeOverride
}

// Cache the probe result since it's expensive to check
multiUprobeSupportOnce.Do(func() {
multiUprobeSupportCached = probeBpfUprobeMultiLink()
// Check for environment variable override inside the Do() to ensure
// it's only evaluated once and consistently cached
if os.Getenv("PARCA_DISABLE_MULTIPROBE") == "1" {
multiUprobeSupportCached = false
} else {
multiUprobeSupportCached = probeBpfUprobeMultiLink()
}
})

return multiUprobeSupportCached
Expand Down
Loading