Skip to content

Commit 6f4f78c

Browse files
committed
Simplify the Ruby stack walker
The overly complicated stack walking in two steps was implemented to try to pack as much work as possible per loop iteration to prevent the BPF verifier from rejecting our program due to the high complexity while analysing all the potential code paths, but I don't think this is needed anymore, so removing it to make the code vastly simpler! Signed-off-by: Francisco Javier Honduvilla Coto <javierhonduco@gmail.com>
1 parent 3e819cc commit 6f4f78c

File tree

3 files changed

+8
-69
lines changed

3 files changed

+8
-69
lines changed

src/bpf/rbperf.bpf.c

Lines changed: 6 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,6 @@ struct {
5454
__type(value, RubyVersionOffsets);
5555
} version_specific_offsets SEC(".maps");
5656

57-
struct {
58-
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
59-
__uint(max_entries, 1);
60-
__type(key, u32);
61-
__type(value, RubyStackAddresses);
62-
} scratch_stack SEC(".maps");
63-
6457
struct {
6558
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
6659
__uint(max_entries, 1);
@@ -199,7 +192,7 @@ read_frame(u64 pc, u64 body, RubyFrame *current_frame,
199192
}
200193

201194
SEC("perf_event")
202-
int read_ruby_stack(struct bpf_perf_event_data *ctx) {
195+
int walk_ruby_stack(struct bpf_perf_event_data *ctx) {
203196
u64 iseq_addr;
204197
u64 pc;
205198
u64 pc_addr;
@@ -221,21 +214,12 @@ int read_ruby_stack(struct bpf_perf_event_data *ctx) {
221214
state->ruby_stack_program_count += 1;
222215
u64 control_frame_t_sizeof = version_offsets->control_frame_t_sizeof;
223216

224-
RubyStackAddresses *ruby_stack_addresses = bpf_map_lookup_elem(&scratch_stack, &zero);
225-
if (ruby_stack_addresses == NULL) {
226-
return 0; // this should never happen
227-
}
228-
229-
int rb_frame_count = 0;
230-
231217
#pragma unroll
232218
for (int i = 0; i < MAX_STACKS_PER_PROGRAM; i++) {
233219
rbperf_read(&iseq_addr, 8, (void *)(cfp + iseq_offset));
234220
rbperf_read(&pc_addr, 8, (void *)(cfp + 0));
235221
rbperf_read(&pc, 8, (void *)pc_addr);
236222

237-
RubyStackAddress ruby_stack_address = {};
238-
239223
if (cfp > state->base_stack) {
240224
LOG("[debug] done reading stack");
241225
break;
@@ -244,45 +228,10 @@ int read_ruby_stack(struct bpf_perf_event_data *ctx) {
244228
if ((void *)iseq_addr == NULL) {
245229
// this could be a native frame, it's missing the check though
246230
// https://github.com/ruby/ruby/blob/4ff3f20/.gdbinit#L1155
247-
ruby_stack_address.iseq_addr = NATIVE_METHOD_MARKER;
248-
ruby_stack_address.pc = NATIVE_METHOD_MARKER;
249-
} else {
250-
ruby_stack_address.iseq_addr = iseq_addr;
251-
ruby_stack_address.pc = pc;
252-
}
253-
254-
unsigned long long offset = rb_frame_count + state->rb_frame_count;
255-
if (offset >= 0 && offset < MAX_STACK) {
256-
ruby_stack_addresses->ruby_stack_address[offset] = ruby_stack_address;
257-
}
258-
259-
rb_frame_count += 1;
260-
cfp += control_frame_t_sizeof;
261-
}
262-
263-
state->cfp = cfp;
264-
265-
#pragma unroll
266-
for (int i = 0; i < MAX_STACKS_PER_PROGRAM; i++) {
267-
RubyStackAddress ruby_stack_address;
268-
unsigned long long offset = i + state->rb_frame_count;
269-
if (i >= rb_frame_count) {
270-
goto end;
271-
}
272-
if (offset >= 0 && offset < MAX_STACK) {
273-
ruby_stack_address = ruby_stack_addresses->ruby_stack_address[offset];
274-
} else {
275-
// this should never happen
276-
return 0;
277-
}
278-
iseq_addr = ruby_stack_address.iseq_addr;
279-
pc = ruby_stack_address.pc;
280-
281-
if (iseq_addr == NATIVE_METHOD_MARKER && pc == NATIVE_METHOD_MARKER) {
231+
// TODO(javierhonduco): Fetch path for native stacks
282232
bpf_probe_read_kernel_str(current_frame.method_name, sizeof(NATIVE_METHOD_NAME), NATIVE_METHOD_NAME);
283233
} else {
284234
rbperf_read(&body, 8, (void *)(iseq_addr + body_offset));
285-
// add check
286235
read_frame(pc, body, &current_frame, version_offsets);
287236
}
288237

@@ -291,9 +240,11 @@ int read_ruby_stack(struct bpf_perf_event_data *ctx) {
291240
state->stack.frames[actual_index] = find_or_insert_frame(&current_frame);
292241
state->stack.size += 1;
293242
}
243+
244+
cfp += control_frame_t_sizeof;
294245
}
295-
end:
296-
state->rb_frame_count += rb_frame_count;
246+
247+
state->cfp = cfp;
297248
state->base_stack = base_stack;
298249

299250
if (cfp <= base_stack &&
@@ -416,7 +367,6 @@ int on_event(struct bpf_perf_event_data *ctx) {
416367
state->base_stack = base_stack;
417368
state->cfp = cfp + version_offsets->control_frame_t_sizeof;
418369
state->ruby_stack_program_count = 0;
419-
state->rb_frame_count = 0;
420370
state->rb_version = process_data->rb_version;
421371

422372
bpf_tail_call(ctx, &programs, RBPERF_STACK_READING_PROGRAM_IDX);

src/bpf/rbperf.h

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
#define SYSCALL_NR_OFFSET 8
5353
#define SYSCALL_NR_SIZE 4
5454

55-
u64 NATIVE_METHOD_MARKER = 0xFABADA;
5655
static char NATIVE_METHOD_NAME[] = "<native code>";
5756

5857
enum ruby_stack_status {
@@ -90,7 +89,6 @@ typedef struct {
9089
u64 base_stack;
9190
u64 cfp;
9291
int ruby_stack_program_count;
93-
long long int rb_frame_count;
9492
int rb_version;
9593
} SampleState;
9694

@@ -115,13 +113,4 @@ typedef struct {
115113
int lineno_offset;
116114
int main_thread_offset;
117115
int ec_offset;
118-
} RubyVersionOffsets;
119-
120-
typedef struct {
121-
u64 iseq_addr;
122-
u64 pc;
123-
} RubyStackAddress;
124-
125-
typedef struct {
126-
RubyStackAddress ruby_stack_address[MAX_STACK];
127-
} RubyStackAddresses;
116+
} RubyVersionOffsets;

src/rbperf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ impl<'a> Rbperf<'a> {
319319

320320
// Insert Ruby stack reading program
321321
let idx: i32 = RBPERF_STACK_READING_PROGRAM_IDX.try_into().unwrap();
322-
let val = self.bpf.obj.prog("read_ruby_stack").unwrap().fd();
322+
let val = self.bpf.obj.prog("walk_ruby_stack").unwrap().fd();
323323

324324
let mut maps = self.bpf.maps_mut();
325325
let programs = maps.programs();

0 commit comments

Comments
 (0)