Skip to main content

ghostscope_protocol/
bpf_abi.rs

1//! Shared eBPF map key/value ABI.
2//!
3//! These layouts are consumed by generated eBPF bytecode and by userspace map
4//! writers. Keep them numeric, `repr(C)`, and free of compiler/loader state.
5
6/// Key for the pinned `proc_module_offsets` map.
7#[repr(C)]
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
9pub struct ProcModuleKey {
10    pub pid: u32,
11    pub pad: u32,
12    pub cookie_lo: u32,
13    pub cookie_hi: u32,
14}
15
16impl ProcModuleKey {
17    pub fn new(pid: u32, module_cookie: u64) -> Self {
18        Self {
19            pid,
20            pad: 0,
21            cookie_lo: module_cookie as u32,
22            cookie_hi: (module_cookie >> 32) as u32,
23        }
24    }
25}
26
27pub const PROC_MODULE_KEY_PID_OFFSET: usize = std::mem::offset_of!(ProcModuleKey, pid);
28pub const PROC_MODULE_KEY_PAD_OFFSET: usize = std::mem::offset_of!(ProcModuleKey, pad);
29pub const PROC_MODULE_KEY_COOKIE_LO_OFFSET: usize = std::mem::offset_of!(ProcModuleKey, cookie_lo);
30pub const PROC_MODULE_KEY_COOKIE_HI_OFFSET: usize = std::mem::offset_of!(ProcModuleKey, cookie_hi);
31pub const PROC_MODULE_KEY_SIZE: usize = std::mem::size_of::<ProcModuleKey>();
32
33/// Value for the pinned `proc_module_offsets` map.
34#[repr(C)]
35#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
36pub struct ProcModuleOffsetsValue {
37    pub text: u64,
38    pub rodata: u64,
39    pub data: u64,
40    pub bss: u64,
41    pub base: u64,
42    pub size: u64,
43}
44
45impl ProcModuleOffsetsValue {
46    pub fn new(text: u64, rodata: u64, data: u64, bss: u64, base: u64, size: u64) -> Self {
47        Self {
48            text,
49            rodata,
50            data,
51            bss,
52            base,
53            size,
54        }
55    }
56}
57
58pub const PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET: usize =
59    std::mem::offset_of!(ProcModuleOffsetsValue, text);
60pub const PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET: usize =
61    std::mem::offset_of!(ProcModuleOffsetsValue, rodata);
62pub const PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET: usize =
63    std::mem::offset_of!(ProcModuleOffsetsValue, data);
64pub const PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET: usize =
65    std::mem::offset_of!(ProcModuleOffsetsValue, bss);
66pub const PROC_MODULE_OFFSETS_VALUE_BASE_OFFSET: usize =
67    std::mem::offset_of!(ProcModuleOffsetsValue, base);
68pub const PROC_MODULE_OFFSETS_VALUE_SIZE_OFFSET: usize =
69    std::mem::offset_of!(ProcModuleOffsetsValue, size);
70pub const PROC_MODULE_OFFSETS_VALUE_SIZE: usize = std::mem::size_of::<ProcModuleOffsetsValue>();
71
72/// Value for the pinned `proc_module_range_meta` map.
73#[repr(C)]
74#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
75pub struct ProcModuleRangeMeta {
76    pub active_slot: u32,
77    pub count: u32,
78}
79
80impl ProcModuleRangeMeta {
81    pub fn new(active_slot: u32, count: u32) -> Self {
82        Self { active_slot, count }
83    }
84}
85
86pub const PROC_MODULE_RANGE_META_ACTIVE_SLOT_OFFSET: usize =
87    std::mem::offset_of!(ProcModuleRangeMeta, active_slot);
88pub const PROC_MODULE_RANGE_META_COUNT_OFFSET: usize =
89    std::mem::offset_of!(ProcModuleRangeMeta, count);
90pub const PROC_MODULE_RANGE_META_SIZE: usize = std::mem::size_of::<ProcModuleRangeMeta>();
91
92/// Key for the pinned `proc_module_ranges` map.
93#[repr(C)]
94#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
95pub struct ProcModuleRangeKey {
96    pub pid: u32,
97    pub slot: u32,
98    pub index: u32,
99    pub pad: u32,
100}
101
102impl ProcModuleRangeKey {
103    pub fn new(pid: u32, slot: u32, index: u32) -> Self {
104        Self {
105            pid,
106            slot,
107            index,
108            pad: 0,
109        }
110    }
111}
112
113pub const PROC_MODULE_RANGE_KEY_PID_OFFSET: usize = std::mem::offset_of!(ProcModuleRangeKey, pid);
114pub const PROC_MODULE_RANGE_KEY_SLOT_OFFSET: usize = std::mem::offset_of!(ProcModuleRangeKey, slot);
115pub const PROC_MODULE_RANGE_KEY_INDEX_OFFSET: usize =
116    std::mem::offset_of!(ProcModuleRangeKey, index);
117pub const PROC_MODULE_RANGE_KEY_PAD_OFFSET: usize = std::mem::offset_of!(ProcModuleRangeKey, pad);
118pub const PROC_MODULE_RANGE_KEY_SIZE: usize = std::mem::size_of::<ProcModuleRangeKey>();
119
120/// Value for the pinned `proc_module_ranges` map.
121#[repr(C)]
122#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
123pub struct ProcModuleRangeValue {
124    pub base: u64,
125    pub end: u64,
126    pub text: u64,
127    pub cookie_lo: u32,
128    pub cookie_hi: u32,
129}
130
131impl ProcModuleRangeValue {
132    pub fn new(base: u64, end: u64, text: u64, module_cookie: u64) -> Self {
133        Self {
134            base,
135            end,
136            text,
137            cookie_lo: module_cookie as u32,
138            cookie_hi: (module_cookie >> 32) as u32,
139        }
140    }
141
142    pub fn module_cookie(self) -> u64 {
143        u64::from(self.cookie_lo) | (u64::from(self.cookie_hi) << 32)
144    }
145}
146
147pub const PROC_MODULE_RANGE_VALUE_BASE_OFFSET: usize =
148    std::mem::offset_of!(ProcModuleRangeValue, base);
149pub const PROC_MODULE_RANGE_VALUE_END_OFFSET: usize =
150    std::mem::offset_of!(ProcModuleRangeValue, end);
151pub const PROC_MODULE_RANGE_VALUE_TEXT_OFFSET: usize =
152    std::mem::offset_of!(ProcModuleRangeValue, text);
153pub const PROC_MODULE_RANGE_VALUE_COOKIE_LO_OFFSET: usize =
154    std::mem::offset_of!(ProcModuleRangeValue, cookie_lo);
155pub const PROC_MODULE_RANGE_VALUE_COOKIE_HI_OFFSET: usize =
156    std::mem::offset_of!(ProcModuleRangeValue, cookie_hi);
157pub const PROC_MODULE_RANGE_VALUE_SIZE: usize = std::mem::size_of::<ProcModuleRangeValue>();
158
159/// Value for the pinned `pid_aliases` map.
160#[repr(C)]
161#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
162pub struct PidAliasValue {
163    pub proc_pid: u32,
164}
165
166pub const PID_ALIAS_VALUE_PROC_PID_OFFSET: usize = std::mem::offset_of!(PidAliasValue, proc_pid);
167pub const PID_ALIAS_VALUE_SIZE: usize = std::mem::size_of::<PidAliasValue>();
168
169/// BPF-facing compact DWARF CFI row used by the `bt` unwinder.
170///
171/// The row is intentionally numeric and path-free: userspace owns module
172/// identity, DWARF parsing, and symbolization, while eBPF receives only the
173/// compact rules it can execute safely.
174#[repr(C)]
175#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
176pub struct BacktraceUnwindRow {
177    pub pc_start: u64,
178    pub pc_end: u64,
179    pub cfa_offset: i64,
180    pub ra_offset: i64,
181    pub rbp_offset: i64,
182    pub cfa_register: u16,
183    pub ra_register: u16,
184    pub rbp_register: u16,
185    pub ra_kind: u8,
186    pub rbp_kind: u8,
187    pub reserved: [u8; 2],
188}
189
190pub const BACKTRACE_UNWIND_ROW_PC_START_OFFSET: usize =
191    std::mem::offset_of!(BacktraceUnwindRow, pc_start);
192pub const BACKTRACE_UNWIND_ROW_PC_END_OFFSET: usize =
193    std::mem::offset_of!(BacktraceUnwindRow, pc_end);
194pub const BACKTRACE_UNWIND_ROW_CFA_OFFSET_OFFSET: usize =
195    std::mem::offset_of!(BacktraceUnwindRow, cfa_offset);
196pub const BACKTRACE_UNWIND_ROW_RA_OFFSET_OFFSET: usize =
197    std::mem::offset_of!(BacktraceUnwindRow, ra_offset);
198pub const BACKTRACE_UNWIND_ROW_RBP_OFFSET_OFFSET: usize =
199    std::mem::offset_of!(BacktraceUnwindRow, rbp_offset);
200pub const BACKTRACE_UNWIND_ROW_CFA_REGISTER_OFFSET: usize =
201    std::mem::offset_of!(BacktraceUnwindRow, cfa_register);
202pub const BACKTRACE_UNWIND_ROW_RA_REGISTER_OFFSET: usize =
203    std::mem::offset_of!(BacktraceUnwindRow, ra_register);
204pub const BACKTRACE_UNWIND_ROW_RBP_REGISTER_OFFSET: usize =
205    std::mem::offset_of!(BacktraceUnwindRow, rbp_register);
206pub const BACKTRACE_UNWIND_ROW_RA_KIND_OFFSET: usize =
207    std::mem::offset_of!(BacktraceUnwindRow, ra_kind);
208pub const BACKTRACE_UNWIND_ROW_RBP_KIND_OFFSET: usize =
209    std::mem::offset_of!(BacktraceUnwindRow, rbp_kind);
210pub const BACKTRACE_UNWIND_ROW_SIZE: usize = std::mem::size_of::<BacktraceUnwindRow>();
211
212pub const BACKTRACE_UNWIND_WORDS_PER_ROW: usize = 6;
213pub const BACKTRACE_UNWIND_WORD_PC_START: usize = 0;
214pub const BACKTRACE_UNWIND_WORD_PC_END: usize = 1;
215pub const BACKTRACE_UNWIND_WORD_CFA_OFFSET: usize = 2;
216pub const BACKTRACE_UNWIND_WORD_RA_OFFSET: usize = 3;
217pub const BACKTRACE_UNWIND_WORD_RBP_OFFSET: usize = 4;
218pub const BACKTRACE_UNWIND_WORD_REGISTERS: usize = 5;
219
220/// Value for the `bt_module_row_ranges` map.
221///
222/// The map key is a module cookie (`u64`). The value points at a half-open
223/// range in `bt_unwind_rows`, allowing one module's CFI rows to be shared by
224/// every PID that maps the same object.
225#[repr(C)]
226#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
227pub struct BacktraceModuleRowRange {
228    pub row_start: u32,
229    pub row_end: u32,
230}
231
232pub const BACKTRACE_MODULE_ROW_RANGE_ROW_START_OFFSET: usize =
233    std::mem::offset_of!(BacktraceModuleRowRange, row_start);
234pub const BACKTRACE_MODULE_ROW_RANGE_ROW_END_OFFSET: usize =
235    std::mem::offset_of!(BacktraceModuleRowRange, row_end);
236pub const BACKTRACE_MODULE_ROW_RANGE_SIZE: usize = std::mem::size_of::<BacktraceModuleRowRange>();
237
238pub fn backtrace_unwind_row_register_word(row: BacktraceUnwindRow) -> u64 {
239    u64::from(row.cfa_register)
240        | (u64::from(row.ra_register) << 16)
241        | (u64::from(row.rbp_register) << 32)
242        | (u64::from(row.ra_kind) << 48)
243        | (u64::from(row.rbp_kind) << 56)
244}
245
246pub fn backtrace_unwind_row_word(row: BacktraceUnwindRow, word: usize) -> u64 {
247    match word {
248        BACKTRACE_UNWIND_WORD_PC_START => row.pc_start,
249        BACKTRACE_UNWIND_WORD_PC_END => row.pc_end,
250        BACKTRACE_UNWIND_WORD_CFA_OFFSET => row.cfa_offset as u64,
251        BACKTRACE_UNWIND_WORD_RA_OFFSET => row.ra_offset as u64,
252        BACKTRACE_UNWIND_WORD_RBP_OFFSET => row.rbp_offset as u64,
253        BACKTRACE_UNWIND_WORD_REGISTERS => backtrace_unwind_row_register_word(row),
254        _ => 0,
255    }
256}
257
258pub fn backtrace_unwind_row_from_words(
259    words: [u64; BACKTRACE_UNWIND_WORDS_PER_ROW],
260) -> BacktraceUnwindRow {
261    let registers = words[BACKTRACE_UNWIND_WORD_REGISTERS];
262    BacktraceUnwindRow {
263        pc_start: words[BACKTRACE_UNWIND_WORD_PC_START],
264        pc_end: words[BACKTRACE_UNWIND_WORD_PC_END],
265        cfa_offset: words[BACKTRACE_UNWIND_WORD_CFA_OFFSET] as i64,
266        ra_offset: words[BACKTRACE_UNWIND_WORD_RA_OFFSET] as i64,
267        rbp_offset: words[BACKTRACE_UNWIND_WORD_RBP_OFFSET] as i64,
268        cfa_register: registers as u16,
269        ra_register: (registers >> 16) as u16,
270        rbp_register: (registers >> 32) as u16,
271        ra_kind: (registers >> 48) as u8,
272        rbp_kind: (registers >> 56) as u8,
273        reserved: [0, 0],
274    }
275}
276
277/// Per-CPU state carried across `bt` tail-call unwind programs.
278#[repr(C)]
279#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
280pub struct BacktraceTailCallState {
281    pub current_ip: u64,
282    pub current_rsp: u64,
283    pub current_rbp: u64,
284    pub module_bias: u64,
285    pub module_cookie: u64,
286    pub inst_offset: u32,
287    pub event_size: u32,
288    pub frame_count: u8,
289    pub requested_depth: u8,
290    pub offsets_found: u8,
291    pub tail_calls: u8,
292    pub flags: u8,
293    pub active_slot: u8,
294    pub error_code: u16,
295    pub next_slot: u8,
296}
297
298pub const BACKTRACE_TAIL_STATE_CURRENT_IP_OFFSET: usize =
299    std::mem::offset_of!(BacktraceTailCallState, current_ip);
300pub const BACKTRACE_TAIL_STATE_CURRENT_RSP_OFFSET: usize =
301    std::mem::offset_of!(BacktraceTailCallState, current_rsp);
302pub const BACKTRACE_TAIL_STATE_CURRENT_RBP_OFFSET: usize =
303    std::mem::offset_of!(BacktraceTailCallState, current_rbp);
304pub const BACKTRACE_TAIL_STATE_MODULE_BIAS_OFFSET: usize =
305    std::mem::offset_of!(BacktraceTailCallState, module_bias);
306pub const BACKTRACE_TAIL_STATE_MODULE_COOKIE_OFFSET: usize =
307    std::mem::offset_of!(BacktraceTailCallState, module_cookie);
308pub const BACKTRACE_TAIL_STATE_INST_OFFSET_OFFSET: usize =
309    std::mem::offset_of!(BacktraceTailCallState, inst_offset);
310pub const BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET: usize =
311    std::mem::offset_of!(BacktraceTailCallState, event_size);
312pub const BACKTRACE_TAIL_STATE_FRAME_COUNT_OFFSET: usize =
313    std::mem::offset_of!(BacktraceTailCallState, frame_count);
314pub const BACKTRACE_TAIL_STATE_REQUESTED_DEPTH_OFFSET: usize =
315    std::mem::offset_of!(BacktraceTailCallState, requested_depth);
316pub const BACKTRACE_TAIL_STATE_OFFSETS_FOUND_OFFSET: usize =
317    std::mem::offset_of!(BacktraceTailCallState, offsets_found);
318pub const BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET: usize =
319    std::mem::offset_of!(BacktraceTailCallState, tail_calls);
320pub const BACKTRACE_TAIL_STATE_FLAGS_OFFSET: usize =
321    std::mem::offset_of!(BacktraceTailCallState, flags);
322pub const BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET: usize =
323    std::mem::offset_of!(BacktraceTailCallState, active_slot);
324pub const BACKTRACE_TAIL_STATE_ERROR_CODE_OFFSET: usize =
325    std::mem::offset_of!(BacktraceTailCallState, error_code);
326pub const BACKTRACE_TAIL_STATE_NEXT_SLOT_OFFSET: usize =
327    std::mem::offset_of!(BacktraceTailCallState, next_slot);
328pub const BACKTRACE_TAIL_STATE_SIZE: usize = std::mem::size_of::<BacktraceTailCallState>();
329pub const BACKTRACE_TAIL_NO_NEXT_SLOT: u8 = u8::MAX;
330
331pub const BACKTRACE_RECOVERY_UNDEFINED: u8 = 0;
332pub const BACKTRACE_RECOVERY_AT_CFA_OFFSET: u8 = 1;
333pub const BACKTRACE_RECOVERY_VAL_CFA_OFFSET: u8 = 2;
334pub const BACKTRACE_RECOVERY_REGISTER: u8 = 3;
335pub const BACKTRACE_RECOVERY_SAME_VALUE: u8 = 4;
336
337pub const BACKTRACE_RA_UNDEFINED: u8 = BACKTRACE_RECOVERY_UNDEFINED;
338pub const BACKTRACE_RA_AT_CFA_OFFSET: u8 = BACKTRACE_RECOVERY_AT_CFA_OFFSET;
339pub const BACKTRACE_RA_VAL_CFA_OFFSET: u8 = BACKTRACE_RECOVERY_VAL_CFA_OFFSET;
340pub const BACKTRACE_RA_REGISTER: u8 = BACKTRACE_RECOVERY_REGISTER;
341pub const BACKTRACE_RA_SAME_VALUE: u8 = BACKTRACE_RECOVERY_SAME_VALUE;
342
343#[cfg(feature = "aya-pod")]
344mod aya_pod {
345    use super::{
346        BacktraceModuleRowRange, BacktraceTailCallState, BacktraceUnwindRow, PidAliasValue,
347        ProcModuleKey, ProcModuleOffsetsValue, ProcModuleRangeKey, ProcModuleRangeMeta,
348        ProcModuleRangeValue,
349    };
350
351    // SAFETY: ProcModuleKey is repr(C), Copy, 'static, and contains only
352    // integer fields with no invalid bit patterns.
353    unsafe impl aya::Pod for ProcModuleKey {}
354    // SAFETY: ProcModuleOffsetsValue is repr(C), Copy, 'static, and contains
355    // only integer fields with no invalid bit patterns.
356    unsafe impl aya::Pod for ProcModuleOffsetsValue {}
357    // SAFETY: PidAliasValue is repr(C), Copy, 'static, and contains only an
358    // integer field with no invalid bit patterns.
359    unsafe impl aya::Pod for PidAliasValue {}
360    // SAFETY: ProcModuleRangeMeta is repr(C), Copy, 'static, and contains only
361    // integer fields with no invalid bit patterns.
362    unsafe impl aya::Pod for ProcModuleRangeMeta {}
363    // SAFETY: ProcModuleRangeKey is repr(C), Copy, 'static, and contains only
364    // integer fields with no invalid bit patterns.
365    unsafe impl aya::Pod for ProcModuleRangeKey {}
366    // SAFETY: ProcModuleRangeValue is repr(C), Copy, 'static, and contains
367    // only integer fields with no invalid bit patterns.
368    unsafe impl aya::Pod for ProcModuleRangeValue {}
369    // SAFETY: BacktraceUnwindRow is repr(C), Copy, 'static, and contains only
370    // integer fields with no invalid bit patterns.
371    unsafe impl aya::Pod for BacktraceUnwindRow {}
372    // SAFETY: BacktraceModuleRowRange is repr(C), Copy, 'static, and contains
373    // only integer fields with no invalid bit patterns.
374    unsafe impl aya::Pod for BacktraceModuleRowRange {}
375    // SAFETY: BacktraceTailCallState is repr(C), Copy, 'static, and contains
376    // only integer fields with no invalid bit patterns.
377    unsafe impl aya::Pod for BacktraceTailCallState {}
378}
379
380#[cfg(test)]
381mod tests {
382    use super::*;
383
384    #[test]
385    fn proc_module_offsets_layout_matches_bpf_maps() {
386        assert_eq!(PROC_MODULE_KEY_SIZE, 16);
387        assert_eq!(PROC_MODULE_KEY_PID_OFFSET, 0);
388        assert_eq!(PROC_MODULE_KEY_PAD_OFFSET, 4);
389        assert_eq!(PROC_MODULE_KEY_COOKIE_LO_OFFSET, 8);
390        assert_eq!(PROC_MODULE_KEY_COOKIE_HI_OFFSET, 12);
391
392        assert_eq!(PROC_MODULE_OFFSETS_VALUE_SIZE, 48);
393        assert_eq!(PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET, 0);
394        assert_eq!(PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET, 8);
395        assert_eq!(PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET, 16);
396        assert_eq!(PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET, 24);
397        assert_eq!(PROC_MODULE_OFFSETS_VALUE_BASE_OFFSET, 32);
398        assert_eq!(PROC_MODULE_OFFSETS_VALUE_SIZE_OFFSET, 40);
399
400        assert_eq!(PID_ALIAS_VALUE_SIZE, 4);
401        assert_eq!(PID_ALIAS_VALUE_PROC_PID_OFFSET, 0);
402
403        assert_eq!(PROC_MODULE_RANGE_META_SIZE, 8);
404        assert_eq!(PROC_MODULE_RANGE_META_ACTIVE_SLOT_OFFSET, 0);
405        assert_eq!(PROC_MODULE_RANGE_META_COUNT_OFFSET, 4);
406
407        assert_eq!(PROC_MODULE_RANGE_KEY_SIZE, 16);
408        assert_eq!(PROC_MODULE_RANGE_KEY_PID_OFFSET, 0);
409        assert_eq!(PROC_MODULE_RANGE_KEY_SLOT_OFFSET, 4);
410        assert_eq!(PROC_MODULE_RANGE_KEY_INDEX_OFFSET, 8);
411        assert_eq!(PROC_MODULE_RANGE_KEY_PAD_OFFSET, 12);
412
413        assert_eq!(PROC_MODULE_RANGE_VALUE_SIZE, 32);
414        assert_eq!(PROC_MODULE_RANGE_VALUE_BASE_OFFSET, 0);
415        assert_eq!(PROC_MODULE_RANGE_VALUE_END_OFFSET, 8);
416        assert_eq!(PROC_MODULE_RANGE_VALUE_TEXT_OFFSET, 16);
417        assert_eq!(PROC_MODULE_RANGE_VALUE_COOKIE_LO_OFFSET, 24);
418        assert_eq!(PROC_MODULE_RANGE_VALUE_COOKIE_HI_OFFSET, 28);
419    }
420
421    #[test]
422    fn backtrace_unwind_row_layout_matches_bpf_map_value() {
423        assert_eq!(BACKTRACE_UNWIND_ROW_SIZE, 56);
424        assert_eq!(BACKTRACE_UNWIND_ROW_PC_START_OFFSET, 0);
425        assert_eq!(BACKTRACE_UNWIND_ROW_PC_END_OFFSET, 8);
426        assert_eq!(BACKTRACE_UNWIND_ROW_CFA_OFFSET_OFFSET, 16);
427        assert_eq!(BACKTRACE_UNWIND_ROW_RA_OFFSET_OFFSET, 24);
428        assert_eq!(BACKTRACE_UNWIND_ROW_RBP_OFFSET_OFFSET, 32);
429        assert_eq!(BACKTRACE_UNWIND_ROW_CFA_REGISTER_OFFSET, 40);
430        assert_eq!(BACKTRACE_UNWIND_ROW_RA_REGISTER_OFFSET, 42);
431        assert_eq!(BACKTRACE_UNWIND_ROW_RBP_REGISTER_OFFSET, 44);
432        assert_eq!(BACKTRACE_UNWIND_ROW_RA_KIND_OFFSET, 46);
433        assert_eq!(BACKTRACE_UNWIND_ROW_RBP_KIND_OFFSET, 47);
434        assert_eq!(BACKTRACE_UNWIND_WORDS_PER_ROW, 6);
435
436        let row = BacktraceUnwindRow {
437            pc_start: 0x10,
438            pc_end: 0x20,
439            cfa_offset: 128,
440            ra_offset: -8,
441            rbp_offset: -16,
442            cfa_register: 7,
443            ra_register: 16,
444            rbp_register: 6,
445            ra_kind: BACKTRACE_RA_AT_CFA_OFFSET,
446            rbp_kind: BACKTRACE_RECOVERY_SAME_VALUE,
447            reserved: [0, 0],
448        };
449        let words = [
450            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_PC_START),
451            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_PC_END),
452            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_CFA_OFFSET),
453            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_RA_OFFSET),
454            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_RBP_OFFSET),
455            backtrace_unwind_row_word(row, BACKTRACE_UNWIND_WORD_REGISTERS),
456        ];
457        assert_eq!(backtrace_unwind_row_from_words(words), row);
458    }
459
460    #[test]
461    fn backtrace_module_row_range_layout_matches_bpf_map_value() {
462        assert_eq!(BACKTRACE_MODULE_ROW_RANGE_SIZE, 8);
463        assert_eq!(BACKTRACE_MODULE_ROW_RANGE_ROW_START_OFFSET, 0);
464        assert_eq!(BACKTRACE_MODULE_ROW_RANGE_ROW_END_OFFSET, 4);
465    }
466
467    #[test]
468    fn backtrace_tail_call_state_layout_matches_bpf_accessors() {
469        assert_eq!(BACKTRACE_TAIL_STATE_SIZE, 64);
470        assert_eq!(BACKTRACE_TAIL_STATE_CURRENT_IP_OFFSET, 0);
471        assert_eq!(BACKTRACE_TAIL_STATE_CURRENT_RSP_OFFSET, 8);
472        assert_eq!(BACKTRACE_TAIL_STATE_CURRENT_RBP_OFFSET, 16);
473        assert_eq!(BACKTRACE_TAIL_STATE_MODULE_BIAS_OFFSET, 24);
474        assert_eq!(BACKTRACE_TAIL_STATE_MODULE_COOKIE_OFFSET, 32);
475        assert_eq!(BACKTRACE_TAIL_STATE_INST_OFFSET_OFFSET, 40);
476        assert_eq!(BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET, 44);
477        assert_eq!(BACKTRACE_TAIL_STATE_FRAME_COUNT_OFFSET, 48);
478        assert_eq!(BACKTRACE_TAIL_STATE_REQUESTED_DEPTH_OFFSET, 49);
479        assert_eq!(BACKTRACE_TAIL_STATE_OFFSETS_FOUND_OFFSET, 50);
480        assert_eq!(BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET, 51);
481        assert_eq!(BACKTRACE_TAIL_STATE_FLAGS_OFFSET, 52);
482        assert_eq!(BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET, 53);
483        assert_eq!(BACKTRACE_TAIL_STATE_ERROR_CODE_OFFSET, 54);
484        assert_eq!(BACKTRACE_TAIL_STATE_NEXT_SLOT_OFFSET, 56);
485    }
486}