Skip to content

Commit 579a830

Browse files
committed
Restore some lost changes causing verifier errors
1 parent d2eb60c commit 579a830

8 files changed

Lines changed: 99 additions & 45 deletions

File tree

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ require (
1313
github.com/elastic/go-freelru v0.16.0
1414
github.com/elastic/go-perf v0.0.0-20241029065020-30bec95324b8
1515
github.com/google/uuid v1.6.0
16-
github.com/ianlancetaylor/demangle v0.0.0-20251118225945-96ee0021ea0f
1716
github.com/klauspost/compress v1.18.0
1817
github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d
1918
github.com/minio/sha256-simd v1.0.1

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV
116116
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
117117
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
118118
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
119-
github.com/ianlancetaylor/demangle v0.0.0-20251118225945-96ee0021ea0f h1:Fnl4pzx8SR7k7JuzyW8lEtSFH6EQ8xgcypgIn8pcGIE=
120-
github.com/ianlancetaylor/demangle v0.0.0-20251118225945-96ee0021ea0f/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
121119
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
122120
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
123121
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=

interpreter/gpu/cuda.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ const (
2424
// These correspond to the function names in cuda.ebpf.c, not the SEC() paths
2525
USDTProgCudaCorrelation = "cuda_correlation"
2626
USDTProgCudaKernel = "cuda_kernel_exec"
27-
USDTProgCudaActivityBatch = "cuda_activity_batch"
28-
USDTProgCudaProbe = "cuda_probe"
27+
USDTProgCudaActivityBatch = "cuda_activity_batch"
28+
USDTProgCudaActivityBatchTail = "cuda_activity_batch_tail"
29+
USDTProgCudaProbe = "cuda_probe"
2930

3031
// BPF attach cookie values — must match CUDA_PROG_* in cuda.ebpf.c.
3132
// Used in the low 32 bits of the BPF attach cookie so cuda_probe can
@@ -188,7 +189,7 @@ func (d *data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, _ libpf.Addre
188189
}
189190
cudaTailCallOnce.Do(func() {
190191
if err := ebpf.UpdateProgArray(cudaProgsMap, 0,
191-
USDTProgCudaActivityBatch); err != nil {
192+
USDTProgCudaActivityBatchTail); err != nil {
192193
log.Errorf("[cuda] activity_batch tail call failed: %v", err)
193194
cudaTailCallFailed = true
194195
}

interpreter/gpu/cuda_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func TestProgramNamesExist(t *testing.T) {
3333
gpu.USDTProgCudaCorrelation,
3434
gpu.USDTProgCudaKernel,
3535
gpu.USDTProgCudaActivityBatch,
36+
gpu.USDTProgCudaActivityBatchTail,
3637
}
3738

3839
for _, progName := range progNames {

support/ebpf/cuda.ebpf.c

Lines changed: 93 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ struct kernel_timing {
3939
// Per-CPU scratch space for large structs that exceed the BPF 512-byte stack limit.
4040
// Used by cuda_kernel_exec and cuda_activity_batch (mutually exclusive on same CPU).
4141
#define MAX_BATCH_SIZE 128
42+
#define PTR_BATCH 16
4243
struct cuda_scratch {
4344
struct kernel_timing timing;
4445
struct cupti_activity_kernel5 rec;
45-
u64 ptrs[MAX_BATCH_SIZE];
46+
// Pre-parsed activity_batch USDT args, set by cuda_probe before tail call.
47+
// bpf_get_attach_cookie does not return the correct cookie after bpf_tail_call,
48+
// so we parse args in cuda_probe and pass them via scratch.
49+
u64 ab_ptrs_base;
50+
u32 ab_num_activities;
4651
};
4752

4853
bpf_map_def SEC("maps") cuda_scratch_heap = {
@@ -100,6 +105,8 @@ int BPF_USDT(
100105
timing->kernel_name[3] = '\0';
101106
}
102107

108+
DEBUG_PRINT("cuda_kernel_exec: pid=%u corr_id=%u dev=%u", pid, correlation_id, device_id);
109+
103110
bpf_perf_event_output(ctx, &cuda_timing_events, BPF_F_CURRENT_CPU, timing, sizeof(*timing));
104111

105112
return 0;
@@ -119,64 +126,100 @@ int BPF_USDT(cuda_activity_batch, u64 ptrs_base, u32 num_activities)
119126
struct kernel_timing *timing = &scratch->timing;
120127
struct cupti_activity_kernel5 *rec = &scratch->rec;
121128

129+
DEBUG_PRINT("cuda_activity_batch: pid=%u num=%u", pid, (u32)num_activities);
130+
DEBUG_PRINT("cuda_activity_batch: ptrs_base=0x%llx", ptrs_base);
131+
122132
if (num_activities > MAX_BATCH_SIZE) {
123133
num_activities = MAX_BATCH_SIZE;
124134
}
125135

126-
// Read all activity pointers into scratch space in one shot.
127-
if (bpf_probe_read_user(scratch->ptrs, num_activities * sizeof(u64), (void *)ptrs_base) != 0) {
128-
return 0;
129-
}
136+
// Stack-local pointer batch — small enough for the BPF stack.
137+
u64 ptrs[PTR_BATCH] = {};
130138

131-
for (u32 i = 0; i < num_activities && i < MAX_BATCH_SIZE; i++) {
132-
u64 rec_ptr = scratch->ptrs[i & (MAX_BATCH_SIZE - 1)];
133-
if (rec_ptr == 0) {
134-
continue;
139+
// Nested loop: outer iterates over batches of PTR_BATCH pointers,
140+
// inner processes each pointer in the batch. This keeps the verifier's
141+
// jump-sequence count well under BPF_COMPLEXITY_LIMIT_JMP_SEQ (8192).
142+
for (u32 batch = 0; batch < MAX_BATCH_SIZE / PTR_BATCH; batch++) {
143+
u32 base = batch * PTR_BATCH;
144+
if (base >= num_activities) {
145+
break;
135146
}
136147

137-
// Read the full activity record and filter by kind.
138-
if (bpf_probe_read_user(rec, sizeof(*rec), (void *)rec_ptr) != 0) {
139-
continue;
140-
}
141-
if (
142-
rec->kind != CUPTI_ACTIVITY_KIND_KERNEL &&
143-
rec->kind != CUPTI_ACTIVITY_KIND_CONCURRENT_KERNEL) {
144-
continue;
148+
if (bpf_probe_read_user(ptrs, sizeof(ptrs), (void *)(ptrs_base + base * sizeof(u64))) != 0) {
149+
break;
145150
}
146151

147-
timing->pid = pid;
148-
timing->correlation_id = rec->correlation_id;
149-
timing->start = rec->start;
150-
timing->end = rec->end;
151-
timing->graph_node_id = rec->graph_node_id;
152-
timing->device_id = rec->device_id;
153-
timing->stream_id = rec->stream_id;
154-
timing->graph_id = rec->graph_id;
155-
156-
const char *name = (const char *)rec->name_ptr;
157-
int chars =
158-
bpf_probe_read_user_str((char *)&timing->kernel_name, sizeof(timing->kernel_name), name);
159-
if (chars <= 0) {
160-
timing->kernel_name[0] = 'e';
161-
timing->kernel_name[1] = 'r';
162-
timing->kernel_name[2] = 'r';
163-
timing->kernel_name[3] = '\0';
164-
}
152+
for (u32 j = 0; j < PTR_BATCH; j++) {
153+
if (base + j >= num_activities) {
154+
break;
155+
}
156+
157+
u64 rec_ptr = ptrs[j];
158+
159+
// Read the full activity record and filter by kind.
160+
if (bpf_probe_read_user(rec, sizeof(*rec), (void *)rec_ptr) != 0) {
161+
continue;
162+
}
163+
if (
164+
rec->kind != CUPTI_ACTIVITY_KIND_KERNEL &&
165+
rec->kind != CUPTI_ACTIVITY_KIND_CONCURRENT_KERNEL) {
166+
continue;
167+
}
168+
169+
timing->pid = pid;
170+
timing->correlation_id = rec->correlation_id;
171+
timing->start = rec->start;
172+
timing->end = rec->end;
173+
timing->graph_node_id = rec->graph_node_id;
174+
timing->device_id = rec->device_id;
175+
timing->stream_id = rec->stream_id;
176+
timing->graph_id = rec->graph_id;
177+
178+
const char *name = (const char *)rec->name_ptr;
179+
int chars =
180+
bpf_probe_read_user_str((char *)&timing->kernel_name, sizeof(timing->kernel_name), name);
181+
if (chars <= 0) {
182+
timing->kernel_name[0] = 'e';
183+
timing->kernel_name[1] = 'r';
184+
timing->kernel_name[2] = 'r';
185+
timing->kernel_name[3] = '\0';
186+
}
187+
188+
DEBUG_PRINT(
189+
"cuda_activity_batch: corr_id=%u kind=%u dev=%u",
190+
rec->correlation_id,
191+
rec->kind,
192+
rec->device_id);
165193

166-
bpf_perf_event_output(ctx, &cuda_timing_events, BPF_F_CURRENT_CPU, timing, sizeof(*timing));
194+
bpf_perf_event_output(ctx, &cuda_timing_events, BPF_F_CURRENT_CPU, timing, sizeof(*timing));
195+
}
167196
}
168197

169198
return 0;
170199
}
171200

201+
// Tail-call entry point for cuda_activity_batch. Reads pre-parsed USDT args
202+
// from the scratch map (set by cuda_probe before bpf_tail_call) and forwards
203+
// them to the inline body generated by BPF_USDT.
204+
SEC("usdt/cuda_activity_batch_tail")
205+
int cuda_activity_batch_tail(struct pt_regs *ctx)
206+
{
207+
u32 zero = 0;
208+
struct cuda_scratch *scratch = bpf_map_lookup_elem(&cuda_scratch_heap, &zero);
209+
if (!scratch) {
210+
return 0;
211+
}
212+
return ____cuda_activity_batch(ctx, scratch->ab_ptrs_base, scratch->ab_num_activities);
213+
}
214+
172215
// Cookie values for the cuda_probe multi-probe dispatcher.
173216
// Must match the cookie values set in cuda.go.
174217
#define CUDA_PROG_CORRELATION 0
175218
#define CUDA_PROG_KERNEL_EXEC 1
176219
#define CUDA_PROG_ACTIVITY_BATCH 2
177220

178221
// Tail-call prog array for cuda_probe. Contains a single entry at key 0
179-
// for cuda_activity_batch, whose batch loop pushes past the BPF verifier's
222+
// for cuda_activity_batch_tail, whose batch loop pushes past the BPF verifier's
180223
// BPF_COMPLEXITY_LIMIT_JMP_SEQ (8192) limit. cuda_correlation and
181224
// cuda_kernel_exec are inlined directly in cuda_probe.
182225
bpf_map_def SEC("maps") cuda_progs = {
@@ -205,7 +248,19 @@ int cuda_probe(struct pt_regs *ctx)
205248
graph_id,
206249
graph_node_id,
207250
name);
208-
case CUDA_PROG_ACTIVITY_BATCH: bpf_tail_call(ctx, &cuda_progs, 0); break;
251+
case CUDA_PROG_ACTIVITY_BATCH: {
252+
// Parse USDT args before the tail call — bpf_get_attach_cookie does not
253+
// return the correct cookie after bpf_tail_call.
254+
u32 zero = 0;
255+
struct cuda_scratch *scratch = bpf_map_lookup_elem(&cuda_scratch_heap, &zero);
256+
if (!scratch) {
257+
break;
258+
}
259+
scratch->ab_ptrs_base = (u64)bpf_usdt_arg0(ctx);
260+
scratch->ab_num_activities = (u32)bpf_usdt_arg1(ctx);
261+
bpf_tail_call(ctx, &cuda_progs, 0);
262+
break;
263+
}
209264
default: DEBUG_PRINT("cuda_probe: unknown cookie %u", cookie); break;
210265
}
211266
return 0;

support/ebpf/tracer.ebpf.amd64

17.5 KB
Binary file not shown.

support/ebpf/tracer.ebpf.arm64

22.7 KB
Binary file not shown.

test/cudaverify/cuda_verifier_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func TestCUDAVerifierMultiProbe(t *testing.T) {
186186
// target — correlation and kernel_exec are inlined in cuda_probe).
187187
for _, probe := range probes {
188188
if probe.Name == "activity_batch" {
189-
err := ebpfHandler.UpdateProgArray("cuda_progs", 0, "cuda_activity_batch")
189+
err := ebpfHandler.UpdateProgArray("cuda_progs", 0, "cuda_activity_batch_tail")
190190
require.NoError(t, err, "UpdateProgArray failed for cuda_activity_batch")
191191
break
192192
}

0 commit comments

Comments
 (0)