Skip to content

Commit 822123b

Browse files
committed
Move findCodeHeader to ebpf
1 parent daf4166 commit 822123b

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

interpreter/beam/beam.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ func (d *beamData) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, bias libp
223223
R: uint64(bias) + d.r,
224224
The_active_code_index: uint64(bias) + d.the_active_code_index,
225225
Ranges_sizeof: uint8(d.vmStructs.ranges.size_of),
226+
Ranges_modules: uint8(d.vmStructs.ranges.modules),
227+
Ranges_n: uint8(d.vmStructs.ranges.n),
226228
}
227229
if err := ebpf.UpdateProcData(libpf.BEAM, pid, unsafe.Pointer(&data)); err != nil {
228230
return nil, err
@@ -299,14 +301,14 @@ func (i *beamInstance) Symbolize(frame *host.Frame, frames *libpf.Frames) error
299301
return interpreter.ErrMismatchInterpreterType
300302
}
301303
pc := libpf.Address(frame.Lineno)
302-
activeRanges := libpf.Address(frame.File)
304+
codeHeader := libpf.Address(frame.File)
303305

304306
// log.Debugf("BEAM symbolizing pc: 0x%x", pc)
305307

306-
codeHeader, err := i.findCodeHeader(activeRanges, pc)
307-
if err != nil {
308-
return err
309-
}
308+
// codeHeader, err := i.findCodeHeader(activeRanges, pc)
309+
// if err != nil {
310+
// return err
311+
// }
310312

311313
functionIndex, moduleID, functionID, arity, err := i.findMFA(pc, codeHeader)
312314
if err != nil {

support/ebpf/beam_tracer.ebpf.c

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,68 @@
55
// The number of frames to unwind per frame-unwinding eBPF program.
66
#define FRAMES_PER_PROGRAM 16
77

8+
// The max number of loops to unroll when searching for the correct CodeHeader.
9+
// Should be log base 2 of a reasonable number of modules to binary-search through.
10+
#define CODE_HEADER_SEARCH_ITERATIONS 16
11+
812
bpf_map_def SEC("maps") beam_procs = {
913
.type = BPF_MAP_TYPE_HASH,
1014
.key_size = sizeof(pid_t),
1115
.value_size = sizeof(BEAMProcInfo),
1216
.max_entries = 256,
1317
};
1418

15-
static EBPF_INLINE ErrorCode unwind_one_beam_frame(PerCPURecord *record, u64 active_ranges) {
19+
typedef struct BEAMRangeEntry {
20+
u64 start;
21+
u64 end;
22+
} BEAMRangeEntry;
23+
24+
typedef struct BEAMRangesInfo {
25+
u64 modules;
26+
u64 n;
27+
BEAMRangeEntry first, mid, last;
28+
} BEAMRangesInfo;
29+
30+
static EBPF_INLINE ErrorCode unwind_one_beam_frame(PerCPURecord *record, BEAMRangesInfo *ranges) {
1631
UnwindState *state = &record->state;
1732
Trace *trace = &record->trace;
18-
u64 sp = state->sp, fp = state->fp, pc = state->pc;
33+
u64 pc = state->pc;
1934

20-
DEBUG_PRINT("beam: pc: %llx, sp: %llx, fp: %llx", pc, sp, fp);
35+
if (pc < ranges->first.start || pc > ranges->last.end) {
36+
return ERR_BEAM_PC_INVALID;
37+
}
2138

22-
bpf_probe_read_user(&state->fp, sizeof(u64), (void*)fp);
23-
bpf_probe_read_user(&state->pc, sizeof(u64), (void*)(fp+8));
24-
bpf_probe_read_user(&state->sp, sizeof(u64), (void*)(fp+16));
39+
u64 low = 0;
40+
u64 high = ranges->n;
41+
u64 current = low + (high - low) / 2;
2542

26-
unwinder_mark_nonleaf_frame(state);
43+
BEAMRangeEntry current_range;
44+
current_range.start = ranges->mid.start;
45+
current_range.end = ranges->mid.end;
2746

28-
_push_with_return_address(trace, active_ranges, pc, FRAME_MARKER_BEAM, state->return_address);
47+
#pragma unroll
48+
for (int i = 0; i < CODE_HEADER_SEARCH_ITERATIONS; i++) {
49+
if (pc < current_range.start) {
50+
high = current;
51+
} else if (pc >= current_range.end) {
52+
low = current + 1;
53+
} else {
54+
// `pc` is in the `current_range` CodeHeader
55+
_push_with_return_address(trace, current_range.start, pc, FRAME_MARKER_BEAM, state->return_address);
56+
break;
57+
}
58+
59+
current = low + (high - low) / 2;
60+
u64 addr = ranges->modules + current * sizeof(BEAMRangeEntry);
61+
if (bpf_probe_read_user((void *)&current_range, sizeof(BEAMRangeEntry), (void*)(addr))) {
62+
DEBUG_PRINT("beam: Failed to read ranges[%llu]", current);
63+
return -1;
64+
}
65+
}
66+
67+
u64 fp = state->fp;
68+
bpf_probe_read_user(&state->fp, sizeof(u64), (void*)fp);
69+
bpf_probe_read_user(&state->pc, sizeof(u64), (void*)(fp+8));
2970

3071
return ERR_OK;
3172
}
@@ -36,41 +77,76 @@ static EBPF_INLINE ErrorCode unwind_one_beam_frame(PerCPURecord *record, u64 act
3677
static EBPF_INLINE int unwind_beam(struct pt_regs *ctx) {
3778
PerCPURecord *record = get_per_cpu_record();
3879
if (!record) {
80+
DEBUG_PRINT("beam: no PerCPURecord found");
3981
return -1;
4082
}
4183

4284
Trace *trace = &record->trace;
4385
u32 pid = trace->pid;
4486

45-
DEBUG_PRINT("==== unwind_beam %d ====", trace->stack_len);
46-
47-
int unwinder = PROG_UNWIND_STOP;
48-
ErrorCode error = ERR_OK;
4987
BEAMProcInfo *info = bpf_map_lookup_elem(&beam_procs, &pid);
5088

5189
if (!info) {
5290
DEBUG_PRINT("beam: no BEAMProcInfo for this pid");
53-
goto exit;
91+
return -1;
5492
}
5593

94+
DEBUG_PRINT("==== unwind_beam %d ====", trace->stack_len);
95+
5696
// "the_active_code_index" symbol is from:
5797
// https://github.com/erlang/otp/blob/OTP-27.2.4/erts/emulator/beam/code_ix.c#L46
5898
u32 the_active_code_index;
59-
bpf_probe_read_user(&the_active_code_index, sizeof(u32), (void*)info->the_active_code_index);
99+
if(bpf_probe_read_user(&the_active_code_index, sizeof(u32), (void*)info->the_active_code_index)) {
100+
DEBUG_PRINT("beam: Failed to read the_active_code_index");
101+
return -1;
102+
}
60103

61104
// Index into the active static `r` variable using the currently-active code index
62105
// https://github.com/erlang/otp/blob/OTP-27.2.4/erts/emulator/beam/beam_ranges.c#L62
63106
u64 active_ranges = info->r + (the_active_code_index * info->ranges_sizeof);
64-
DEBUG_PRINT("==== unwind_beam active_ranges: %llx, the_active_code_index: %d ====", active_ranges, the_active_code_index);
65107

108+
DEBUG_PRINT("beam: r: %llx, the_active_code_index: %d, active_ranges: %llx", info->r, the_active_code_index, active_ranges);
109+
110+
BEAMRangesInfo ranges;
111+
112+
if(bpf_probe_read_user(&ranges.modules, sizeof(u64), (void*)(active_ranges + info->ranges_modules))) {
113+
DEBUG_PRINT("beam: Failed to read ranges.modules");
114+
return -1;
115+
}
116+
117+
if (bpf_probe_read_user(&ranges.n, sizeof(u64), (void*)(active_ranges + info->ranges_n))) {
118+
DEBUG_PRINT("beam: Failed to read ranges.n");
119+
return -1;
120+
}
121+
122+
DEBUG_PRINT("beam: modules: %llx, n: %llu", ranges.modules, ranges.n);
123+
124+
if (bpf_probe_read_user(&ranges.first, sizeof(BEAMRangeEntry), (void*)(ranges.modules))) {
125+
DEBUG_PRINT("beam: Failed to read ranges.first");
126+
return -1;
127+
}
128+
if (bpf_probe_read_user(&ranges.mid, sizeof(BEAMRangeEntry), (void*)(ranges.modules + (ranges.n / 2) * sizeof(BEAMRangeEntry)))) {
129+
DEBUG_PRINT("beam: Failed to read ranges.mid");
130+
return -1;
131+
}
132+
if (bpf_probe_read_user(&ranges.last, sizeof(BEAMRangeEntry), (void*)(ranges.modules + (ranges.n - 1) * sizeof(BEAMRangeEntry)))) {
133+
DEBUG_PRINT("beam: Failed to read ranges.last");
134+
return -1;
135+
}
136+
137+
DEBUG_PRINT("beam: ranges.first.start: %llx, ranges.last.end: %llx", ranges.first.start, ranges.last.end);
138+
139+
int unwinder = PROG_UNWIND_STOP;
140+
ErrorCode error = ERR_OK;
66141
#pragma unroll
67142
for (int i = 0; i < FRAMES_PER_PROGRAM; i++) {
68143
if (record->state.fp & 0x3) {
69144
unwinder = PROG_UNWIND_NATIVE;
70145
break;
71146
}
72147

73-
error = unwind_one_beam_frame(record, active_ranges);
148+
unwinder_mark_nonleaf_frame(&record->state);
149+
error = unwind_one_beam_frame(record, &ranges);
74150
if (error) {
75151
break;
76152
}

support/ebpf/errors.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ typedef enum ErrorCode {
162162
ERR_DOTNET_CODE_HEADER = 6002,
163163

164164
// Dotnet: Code object was too large to unwind in eBPF
165-
ERR_DOTNET_CODE_TOO_LARGE = 6003
165+
ERR_DOTNET_CODE_TOO_LARGE = 6003,
166+
167+
// BEAM: PC was outside the expected address range
168+
ERR_BEAM_PC_INVALID = 7000
166169
} ErrorCode;
167170

168171
#endif // OPTI_ERRORS_H

support/ebpf/tracer.ebpf.amd64

347 KB
Binary file not shown.

0 commit comments

Comments
 (0)