Skip to main content

ghostscope_compiler/ebpf/codegen/
backtrace.rs

1use super::backtrace_plan::{
2    BacktraceEmitMode, BacktraceInstructionPlan, BPF_INLINE_BACKTRACE_FRAME_LIMIT,
3};
4use super::*;
5use crate::script::BacktraceStatement;
6use aya_ebpf_bindings::bindings::bpf_func_id::{BPF_FUNC_map_lookup_elem, BPF_FUNC_tail_call};
7use ghostscope_dwarf::{CompactUnwindRow, MemoryAccessSize, ModuleAddress};
8use inkwell::basic_block::BasicBlock;
9use inkwell::values::BasicMetadataValueEnum;
10use std::path::PathBuf;
11
12const X86_64_DWARF_RIP: u16 = 16;
13const X86_64_DWARF_RBP: u16 = 6;
14const X86_64_DWARF_RSP: u16 = 7;
15const BPF_BACKTRACE_FRAMES_PER_TAIL_CALL: u8 = 4;
16const BPF_BACKTRACE_MAX_STEP_INVOCATIONS: u8 = 32;
17const BPF_BACKTRACE_STEP_PROG_INDEX: u32 = 0;
18
19struct RuntimeBtUnwindRow<'ctx> {
20    found: IntValue<'ctx>,
21    cfa_register: IntValue<'ctx>,
22    cfa_offset: IntValue<'ctx>,
23    ra_kind: IntValue<'ctx>,
24    ra_register: IntValue<'ctx>,
25    ra_offset: IntValue<'ctx>,
26    rbp_kind: IntValue<'ctx>,
27    rbp_register: IntValue<'ctx>,
28    rbp_offset: IntValue<'ctx>,
29}
30
31struct BtFrameModule<'ctx> {
32    cookie: IntValue<'ctx>,
33    bias: IntValue<'ctx>,
34    found: IntValue<'ctx>,
35}
36
37struct BtRowBounds<'ctx> {
38    start: IntValue<'ctx>,
39    end: IntValue<'ctx>,
40}
41
42struct BtModuleRangeMeta<'ctx> {
43    found: IntValue<'ctx>,
44    active_slot: IntValue<'ctx>,
45    count: IntValue<'ctx>,
46}
47
48struct BtModuleRangeValue<'ctx> {
49    found: IntValue<'ctx>,
50    base: IntValue<'ctx>,
51    end: IntValue<'ctx>,
52    text: IntValue<'ctx>,
53    cookie: IntValue<'ctx>,
54}
55
56struct RuntimeBtRowScratch<'ctx> {
57    found_ptr: PointerValue<'ctx>,
58    cfa_register_ptr: PointerValue<'ctx>,
59    cfa_offset_ptr: PointerValue<'ctx>,
60    ra_kind_ptr: PointerValue<'ctx>,
61    ra_register_ptr: PointerValue<'ctx>,
62    ra_offset_ptr: PointerValue<'ctx>,
63    rbp_kind_ptr: PointerValue<'ctx>,
64    rbp_register_ptr: PointerValue<'ctx>,
65    rbp_offset_ptr: PointerValue<'ctx>,
66}
67
68struct BtScratch<'ctx> {
69    row: RuntimeBtRowScratch<'ctx>,
70    next_rbp_ptr: PointerValue<'ctx>,
71    next_error_code_ptr: PointerValue<'ctx>,
72}
73
74#[derive(Clone, Copy)]
75struct BtRegisterState<'ctx> {
76    ip: IntValue<'ctx>,
77    rsp: IntValue<'ctx>,
78    rbp: IntValue<'ctx>,
79}
80
81#[derive(Clone, Copy)]
82struct BtNextFrame<'ctx> {
83    ip: IntValue<'ctx>,
84    rsp: IntValue<'ctx>,
85    rbp: IntValue<'ctx>,
86    error_code: IntValue<'ctx>,
87}
88
89struct BtFrameValidation<'ctx> {
90    valid: IntValue<'ctx>,
91    complete: IntValue<'ctx>,
92    error_code: IntValue<'ctx>,
93}
94
95enum BacktraceUnwindRowForPc {
96    Usable(ghostscope_protocol::BacktraceUnwindRow),
97    Missing,
98    Unsupported,
99}
100
101impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
102    pub fn generate_backtrace_instruction(&mut self, stmt: &BacktraceStatement) -> Result<()> {
103        let plan = self.plan_backtrace_instruction(stmt);
104        if matches!(plan.mode, BacktraceEmitMode::TailCall) {
105            return self.generate_tail_call_backtrace_instruction(&plan);
106        }
107
108        self.generate_inline_backtrace_instruction(&plan)
109    }
110
111    /// Generate a DWARF-backed Backtrace instruction.
112    ///
113    /// eBPF records `(module_cookie, normalized_pc, raw_ip)` frames and advances
114    /// the unwind state from compact DWARF CFI rows. Userspace owns final source
115    /// line and inline-chain symbolization.
116    fn generate_inline_backtrace_instruction(
117        &mut self,
118        plan: &BacktraceInstructionPlan,
119    ) -> Result<()> {
120        let depth = plan.depth;
121        let flags = plan.flags;
122        info!("Generating Backtrace instruction: depth={}", depth);
123
124        let payload_size = plan.payload_size;
125        let instruction_size = plan.instruction_size;
126        let inst_buffer = self
127            .reserve_instruction_region_or_return_zero(instruction_size as u64)?
128            .into_value_after_runtime_returns();
129
130        self.store_u8_const(
131            inst_buffer,
132            std::mem::offset_of!(InstructionHeader, inst_type),
133            InstructionType::Backtrace as u8,
134            "bt_inst_type",
135        )?;
136        self.store_u16_const(
137            inst_buffer,
138            std::mem::offset_of!(InstructionHeader, data_length),
139            payload_size as u16,
140            "bt_data_length",
141        )?;
142
143        let data_base = INSTRUCTION_HEADER_SIZE;
144        self.store_u8_const(
145            inst_buffer,
146            data_base + BACKTRACE_DATA_REQUESTED_DEPTH_OFFSET,
147            depth,
148            "bt_requested_depth",
149        )?;
150        self.store_u8_const(
151            inst_buffer,
152            data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
153            1,
154            "bt_frame_count",
155        )?;
156        self.store_u8_const(
157            inst_buffer,
158            data_base + BACKTRACE_DATA_FLAGS_OFFSET,
159            flags,
160            "bt_flags",
161        )?;
162        self.store_u16_const(
163            inst_buffer,
164            data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
165            0,
166            "bt_error_code",
167        )?;
168
169        let Some(compile_ctx) = self.current_compile_time_context.clone() else {
170            self.store_u8_const(
171                inst_buffer,
172                data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
173                0,
174                "bt_frame_count_no_context",
175            )?;
176            self.store_u8_const(
177                inst_buffer,
178                data_base + BACKTRACE_DATA_STATUS_OFFSET,
179                BacktraceStatus::DwarfUnavailable as u8,
180                "bt_status_no_context",
181            )?;
182            return Ok(());
183        };
184
185        let module_cookie = self.cookie_for_module_or_fallback(&compile_ctx.module_path);
186        let module_cookie_value = self.context.i64_type().const_int(module_cookie, false);
187        let pt_regs = self.get_pt_regs_parameter()?;
188        let raw_ip = self.load_dwarf_register_i64(X86_64_DWARF_RIP, pt_regs)?;
189        let (module_bias, offsets_found) = self.generate_runtime_address_from_offsets(
190            self.context.i64_type().const_zero(),
191            0,
192            module_cookie,
193        )?;
194        let normalized_pc = self.normalized_pc_from_raw(raw_ip, module_bias, offsets_found)?;
195        let caller_fallback_found = self.backtrace_module_fallback_found(offsets_found);
196
197        self.store_backtrace_frame(
198            inst_buffer,
199            0,
200            module_cookie_value,
201            normalized_pc,
202            raw_ip,
203            0,
204        )?;
205
206        if depth == 1 {
207            let status =
208                self.status_or_offsets_unavailable(BacktraceStatus::Truncated, offsets_found)?;
209            self.store_u8_value(
210                inst_buffer,
211                data_base + BACKTRACE_DATA_STATUS_OFFSET,
212                status,
213                "bt_status_depth_one",
214            )?;
215            return Ok(());
216        }
217
218        let row = self
219            .usable_backtrace_unwind_row_for_pc(&compile_ctx.module_path, compile_ctx.pc_address);
220        let initial_status = self.status_for_backtrace_unwind_row_for_pc(&row);
221        let status = self.status_or_offsets_unavailable(initial_status, offsets_found)?;
222        self.store_u8_value(
223            inst_buffer,
224            data_base + BACKTRACE_DATA_STATUS_OFFSET,
225            status,
226            "bt_initial_status",
227        )?;
228
229        let BacktraceUnwindRowForPc::Usable(row) = row else {
230            return Ok(());
231        };
232
233        let i64_type = self.context.i64_type();
234        let ip_ptr = self.build_entry_alloca(i64_type, "bt_state_ip")?;
235        let rsp_ptr = self.build_entry_alloca(i64_type, "bt_state_rsp")?;
236        let rbp_ptr = self.build_entry_alloca(i64_type, "bt_state_rbp")?;
237        let module_bias_ptr = self.build_entry_alloca(i64_type, "bt_state_module_bias")?;
238        let module_cookie_ptr = self.build_entry_alloca(i64_type, "bt_state_module_cookie")?;
239        let module_found_ptr =
240            self.build_entry_alloca(self.context.bool_type(), "bt_state_module_found")?;
241        let scratch = self.allocate_backtrace_scratch()?;
242        self.builder
243            .build_store(ip_ptr, raw_ip)
244            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
245        let initial_rsp = self.load_dwarf_register_i64(X86_64_DWARF_RSP, pt_regs)?;
246        let initial_rbp = self.load_dwarf_register_i64(X86_64_DWARF_RBP, pt_regs)?;
247        self.builder
248            .build_store(rsp_ptr, initial_rsp)
249            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
250        self.builder
251            .build_store(rbp_ptr, initial_rbp)
252            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
253        self.builder
254            .build_store(module_bias_ptr, module_bias)
255            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
256        self.builder
257            .build_store(module_cookie_ptr, module_cookie_value)
258            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
259        self.builder
260            .build_store(module_found_ptr, offsets_found)
261            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
262
263        let current_fn = self.current_function("generate DWARF backtrace")?;
264        let done = self.context.append_basic_block(current_fn, "bt_done");
265
266        let runtime_row = self.runtime_row_from_static(row);
267        let state = BtRegisterState {
268            ip: self.load_i64(ip_ptr, "bt_initial_current_ip")?,
269            rsp: self.load_i64(rsp_ptr, "bt_initial_current_rsp")?,
270            rbp: self.load_i64(rbp_ptr, "bt_initial_current_rbp")?,
271        };
272        let next = self.recover_next_frame_from_runtime_row(&runtime_row, state, &scratch)?;
273        let validation = self.validate_backtrace_next_frame(state, next)?;
274        let initial_store_block = self
275            .context
276            .append_basic_block(current_fn, "bt_initial_store_frame");
277        let initial_stop_block = self
278            .context
279            .append_basic_block(current_fn, "bt_initial_stop");
280        self.builder
281            .build_conditional_branch(validation.valid, initial_store_block, initial_stop_block)
282            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
283
284        self.builder.position_at_end(initial_stop_block);
285        let stop_status = self.status_for_backtrace_stop(
286            validation.complete,
287            validation.error_code,
288            offsets_found,
289        )?;
290        self.store_u8_value(
291            inst_buffer,
292            data_base + BACKTRACE_DATA_STATUS_OFFSET,
293            stop_status,
294            "bt_initial_stop_status",
295        )?;
296        self.store_u16_value(
297            inst_buffer,
298            data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
299            validation.error_code,
300            "bt_initial_stop_error_code",
301        )?;
302        self.builder
303            .build_unconditional_branch(done)
304            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
305
306        self.builder.position_at_end(initial_store_block);
307        let frame_module = self.resolve_backtrace_frame_module(
308            next.ip,
309            module_cookie_value,
310            module_bias,
311            caller_fallback_found,
312            "bt_initial_frame_module",
313        )?;
314        let next_pc =
315            self.normalized_pc_from_raw(next.ip, frame_module.bias, frame_module.found)?;
316        self.store_backtrace_frame(inst_buffer, 1, frame_module.cookie, next_pc, next.ip, 0)?;
317        self.store_u8_const(
318            inst_buffer,
319            data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
320            2,
321            "bt_initial_frame_count",
322        )?;
323        let status = if depth == 2 {
324            BacktraceStatus::Truncated
325        } else {
326            BacktraceStatus::ReadError
327        };
328        let status = self.status_or_offsets_unavailable(status, offsets_found)?;
329        self.store_u8_value(
330            inst_buffer,
331            data_base + BACKTRACE_DATA_STATUS_OFFSET,
332            status,
333            "bt_initial_status_after_frame",
334        )?;
335        self.builder
336            .build_store(ip_ptr, next.ip)
337            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
338        self.builder
339            .build_store(rsp_ptr, next.rsp)
340            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
341        self.builder
342            .build_store(rbp_ptr, next.rbp)
343            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
344        self.builder
345            .build_store(module_bias_ptr, frame_module.bias)
346            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
347        self.builder
348            .build_store(module_cookie_ptr, frame_module.cookie)
349            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
350        self.builder
351            .build_store(module_found_ptr, frame_module.found)
352            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
353
354        if depth == 2 {
355            self.builder
356                .build_unconditional_branch(done)
357                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
358        } else if self.backtrace_unwind_rows.is_empty() {
359            let status = self
360                .status_or_offsets_unavailable(BacktraceStatus::NoUnwindRowsForPc, offsets_found)?;
361            self.store_u8_value(
362                inst_buffer,
363                data_base + BACKTRACE_DATA_STATUS_OFFSET,
364                status,
365                "bt_status_no_rows",
366            )?;
367            self.builder
368                .build_unconditional_branch(done)
369                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
370        } else {
371            let inline_depth = depth.min(BPF_INLINE_BACKTRACE_FRAME_LIMIT);
372            for frame_index in 2..inline_depth {
373                let current_ip = self.load_i64(ip_ptr, "bt_lookup_ip")?;
374                let current_module_bias =
375                    self.load_i64(module_bias_ptr, "bt_lookup_module_bias")?;
376                let current_module_cookie =
377                    self.load_i64(module_cookie_ptr, "bt_lookup_module_cookie")?;
378                let current_module_found =
379                    self.load_bool(module_found_ptr, "bt_lookup_module_found")?;
380                let lookup_raw = self.add_signed_offset(current_ip, -1, "bt_lookup_raw")?;
381                let lookup_pc = self.backtrace_lookup_pc_from_raw(
382                    lookup_raw,
383                    current_module_bias,
384                    current_module_found,
385                )?;
386                let runtime_row = self.lookup_backtrace_unwind_row(
387                    lookup_pc,
388                    current_module_cookie,
389                    &scratch.row,
390                    &format!("bt_frame_{frame_index}_row"),
391                )?;
392                let found_block = self.context.append_basic_block(current_fn, "bt_row_found");
393                let missing_block = self
394                    .context
395                    .append_basic_block(current_fn, "bt_row_missing");
396                self.builder
397                    .build_conditional_branch(runtime_row.found, found_block, missing_block)
398                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
399
400                self.builder.position_at_end(missing_block);
401                let status = self.status_or_offsets_unavailable(
402                    BacktraceStatus::NoUnwindRowsForPc,
403                    current_module_found,
404                )?;
405                self.store_u8_value(
406                    inst_buffer,
407                    data_base + BACKTRACE_DATA_STATUS_OFFSET,
408                    status,
409                    "bt_status_missing_row",
410                )?;
411                self.builder
412                    .build_unconditional_branch(done)
413                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
414
415                self.builder.position_at_end(found_block);
416                let state = BtRegisterState {
417                    ip: self.load_i64(ip_ptr, "bt_current_ip")?,
418                    rsp: self.load_i64(rsp_ptr, "bt_current_rsp")?,
419                    rbp: self.load_i64(rbp_ptr, "bt_current_rbp")?,
420                };
421                let next =
422                    self.recover_next_frame_from_runtime_row(&runtime_row, state, &scratch)?;
423                let validation = self.validate_backtrace_next_frame(state, next)?;
424                let store_block = self
425                    .context
426                    .append_basic_block(current_fn, "bt_store_frame");
427                let stop_block = self.context.append_basic_block(current_fn, "bt_stop");
428                self.builder
429                    .build_conditional_branch(validation.valid, store_block, stop_block)
430                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
431
432                self.builder.position_at_end(stop_block);
433                let stop_status = self.status_for_backtrace_stop(
434                    validation.complete,
435                    validation.error_code,
436                    current_module_found,
437                )?;
438                self.store_u8_value(
439                    inst_buffer,
440                    data_base + BACKTRACE_DATA_STATUS_OFFSET,
441                    stop_status,
442                    "bt_status_stop",
443                )?;
444                self.store_u16_value(
445                    inst_buffer,
446                    data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
447                    validation.error_code,
448                    "bt_error_code_stop",
449                )?;
450                self.builder
451                    .build_unconditional_branch(done)
452                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
453
454                self.builder.position_at_end(store_block);
455                let frame_module = self.resolve_backtrace_frame_module(
456                    next.ip,
457                    current_module_cookie,
458                    current_module_bias,
459                    self.backtrace_module_fallback_found(current_module_found),
460                    &format!("bt_frame_{frame_index}_module"),
461                )?;
462                let next_pc =
463                    self.normalized_pc_from_raw(next.ip, frame_module.bias, frame_module.found)?;
464                self.store_backtrace_frame(
465                    inst_buffer,
466                    frame_index as usize,
467                    frame_module.cookie,
468                    next_pc,
469                    next.ip,
470                    0,
471                )?;
472                self.store_u8_const(
473                    inst_buffer,
474                    data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
475                    frame_index + 1,
476                    "bt_frame_count",
477                )?;
478                let next_status = if frame_index + 1 == inline_depth {
479                    BacktraceStatus::Truncated
480                } else {
481                    BacktraceStatus::ReadError
482                };
483                let next_status =
484                    self.status_or_offsets_unavailable(next_status, current_module_found)?;
485                self.store_u8_value(
486                    inst_buffer,
487                    data_base + BACKTRACE_DATA_STATUS_OFFSET,
488                    next_status,
489                    "bt_status_after_frame",
490                )?;
491                self.builder
492                    .build_store(ip_ptr, next.ip)
493                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
494                self.builder
495                    .build_store(rsp_ptr, next.rsp)
496                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
497                self.builder
498                    .build_store(rbp_ptr, next.rbp)
499                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
500                self.builder
501                    .build_store(module_bias_ptr, frame_module.bias)
502                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
503                self.builder
504                    .build_store(module_cookie_ptr, frame_module.cookie)
505                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
506                self.builder
507                    .build_store(module_found_ptr, frame_module.found)
508                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
509
510                if frame_index + 1 == inline_depth {
511                    self.builder
512                        .build_unconditional_branch(done)
513                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
514                }
515            }
516        }
517
518        self.builder.position_at_end(done);
519        Ok(())
520    }
521
522    fn generate_tail_call_backtrace_instruction(
523        &mut self,
524        plan: &BacktraceInstructionPlan,
525    ) -> Result<()> {
526        let depth = plan.depth;
527        let flags = plan.flags;
528        info!(
529            "Generating tail-call Backtrace instruction: depth={}",
530            depth
531        );
532
533        let payload_size = plan.payload_size;
534        let instruction_size = plan.instruction_size;
535        let offset_ptr = self.event_offset_alloca.ok_or_else(|| {
536            CodeGenError::LLVMError("event_offset not allocated in entry block".to_string())
537        })?;
538        let inst_offset = self
539            .builder
540            .build_load(self.context.i32_type(), offset_ptr, "bt_tail_inst_offset")
541            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
542            .into_int_value();
543        let inst_buffer = self
544            .reserve_instruction_region_or_return_zero(instruction_size as u64)?
545            .into_value_after_runtime_returns();
546
547        self.store_u8_const(
548            inst_buffer,
549            std::mem::offset_of!(InstructionHeader, inst_type),
550            InstructionType::Backtrace as u8,
551            "bt_inst_type",
552        )?;
553        self.store_u16_const(
554            inst_buffer,
555            std::mem::offset_of!(InstructionHeader, data_length),
556            payload_size as u16,
557            "bt_data_length",
558        )?;
559
560        let data_base = INSTRUCTION_HEADER_SIZE;
561        self.store_u8_const(
562            inst_buffer,
563            data_base + BACKTRACE_DATA_REQUESTED_DEPTH_OFFSET,
564            depth,
565            "bt_requested_depth",
566        )?;
567        self.store_u8_const(
568            inst_buffer,
569            data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
570            1,
571            "bt_frame_count",
572        )?;
573        self.store_u8_const(
574            inst_buffer,
575            data_base + BACKTRACE_DATA_FLAGS_OFFSET,
576            flags,
577            "bt_flags",
578        )?;
579        self.store_u16_const(
580            inst_buffer,
581            data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
582            0,
583            "bt_error_code",
584        )?;
585
586        let Some(compile_ctx) = self.current_compile_time_context.clone() else {
587            self.store_u8_const(
588                inst_buffer,
589                data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
590                0,
591                "bt_frame_count_no_context",
592            )?;
593            self.store_u8_const(
594                inst_buffer,
595                data_base + BACKTRACE_DATA_STATUS_OFFSET,
596                BacktraceStatus::DwarfUnavailable as u8,
597                "bt_status_no_context",
598            )?;
599            return Ok(());
600        };
601
602        let module_cookie = self.cookie_for_module_or_fallback(&compile_ctx.module_path);
603        let module_cookie_value = self.context.i64_type().const_int(module_cookie, false);
604        let pt_regs = self.get_pt_regs_parameter()?;
605        let raw_ip = self.load_dwarf_register_i64(X86_64_DWARF_RIP, pt_regs)?;
606        let initial_rsp = self.load_dwarf_register_i64(X86_64_DWARF_RSP, pt_regs)?;
607        let initial_rbp = self.load_dwarf_register_i64(X86_64_DWARF_RBP, pt_regs)?;
608        let (module_bias, offsets_found) = self.generate_runtime_address_from_offsets(
609            self.context.i64_type().const_zero(),
610            0,
611            module_cookie,
612        )?;
613        let normalized_pc = self.normalized_pc_from_raw(raw_ip, module_bias, offsets_found)?;
614        let caller_fallback_found = self.backtrace_module_fallback_found(offsets_found);
615
616        self.store_backtrace_frame(
617            inst_buffer,
618            0,
619            module_cookie_value,
620            normalized_pc,
621            raw_ip,
622            0,
623        )?;
624
625        if depth == 1 {
626            let status =
627                self.status_or_offsets_unavailable(BacktraceStatus::Truncated, offsets_found)?;
628            self.store_u8_value(
629                inst_buffer,
630                data_base + BACKTRACE_DATA_STATUS_OFFSET,
631                status,
632                "bt_status_depth_one",
633            )?;
634            return Ok(());
635        }
636
637        if self.backtrace_unwind_rows.is_empty() {
638            let status = self
639                .status_or_offsets_unavailable(BacktraceStatus::NoUnwindRowsForPc, offsets_found)?;
640            self.store_u8_value(
641                inst_buffer,
642                data_base + BACKTRACE_DATA_STATUS_OFFSET,
643                status,
644                "bt_status_no_rows",
645            )?;
646            return Ok(());
647        }
648
649        let row = match self
650            .usable_backtrace_unwind_row_for_pc(&compile_ctx.module_path, compile_ctx.pc_address)
651        {
652            BacktraceUnwindRowForPc::Usable(row) => row,
653            row_status => {
654                let initial_status = self.status_for_backtrace_unwind_row_for_pc(&row_status);
655                let status = self.status_or_offsets_unavailable(initial_status, offsets_found)?;
656                self.store_u8_value(
657                    inst_buffer,
658                    data_base + BACKTRACE_DATA_STATUS_OFFSET,
659                    status,
660                    "bt_status_no_initial_row",
661                )?;
662                return Ok(());
663            }
664        };
665
666        let status =
667            self.status_or_offsets_unavailable(BacktraceStatus::ReadError, offsets_found)?;
668        self.store_u8_value(
669            inst_buffer,
670            data_base + BACKTRACE_DATA_STATUS_OFFSET,
671            status,
672            "bt_tail_initial_status",
673        )?;
674
675        let scratch = self.allocate_backtrace_scratch()?;
676        let current_fn = self.current_function("initialize bt tail-call state")?;
677        let done_block = self
678            .context
679            .append_basic_block(current_fn, "bt_tail_state_done");
680        let i64_type = self.context.i64_type();
681        let ip_ptr = self.build_entry_alloca(i64_type, "bt_tail_prefix_ip")?;
682        let rsp_ptr = self.build_entry_alloca(i64_type, "bt_tail_prefix_rsp")?;
683        let rbp_ptr = self.build_entry_alloca(i64_type, "bt_tail_prefix_rbp")?;
684        let module_bias_ptr = self.build_entry_alloca(i64_type, "bt_tail_prefix_module_bias")?;
685        let module_cookie_ptr =
686            self.build_entry_alloca(i64_type, "bt_tail_prefix_module_cookie")?;
687        let module_found_ptr =
688            self.build_entry_alloca(self.context.bool_type(), "bt_tail_prefix_module_found")?;
689        self.builder
690            .build_store(module_bias_ptr, module_bias)
691            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
692        self.builder
693            .build_store(module_cookie_ptr, module_cookie_value)
694            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
695        self.builder
696            .build_store(module_found_ptr, offsets_found)
697            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
698
699        let runtime_row = self.runtime_row_from_static(row);
700        let state = BtRegisterState {
701            ip: raw_ip,
702            rsp: initial_rsp,
703            rbp: initial_rbp,
704        };
705        let next = self.recover_next_frame_from_runtime_row(&runtime_row, state, &scratch)?;
706        let validation = self.validate_backtrace_next_frame(state, next)?;
707        let initial_store_block = self
708            .context
709            .append_basic_block(current_fn, "bt_tail_initial_store_frame");
710        let initial_stop_block = self
711            .context
712            .append_basic_block(current_fn, "bt_tail_initial_stop");
713        self.builder
714            .build_conditional_branch(validation.valid, initial_store_block, initial_stop_block)
715            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
716
717        self.builder.position_at_end(initial_stop_block);
718        let stop_status = self.status_for_backtrace_stop(
719            validation.complete,
720            validation.error_code,
721            offsets_found,
722        )?;
723        self.store_u8_value(
724            inst_buffer,
725            data_base + BACKTRACE_DATA_STATUS_OFFSET,
726            stop_status,
727            "bt_tail_initial_stop_status",
728        )?;
729        self.store_u16_value(
730            inst_buffer,
731            data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
732            validation.error_code,
733            "bt_tail_initial_stop_error_code",
734        )?;
735        self.builder
736            .build_unconditional_branch(done_block)
737            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
738
739        self.builder.position_at_end(initial_store_block);
740        let frame_module = self.resolve_backtrace_frame_module(
741            next.ip,
742            module_cookie_value,
743            module_bias,
744            caller_fallback_found,
745            "bt_tail_initial_frame_module",
746        )?;
747        let next_pc =
748            self.normalized_pc_from_raw(next.ip, frame_module.bias, frame_module.found)?;
749        self.store_backtrace_frame(inst_buffer, 1, frame_module.cookie, next_pc, next.ip, 0)?;
750        self.store_u8_const(
751            inst_buffer,
752            data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
753            2,
754            "bt_tail_initial_frame_count",
755        )?;
756        let status = if depth == 2 {
757            BacktraceStatus::Truncated
758        } else {
759            BacktraceStatus::ReadError
760        };
761        let status = self.status_or_offsets_unavailable(status, offsets_found)?;
762        self.store_u8_value(
763            inst_buffer,
764            data_base + BACKTRACE_DATA_STATUS_OFFSET,
765            status,
766            "bt_tail_initial_status_after_frame",
767        )?;
768        self.builder
769            .build_store(ip_ptr, next.ip)
770            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
771        self.builder
772            .build_store(rsp_ptr, next.rsp)
773            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
774        self.builder
775            .build_store(rbp_ptr, next.rbp)
776            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
777        self.builder
778            .build_store(module_bias_ptr, frame_module.bias)
779            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
780        self.builder
781            .build_store(module_cookie_ptr, frame_module.cookie)
782            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
783        self.builder
784            .build_store(module_found_ptr, frame_module.found)
785            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
786
787        if depth == 2 {
788            self.builder
789                .build_unconditional_branch(done_block)
790                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
791            self.builder.position_at_end(done_block);
792            return Ok(());
793        }
794
795        let prefix_depth = depth.min(BPF_INLINE_BACKTRACE_FRAME_LIMIT);
796        for frame_index in 2..prefix_depth {
797            let current_ip = self.load_i64(ip_ptr, "bt_tail_prefix_lookup_ip")?;
798            let current_module_bias =
799                self.load_i64(module_bias_ptr, "bt_tail_prefix_lookup_module_bias")?;
800            let current_module_cookie =
801                self.load_i64(module_cookie_ptr, "bt_tail_prefix_lookup_module_cookie")?;
802            let current_module_found =
803                self.load_bool(module_found_ptr, "bt_tail_prefix_lookup_module_found")?;
804            let lookup_raw = self.add_signed_offset(current_ip, -1, "bt_tail_prefix_lookup_raw")?;
805            let lookup_pc = self.backtrace_lookup_pc_from_raw(
806                lookup_raw,
807                current_module_bias,
808                current_module_found,
809            )?;
810            let runtime_row = self.lookup_backtrace_unwind_row(
811                lookup_pc,
812                current_module_cookie,
813                &scratch.row,
814                &format!("bt_tail_prefix_frame_{frame_index}_row"),
815            )?;
816            let found_block = self
817                .context
818                .append_basic_block(current_fn, "bt_tail_prefix_row_found");
819            let missing_block = self
820                .context
821                .append_basic_block(current_fn, "bt_tail_prefix_row_missing");
822            self.builder
823                .build_conditional_branch(runtime_row.found, found_block, missing_block)
824                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
825
826            self.builder.position_at_end(missing_block);
827            let status = self.status_or_offsets_unavailable(
828                BacktraceStatus::NoUnwindRowsForPc,
829                current_module_found,
830            )?;
831            self.store_u8_value(
832                inst_buffer,
833                data_base + BACKTRACE_DATA_STATUS_OFFSET,
834                status,
835                "bt_tail_prefix_status_missing_row",
836            )?;
837            self.builder
838                .build_unconditional_branch(done_block)
839                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
840
841            self.builder.position_at_end(found_block);
842            let state = BtRegisterState {
843                ip: self.load_i64(ip_ptr, "bt_tail_prefix_current_ip")?,
844                rsp: self.load_i64(rsp_ptr, "bt_tail_prefix_current_rsp")?,
845                rbp: self.load_i64(rbp_ptr, "bt_tail_prefix_current_rbp")?,
846            };
847            let next = self.recover_next_frame_from_runtime_row(&runtime_row, state, &scratch)?;
848            let validation = self.validate_backtrace_next_frame(state, next)?;
849            let store_block = self
850                .context
851                .append_basic_block(current_fn, "bt_tail_prefix_store_frame");
852            let stop_block = self
853                .context
854                .append_basic_block(current_fn, "bt_tail_prefix_stop");
855            self.builder
856                .build_conditional_branch(validation.valid, store_block, stop_block)
857                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
858
859            self.builder.position_at_end(stop_block);
860            let stop_status = self.status_for_backtrace_stop(
861                validation.complete,
862                validation.error_code,
863                current_module_found,
864            )?;
865            self.store_u8_value(
866                inst_buffer,
867                data_base + BACKTRACE_DATA_STATUS_OFFSET,
868                stop_status,
869                "bt_tail_prefix_stop_status",
870            )?;
871            self.store_u16_value(
872                inst_buffer,
873                data_base + BACKTRACE_DATA_ERROR_CODE_OFFSET,
874                validation.error_code,
875                "bt_tail_prefix_stop_error_code",
876            )?;
877            self.builder
878                .build_unconditional_branch(done_block)
879                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
880
881            self.builder.position_at_end(store_block);
882            let frame_module = self.resolve_backtrace_frame_module(
883                next.ip,
884                current_module_cookie,
885                current_module_bias,
886                self.backtrace_module_fallback_found(current_module_found),
887                &format!("bt_tail_prefix_frame_{frame_index}_module"),
888            )?;
889            let next_pc =
890                self.normalized_pc_from_raw(next.ip, frame_module.bias, frame_module.found)?;
891            self.store_backtrace_frame(
892                inst_buffer,
893                frame_index as usize,
894                frame_module.cookie,
895                next_pc,
896                next.ip,
897                0,
898            )?;
899            self.store_u8_const(
900                inst_buffer,
901                data_base + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
902                frame_index + 1,
903                "bt_tail_prefix_frame_count",
904            )?;
905            let status = if frame_index + 1 == depth {
906                BacktraceStatus::Truncated
907            } else {
908                BacktraceStatus::ReadError
909            };
910            let status = self.status_or_offsets_unavailable(status, current_module_found)?;
911            self.store_u8_value(
912                inst_buffer,
913                data_base + BACKTRACE_DATA_STATUS_OFFSET,
914                status,
915                "bt_tail_prefix_status_after_frame",
916            )?;
917            self.builder
918                .build_store(ip_ptr, next.ip)
919                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
920            self.builder
921                .build_store(rsp_ptr, next.rsp)
922                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
923            self.builder
924                .build_store(rbp_ptr, next.rbp)
925                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
926            self.builder
927                .build_store(module_bias_ptr, frame_module.bias)
928                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
929            self.builder
930                .build_store(module_cookie_ptr, frame_module.cookie)
931                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
932            self.builder
933                .build_store(module_found_ptr, frame_module.found)
934                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
935
936            if frame_index + 1 == depth {
937                self.builder
938                    .build_unconditional_branch(done_block)
939                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
940            }
941        }
942
943        if prefix_depth == depth {
944            self.builder.position_at_end(done_block);
945            return Ok(());
946        }
947
948        let tail_slot = self.next_backtrace_tail_call_slot;
949        self.next_backtrace_tail_call_slot = self.next_backtrace_tail_call_slot.saturating_add(1);
950        if self.pending_backtrace_tail_call.is_none() {
951            let step_program_name = format!(
952                "{}_bt_step",
953                self.current_function("name bt tail-call step")?
954                    .get_name()
955                    .to_string_lossy()
956            );
957            self.pending_backtrace_tail_call =
958                Some(crate::ebpf::context::PendingBacktraceTailCall {
959                    step_program_name,
960                    depth,
961                    instruction_size,
962                });
963        }
964
965        let tail_enabled_ptr = self.get_or_create_backtrace_tail_enabled_flag()?;
966
967        let state_ptr = self.lookup_bt_state_ptr(tail_slot as u32)?;
968        let state_is_null = self
969            .builder
970            .build_is_null(state_ptr, "bt_state_is_null")
971            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
972        let init_block = self
973            .context
974            .append_basic_block(current_fn, "bt_tail_state_init");
975        let null_block = self
976            .context
977            .append_basic_block(current_fn, "bt_tail_state_null");
978        self.builder
979            .build_conditional_branch(state_is_null, null_block, init_block)
980            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
981
982        self.builder.position_at_end(null_block);
983        self.store_u8_const(
984            inst_buffer,
985            data_base + BACKTRACE_DATA_STATUS_OFFSET,
986            BacktraceStatus::InternalError as u8,
987            "bt_status_state_null",
988        )?;
989        self.builder
990            .build_store(tail_enabled_ptr, self.context.i8_type().const_zero())
991            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
992        self.builder
993            .build_unconditional_branch(done_block)
994            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
995
996        self.builder.position_at_end(init_block);
997        let tail_ip = self.load_i64(ip_ptr, "bt_tail_state_prefix_ip")?;
998        let tail_rsp = self.load_i64(rsp_ptr, "bt_tail_state_prefix_rsp")?;
999        let tail_rbp = self.load_i64(rbp_ptr, "bt_tail_state_prefix_rbp")?;
1000        let tail_module_bias = self.load_i64(module_bias_ptr, "bt_tail_state_module_bias")?;
1001        let tail_module_cookie = self.load_i64(module_cookie_ptr, "bt_tail_state_module_cookie")?;
1002        let tail_module_found = self.load_bool(module_found_ptr, "bt_tail_state_module_found")?;
1003        self.store_state_i64(
1004            state_ptr,
1005            crate::BACKTRACE_TAIL_STATE_CURRENT_IP_OFFSET,
1006            tail_ip,
1007            "bt_state_ip",
1008        )?;
1009        self.store_state_i64(
1010            state_ptr,
1011            crate::BACKTRACE_TAIL_STATE_CURRENT_RSP_OFFSET,
1012            tail_rsp,
1013            "bt_state_rsp",
1014        )?;
1015        self.store_state_i64(
1016            state_ptr,
1017            crate::BACKTRACE_TAIL_STATE_CURRENT_RBP_OFFSET,
1018            tail_rbp,
1019            "bt_state_rbp",
1020        )?;
1021        self.store_state_i64(
1022            state_ptr,
1023            crate::BACKTRACE_TAIL_STATE_MODULE_BIAS_OFFSET,
1024            tail_module_bias,
1025            "bt_state_module_bias",
1026        )?;
1027        self.store_u64_value(
1028            state_ptr,
1029            crate::BACKTRACE_TAIL_STATE_MODULE_COOKIE_OFFSET,
1030            tail_module_cookie,
1031            "bt_state_module_cookie",
1032        )?;
1033        self.store_state_i32(
1034            state_ptr,
1035            crate::BACKTRACE_TAIL_STATE_INST_OFFSET_OFFSET,
1036            inst_offset,
1037            "bt_state_inst_offset",
1038        )?;
1039        self.store_state_i32(
1040            state_ptr,
1041            crate::BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET,
1042            self.context.i32_type().const_zero(),
1043            "bt_state_event_size",
1044        )?;
1045        self.store_u8_const(
1046            state_ptr,
1047            crate::BACKTRACE_TAIL_STATE_FRAME_COUNT_OFFSET,
1048            prefix_depth,
1049            "bt_state_frame_count",
1050        )?;
1051        self.store_u8_const(
1052            state_ptr,
1053            crate::BACKTRACE_TAIL_STATE_REQUESTED_DEPTH_OFFSET,
1054            depth,
1055            "bt_state_requested_depth",
1056        )?;
1057        let offsets_found_u8 = self.bool_to_u8(tail_module_found, "bt_offsets_found_u8")?;
1058        self.store_u8_value(
1059            state_ptr,
1060            crate::BACKTRACE_TAIL_STATE_OFFSETS_FOUND_OFFSET,
1061            offsets_found_u8,
1062            "bt_state_offsets_found",
1063        )?;
1064        self.store_u8_const(
1065            state_ptr,
1066            crate::BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET,
1067            1,
1068            "bt_state_tail_calls",
1069        )?;
1070        self.store_u8_const(
1071            state_ptr,
1072            crate::BACKTRACE_TAIL_STATE_FLAGS_OFFSET,
1073            flags,
1074            "bt_state_flags",
1075        )?;
1076        self.store_u8_const(
1077            state_ptr,
1078            crate::BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET,
1079            tail_slot,
1080            "bt_state_active_slot",
1081        )?;
1082        self.store_u16_const(
1083            state_ptr,
1084            crate::BACKTRACE_TAIL_STATE_ERROR_CODE_OFFSET,
1085            BACKTRACE_ERROR_NONE,
1086            "bt_state_error_code",
1087        )?;
1088        self.store_u8_const(
1089            state_ptr,
1090            crate::BACKTRACE_TAIL_STATE_NEXT_SLOT_OFFSET,
1091            crate::BACKTRACE_TAIL_NO_NEXT_SLOT,
1092            "bt_state_next_slot",
1093        )?;
1094        self.link_backtrace_tail_slot(tail_slot, offsets_found_u8, done_block)?;
1095
1096        self.builder.position_at_end(done_block);
1097        Ok(())
1098    }
1099
1100    pub(crate) fn finish_event_after_instructions(&mut self) -> Result<()> {
1101        let Some(plan) = self.pending_backtrace_tail_call.clone() else {
1102            return self.emit_accumulated_event_output_from_stack_offset();
1103        };
1104
1105        let main_block = self.current_insert_block("finish bt tail-call event")?;
1106        let main_pm_key_alloca = self.pm_key_alloca;
1107        self.generate_backtrace_tail_call_step_program(&plan)?;
1108        self.pm_key_alloca = main_pm_key_alloca;
1109        self.builder.position_at_end(main_block);
1110
1111        let tail_enabled_ptr = self.get_or_create_backtrace_tail_enabled_flag()?;
1112        let enabled_value = self
1113            .builder
1114            .build_load(
1115                self.context.i8_type(),
1116                tail_enabled_ptr,
1117                "bt_tail_enabled_value",
1118            )
1119            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1120            .into_int_value();
1121        let enabled = self
1122            .builder
1123            .build_int_compare(
1124                inkwell::IntPredicate::NE,
1125                enabled_value,
1126                self.context.i8_type().const_zero(),
1127                "bt_tail_enabled",
1128            )
1129            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1130        let current_fn = self.current_function("finish bt tail-call event")?;
1131        let tail_block = self
1132            .context
1133            .append_basic_block(current_fn, "bt_tail_dispatch");
1134        let output_block = self
1135            .context
1136            .append_basic_block(current_fn, "bt_tail_fallback_output");
1137        self.builder
1138            .build_conditional_branch(enabled, tail_block, output_block)
1139            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1140
1141        self.builder.position_at_end(tail_block);
1142        let state0_ptr = self.lookup_bt_state_ptr(0)?;
1143        let state0_is_null = self
1144            .builder
1145            .build_is_null(state0_ptr, "bt_tail_dispatch_state_null")
1146            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1147        let state_ok_block = self
1148            .context
1149            .append_basic_block(current_fn, "bt_tail_dispatch_state_ok");
1150        self.builder
1151            .build_conditional_branch(state0_is_null, output_block, state_ok_block)
1152            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1153
1154        self.builder.position_at_end(state_ok_block);
1155        let active_slot = self.load_row_i8(
1156            state0_ptr,
1157            crate::BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET,
1158            "bt_tail_dispatch_active_slot",
1159        )?;
1160        let state_ptr = self.lookup_bt_state_ptr_dynamic(active_slot)?;
1161        let state_is_null = self
1162            .builder
1163            .build_is_null(state_ptr, "bt_tail_dispatch_active_state_null")
1164            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1165        let active_state_ok_block = self
1166            .context
1167            .append_basic_block(current_fn, "bt_tail_dispatch_active_state_ok");
1168        self.builder
1169            .build_conditional_branch(state_is_null, output_block, active_state_ok_block)
1170            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1171
1172        self.builder.position_at_end(active_state_ok_block);
1173        let event_size = self
1174            .builder
1175            .build_load(
1176                self.context.i32_type(),
1177                self.event_offset_alloca.ok_or_else(|| {
1178                    CodeGenError::LLVMError("event_offset not allocated in entry block".to_string())
1179                })?,
1180                "bt_tail_event_size",
1181            )
1182            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1183            .into_int_value();
1184        self.store_state_i32(
1185            state_ptr,
1186            crate::BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET,
1187            event_size,
1188            "bt_tail_state_event_size",
1189        )?;
1190        self.emit_bpf_tail_call(BPF_BACKTRACE_STEP_PROG_INDEX)?;
1191        self.builder
1192            .build_unconditional_branch(output_block)
1193            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1194
1195        self.builder.position_at_end(output_block);
1196        self.emit_accumulated_event_output_from_stack_offset()
1197    }
1198
1199    fn generate_backtrace_tail_call_step_program(
1200        &mut self,
1201        plan: &crate::ebpf::context::PendingBacktraceTailCall,
1202    ) -> Result<()> {
1203        self.create_tail_call_function(&plan.step_program_name)?;
1204        let current_fn = self.current_function("generate bt tail-call step")?;
1205        let return_block = self
1206            .context
1207            .append_basic_block(current_fn, "bt_step_return");
1208        let state_ok_block = self
1209            .context
1210            .append_basic_block(current_fn, "bt_step_state_ok");
1211        let accum_ok_block = self
1212            .context
1213            .append_basic_block(current_fn, "bt_step_accum_ok");
1214        let bounds_ok_block = self
1215            .context
1216            .append_basic_block(current_fn, "bt_step_bounds_ok");
1217        let inst_bounds_ok_block = self
1218            .context
1219            .append_basic_block(current_fn, "bt_step_inst_bounds_ok");
1220        let finalize_block = self
1221            .context
1222            .append_basic_block(current_fn, "bt_step_finalize");
1223
1224        let state0_ptr = self.lookup_bt_state_ptr(0)?;
1225        let state_is_null = self
1226            .builder
1227            .build_is_null(state0_ptr, "bt_step_state_null")
1228            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1229        self.builder
1230            .build_conditional_branch(state_is_null, return_block, state_ok_block)
1231            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1232
1233        self.builder.position_at_end(state_ok_block);
1234        let active_slot = self.load_row_i8(
1235            state0_ptr,
1236            crate::BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET,
1237            "bt_step_active_slot",
1238        )?;
1239        let state_ptr = self.lookup_bt_state_ptr_dynamic(active_slot)?;
1240        let active_state_is_null = self
1241            .builder
1242            .build_is_null(state_ptr, "bt_step_active_state_null")
1243            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1244        let active_state_ok_block = self
1245            .context
1246            .append_basic_block(current_fn, "bt_step_active_state_ok");
1247        self.builder
1248            .build_conditional_branch(active_state_is_null, return_block, active_state_ok_block)
1249            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1250
1251        self.builder.position_at_end(active_state_ok_block);
1252        let accum_buffer = self.lookup_percpu_value_ptr("event_accum_buffer", 0)?;
1253        let accum_is_null = self
1254            .builder
1255            .build_is_null(accum_buffer, "bt_step_accum_null")
1256            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1257        self.builder
1258            .build_conditional_branch(accum_is_null, return_block, accum_ok_block)
1259            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1260
1261        self.builder.position_at_end(accum_ok_block);
1262        let inst_offset = self.load_state_i32(
1263            state_ptr,
1264            crate::BACKTRACE_TAIL_STATE_INST_OFFSET_OFFSET,
1265            "bt_step_inst_offset",
1266        )?;
1267        let event_size = self.load_state_i32(
1268            state_ptr,
1269            crate::BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET,
1270            "bt_step_event_size",
1271        )?;
1272        let max_event_size = self
1273            .context
1274            .i32_type()
1275            .const_int(self.compile_options.max_trace_event_size as u64, false);
1276        let max_inst_offset = self.context.i32_type().const_int(
1277            self.compile_options
1278                .max_trace_event_size
1279                .saturating_sub(plan.instruction_size as u32) as u64,
1280            false,
1281        );
1282        let inst_in_bounds = self
1283            .builder
1284            .build_int_compare(
1285                inkwell::IntPredicate::ULE,
1286                inst_offset,
1287                max_inst_offset,
1288                "bt_step_inst_offset_in_bounds",
1289            )
1290            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1291        self.builder
1292            .build_conditional_branch(inst_in_bounds, inst_bounds_ok_block, return_block)
1293            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1294
1295        self.builder.position_at_end(inst_bounds_ok_block);
1296        let event_in_bounds = self
1297            .builder
1298            .build_int_compare(
1299                inkwell::IntPredicate::ULE,
1300                event_size,
1301                max_event_size,
1302                "bt_step_event_size_in_bounds",
1303            )
1304            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1305        self.builder
1306            .build_conditional_branch(event_in_bounds, bounds_ok_block, return_block)
1307            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1308
1309        self.builder.position_at_end(bounds_ok_block);
1310        let inst_offset_i64 = self
1311            .builder
1312            .build_int_z_extend(
1313                inst_offset,
1314                self.context.i64_type(),
1315                "bt_step_inst_offset_i64",
1316            )
1317            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1318        let inst_buffer =
1319            self.dynamic_byte_gep(accum_buffer, inst_offset_i64, "bt_step_inst_buffer")?;
1320        let scratch = self.allocate_backtrace_scratch()?;
1321        for _ in 0..BPF_BACKTRACE_FRAMES_PER_TAIL_CALL {
1322            self.generate_backtrace_tail_call_step_iteration(
1323                plan.depth,
1324                state_ptr,
1325                inst_buffer,
1326                &scratch,
1327                finalize_block,
1328            )?;
1329        }
1330
1331        let tail_calls = self.load_row_i8(
1332            state_ptr,
1333            crate::BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET,
1334            "bt_step_tail_calls",
1335        )?;
1336        let can_tail_call = self
1337            .builder
1338            .build_int_compare(
1339                inkwell::IntPredicate::ULT,
1340                tail_calls,
1341                self.context
1342                    .i8_type()
1343                    .const_int(BPF_BACKTRACE_MAX_STEP_INVOCATIONS as u64, false),
1344                "bt_step_can_tail_call",
1345            )
1346            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1347        let self_tail_block = self
1348            .context
1349            .append_basic_block(current_fn, "bt_step_self_tail");
1350        let tail_budget_done = self
1351            .context
1352            .append_basic_block(current_fn, "bt_step_tail_budget_done");
1353        self.builder
1354            .build_conditional_branch(can_tail_call, self_tail_block, tail_budget_done)
1355            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1356
1357        self.builder.position_at_end(tail_budget_done);
1358        self.store_tail_backtrace_status(
1359            inst_buffer,
1360            BacktraceStatus::Truncated,
1361            BACKTRACE_ERROR_NONE,
1362        )?;
1363        self.builder
1364            .build_unconditional_branch(finalize_block)
1365            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1366
1367        self.builder.position_at_end(self_tail_block);
1368        let next_tail_calls = self
1369            .builder
1370            .build_int_add(
1371                tail_calls,
1372                self.context.i8_type().const_int(1, false),
1373                "bt_step_next_tail_calls",
1374            )
1375            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1376        self.store_u8_value(
1377            state_ptr,
1378            crate::BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET,
1379            next_tail_calls,
1380            "bt_step_store_tail_calls",
1381        )?;
1382        self.emit_bpf_tail_call(BPF_BACKTRACE_STEP_PROG_INDEX)?;
1383        self.store_tail_backtrace_status(
1384            inst_buffer,
1385            BacktraceStatus::InternalError,
1386            BACKTRACE_ERROR_NONE,
1387        )?;
1388        self.builder
1389            .build_unconditional_branch(finalize_block)
1390            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1391
1392        self.builder.position_at_end(finalize_block);
1393        let next_slot = self.load_row_i8(
1394            state_ptr,
1395            crate::BACKTRACE_TAIL_STATE_NEXT_SLOT_OFFSET,
1396            "bt_final_next_slot",
1397        )?;
1398        let has_next_slot = self
1399            .builder
1400            .build_int_compare(
1401                inkwell::IntPredicate::NE,
1402                next_slot,
1403                self.context
1404                    .i8_type()
1405                    .const_int(crate::BACKTRACE_TAIL_NO_NEXT_SLOT as u64, false),
1406                "bt_final_has_next_slot",
1407            )
1408            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1409        let tail_calls = self.load_row_i8(
1410            state_ptr,
1411            crate::BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET,
1412            "bt_final_tail_calls",
1413        )?;
1414        let can_tail_call_next = self
1415            .builder
1416            .build_int_compare(
1417                inkwell::IntPredicate::ULT,
1418                tail_calls,
1419                self.context
1420                    .i8_type()
1421                    .const_int(BPF_BACKTRACE_MAX_STEP_INVOCATIONS as u64, false),
1422                "bt_final_can_tail_call_next",
1423            )
1424            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1425        let should_continue_next = self
1426            .builder
1427            .build_and(
1428                has_next_slot,
1429                can_tail_call_next,
1430                "bt_final_should_continue_next_slot",
1431            )
1432            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1433        let next_slot_block = self
1434            .context
1435            .append_basic_block(current_fn, "bt_final_next_slot");
1436        let emit_block = self.context.append_basic_block(current_fn, "bt_final_emit");
1437        self.builder
1438            .build_conditional_branch(should_continue_next, next_slot_block, emit_block)
1439            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1440
1441        self.builder.position_at_end(next_slot_block);
1442        self.store_u8_value(
1443            state0_ptr,
1444            crate::BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET,
1445            next_slot,
1446            "bt_store_active_slot",
1447        )?;
1448        let next_state_ptr = self.lookup_bt_state_ptr_dynamic(next_slot)?;
1449        let next_state_is_null = self
1450            .builder
1451            .build_is_null(next_state_ptr, "bt_next_state_null")
1452            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1453        let next_state_ok_block = self
1454            .context
1455            .append_basic_block(current_fn, "bt_next_state_ok");
1456        self.builder
1457            .build_conditional_branch(next_state_is_null, emit_block, next_state_ok_block)
1458            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1459
1460        self.builder.position_at_end(next_state_ok_block);
1461        self.store_state_i32(
1462            next_state_ptr,
1463            crate::BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET,
1464            event_size,
1465            "bt_next_slot_event_size",
1466        )?;
1467        let next_tail_calls = self
1468            .builder
1469            .build_int_add(
1470                tail_calls,
1471                self.context.i8_type().const_int(1, false),
1472                "bt_next_slot_tail_calls",
1473            )
1474            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1475        self.store_u8_value(
1476            next_state_ptr,
1477            crate::BACKTRACE_TAIL_STATE_TAIL_CALLS_OFFSET,
1478            next_tail_calls,
1479            "bt_next_slot_store_tail_calls",
1480        )?;
1481        self.emit_bpf_tail_call(BPF_BACKTRACE_STEP_PROG_INDEX)?;
1482        self.builder
1483            .build_unconditional_branch(emit_block)
1484            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1485
1486        self.builder.position_at_end(emit_block);
1487        self.emit_tail_final_event(state_ptr, accum_buffer)?;
1488        self.builder
1489            .build_unconditional_branch(return_block)
1490            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1491
1492        self.builder.position_at_end(return_block);
1493        self.build_return_zero()
1494    }
1495
1496    fn generate_backtrace_tail_call_step_iteration(
1497        &mut self,
1498        depth: u8,
1499        state_ptr: PointerValue<'ctx>,
1500        inst_buffer: PointerValue<'ctx>,
1501        scratch: &BtScratch<'ctx>,
1502        finalize_block: BasicBlock<'ctx>,
1503    ) -> Result<()> {
1504        let current_fn = self.current_function("generate bt tail-call step iteration")?;
1505        let depth_block = self
1506            .context
1507            .append_basic_block(current_fn, "bt_step_depth_done");
1508        let unwind_block = self
1509            .context
1510            .append_basic_block(current_fn, "bt_step_unwind");
1511        let frame_count = self.load_row_i8(
1512            state_ptr,
1513            crate::BACKTRACE_TAIL_STATE_FRAME_COUNT_OFFSET,
1514            "bt_step_frame_count",
1515        )?;
1516        let depth_value = self.context.i8_type().const_int(depth as u64, false);
1517        let at_depth = self
1518            .builder
1519            .build_int_compare(
1520                inkwell::IntPredicate::UGE,
1521                frame_count,
1522                depth_value,
1523                "bt_step_at_depth",
1524            )
1525            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1526        self.builder
1527            .build_conditional_branch(at_depth, depth_block, unwind_block)
1528            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1529
1530        self.builder.position_at_end(depth_block);
1531        self.store_tail_backtrace_status(
1532            inst_buffer,
1533            BacktraceStatus::Truncated,
1534            BACKTRACE_ERROR_NONE,
1535        )?;
1536        self.builder
1537            .build_unconditional_branch(finalize_block)
1538            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1539
1540        self.builder.position_at_end(unwind_block);
1541        let current_ip = self.load_row_i64(
1542            state_ptr,
1543            crate::BACKTRACE_TAIL_STATE_CURRENT_IP_OFFSET,
1544            "bt_step_current_ip",
1545        )?;
1546        let current_rsp = self.load_row_i64(
1547            state_ptr,
1548            crate::BACKTRACE_TAIL_STATE_CURRENT_RSP_OFFSET,
1549            "bt_step_current_rsp",
1550        )?;
1551        let current_rbp = self.load_row_i64(
1552            state_ptr,
1553            crate::BACKTRACE_TAIL_STATE_CURRENT_RBP_OFFSET,
1554            "bt_step_current_rbp",
1555        )?;
1556        let module_bias = self.load_row_i64(
1557            state_ptr,
1558            crate::BACKTRACE_TAIL_STATE_MODULE_BIAS_OFFSET,
1559            "bt_step_module_bias",
1560        )?;
1561        let module_cookie = self.load_row_i64(
1562            state_ptr,
1563            crate::BACKTRACE_TAIL_STATE_MODULE_COOKIE_OFFSET,
1564            "bt_step_module_cookie",
1565        )?;
1566        let offsets_found_u8 = self.load_row_i8(
1567            state_ptr,
1568            crate::BACKTRACE_TAIL_STATE_OFFSETS_FOUND_OFFSET,
1569            "bt_step_offsets_found_u8",
1570        )?;
1571        let offsets_found = self
1572            .builder
1573            .build_int_compare(
1574                inkwell::IntPredicate::NE,
1575                offsets_found_u8,
1576                self.context.i8_type().const_zero(),
1577                "bt_step_offsets_found",
1578            )
1579            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1580        let caller_fallback_found = self.backtrace_module_fallback_found(offsets_found);
1581        let is_first_unwind = self
1582            .builder
1583            .build_int_compare(
1584                inkwell::IntPredicate::EQ,
1585                frame_count,
1586                self.context.i8_type().const_int(1, false),
1587                "bt_step_first_unwind",
1588            )
1589            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1590        let caller_lookup_ip =
1591            self.add_signed_offset(current_ip, -1, "bt_step_caller_lookup_ip")?;
1592        let lookup_raw = self
1593            .builder
1594            .build_select::<BasicValueEnum<'ctx>, _>(
1595                is_first_unwind,
1596                current_ip.into(),
1597                caller_lookup_ip.into(),
1598                "bt_step_lookup_raw",
1599            )
1600            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1601            .into_int_value();
1602        let lookup_pc =
1603            self.backtrace_lookup_pc_from_raw(lookup_raw, module_bias, offsets_found)?;
1604        let runtime_row = self.lookup_backtrace_unwind_row(
1605            lookup_pc,
1606            module_cookie,
1607            &scratch.row,
1608            "bt_step_row",
1609        )?;
1610        let row_found_block = self
1611            .context
1612            .append_basic_block(current_fn, "bt_step_row_found");
1613        let row_missing_block = self
1614            .context
1615            .append_basic_block(current_fn, "bt_step_row_missing");
1616        self.builder
1617            .build_conditional_branch(runtime_row.found, row_found_block, row_missing_block)
1618            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1619
1620        self.builder.position_at_end(row_missing_block);
1621        self.store_tail_backtrace_status(
1622            inst_buffer,
1623            BacktraceStatus::NoUnwindRowsForPc,
1624            BACKTRACE_ERROR_NONE,
1625        )?;
1626        self.builder
1627            .build_unconditional_branch(finalize_block)
1628            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1629
1630        self.builder.position_at_end(row_found_block);
1631        let state = BtRegisterState {
1632            ip: current_ip,
1633            rsp: current_rsp,
1634            rbp: current_rbp,
1635        };
1636        let next = self.recover_next_frame_from_runtime_row(&runtime_row, state, scratch)?;
1637        let validation = self.validate_backtrace_next_frame(state, next)?;
1638        let store_block = self
1639            .context
1640            .append_basic_block(current_fn, "bt_step_store_frame");
1641        let stop_block = self.context.append_basic_block(current_fn, "bt_step_stop");
1642        self.builder
1643            .build_conditional_branch(validation.valid, store_block, stop_block)
1644            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1645
1646        self.builder.position_at_end(stop_block);
1647        let stop_status = self.status_for_backtrace_stop(
1648            validation.complete,
1649            validation.error_code,
1650            offsets_found,
1651        )?;
1652        self.store_u8_value(
1653            inst_buffer,
1654            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_STATUS_OFFSET,
1655            stop_status,
1656            "bt_step_stop_status",
1657        )?;
1658        self.store_u16_value(
1659            inst_buffer,
1660            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_ERROR_CODE_OFFSET,
1661            validation.error_code,
1662            "bt_step_stop_error",
1663        )?;
1664        self.builder
1665            .build_unconditional_branch(finalize_block)
1666            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1667
1668        self.builder.position_at_end(store_block);
1669        let frame_module = self.resolve_backtrace_frame_module(
1670            next.ip,
1671            module_cookie,
1672            module_bias,
1673            caller_fallback_found,
1674            "bt_step_frame_module",
1675        )?;
1676        let next_pc =
1677            self.normalized_pc_from_raw(next.ip, frame_module.bias, frame_module.found)?;
1678        self.store_backtrace_frame_dynamic(
1679            inst_buffer,
1680            frame_count,
1681            depth.saturating_sub(1),
1682            frame_module.cookie,
1683            next_pc,
1684            next.ip,
1685        )?;
1686        let next_count = self
1687            .builder
1688            .build_int_add(
1689                frame_count,
1690                self.context.i8_type().const_int(1, false),
1691                "bt_step_next_frame_count",
1692            )
1693            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1694        self.store_u8_value(
1695            state_ptr,
1696            crate::BACKTRACE_TAIL_STATE_FRAME_COUNT_OFFSET,
1697            next_count,
1698            "bt_step_state_frame_count",
1699        )?;
1700        self.store_u8_value(
1701            inst_buffer,
1702            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_FRAME_COUNT_OFFSET,
1703            next_count,
1704            "bt_step_inst_frame_count",
1705        )?;
1706        self.store_state_i64(
1707            state_ptr,
1708            crate::BACKTRACE_TAIL_STATE_CURRENT_IP_OFFSET,
1709            next.ip,
1710            "bt_step_state_next_ip",
1711        )?;
1712        self.store_state_i64(
1713            state_ptr,
1714            crate::BACKTRACE_TAIL_STATE_CURRENT_RSP_OFFSET,
1715            next.rsp,
1716            "bt_step_state_next_rsp",
1717        )?;
1718        self.store_state_i64(
1719            state_ptr,
1720            crate::BACKTRACE_TAIL_STATE_CURRENT_RBP_OFFSET,
1721            next.rbp,
1722            "bt_step_state_next_rbp",
1723        )?;
1724        self.store_state_i64(
1725            state_ptr,
1726            crate::BACKTRACE_TAIL_STATE_MODULE_BIAS_OFFSET,
1727            frame_module.bias,
1728            "bt_step_state_next_module_bias",
1729        )?;
1730        self.store_state_i64(
1731            state_ptr,
1732            crate::BACKTRACE_TAIL_STATE_MODULE_COOKIE_OFFSET,
1733            frame_module.cookie,
1734            "bt_step_state_next_module_cookie",
1735        )?;
1736        let next_offsets_found_u8 =
1737            self.bool_to_u8(frame_module.found, "bt_step_next_offsets_found_u8")?;
1738        self.store_u8_value(
1739            state_ptr,
1740            crate::BACKTRACE_TAIL_STATE_OFFSETS_FOUND_OFFSET,
1741            next_offsets_found_u8,
1742            "bt_step_state_next_offsets_found",
1743        )?;
1744
1745        let reached_depth = self
1746            .builder
1747            .build_int_compare(
1748                inkwell::IntPredicate::UGE,
1749                next_count,
1750                depth_value,
1751                "bt_step_reached_depth",
1752            )
1753            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1754        let reached_depth_block = self
1755            .context
1756            .append_basic_block(current_fn, "bt_step_reached_depth");
1757        let continue_block = self
1758            .context
1759            .append_basic_block(current_fn, "bt_step_continue");
1760        self.builder
1761            .build_conditional_branch(reached_depth, reached_depth_block, continue_block)
1762            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1763
1764        self.builder.position_at_end(reached_depth_block);
1765        self.store_tail_backtrace_status(
1766            inst_buffer,
1767            BacktraceStatus::Truncated,
1768            BACKTRACE_ERROR_NONE,
1769        )?;
1770        self.builder
1771            .build_unconditional_branch(finalize_block)
1772            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1773
1774        self.builder.position_at_end(continue_block);
1775        Ok(())
1776    }
1777
1778    fn emit_bpf_tail_call(&mut self, index: u32) -> Result<()> {
1779        let ctx = self.get_pt_regs_parameter()?;
1780        let prog_array = self.lookup_bt_prog_array_ptr()?;
1781        let args = [
1782            ctx.into(),
1783            prog_array.into(),
1784            self.context
1785                .i32_type()
1786                .const_int(index as u64, false)
1787                .into(),
1788        ];
1789        let _ = self.create_bpf_helper_call(
1790            BPF_FUNC_tail_call as u64,
1791            &args,
1792            self.context.i64_type().into(),
1793            "bt_bpf_tail_call",
1794        )?;
1795        Ok(())
1796    }
1797
1798    fn store_tail_backtrace_status(
1799        &self,
1800        inst_buffer: PointerValue<'ctx>,
1801        status: BacktraceStatus,
1802        error_code: u16,
1803    ) -> Result<()> {
1804        self.store_u8_const(
1805            inst_buffer,
1806            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_STATUS_OFFSET,
1807            status as u8,
1808            "bt_tail_status",
1809        )?;
1810        self.store_u16_const(
1811            inst_buffer,
1812            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_ERROR_CODE_OFFSET,
1813            error_code,
1814            "bt_tail_error_code",
1815        )
1816    }
1817
1818    fn emit_tail_final_event(
1819        &mut self,
1820        state_ptr: PointerValue<'ctx>,
1821        accum_buffer: PointerValue<'ctx>,
1822    ) -> Result<()> {
1823        let event_size = self.load_state_i32(
1824            state_ptr,
1825            crate::BACKTRACE_TAIL_STATE_EVENT_SIZE_OFFSET,
1826            "bt_final_event_size",
1827        )?;
1828        self.emit_accumulated_event_output(accum_buffer, event_size)
1829    }
1830
1831    fn build_return_zero(&mut self) -> Result<()> {
1832        self.builder
1833            .build_return(Some(&self.context.i32_type().const_zero()))
1834            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1835        Ok(())
1836    }
1837
1838    fn compact_unwind_row_for_backtrace(
1839        &self,
1840        module_path: &str,
1841        pc: u64,
1842    ) -> Option<CompactUnwindRow> {
1843        let analyzer = self.process_analyzer?;
1844        let module_address = ModuleAddress::new(PathBuf::from(module_path), pc);
1845        let ctx = analyzer.resolve_pc(&module_address).ok()?;
1846        analyzer.compact_unwind_row_for_context(&ctx).ok().flatten()
1847    }
1848
1849    fn usable_backtrace_unwind_row_for_pc(
1850        &self,
1851        module_path: &str,
1852        pc: u64,
1853    ) -> BacktraceUnwindRowForPc {
1854        let Some(row) = self.compact_unwind_row_for_backtrace(module_path, pc) else {
1855            return BacktraceUnwindRowForPc::Missing;
1856        };
1857        match crate::backtrace_unwind_row_from_compact(&row) {
1858            Some(row) => BacktraceUnwindRowForPc::Usable(row),
1859            None => BacktraceUnwindRowForPc::Unsupported,
1860        }
1861    }
1862
1863    fn status_for_backtrace_unwind_row_for_pc(
1864        &self,
1865        row: &BacktraceUnwindRowForPc,
1866    ) -> BacktraceStatus {
1867        match row {
1868            BacktraceUnwindRowForPc::Usable(_) => BacktraceStatus::ReadError,
1869            BacktraceUnwindRowForPc::Missing if self.process_analyzer.is_some() => {
1870                BacktraceStatus::NoUnwindRowsForPc
1871            }
1872            BacktraceUnwindRowForPc::Unsupported if self.process_analyzer.is_some() => {
1873                BacktraceStatus::UnsupportedCfi
1874            }
1875            BacktraceUnwindRowForPc::Missing | BacktraceUnwindRowForPc::Unsupported => {
1876                BacktraceStatus::DwarfUnavailable
1877            }
1878        }
1879    }
1880
1881    fn runtime_row_from_static(
1882        &self,
1883        row: ghostscope_protocol::BacktraceUnwindRow,
1884    ) -> RuntimeBtUnwindRow<'ctx> {
1885        let i8_type = self.context.i8_type();
1886        let i16_type = self.context.i16_type();
1887        let i64_type = self.context.i64_type();
1888        RuntimeBtUnwindRow {
1889            found: self.context.bool_type().const_int(1, false),
1890            cfa_register: i16_type.const_int(row.cfa_register as u64, false),
1891            cfa_offset: i64_type.const_int(row.cfa_offset as u64, true),
1892            ra_kind: i8_type.const_int(row.ra_kind as u64, false),
1893            ra_register: i16_type.const_int(row.ra_register as u64, false),
1894            ra_offset: i64_type.const_int(row.ra_offset as u64, true),
1895            rbp_kind: i8_type.const_int(row.rbp_kind as u64, false),
1896            rbp_register: i16_type.const_int(row.rbp_register as u64, false),
1897            rbp_offset: i64_type.const_int(row.rbp_offset as u64, true),
1898        }
1899    }
1900
1901    fn allocate_backtrace_scratch(&self) -> Result<BtScratch<'ctx>> {
1902        let i16_type = self.context.i16_type();
1903        let i32_type = self.context.i32_type();
1904        let i64_type = self.context.i64_type();
1905
1906        Ok(BtScratch {
1907            row: RuntimeBtRowScratch {
1908                found_ptr: self.build_entry_alloca(i32_type, "bt_row_found")?,
1909                cfa_register_ptr: self.build_entry_alloca(i16_type, "bt_row_cfa_register")?,
1910                cfa_offset_ptr: self.build_entry_alloca(i64_type, "bt_row_cfa_offset")?,
1911                ra_kind_ptr: self.build_entry_alloca(self.context.i8_type(), "bt_row_ra_kind")?,
1912                ra_register_ptr: self.build_entry_alloca(i16_type, "bt_row_ra_register")?,
1913                ra_offset_ptr: self.build_entry_alloca(i64_type, "bt_row_ra_offset")?,
1914                rbp_kind_ptr: self.build_entry_alloca(self.context.i8_type(), "bt_row_rbp_kind")?,
1915                rbp_register_ptr: self.build_entry_alloca(i16_type, "bt_row_rbp_register")?,
1916                rbp_offset_ptr: self.build_entry_alloca(i64_type, "bt_row_rbp_offset")?,
1917            },
1918            next_rbp_ptr: self.build_entry_alloca(i64_type, "bt_next_rbp")?,
1919            next_error_code_ptr: self.build_entry_alloca(i16_type, "bt_next_error_code")?,
1920        })
1921    }
1922
1923    fn lookup_backtrace_unwind_row(
1924        &mut self,
1925        normalized_pc: IntValue<'ctx>,
1926        module_cookie: IntValue<'ctx>,
1927        scratch: &RuntimeBtRowScratch<'ctx>,
1928        name_prefix: &str,
1929    ) -> Result<RuntimeBtUnwindRow<'ctx>> {
1930        let bounds = self.backtrace_unwind_row_bounds_for_module(module_cookie, name_prefix)?;
1931        self.lookup_backtrace_unwind_row_in_range(normalized_pc, bounds.start, bounds.end, scratch)
1932    }
1933
1934    fn backtrace_unwind_row_bounds_for_module(
1935        &mut self,
1936        module_cookie: IntValue<'ctx>,
1937        name_prefix: &str,
1938    ) -> Result<BtRowBounds<'ctx>> {
1939        let i32_type = self.context.i32_type();
1940        if self.backtrace_module_row_ranges.is_empty() {
1941            return Ok(BtRowBounds {
1942                start: i32_type.const_zero(),
1943                end: i32_type.const_int(self.backtrace_unwind_rows.len() as u64, false),
1944            });
1945        }
1946
1947        let i64_type = self.context.i64_type();
1948        let ptr_type = self.context.ptr_type(AddressSpace::default());
1949        let map_global = self
1950            .module
1951            .get_global("bt_module_row_ranges")
1952            .ok_or_else(|| {
1953                CodeGenError::LLVMError("bt_module_row_ranges map not found".to_string())
1954            })?;
1955        let map_ptr = self
1956            .builder
1957            .build_bit_cast(
1958                map_global.as_pointer_value(),
1959                ptr_type,
1960                &format!("{name_prefix}_row_ranges_map_ptr"),
1961            )
1962            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1963        let key_alloca =
1964            self.build_entry_alloca(i64_type, &format!("{name_prefix}_row_range_key"))?;
1965        self.builder
1966            .build_store(key_alloca, module_cookie)
1967            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1968        let key_ptr = self
1969            .builder
1970            .build_bit_cast(
1971                key_alloca,
1972                ptr_type,
1973                &format!("{name_prefix}_row_range_key_void"),
1974            )
1975            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1976        let result = self.create_bpf_helper_call(
1977            BPF_FUNC_map_lookup_elem as u64,
1978            &[map_ptr, key_ptr],
1979            ptr_type.into(),
1980            &format!("{name_prefix}_row_range_lookup"),
1981        )?;
1982        let range_ptr = match result {
1983            BasicValueEnum::PointerValue(ptr) => ptr,
1984            _ => {
1985                return Err(CodeGenError::LLVMError(
1986                    "bt_module_row_ranges lookup did not return pointer".to_string(),
1987                ))
1988            }
1989        };
1990        let is_null = self
1991            .builder
1992            .build_int_compare(
1993                inkwell::IntPredicate::EQ,
1994                self.builder
1995                    .build_ptr_to_int(
1996                        range_ptr,
1997                        i64_type,
1998                        &format!("{name_prefix}_row_range_ptr_i64"),
1999                    )
2000                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
2001                i64_type.const_zero(),
2002                &format!("{name_prefix}_row_range_is_null"),
2003            )
2004            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2005        let current_fn = self.current_function("lookup bt row range")?;
2006        let found_block = self
2007            .context
2008            .append_basic_block(current_fn, &format!("{name_prefix}_found_row_range"));
2009        let miss_block = self
2010            .context
2011            .append_basic_block(current_fn, &format!("{name_prefix}_miss_row_range"));
2012        let cont_block = self
2013            .context
2014            .append_basic_block(current_fn, &format!("{name_prefix}_cont_row_range"));
2015        self.builder
2016            .build_conditional_branch(is_null, miss_block, found_block)
2017            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2018
2019        self.builder.position_at_end(found_block);
2020        let load_range_field = |offset: usize,
2021                                field_name: &str,
2022                                ctx: &mut EbpfContext<'ctx, 'dw>|
2023         -> Result<IntValue<'ctx>> {
2024            let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
2025            // SAFETY: range_ptr is a non-null BacktraceModuleRowRange pointer
2026            // returned by bpf_map_lookup_elem, and offsets are shared ABI
2027            // constants from ghostscope-protocol.
2028            let field_ptr = unsafe {
2029                ctx.builder
2030                    .build_gep(ctx.context.i8_type(), range_ptr, &[offset_i32], field_name)
2031                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2032            };
2033            Ok(ctx
2034                .builder
2035                .build_load(ctx.context.i32_type(), field_ptr, field_name)
2036                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2037                .into_int_value())
2038        };
2039        let found_start = load_range_field(
2040            ghostscope_protocol::BACKTRACE_MODULE_ROW_RANGE_ROW_START_OFFSET,
2041            &format!("{name_prefix}_row_range_start"),
2042            self,
2043        )?;
2044        let found_end = load_range_field(
2045            ghostscope_protocol::BACKTRACE_MODULE_ROW_RANGE_ROW_END_OFFSET,
2046            &format!("{name_prefix}_row_range_end"),
2047            self,
2048        )?;
2049        self.builder
2050            .build_unconditional_branch(cont_block)
2051            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2052        let found_end_block = self.current_insert_block("finish bt row range found block")?;
2053
2054        self.builder.position_at_end(miss_block);
2055        self.builder
2056            .build_unconditional_branch(cont_block)
2057            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2058        let miss_end_block = self.current_insert_block("finish bt row range miss block")?;
2059
2060        self.builder.position_at_end(cont_block);
2061        let start_phi = self
2062            .builder
2063            .build_phi(i32_type, &format!("{name_prefix}_row_start_phi"))
2064            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2065        start_phi.add_incoming(&[
2066            (&found_start, found_end_block),
2067            (&i32_type.const_zero(), miss_end_block),
2068        ]);
2069        let end_phi = self
2070            .builder
2071            .build_phi(i32_type, &format!("{name_prefix}_row_end_phi"))
2072            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2073        end_phi.add_incoming(&[
2074            (&found_end, found_end_block),
2075            (&i32_type.const_zero(), miss_end_block),
2076        ]);
2077
2078        Ok(BtRowBounds {
2079            start: start_phi.as_basic_value().into_int_value(),
2080            end: end_phi.as_basic_value().into_int_value(),
2081        })
2082    }
2083
2084    fn lookup_backtrace_unwind_row_in_range(
2085        &mut self,
2086        normalized_pc: IntValue<'ctx>,
2087        row_start: IntValue<'ctx>,
2088        row_end: IntValue<'ctx>,
2089        scratch: &RuntimeBtRowScratch<'ctx>,
2090    ) -> Result<RuntimeBtUnwindRow<'ctx>> {
2091        let row_count = self.backtrace_unwind_row_map_entries() as usize;
2092        let i16_type = self.context.i16_type();
2093        let i32_type = self.context.i32_type();
2094        let i64_type = self.context.i64_type();
2095        let i8_type = self.context.i8_type();
2096        let sentinel = i32_type.const_int(row_count as u64, false);
2097
2098        self.builder
2099            .build_store(scratch.found_ptr, sentinel)
2100            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2101        self.builder
2102            .build_store(scratch.cfa_register_ptr, i16_type.const_zero())
2103            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2104        self.builder
2105            .build_store(scratch.cfa_offset_ptr, i64_type.const_zero())
2106            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2107        self.builder
2108            .build_store(scratch.ra_kind_ptr, i8_type.const_zero())
2109            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2110        self.builder
2111            .build_store(scratch.ra_register_ptr, i16_type.const_zero())
2112            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2113        self.builder
2114            .build_store(scratch.ra_offset_ptr, i64_type.const_zero())
2115            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2116        self.builder
2117            .build_store(scratch.rbp_kind_ptr, i8_type.const_zero())
2118            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2119        self.builder
2120            .build_store(scratch.rbp_register_ptr, i16_type.const_zero())
2121            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2122        self.builder
2123            .build_store(scratch.rbp_offset_ptr, i64_type.const_zero())
2124            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2125
2126        let current_fn = self.current_function("lookup bt unwind row")?;
2127        let return_block = self
2128            .context
2129            .append_basic_block(current_fn, "bt_row_lookup_return");
2130        if row_count == 0 {
2131            self.builder
2132                .build_unconditional_branch(return_block)
2133                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2134        } else {
2135            let lo_ptr = self.build_entry_alloca(i32_type, "bt_row_lo")?;
2136            let hi_ptr = self.build_entry_alloca(i32_type, "bt_row_hi")?;
2137            self.builder
2138                .build_store(lo_ptr, row_start)
2139                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2140            self.builder
2141                .build_store(hi_ptr, row_end)
2142                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2143            self.emit_backtrace_row_runtime_binary_search(
2144                normalized_pc,
2145                scratch,
2146                lo_ptr,
2147                hi_ptr,
2148                row_count,
2149                return_block,
2150            )?;
2151        }
2152        self.builder.position_at_end(return_block);
2153        let final_found_idx = self.load_i32(scratch.found_ptr, "bt_final_found_idx")?;
2154        let found = self
2155            .builder
2156            .build_int_compare(
2157                inkwell::IntPredicate::NE,
2158                final_found_idx,
2159                sentinel,
2160                "bt_final_row_found",
2161            )
2162            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2163        Ok(RuntimeBtUnwindRow {
2164            found,
2165            cfa_register: self.load_i16(scratch.cfa_register_ptr, "bt_final_cfa_reg")?,
2166            cfa_offset: self.load_i64(scratch.cfa_offset_ptr, "bt_final_cfa_off")?,
2167            ra_kind: self.load_i8(scratch.ra_kind_ptr, "bt_final_ra_kind")?,
2168            ra_register: self.load_i16(scratch.ra_register_ptr, "bt_final_ra_reg")?,
2169            ra_offset: self.load_i64(scratch.ra_offset_ptr, "bt_final_ra_off")?,
2170            rbp_kind: self.load_i8(scratch.rbp_kind_ptr, "bt_final_rbp_kind")?,
2171            rbp_register: self.load_i16(scratch.rbp_register_ptr, "bt_final_rbp_reg")?,
2172            rbp_offset: self.load_i64(scratch.rbp_offset_ptr, "bt_final_rbp_off")?,
2173        })
2174    }
2175
2176    fn emit_backtrace_row_runtime_binary_search(
2177        &mut self,
2178        normalized_pc: IntValue<'ctx>,
2179        scratch: &RuntimeBtRowScratch<'ctx>,
2180        lo_ptr: PointerValue<'ctx>,
2181        hi_ptr: PointerValue<'ctx>,
2182        row_count: usize,
2183        return_block: BasicBlock<'ctx>,
2184    ) -> Result<()> {
2185        let current_fn = self.current_function("emit bt row lookup tree")?;
2186        let i32_type = self.context.i32_type();
2187        let sentinel = i32_type.const_int(row_count as u64, false);
2188        let max_steps = backtrace_row_binary_search_steps(row_count);
2189
2190        for _ in 0..max_steps {
2191            let found_idx = self.load_i32(scratch.found_ptr, "bt_lookup_found_idx")?;
2192            let not_found = self
2193                .builder
2194                .build_int_compare(
2195                    inkwell::IntPredicate::EQ,
2196                    found_idx,
2197                    sentinel,
2198                    "bt_lookup_not_found",
2199                )
2200                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2201            let lo = self.load_i32(lo_ptr, "bt_lookup_lo")?;
2202            let hi = self.load_i32(hi_ptr, "bt_lookup_hi")?;
2203            let range_active = self
2204                .builder
2205                .build_int_compare(inkwell::IntPredicate::ULT, lo, hi, "bt_lookup_range_active")
2206                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2207            let should_search = self
2208                .builder
2209                .build_and(not_found, range_active, "bt_lookup_should_search")
2210                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2211
2212            let search_block = self
2213                .context
2214                .append_basic_block(current_fn, "bt_lookup_search");
2215            let skip_block = self
2216                .context
2217                .append_basic_block(current_fn, "bt_lookup_skip");
2218            let after_block = self
2219                .context
2220                .append_basic_block(current_fn, "bt_lookup_after");
2221            self.builder
2222                .build_conditional_branch(should_search, search_block, skip_block)
2223                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2224
2225            self.builder.position_at_end(skip_block);
2226            self.builder
2227                .build_unconditional_branch(after_block)
2228                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2229
2230            self.builder.position_at_end(search_block);
2231            let lo_plus_hi = self
2232                .builder
2233                .build_int_add(lo, hi, "bt_lookup_lo_plus_hi")
2234                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2235            let mid = self
2236                .builder
2237                .build_right_shift(
2238                    lo_plus_hi,
2239                    i32_type.const_int(1, false),
2240                    false,
2241                    "bt_lookup_mid",
2242                )
2243                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2244            let row_ptr = self.lookup_bt_unwind_row_ptr(mid)?;
2245            let row_is_null = self
2246                .builder
2247                .build_is_null(row_ptr, "bt_lookup_row_is_null")
2248                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2249            let row_null_block = self
2250                .context
2251                .append_basic_block(current_fn, "bt_lookup_row_null");
2252            let row_load_block = self
2253                .context
2254                .append_basic_block(current_fn, "bt_lookup_row_load");
2255            self.builder
2256                .build_conditional_branch(row_is_null, row_null_block, row_load_block)
2257                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2258
2259            self.builder.position_at_end(row_null_block);
2260            self.builder
2261                .build_store(lo_ptr, hi)
2262                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2263            self.builder
2264                .build_unconditional_branch(after_block)
2265                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2266
2267            self.builder.position_at_end(row_load_block);
2268            let pc_start = self.load_row_i64(
2269                row_ptr,
2270                crate::BACKTRACE_UNWIND_ROW_PC_START_OFFSET,
2271                "bt_lookup_row_pc_start",
2272            )?;
2273            let pc_end = self.load_row_i64(
2274                row_ptr,
2275                crate::BACKTRACE_UNWIND_ROW_PC_END_OFFSET,
2276                "bt_lookup_row_pc_end",
2277            )?;
2278            let before = self
2279                .builder
2280                .build_int_compare(
2281                    inkwell::IntPredicate::ULT,
2282                    normalized_pc,
2283                    pc_start,
2284                    "bt_lookup_pc_before",
2285                )
2286                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2287            let before_block = self
2288                .context
2289                .append_basic_block(current_fn, "bt_lookup_before");
2290            let not_before_block = self
2291                .context
2292                .append_basic_block(current_fn, "bt_lookup_not_before");
2293            self.builder
2294                .build_conditional_branch(before, before_block, not_before_block)
2295                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2296
2297            self.builder.position_at_end(before_block);
2298            self.builder
2299                .build_store(hi_ptr, mid)
2300                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2301            self.builder
2302                .build_unconditional_branch(after_block)
2303                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2304
2305            self.builder.position_at_end(not_before_block);
2306            let after = self
2307                .builder
2308                .build_int_compare(
2309                    inkwell::IntPredicate::UGE,
2310                    normalized_pc,
2311                    pc_end,
2312                    "bt_lookup_pc_after",
2313                )
2314                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2315            let after_range_block = self
2316                .context
2317                .append_basic_block(current_fn, "bt_lookup_after_range");
2318            let match_block = self
2319                .context
2320                .append_basic_block(current_fn, "bt_lookup_match");
2321            self.builder
2322                .build_conditional_branch(after, after_range_block, match_block)
2323                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2324
2325            self.builder.position_at_end(after_range_block);
2326            let mid_plus_one = self
2327                .builder
2328                .build_int_add(mid, i32_type.const_int(1, false), "bt_lookup_mid_plus_one")
2329                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2330            self.builder
2331                .build_store(lo_ptr, mid_plus_one)
2332                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2333            self.builder
2334                .build_unconditional_branch(after_block)
2335                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2336
2337            self.builder.position_at_end(match_block);
2338            self.store_backtrace_unwind_row_from_ptr(row_ptr, mid, scratch)?;
2339            self.builder
2340                .build_unconditional_branch(after_block)
2341                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2342
2343            self.builder.position_at_end(after_block);
2344        }
2345
2346        self.builder
2347            .build_unconditional_branch(return_block)
2348            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2349        Ok(())
2350    }
2351
2352    fn store_backtrace_unwind_row_from_ptr(
2353        &self,
2354        row_ptr: PointerValue<'ctx>,
2355        row_index: IntValue<'ctx>,
2356        scratch: &RuntimeBtRowScratch<'ctx>,
2357    ) -> Result<()> {
2358        self.builder
2359            .build_store(scratch.found_ptr, row_index)
2360            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2361        let cfa_register = self.load_row_i16(
2362            row_ptr,
2363            crate::BACKTRACE_UNWIND_ROW_CFA_REGISTER_OFFSET,
2364            "bt_tree_row_cfa_reg",
2365        )?;
2366        let cfa_offset = self.load_row_i64(
2367            row_ptr,
2368            crate::BACKTRACE_UNWIND_ROW_CFA_OFFSET_OFFSET,
2369            "bt_tree_row_cfa_off",
2370        )?;
2371        let ra_kind = self.load_row_i8(
2372            row_ptr,
2373            crate::BACKTRACE_UNWIND_ROW_RA_KIND_OFFSET,
2374            "bt_tree_row_ra_kind",
2375        )?;
2376        let ra_register = self.load_row_i16(
2377            row_ptr,
2378            crate::BACKTRACE_UNWIND_ROW_RA_REGISTER_OFFSET,
2379            "bt_tree_row_ra_reg",
2380        )?;
2381        let ra_offset = self.load_row_i64(
2382            row_ptr,
2383            crate::BACKTRACE_UNWIND_ROW_RA_OFFSET_OFFSET,
2384            "bt_tree_row_ra_off",
2385        )?;
2386        let rbp_kind = self.load_row_i8(
2387            row_ptr,
2388            crate::BACKTRACE_UNWIND_ROW_RBP_KIND_OFFSET,
2389            "bt_tree_row_rbp_kind",
2390        )?;
2391        let rbp_register = self.load_row_i16(
2392            row_ptr,
2393            crate::BACKTRACE_UNWIND_ROW_RBP_REGISTER_OFFSET,
2394            "bt_tree_row_rbp_reg",
2395        )?;
2396        let rbp_offset = self.load_row_i64(
2397            row_ptr,
2398            crate::BACKTRACE_UNWIND_ROW_RBP_OFFSET_OFFSET,
2399            "bt_tree_row_rbp_off",
2400        )?;
2401        self.builder
2402            .build_store(scratch.cfa_register_ptr, cfa_register)
2403            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2404        self.builder
2405            .build_store(scratch.cfa_offset_ptr, cfa_offset)
2406            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2407        self.builder
2408            .build_store(scratch.ra_kind_ptr, ra_kind)
2409            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2410        self.builder
2411            .build_store(scratch.ra_register_ptr, ra_register)
2412            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2413        self.builder
2414            .build_store(scratch.ra_offset_ptr, ra_offset)
2415            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2416        self.builder
2417            .build_store(scratch.rbp_kind_ptr, rbp_kind)
2418            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2419        self.builder
2420            .build_store(scratch.rbp_register_ptr, rbp_register)
2421            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2422        self.builder
2423            .build_store(scratch.rbp_offset_ptr, rbp_offset)
2424            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2425        Ok(())
2426    }
2427
2428    fn recover_next_frame_from_runtime_row(
2429        &mut self,
2430        row: &RuntimeBtUnwindRow<'ctx>,
2431        state: BtRegisterState<'ctx>,
2432        scratch: &BtScratch<'ctx>,
2433    ) -> Result<BtNextFrame<'ctx>> {
2434        let cfa_base = self.select_register_state(row.cfa_register, state, "bt_cfa_base")?;
2435        let cfa = self
2436            .builder
2437            .build_int_add(cfa_base, row.cfa_offset, "bt_runtime_cfa")
2438            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2439
2440        let ra_addr = self
2441            .builder
2442            .build_int_add(cfa, row.ra_offset, "bt_runtime_ra_addr")
2443            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2444        self.builder
2445            .build_store(
2446                scratch.next_error_code_ptr,
2447                self.context
2448                    .i16_type()
2449                    .const_int(BACKTRACE_ERROR_NONE as u64, false),
2450            )
2451            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2452        let (ra_from_memory, ra_read_failed) = self.generate_memory_read_with_fail_flag(
2453            RuntimeAddress::available(ra_addr, self.context),
2454            MemoryAccessSize::U64,
2455            "bt_ra_read",
2456        )?;
2457        let ra_from_memory = ra_from_memory.into_int_value();
2458        let ra_uses_memory = self.is_recovery_kind(
2459            row.ra_kind,
2460            crate::BACKTRACE_RECOVERY_AT_CFA_OFFSET,
2461            "bt_ra_at_kind",
2462        )?;
2463        let ra_read_failed = self
2464            .builder
2465            .build_and(ra_read_failed, ra_uses_memory, "bt_ra_read_failed")
2466            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2467        self.store_backtrace_error_code_if(
2468            scratch.next_error_code_ptr,
2469            ra_read_failed,
2470            BACKTRACE_ERROR_RETURN_ADDRESS_READ,
2471            "bt_ra_error_code",
2472        )?;
2473        let ra_from_val = self
2474            .builder
2475            .build_int_add(cfa, row.ra_offset, "bt_runtime_ra_val")
2476            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2477        let ra_from_register = self.select_register_state(row.ra_register, state, "bt_ra_reg")?;
2478        let ra_is_val = self.is_recovery_kind(
2479            row.ra_kind,
2480            crate::BACKTRACE_RECOVERY_VAL_CFA_OFFSET,
2481            "bt_ra_val_kind",
2482        )?;
2483        let ra_is_register = self.is_recovery_kind(
2484            row.ra_kind,
2485            crate::BACKTRACE_RECOVERY_REGISTER,
2486            "bt_ra_reg_kind",
2487        )?;
2488        let ra_is_same = self.is_recovery_kind(
2489            row.ra_kind,
2490            crate::BACKTRACE_RECOVERY_SAME_VALUE,
2491            "bt_ra_same_kind",
2492        )?;
2493        let ra_is_register_like = self
2494            .builder
2495            .build_or(ra_is_register, ra_is_same, "bt_ra_register_like")
2496            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2497        let ra_value_or_memory = self
2498            .builder
2499            .build_select::<BasicValueEnum<'ctx>, _>(
2500                ra_is_val,
2501                ra_from_val.into(),
2502                ra_from_memory.into(),
2503                "bt_ra_val_or_memory",
2504            )
2505            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2506            .into_int_value();
2507        let next_ip = self
2508            .builder
2509            .build_select::<BasicValueEnum<'ctx>, _>(
2510                ra_is_register_like,
2511                ra_from_register.into(),
2512                ra_value_or_memory.into(),
2513                "bt_next_ip",
2514            )
2515            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2516            .into_int_value();
2517        let next_rbp = self.recover_rbp_from_runtime_row(
2518            row,
2519            cfa,
2520            state,
2521            scratch.next_rbp_ptr,
2522            scratch.next_error_code_ptr,
2523        )?;
2524        let error_code = self.load_i16(scratch.next_error_code_ptr, "bt_next_error_code_value")?;
2525
2526        Ok(BtNextFrame {
2527            ip: next_ip,
2528            rsp: cfa,
2529            rbp: next_rbp,
2530            error_code,
2531        })
2532    }
2533
2534    fn validate_backtrace_next_frame(
2535        &self,
2536        state: BtRegisterState<'ctx>,
2537        next: BtNextFrame<'ctx>,
2538    ) -> Result<BtFrameValidation<'ctx>> {
2539        let i64_type = self.context.i64_type();
2540        let i16_type = self.context.i16_type();
2541        let zero = i64_type.const_zero();
2542        let zero_i16 = i16_type.const_zero();
2543        let min_user_ip = i64_type.const_int(0x1000, false);
2544        let high_byte_mask = i64_type.const_int(0xff00_0000_0000_0000, false);
2545
2546        let no_read_error = self
2547            .builder
2548            .build_int_compare(
2549                inkwell::IntPredicate::EQ,
2550                next.error_code,
2551                zero_i16,
2552                "bt_no_read_error",
2553            )
2554            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2555        let ip_high_enough = self
2556            .builder
2557            .build_int_compare(
2558                inkwell::IntPredicate::UGE,
2559                next.ip,
2560                min_user_ip,
2561                "bt_next_ip_user_min",
2562            )
2563            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2564        let ip_high_byte = self
2565            .builder
2566            .build_and(next.ip, high_byte_mask, "bt_next_ip_high_byte")
2567            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2568        let ip_is_zero = self
2569            .builder
2570            .build_int_compare(inkwell::IntPredicate::EQ, next.ip, zero, "bt_next_ip_zero")
2571            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2572        let ip_not_kernel_like = self
2573            .builder
2574            .build_int_compare(
2575                inkwell::IntPredicate::EQ,
2576                ip_high_byte,
2577                zero,
2578                "bt_next_ip_not_kernel_like",
2579            )
2580            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2581        let cfa_nonzero = self
2582            .builder
2583            .build_int_compare(
2584                inkwell::IntPredicate::NE,
2585                next.rsp,
2586                zero,
2587                "bt_next_cfa_nonzero",
2588            )
2589            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2590        let cfa_changed = self
2591            .builder
2592            .build_int_compare(
2593                inkwell::IntPredicate::NE,
2594                next.rsp,
2595                state.rsp,
2596                "bt_next_cfa_changed",
2597            )
2598            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2599        let ip_changed = self
2600            .builder
2601            .build_int_compare(
2602                inkwell::IntPredicate::NE,
2603                next.ip,
2604                state.ip,
2605                "bt_next_ip_changed",
2606            )
2607            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2608        let cfa_progress = self
2609            .builder
2610            .build_or(cfa_changed, ip_changed, "bt_next_frame_progress")
2611            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2612
2613        let ip_valid = self
2614            .builder
2615            .build_and(ip_high_enough, ip_not_kernel_like, "bt_next_ip_valid")
2616            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2617        let cfa_valid = self
2618            .builder
2619            .build_and(cfa_nonzero, cfa_progress, "bt_next_cfa_valid")
2620            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2621        let frame_shape_valid = self
2622            .builder
2623            .build_and(ip_valid, cfa_valid, "bt_next_frame_valid")
2624            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2625        let valid = self
2626            .builder
2627            .build_and(
2628                no_read_error,
2629                frame_shape_valid,
2630                "bt_next_frame_valid_no_read_error",
2631            )
2632            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2633        let complete = self
2634            .builder
2635            .build_and(no_read_error, ip_is_zero, "bt_next_frame_complete")
2636            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2637
2638        let mut error_code = next.error_code;
2639        error_code = self.select_backtrace_error_code_if(
2640            error_code,
2641            ip_high_enough,
2642            BACKTRACE_ERROR_NEXT_IP_BELOW_USER,
2643            "bt_next_ip_below_user_code",
2644        )?;
2645        error_code = self.select_backtrace_error_code_if(
2646            error_code,
2647            ip_not_kernel_like,
2648            BACKTRACE_ERROR_NEXT_IP_KERNEL_LIKE,
2649            "bt_next_ip_kernel_like_code",
2650        )?;
2651        error_code = self.select_backtrace_error_code_if(
2652            error_code,
2653            cfa_nonzero,
2654            BACKTRACE_ERROR_NEXT_CFA_ZERO,
2655            "bt_next_cfa_zero_code",
2656        )?;
2657        error_code = self.select_backtrace_error_code_if(
2658            error_code,
2659            cfa_progress,
2660            BACKTRACE_ERROR_NEXT_CFA_NOT_ADVANCING,
2661            "bt_next_cfa_not_advancing_code",
2662        )?;
2663        error_code = self
2664            .builder
2665            .build_select::<BasicValueEnum<'ctx>, _>(
2666                complete,
2667                self.context
2668                    .i16_type()
2669                    .const_int(BACKTRACE_ERROR_NONE as u64, false)
2670                    .into(),
2671                error_code.into(),
2672                "bt_complete_error_code",
2673            )
2674            .map(|value| value.into_int_value())
2675            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2676
2677        Ok(BtFrameValidation {
2678            valid,
2679            complete,
2680            error_code,
2681        })
2682    }
2683
2684    fn select_backtrace_error_code_if(
2685        &self,
2686        current: IntValue<'ctx>,
2687        condition_ok: IntValue<'ctx>,
2688        error_code: u16,
2689        name: &str,
2690    ) -> Result<IntValue<'ctx>> {
2691        let i16_type = self.context.i16_type();
2692        let current_is_none = self
2693            .builder
2694            .build_int_compare(
2695                inkwell::IntPredicate::EQ,
2696                current,
2697                i16_type.const_int(BACKTRACE_ERROR_NONE as u64, false),
2698                &format!("{name}_current_none"),
2699            )
2700            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2701        let condition_failed = self
2702            .builder
2703            .build_not(condition_ok, &format!("{name}_condition_failed"))
2704            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2705        let should_set = self
2706            .builder
2707            .build_and(
2708                current_is_none,
2709                condition_failed,
2710                &format!("{name}_should_set"),
2711            )
2712            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2713        self.builder
2714            .build_select::<BasicValueEnum<'ctx>, _>(
2715                should_set,
2716                i16_type.const_int(error_code as u64, false).into(),
2717                current.into(),
2718                name,
2719            )
2720            .map(|value| value.into_int_value())
2721            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
2722    }
2723
2724    fn store_backtrace_error_code_if(
2725        &self,
2726        error_code_ptr: PointerValue<'ctx>,
2727        condition: IntValue<'ctx>,
2728        error_code: u16,
2729        name: &str,
2730    ) -> Result<()> {
2731        let current = self.load_i16(error_code_ptr, &format!("{name}_current"))?;
2732        let i16_type = self.context.i16_type();
2733        let current_is_none = self
2734            .builder
2735            .build_int_compare(
2736                inkwell::IntPredicate::EQ,
2737                current,
2738                i16_type.const_int(BACKTRACE_ERROR_NONE as u64, false),
2739                &format!("{name}_current_none"),
2740            )
2741            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2742        let should_set = self
2743            .builder
2744            .build_and(condition, current_is_none, &format!("{name}_should_set"))
2745            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2746        let next = self
2747            .builder
2748            .build_select::<BasicValueEnum<'ctx>, _>(
2749                should_set,
2750                i16_type.const_int(error_code as u64, false).into(),
2751                current.into(),
2752                name,
2753            )
2754            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2755        self.builder
2756            .build_store(error_code_ptr, next)
2757            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2758        Ok(())
2759    }
2760
2761    fn recover_rbp_from_runtime_row(
2762        &mut self,
2763        row: &RuntimeBtUnwindRow<'ctx>,
2764        cfa: IntValue<'ctx>,
2765        state: BtRegisterState<'ctx>,
2766        next_rbp_ptr: PointerValue<'ctx>,
2767        next_error_code_ptr: PointerValue<'ctx>,
2768    ) -> Result<IntValue<'ctx>> {
2769        let is_at = self.is_recovery_kind(
2770            row.rbp_kind,
2771            crate::BACKTRACE_RECOVERY_AT_CFA_OFFSET,
2772            "bt_rbp_at_kind",
2773        )?;
2774        let cfa_uses_rbp = self
2775            .builder
2776            .build_int_compare(
2777                inkwell::IntPredicate::EQ,
2778                row.cfa_register,
2779                self.context
2780                    .i16_type()
2781                    .const_int(X86_64_DWARF_RBP as u64, false),
2782                "bt_rbp_cfa_uses_rbp",
2783            )
2784            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2785        let cfa_offset_is_frame_pointer_call_frame = self
2786            .builder
2787            .build_int_compare(
2788                inkwell::IntPredicate::EQ,
2789                row.cfa_offset,
2790                self.context.i64_type().const_int(16, false),
2791                "bt_rbp_cfa_offset_is_16",
2792            )
2793            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2794        let frame_pointer_call_frame = self
2795            .builder
2796            .build_and(
2797                cfa_uses_rbp,
2798                cfa_offset_is_frame_pointer_call_frame,
2799                "bt_rbp_frame_pointer_call_frame",
2800            )
2801            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2802        let is_at = self
2803            .builder
2804            .build_or(
2805                is_at,
2806                frame_pointer_call_frame,
2807                "bt_rbp_at_or_frame_pointer",
2808            )
2809            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2810        let rbp_offset = self
2811            .builder
2812            .build_select::<BasicValueEnum<'ctx>, _>(
2813                frame_pointer_call_frame,
2814                self.context
2815                    .i64_type()
2816                    .const_int((-16i64) as u64, true)
2817                    .into(),
2818                row.rbp_offset.into(),
2819                "bt_rbp_effective_offset",
2820            )
2821            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2822            .into_int_value();
2823        let current_fn = self.current_function("recover bt rbp")?;
2824        let at_block = self.context.append_basic_block(current_fn, "bt_rbp_at");
2825        let non_at_block = self.context.append_basic_block(current_fn, "bt_rbp_non_at");
2826        let join_block = self.context.append_basic_block(current_fn, "bt_rbp_join");
2827        self.builder
2828            .build_conditional_branch(is_at, at_block, non_at_block)
2829            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2830
2831        self.builder.position_at_end(at_block);
2832        let rbp_addr = self
2833            .builder
2834            .build_int_add(cfa, rbp_offset, "bt_runtime_rbp_addr")
2835            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2836        let (rbp_from_memory, rbp_read_failed) = self.generate_memory_read_with_fail_flag(
2837            RuntimeAddress::available(rbp_addr, self.context),
2838            MemoryAccessSize::U64,
2839            "bt_rbp_read",
2840        )?;
2841        self.store_backtrace_error_code_if(
2842            next_error_code_ptr,
2843            rbp_read_failed,
2844            BACKTRACE_ERROR_FRAME_POINTER_READ,
2845            "bt_rbp_error_code",
2846        )?;
2847        self.builder
2848            .build_store(next_rbp_ptr, rbp_from_memory.into_int_value())
2849            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2850        self.builder
2851            .build_unconditional_branch(join_block)
2852            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2853
2854        self.builder.position_at_end(non_at_block);
2855        let rbp_from_val = self
2856            .builder
2857            .build_int_add(cfa, row.rbp_offset, "bt_runtime_rbp_val")
2858            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2859        let rbp_from_register =
2860            self.select_register_state(row.rbp_register, state, "bt_rbp_reg")?;
2861        let rbp_is_val = self.is_recovery_kind(
2862            row.rbp_kind,
2863            crate::BACKTRACE_RECOVERY_VAL_CFA_OFFSET,
2864            "bt_rbp_val_kind",
2865        )?;
2866        let rbp_is_register = self.is_recovery_kind(
2867            row.rbp_kind,
2868            crate::BACKTRACE_RECOVERY_REGISTER,
2869            "bt_rbp_reg_kind",
2870        )?;
2871        let rbp_is_same = self.is_recovery_kind(
2872            row.rbp_kind,
2873            crate::BACKTRACE_RECOVERY_SAME_VALUE,
2874            "bt_rbp_same_kind",
2875        )?;
2876        let rbp_is_register_like = self
2877            .builder
2878            .build_or(rbp_is_register, rbp_is_same, "bt_rbp_register_like")
2879            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2880        let rbp_value_or_current = self
2881            .builder
2882            .build_select::<BasicValueEnum<'ctx>, _>(
2883                rbp_is_val,
2884                rbp_from_val.into(),
2885                state.rbp.into(),
2886                "bt_rbp_val_or_current",
2887            )
2888            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2889            .into_int_value();
2890        let rbp_non_at = self
2891            .builder
2892            .build_select::<BasicValueEnum<'ctx>, _>(
2893                rbp_is_register_like,
2894                rbp_from_register.into(),
2895                rbp_value_or_current.into(),
2896                "bt_rbp_non_at_value",
2897            )
2898            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2899            .into_int_value();
2900        self.builder
2901            .build_store(next_rbp_ptr, rbp_non_at)
2902            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2903        self.builder
2904            .build_unconditional_branch(join_block)
2905            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2906
2907        self.builder.position_at_end(join_block);
2908        self.load_i64(next_rbp_ptr, "bt_next_rbp_value")
2909    }
2910
2911    fn select_register_state(
2912        &self,
2913        register: IntValue<'ctx>,
2914        state: BtRegisterState<'ctx>,
2915        name: &str,
2916    ) -> Result<IntValue<'ctx>> {
2917        let is_rbp = self
2918            .builder
2919            .build_int_compare(
2920                inkwell::IntPredicate::EQ,
2921                register,
2922                self.context
2923                    .i16_type()
2924                    .const_int(X86_64_DWARF_RBP as u64, false),
2925                &format!("{name}_is_rbp"),
2926            )
2927            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2928        let is_rip = self
2929            .builder
2930            .build_int_compare(
2931                inkwell::IntPredicate::EQ,
2932                register,
2933                self.context
2934                    .i16_type()
2935                    .const_int(X86_64_DWARF_RIP as u64, false),
2936                &format!("{name}_is_rip"),
2937            )
2938            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2939        let rbp_or_rsp = self
2940            .builder
2941            .build_select::<BasicValueEnum<'ctx>, _>(
2942                is_rbp,
2943                state.rbp.into(),
2944                state.rsp.into(),
2945                &format!("{name}_rbp_or_rsp"),
2946            )
2947            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2948            .into_int_value();
2949        self.builder
2950            .build_select::<BasicValueEnum<'ctx>, _>(
2951                is_rip,
2952                state.ip.into(),
2953                rbp_or_rsp.into(),
2954                name,
2955            )
2956            .map(|value| value.into_int_value())
2957            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
2958    }
2959
2960    fn is_recovery_kind(
2961        &self,
2962        kind: IntValue<'ctx>,
2963        expected: u8,
2964        name: &str,
2965    ) -> Result<IntValue<'ctx>> {
2966        self.builder
2967            .build_int_compare(
2968                inkwell::IntPredicate::EQ,
2969                kind,
2970                self.context.i8_type().const_int(expected as u64, false),
2971                name,
2972            )
2973            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
2974    }
2975
2976    fn lookup_bt_unwind_row_ptr(
2977        &mut self,
2978        row_index: IntValue<'ctx>,
2979    ) -> Result<PointerValue<'ctx>> {
2980        let ptr_type = self.context.ptr_type(AddressSpace::default());
2981        let i32_type = self.context.i32_type();
2982        let map_global = self
2983            .module
2984            .get_global("bt_unwind_rows")
2985            .ok_or_else(|| CodeGenError::LLVMError("bt_unwind_rows map not found".to_string()))?;
2986        let map_ptr = self
2987            .builder
2988            .build_bit_cast(
2989                map_global.as_pointer_value(),
2990                ptr_type,
2991                "bt_unwind_rows_map_ptr",
2992            )
2993            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2994        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
2995            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
2996        })?;
2997        let key_arr_ty = i32_type.array_type(4);
2998        let zero = i32_type.const_zero();
2999        // SAFETY: pm_key_alloca is a [4 x i32] entry-block alloca and [0, 0]
3000        // addresses its first element, which is sufficient for an Array u32 key.
3001        let key_ptr = unsafe {
3002            self.builder
3003                .build_gep(
3004                    key_arr_ty,
3005                    key_alloca,
3006                    &[zero, zero],
3007                    "bt_unwind_row_key_ptr",
3008                )
3009                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3010        };
3011        self.builder
3012            .build_store(key_ptr, row_index)
3013            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3014        let key_ptr = self
3015            .builder
3016            .build_bit_cast(key_ptr, ptr_type, "bt_unwind_row_key_void")
3017            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3018        let result = self.create_bpf_helper_call(
3019            BPF_FUNC_map_lookup_elem as u64,
3020            &[map_ptr, key_ptr],
3021            ptr_type.into(),
3022            "bt_unwind_row_lookup",
3023        )?;
3024        match result {
3025            BasicValueEnum::PointerValue(ptr) => Ok(ptr),
3026            _ => Err(CodeGenError::LLVMError(
3027                "bt_unwind_rows lookup did not return pointer".to_string(),
3028            )),
3029        }
3030    }
3031
3032    fn lookup_bt_state_ptr(&mut self, key_const: u32) -> Result<PointerValue<'ctx>> {
3033        self.lookup_percpu_value_ptr("bt_state", key_const)
3034    }
3035
3036    fn lookup_bt_state_ptr_dynamic(
3037        &mut self,
3038        state_index: IntValue<'ctx>,
3039    ) -> Result<PointerValue<'ctx>> {
3040        let ptr_type = self.context.ptr_type(AddressSpace::default());
3041        let i32_type = self.context.i32_type();
3042        let map_global = self
3043            .map_manager
3044            .get_map(&self.module, "bt_state")
3045            .map_err(|e| CodeGenError::LLVMError(format!("Map not found bt_state: {e}")))?;
3046        let map_ptr = self
3047            .builder
3048            .build_bit_cast(map_global, ptr_type, "bt_state_map_ptr")
3049            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3050        let key = match state_index
3051            .get_type()
3052            .get_bit_width()
3053            .cmp(&i32_type.get_bit_width())
3054        {
3055            std::cmp::Ordering::Equal => state_index,
3056            std::cmp::Ordering::Less => self
3057                .builder
3058                .build_int_z_extend(state_index, i32_type, "bt_state_key_i32")
3059                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
3060            std::cmp::Ordering::Greater => self
3061                .builder
3062                .build_int_truncate(state_index, i32_type, "bt_state_key_i32")
3063                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
3064        };
3065
3066        let key_arr_ty = i32_type.array_type(4);
3067        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
3068            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
3069        })?;
3070        let zero = i32_type.const_zero();
3071        // SAFETY: pm_key_alloca is a [4 x i32] entry-block alloca and [0, 0]
3072        // addresses its first element, which is sufficient for an Array u32 key.
3073        let key_ptr = unsafe {
3074            self.builder
3075                .build_gep(key_arr_ty, key_alloca, &[zero, zero], "bt_state_key_ptr")
3076                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3077        };
3078        self.builder
3079            .build_store(key_ptr, key)
3080            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3081        let key_ptr = self
3082            .builder
3083            .build_bit_cast(key_ptr, ptr_type, "bt_state_key_void")
3084            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3085        let result = self.create_bpf_helper_call(
3086            BPF_FUNC_map_lookup_elem as u64,
3087            &[map_ptr, key_ptr],
3088            ptr_type.into(),
3089            "bt_state_lookup",
3090        )?;
3091        match result {
3092            BasicValueEnum::PointerValue(ptr) => Ok(ptr),
3093            _ => Err(CodeGenError::LLVMError(
3094                "bt_state lookup did not return pointer".to_string(),
3095            )),
3096        }
3097    }
3098
3099    fn lookup_bt_prog_array_ptr(&mut self) -> Result<PointerValue<'ctx>> {
3100        let ptr_type = self.context.ptr_type(AddressSpace::default());
3101        let map_global = self
3102            .module
3103            .get_global("bt_prog_array")
3104            .ok_or_else(|| CodeGenError::LLVMError("bt_prog_array map not found".to_string()))?;
3105        let map_ptr = self
3106            .builder
3107            .build_bit_cast(map_global.as_pointer_value(), ptr_type, "bt_prog_array_ptr")
3108            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3109        match map_ptr {
3110            BasicValueEnum::PointerValue(ptr) => Ok(ptr),
3111            _ => Err(CodeGenError::LLVMError(
3112                "bt_prog_array cast did not return pointer".to_string(),
3113            )),
3114        }
3115    }
3116
3117    fn get_or_create_backtrace_tail_enabled_flag(&mut self) -> Result<PointerValue<'ctx>> {
3118        if let Some(ptr) = self.backtrace_tail_enabled_alloca {
3119            return Ok(ptr);
3120        }
3121
3122        let current_block = self.builder.get_insert_block().ok_or_else(|| {
3123            CodeGenError::LLVMError("no current block for bt tail flag allocation".to_string())
3124        })?;
3125        let current_fn = self.current_function("allocate bt tail flag")?;
3126        let entry_block = current_fn.get_first_basic_block().ok_or_else(|| {
3127            CodeGenError::LLVMError("no entry block for bt tail flag allocation".to_string())
3128        })?;
3129
3130        if let Some(first_instruction) = entry_block.get_first_instruction() {
3131            self.builder.position_before(&first_instruction);
3132        } else {
3133            self.builder.position_at_end(entry_block);
3134        }
3135        let alloca = self
3136            .builder
3137            .build_alloca(self.context.i8_type(), "bt_tail_enabled")
3138            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3139        self.builder
3140            .build_store(alloca, self.context.i8_type().const_zero())
3141            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3142        self.builder.position_at_end(current_block);
3143        self.backtrace_tail_enabled_alloca = Some(alloca);
3144        Ok(alloca)
3145    }
3146
3147    fn get_or_create_backtrace_tail_last_slot(&mut self) -> Result<PointerValue<'ctx>> {
3148        if let Some(ptr) = self.backtrace_tail_last_slot_alloca {
3149            return Ok(ptr);
3150        }
3151
3152        let current_block = self.builder.get_insert_block().ok_or_else(|| {
3153            CodeGenError::LLVMError("no current block for bt tail slot allocation".to_string())
3154        })?;
3155        let current_fn = self.current_function("allocate bt tail slot")?;
3156        let entry_block = current_fn.get_first_basic_block().ok_or_else(|| {
3157            CodeGenError::LLVMError("no entry block for bt tail slot allocation".to_string())
3158        })?;
3159
3160        if let Some(first_instruction) = entry_block.get_first_instruction() {
3161            self.builder.position_before(&first_instruction);
3162        } else {
3163            self.builder.position_at_end(entry_block);
3164        }
3165        let alloca = self
3166            .builder
3167            .build_alloca(self.context.i8_type(), "bt_tail_last_slot")
3168            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3169        self.builder
3170            .build_store(
3171                alloca,
3172                self.context
3173                    .i8_type()
3174                    .const_int(crate::BACKTRACE_TAIL_NO_NEXT_SLOT as u64, false),
3175            )
3176            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3177        self.builder.position_at_end(current_block);
3178        self.backtrace_tail_last_slot_alloca = Some(alloca);
3179        Ok(alloca)
3180    }
3181
3182    fn link_backtrace_tail_slot(
3183        &mut self,
3184        tail_slot: u8,
3185        offsets_found_u8: IntValue<'ctx>,
3186        done_block: BasicBlock<'ctx>,
3187    ) -> Result<()> {
3188        let current_fn = self.current_function("link bt tail slot")?;
3189        let offsets_found = self
3190            .builder
3191            .build_int_compare(
3192                inkwell::IntPredicate::NE,
3193                offsets_found_u8,
3194                self.context.i8_type().const_zero(),
3195                "bt_tail_link_offsets_found",
3196            )
3197            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3198        let link_block = self
3199            .context
3200            .append_basic_block(current_fn, "bt_tail_link_slot");
3201        self.builder
3202            .build_conditional_branch(offsets_found, link_block, done_block)
3203            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3204
3205        self.builder.position_at_end(link_block);
3206        let state0_ptr = self.lookup_bt_state_ptr(0)?;
3207        let state0_is_null = self
3208            .builder
3209            .build_is_null(state0_ptr, "bt_tail_link_state0_null")
3210            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3211        let state0_ok_block = self
3212            .context
3213            .append_basic_block(current_fn, "bt_tail_link_state0_ok");
3214        self.builder
3215            .build_conditional_branch(state0_is_null, done_block, state0_ok_block)
3216            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3217
3218        self.builder.position_at_end(state0_ok_block);
3219        let tail_enabled_ptr = self.get_or_create_backtrace_tail_enabled_flag()?;
3220        let last_slot_ptr = self.get_or_create_backtrace_tail_last_slot()?;
3221        let enabled_value = self
3222            .builder
3223            .build_load(
3224                self.context.i8_type(),
3225                tail_enabled_ptr,
3226                "bt_tail_link_enabled_value",
3227            )
3228            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3229            .into_int_value();
3230        let has_prev_slot = self
3231            .builder
3232            .build_int_compare(
3233                inkwell::IntPredicate::NE,
3234                enabled_value,
3235                self.context.i8_type().const_zero(),
3236                "bt_tail_link_has_prev",
3237            )
3238            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3239        let first_slot_block = self
3240            .context
3241            .append_basic_block(current_fn, "bt_tail_link_first");
3242        let append_slot_block = self
3243            .context
3244            .append_basic_block(current_fn, "bt_tail_link_append");
3245        let linked_block = self
3246            .context
3247            .append_basic_block(current_fn, "bt_tail_linked");
3248        self.builder
3249            .build_conditional_branch(has_prev_slot, append_slot_block, first_slot_block)
3250            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3251
3252        self.builder.position_at_end(first_slot_block);
3253        self.store_u8_const(
3254            state0_ptr,
3255            crate::BACKTRACE_TAIL_STATE_ACTIVE_SLOT_OFFSET,
3256            tail_slot,
3257            "bt_tail_link_active_slot",
3258        )?;
3259        self.builder
3260            .build_unconditional_branch(linked_block)
3261            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3262
3263        self.builder.position_at_end(append_slot_block);
3264        let prev_slot = self.load_i8(last_slot_ptr, "bt_tail_link_prev_slot")?;
3265        let prev_state_ptr = self.lookup_bt_state_ptr_dynamic(prev_slot)?;
3266        let prev_state_is_null = self
3267            .builder
3268            .build_is_null(prev_state_ptr, "bt_tail_link_prev_null")
3269            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3270        let prev_state_ok_block = self
3271            .context
3272            .append_basic_block(current_fn, "bt_tail_link_prev_ok");
3273        self.builder
3274            .build_conditional_branch(prev_state_is_null, first_slot_block, prev_state_ok_block)
3275            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3276
3277        self.builder.position_at_end(prev_state_ok_block);
3278        self.store_u8_const(
3279            prev_state_ptr,
3280            crate::BACKTRACE_TAIL_STATE_NEXT_SLOT_OFFSET,
3281            tail_slot,
3282            "bt_tail_link_prev_next_slot",
3283        )?;
3284        self.builder
3285            .build_unconditional_branch(linked_block)
3286            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3287
3288        self.builder.position_at_end(linked_block);
3289        self.builder
3290            .build_store(
3291                last_slot_ptr,
3292                self.context.i8_type().const_int(tail_slot as u64, false),
3293            )
3294            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3295        self.builder
3296            .build_store(tail_enabled_ptr, self.context.i8_type().const_int(1, false))
3297            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3298        self.builder
3299            .build_unconditional_branch(done_block)
3300            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3301        Ok(())
3302    }
3303
3304    fn store_state_i64(
3305        &self,
3306        base: PointerValue<'ctx>,
3307        offset: usize,
3308        value: IntValue<'ctx>,
3309        name: &str,
3310    ) -> Result<()> {
3311        self.store_u64_value(base, offset, value, name)
3312    }
3313
3314    fn store_state_i32(
3315        &self,
3316        base: PointerValue<'ctx>,
3317        offset: usize,
3318        value: IntValue<'ctx>,
3319        name: &str,
3320    ) -> Result<()> {
3321        let ptr = self.byte_gep(base, offset, name)?;
3322        let ptr = self
3323            .builder
3324            .build_pointer_cast(
3325                ptr,
3326                self.context.ptr_type(AddressSpace::default()),
3327                &format!("{name}_u32_ptr"),
3328            )
3329            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3330        self.builder
3331            .build_store(ptr, value)
3332            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3333        Ok(())
3334    }
3335
3336    fn load_state_i32(
3337        &self,
3338        base: PointerValue<'ctx>,
3339        offset: usize,
3340        name: &str,
3341    ) -> Result<IntValue<'ctx>> {
3342        let ptr = self.byte_gep(base, offset, name)?;
3343        let ptr = self
3344            .builder
3345            .build_pointer_cast(
3346                ptr,
3347                self.context.ptr_type(AddressSpace::default()),
3348                &format!("{name}_u32_ptr"),
3349            )
3350            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3351        Ok(self
3352            .builder
3353            .build_load(self.context.i32_type(), ptr, name)
3354            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3355            .into_int_value())
3356    }
3357
3358    fn load_row_i64(
3359        &self,
3360        row_ptr: PointerValue<'ctx>,
3361        offset: usize,
3362        name: &str,
3363    ) -> Result<IntValue<'ctx>> {
3364        let ptr = self.byte_gep(row_ptr, offset, name)?;
3365        let ptr = self
3366            .builder
3367            .build_pointer_cast(
3368                ptr,
3369                self.context.ptr_type(AddressSpace::default()),
3370                &format!("{name}_i64_ptr"),
3371            )
3372            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3373        Ok(self
3374            .builder
3375            .build_load(self.context.i64_type(), ptr, name)
3376            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3377            .into_int_value())
3378    }
3379
3380    fn load_row_i16(
3381        &self,
3382        row_ptr: PointerValue<'ctx>,
3383        offset: usize,
3384        name: &str,
3385    ) -> Result<IntValue<'ctx>> {
3386        let ptr = self.byte_gep(row_ptr, offset, name)?;
3387        Ok(self
3388            .builder
3389            .build_load(self.context.i16_type(), ptr, name)
3390            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3391            .into_int_value())
3392    }
3393
3394    fn load_row_i8(
3395        &self,
3396        row_ptr: PointerValue<'ctx>,
3397        offset: usize,
3398        name: &str,
3399    ) -> Result<IntValue<'ctx>> {
3400        let ptr = self.byte_gep(row_ptr, offset, name)?;
3401        Ok(self
3402            .builder
3403            .build_load(self.context.i8_type(), ptr, name)
3404            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3405            .into_int_value())
3406    }
3407
3408    fn load_i8(&self, ptr: PointerValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3409        Ok(self
3410            .builder
3411            .build_load(self.context.i8_type(), ptr, name)
3412            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3413            .into_int_value())
3414    }
3415
3416    fn load_bool(&self, ptr: PointerValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3417        Ok(self
3418            .builder
3419            .build_load(self.context.bool_type(), ptr, name)
3420            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3421            .into_int_value())
3422    }
3423
3424    fn load_i32(&self, ptr: PointerValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3425        Ok(self
3426            .builder
3427            .build_load(self.context.i32_type(), ptr, name)
3428            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3429            .into_int_value())
3430    }
3431
3432    fn load_i16(&self, ptr: PointerValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3433        Ok(self
3434            .builder
3435            .build_load(self.context.i16_type(), ptr, name)
3436            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3437            .into_int_value())
3438    }
3439
3440    fn load_i64(&self, ptr: PointerValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3441        Ok(self
3442            .builder
3443            .build_load(self.context.i64_type(), ptr, name)
3444            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3445            .into_int_value())
3446    }
3447
3448    fn load_dwarf_register_i64(
3449        &mut self,
3450        reg: u16,
3451        pt_regs: PointerValue<'ctx>,
3452    ) -> Result<IntValue<'ctx>> {
3453        let value = self.load_register_value(reg, pt_regs)?;
3454        match value {
3455            BasicValueEnum::IntValue(value) => Ok(value),
3456            _ => Err(CodeGenError::RegisterMappingError(format!(
3457                "DWARF register {reg} did not load as integer"
3458            ))),
3459        }
3460    }
3461
3462    fn add_signed_offset(
3463        &self,
3464        base: IntValue<'ctx>,
3465        offset: i64,
3466        name: &str,
3467    ) -> Result<IntValue<'ctx>> {
3468        if offset == 0 {
3469            return Ok(base);
3470        }
3471        self.builder
3472            .build_int_add(
3473                base,
3474                self.context.i64_type().const_int(offset as u64, true),
3475                name,
3476            )
3477            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3478    }
3479
3480    fn normalized_pc_from_raw(
3481        &self,
3482        raw_ip: IntValue<'ctx>,
3483        module_bias: IntValue<'ctx>,
3484        offsets_found: IntValue<'ctx>,
3485    ) -> Result<IntValue<'ctx>> {
3486        let rebased = self
3487            .builder
3488            .build_int_sub(raw_ip, module_bias, "bt_normalized_pc")
3489            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3490        self.builder
3491            .build_select::<BasicValueEnum<'ctx>, _>(
3492                offsets_found,
3493                rebased.into(),
3494                raw_ip.into(),
3495                "bt_pc_or_raw",
3496            )
3497            .map(|value| value.into_int_value())
3498            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3499    }
3500
3501    fn lookup_proc_module_range_meta(
3502        &mut self,
3503        pid: IntValue<'ctx>,
3504        name_prefix: &str,
3505    ) -> Result<BtModuleRangeMeta<'ctx>> {
3506        let i32_type = self.context.i32_type();
3507        let i64_type = self.context.i64_type();
3508        let ptr_type = self.context.ptr_type(AddressSpace::default());
3509        let map_global = self
3510            .module
3511            .get_global("proc_module_range_meta")
3512            .ok_or_else(|| {
3513                CodeGenError::LLVMError("proc_module_range_meta map not found".to_string())
3514            })?;
3515        let map_ptr = self
3516            .builder
3517            .build_bit_cast(
3518                map_global.as_pointer_value(),
3519                ptr_type,
3520                &format!("{name_prefix}_meta_map_ptr"),
3521            )
3522            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3523        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
3524            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
3525        })?;
3526        let key_arr_ty = i32_type.array_type(4);
3527        let zero = i32_type.const_zero();
3528        // SAFETY: key_alloca is the [4 x i32] pm_key stack slot and [0, 0]
3529        // addresses the pid key element for proc_module_range_meta.
3530        let key_ptr = unsafe {
3531            self.builder
3532                .build_gep(
3533                    key_arr_ty,
3534                    key_alloca,
3535                    &[zero, zero],
3536                    &format!("{name_prefix}_meta_key_ptr"),
3537                )
3538                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3539        };
3540        self.builder
3541            .build_store(key_ptr, pid)
3542            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3543        let key_arg = self
3544            .builder
3545            .build_bit_cast(key_ptr, ptr_type, &format!("{name_prefix}_meta_key_arg"))
3546            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3547        let lookup_id = i64_type.const_int(BPF_FUNC_map_lookup_elem as u64, false);
3548        let lookup_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
3549        let lookup_fn_ptr = self
3550            .builder
3551            .build_int_to_ptr(
3552                lookup_id,
3553                ptr_type,
3554                &format!("{name_prefix}_meta_lookup_fn"),
3555            )
3556            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3557        let args: Vec<BasicMetadataValueEnum> = vec![map_ptr.into(), key_arg.into()];
3558        let value_ptr_any = self
3559            .builder
3560            .build_indirect_call(
3561                lookup_fn_type,
3562                lookup_fn_ptr,
3563                &args,
3564                &format!("{name_prefix}_meta_lookup"),
3565            )
3566            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3567            .try_as_basic_value()
3568            .left()
3569            .ok_or_else(|| {
3570                CodeGenError::LLVMError("proc_module_range_meta lookup returned void".to_string())
3571            })?;
3572        let value_ptr = match value_ptr_any {
3573            BasicValueEnum::PointerValue(p) => p,
3574            _ => {
3575                return Err(CodeGenError::LLVMError(
3576                    "proc_module_range_meta lookup did not return pointer".to_string(),
3577                ));
3578            }
3579        };
3580        let current_fn = self.current_function("lookup proc module range meta")?;
3581        let hit_block = self
3582            .context
3583            .append_basic_block(current_fn, &format!("{name_prefix}_meta_hit"));
3584        let miss_block = self
3585            .context
3586            .append_basic_block(current_fn, &format!("{name_prefix}_meta_miss"));
3587        let cont_block = self
3588            .context
3589            .append_basic_block(current_fn, &format!("{name_prefix}_meta_cont"));
3590        let value_ptr_int = self
3591            .builder
3592            .build_ptr_to_int(
3593                value_ptr,
3594                i64_type,
3595                &format!("{name_prefix}_meta_value_ptr_int"),
3596            )
3597            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3598        let is_hit = self
3599            .builder
3600            .build_int_compare(
3601                inkwell::IntPredicate::NE,
3602                value_ptr_int,
3603                i64_type.const_zero(),
3604                &format!("{name_prefix}_meta_found"),
3605            )
3606            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3607        self.builder
3608            .build_conditional_branch(is_hit, hit_block, miss_block)
3609            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3610
3611        self.builder.position_at_end(hit_block);
3612        let load_i32_field = |offset: usize, field_name: &str, ctx: &mut EbpfContext<'ctx, 'dw>| {
3613            let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
3614            // SAFETY: value_ptr points at ProcModuleRangeMeta returned by
3615            // bpf_map_lookup_elem and offset is an i32 field offset.
3616            let field_ptr = unsafe {
3617                ctx.builder
3618                    .build_gep(ctx.context.i8_type(), value_ptr, &[offset_i32], field_name)
3619                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3620            };
3621            ctx.builder
3622                .build_load(ctx.context.i32_type(), field_ptr, field_name)
3623                .map(|value| value.into_int_value())
3624                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3625        };
3626        let active_slot = load_i32_field(
3627            ghostscope_protocol::PROC_MODULE_RANGE_META_ACTIVE_SLOT_OFFSET,
3628            &format!("{name_prefix}_meta_active_slot"),
3629            self,
3630        )?;
3631        let count = load_i32_field(
3632            ghostscope_protocol::PROC_MODULE_RANGE_META_COUNT_OFFSET,
3633            &format!("{name_prefix}_meta_count"),
3634            self,
3635        )?;
3636        self.builder
3637            .build_unconditional_branch(cont_block)
3638            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3639        let hit_end = self.current_insert_block("finish module range meta hit block")?;
3640
3641        self.builder.position_at_end(miss_block);
3642        self.builder
3643            .build_unconditional_branch(cont_block)
3644            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3645        let miss_end = self.current_insert_block("finish module range meta miss block")?;
3646
3647        self.builder.position_at_end(cont_block);
3648        let found_type = self.context.bool_type();
3649        let found_phi = self
3650            .builder
3651            .build_phi(found_type, &format!("{name_prefix}_meta_found_phi"))
3652            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3653        found_phi.add_incoming(&[
3654            (&found_type.const_int(1, false), hit_end),
3655            (&found_type.const_zero(), miss_end),
3656        ]);
3657        let phi_i32 = |ctx: &mut EbpfContext<'ctx, 'dw>, name: &str, hit_value: IntValue<'ctx>| {
3658            let phi = ctx
3659                .builder
3660                .build_phi(ctx.context.i32_type(), name)
3661                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3662            phi.add_incoming(&[
3663                (&hit_value, hit_end),
3664                (&ctx.context.i32_type().const_zero(), miss_end),
3665            ]);
3666            Ok(phi.as_basic_value().into_int_value())
3667        };
3668        Ok(BtModuleRangeMeta {
3669            found: found_phi.as_basic_value().into_int_value(),
3670            active_slot: phi_i32(self, &format!("{name_prefix}_meta_slot_phi"), active_slot)?,
3671            count: phi_i32(self, &format!("{name_prefix}_meta_count_phi"), count)?,
3672        })
3673    }
3674
3675    fn lookup_proc_module_range_value(
3676        &mut self,
3677        pid: IntValue<'ctx>,
3678        slot: IntValue<'ctx>,
3679        index: IntValue<'ctx>,
3680        name_prefix: &str,
3681    ) -> Result<BtModuleRangeValue<'ctx>> {
3682        let i32_type = self.context.i32_type();
3683        let i64_type = self.context.i64_type();
3684        let ptr_type = self.context.ptr_type(AddressSpace::default());
3685        let map_global = self
3686            .module
3687            .get_global("proc_module_ranges")
3688            .ok_or_else(|| {
3689                CodeGenError::LLVMError("proc_module_ranges map not found".to_string())
3690            })?;
3691        let map_ptr = self
3692            .builder
3693            .build_bit_cast(
3694                map_global.as_pointer_value(),
3695                ptr_type,
3696                &format!("{name_prefix}_range_map_ptr"),
3697            )
3698            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3699        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
3700            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
3701        })?;
3702        self.builder
3703            .build_store(key_alloca, i32_type.array_type(4).const_zero())
3704            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3705        let store_key_u32 = |offset: usize,
3706                             value: IntValue<'ctx>,
3707                             field_name: &str,
3708                             ctx: &mut EbpfContext<'ctx, 'dw>|
3709         -> Result<()> {
3710            let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
3711            // SAFETY: key_alloca is the ProcModuleRangeKey stack slot and
3712            // offset is one of its u32 field offsets.
3713            let field_ptr = unsafe {
3714                ctx.builder
3715                    .build_gep(ctx.context.i8_type(), key_alloca, &[offset_i32], field_name)
3716                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3717            };
3718            ctx.builder
3719                .build_store(field_ptr, value)
3720                .map(|_| ())
3721                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3722        };
3723        store_key_u32(
3724            ghostscope_protocol::PROC_MODULE_RANGE_KEY_PID_OFFSET,
3725            pid,
3726            &format!("{name_prefix}_range_key_pid"),
3727            self,
3728        )?;
3729        store_key_u32(
3730            ghostscope_protocol::PROC_MODULE_RANGE_KEY_SLOT_OFFSET,
3731            slot,
3732            &format!("{name_prefix}_range_key_slot"),
3733            self,
3734        )?;
3735        store_key_u32(
3736            ghostscope_protocol::PROC_MODULE_RANGE_KEY_INDEX_OFFSET,
3737            index,
3738            &format!("{name_prefix}_range_key_index"),
3739            self,
3740        )?;
3741        store_key_u32(
3742            ghostscope_protocol::PROC_MODULE_RANGE_KEY_PAD_OFFSET,
3743            i32_type.const_zero(),
3744            &format!("{name_prefix}_range_key_pad"),
3745            self,
3746        )?;
3747        let key_arg = self
3748            .builder
3749            .build_bit_cast(
3750                key_alloca,
3751                ptr_type,
3752                &format!("{name_prefix}_range_key_arg"),
3753            )
3754            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3755        let lookup_id = i64_type.const_int(BPF_FUNC_map_lookup_elem as u64, false);
3756        let lookup_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
3757        let lookup_fn_ptr = self
3758            .builder
3759            .build_int_to_ptr(
3760                lookup_id,
3761                ptr_type,
3762                &format!("{name_prefix}_range_lookup_fn"),
3763            )
3764            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3765        let args: Vec<BasicMetadataValueEnum> = vec![map_ptr.into(), key_arg.into()];
3766        let value_ptr_any = self
3767            .builder
3768            .build_indirect_call(
3769                lookup_fn_type,
3770                lookup_fn_ptr,
3771                &args,
3772                &format!("{name_prefix}_range_lookup"),
3773            )
3774            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3775            .try_as_basic_value()
3776            .left()
3777            .ok_or_else(|| {
3778                CodeGenError::LLVMError("proc_module_ranges lookup returned void".to_string())
3779            })?;
3780        let value_ptr = match value_ptr_any {
3781            BasicValueEnum::PointerValue(p) => p,
3782            _ => {
3783                return Err(CodeGenError::LLVMError(
3784                    "proc_module_ranges lookup did not return pointer".to_string(),
3785                ));
3786            }
3787        };
3788        let current_fn = self.current_function("lookup proc module range value")?;
3789        let hit_block = self
3790            .context
3791            .append_basic_block(current_fn, &format!("{name_prefix}_range_hit"));
3792        let miss_block = self
3793            .context
3794            .append_basic_block(current_fn, &format!("{name_prefix}_range_miss"));
3795        let cont_block = self
3796            .context
3797            .append_basic_block(current_fn, &format!("{name_prefix}_range_cont"));
3798        let value_ptr_int = self
3799            .builder
3800            .build_ptr_to_int(
3801                value_ptr,
3802                i64_type,
3803                &format!("{name_prefix}_range_value_ptr_int"),
3804            )
3805            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3806        let is_hit = self
3807            .builder
3808            .build_int_compare(
3809                inkwell::IntPredicate::NE,
3810                value_ptr_int,
3811                i64_type.const_zero(),
3812                &format!("{name_prefix}_range_found"),
3813            )
3814            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3815        self.builder
3816            .build_conditional_branch(is_hit, hit_block, miss_block)
3817            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3818
3819        self.builder.position_at_end(hit_block);
3820        let load_i64_field = |offset: usize, field_name: &str, ctx: &mut EbpfContext<'ctx, 'dw>| {
3821            let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
3822            // SAFETY: value_ptr points at ProcModuleRangeValue returned by
3823            // bpf_map_lookup_elem and offset is a u64 field offset.
3824            let field_ptr = unsafe {
3825                ctx.builder
3826                    .build_gep(ctx.context.i8_type(), value_ptr, &[offset_i32], field_name)
3827                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3828            };
3829            ctx.builder
3830                .build_load(ctx.context.i64_type(), field_ptr, field_name)
3831                .map(|value| value.into_int_value())
3832                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3833        };
3834        let load_i32_field = |offset: usize, field_name: &str, ctx: &mut EbpfContext<'ctx, 'dw>| {
3835            let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
3836            // SAFETY: value_ptr points at ProcModuleRangeValue returned by
3837            // bpf_map_lookup_elem and offset is a u32 field offset.
3838            let field_ptr = unsafe {
3839                ctx.builder
3840                    .build_gep(ctx.context.i8_type(), value_ptr, &[offset_i32], field_name)
3841                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3842            };
3843            ctx.builder
3844                .build_load(ctx.context.i32_type(), field_ptr, field_name)
3845                .map(|value| value.into_int_value())
3846                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
3847        };
3848        let base = load_i64_field(
3849            ghostscope_protocol::PROC_MODULE_RANGE_VALUE_BASE_OFFSET,
3850            &format!("{name_prefix}_range_base"),
3851            self,
3852        )?;
3853        let end = load_i64_field(
3854            ghostscope_protocol::PROC_MODULE_RANGE_VALUE_END_OFFSET,
3855            &format!("{name_prefix}_range_end"),
3856            self,
3857        )?;
3858        let text = load_i64_field(
3859            ghostscope_protocol::PROC_MODULE_RANGE_VALUE_TEXT_OFFSET,
3860            &format!("{name_prefix}_range_text"),
3861            self,
3862        )?;
3863        let cookie_lo = load_i32_field(
3864            ghostscope_protocol::PROC_MODULE_RANGE_VALUE_COOKIE_LO_OFFSET,
3865            &format!("{name_prefix}_range_cookie_lo"),
3866            self,
3867        )?;
3868        let cookie_hi = load_i32_field(
3869            ghostscope_protocol::PROC_MODULE_RANGE_VALUE_COOKIE_HI_OFFSET,
3870            &format!("{name_prefix}_range_cookie_hi"),
3871            self,
3872        )?;
3873        let cookie_lo64 = self
3874            .builder
3875            .build_int_z_extend(cookie_lo, i64_type, &format!("{name_prefix}_cookie_lo64"))
3876            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3877        let cookie_hi64 = self
3878            .builder
3879            .build_int_z_extend(cookie_hi, i64_type, &format!("{name_prefix}_cookie_hi64"))
3880            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3881        let cookie_hi_shifted = self
3882            .builder
3883            .build_left_shift(
3884                cookie_hi64,
3885                i64_type.const_int(32, false),
3886                &format!("{name_prefix}_cookie_hi_shift"),
3887            )
3888            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3889        let cookie = self
3890            .builder
3891            .build_or(
3892                cookie_lo64,
3893                cookie_hi_shifted,
3894                &format!("{name_prefix}_cookie"),
3895            )
3896            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3897        self.builder
3898            .build_unconditional_branch(cont_block)
3899            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3900        let hit_end = self.current_insert_block("finish module range value hit block")?;
3901
3902        self.builder.position_at_end(miss_block);
3903        self.builder
3904            .build_unconditional_branch(cont_block)
3905            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3906        let miss_end = self.current_insert_block("finish module range value miss block")?;
3907
3908        self.builder.position_at_end(cont_block);
3909        let zero_i64 = i64_type.const_zero();
3910        let found_type = self.context.bool_type();
3911        let found_phi = self
3912            .builder
3913            .build_phi(found_type, &format!("{name_prefix}_range_found_phi"))
3914            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3915        found_phi.add_incoming(&[
3916            (&found_type.const_int(1, false), hit_end),
3917            (&found_type.const_zero(), miss_end),
3918        ]);
3919        let phi_i64 = |ctx: &mut EbpfContext<'ctx, 'dw>, name: &str, hit_value: IntValue<'ctx>| {
3920            let phi = ctx
3921                .builder
3922                .build_phi(ctx.context.i64_type(), name)
3923                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3924            phi.add_incoming(&[(&hit_value, hit_end), (&zero_i64, miss_end)]);
3925            Ok(phi.as_basic_value().into_int_value())
3926        };
3927        Ok(BtModuleRangeValue {
3928            found: found_phi.as_basic_value().into_int_value(),
3929            base: phi_i64(self, &format!("{name_prefix}_range_base_phi"), base)?,
3930            end: phi_i64(self, &format!("{name_prefix}_range_end_phi"), end)?,
3931            text: phi_i64(self, &format!("{name_prefix}_range_text_phi"), text)?,
3932            cookie: phi_i64(self, &format!("{name_prefix}_range_cookie_phi"), cookie)?,
3933        })
3934    }
3935
3936    fn lookup_backtrace_frame_module_in_ranges(
3937        &mut self,
3938        raw_ip: IntValue<'ctx>,
3939        pid: IntValue<'ctx>,
3940        meta: BtModuleRangeMeta<'ctx>,
3941        fallback_cookie: IntValue<'ctx>,
3942        fallback_bias: IntValue<'ctx>,
3943        name_prefix: &str,
3944    ) -> Result<BtFrameModule<'ctx>> {
3945        let i32_type = self.context.i32_type();
3946        let i64_type = self.context.i64_type();
3947        let bool_type = self.context.bool_type();
3948        let max_steps = backtrace_row_binary_search_steps(
3949            (self.compile_options.proc_module_offsets_max_entries as usize).saturating_mul(2),
3950        );
3951        let current_fn = self.current_function("lookup backtrace frame module")?;
3952
3953        let mut found = bool_type.const_zero();
3954        let mut cookie = fallback_cookie;
3955        let mut bias = fallback_bias;
3956        let mut lo = i32_type.const_zero();
3957        let mut hi = meta.count;
3958
3959        for step in 0..max_steps {
3960            let not_found = self
3961                .builder
3962                .build_not(found, &format!("{name_prefix}_{step}_not_found"))
3963                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3964            let range_active = self
3965                .builder
3966                .build_int_compare(
3967                    inkwell::IntPredicate::ULT,
3968                    lo,
3969                    hi,
3970                    &format!("{name_prefix}_{step}_active"),
3971                )
3972                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3973            let pending = self
3974                .builder
3975                .build_and(
3976                    not_found,
3977                    range_active,
3978                    &format!("{name_prefix}_{step}_pending"),
3979                )
3980                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3981            let should_search = self
3982                .builder
3983                .build_and(
3984                    pending,
3985                    meta.found,
3986                    &format!("{name_prefix}_{step}_should_search"),
3987                )
3988                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3989            let search_block = self
3990                .context
3991                .append_basic_block(current_fn, &format!("{name_prefix}_{step}_search"));
3992            let skip_block = self
3993                .context
3994                .append_basic_block(current_fn, &format!("{name_prefix}_{step}_skip"));
3995            let after_block = self
3996                .context
3997                .append_basic_block(current_fn, &format!("{name_prefix}_{step}_after"));
3998            self.builder
3999                .build_conditional_branch(should_search, search_block, skip_block)
4000                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4001
4002            self.builder.position_at_end(skip_block);
4003            self.builder
4004                .build_unconditional_branch(after_block)
4005                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4006            let skip_end = self.current_insert_block("finish module range skip block")?;
4007
4008            self.builder.position_at_end(search_block);
4009            let lo_plus_hi = self
4010                .builder
4011                .build_int_add(lo, hi, &format!("{name_prefix}_{step}_lo_plus_hi"))
4012                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4013            let mid = self
4014                .builder
4015                .build_right_shift(
4016                    lo_plus_hi,
4017                    i32_type.const_int(1, false),
4018                    false,
4019                    &format!("{name_prefix}_{step}_mid"),
4020                )
4021                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4022            let range = self.lookup_proc_module_range_value(
4023                pid,
4024                meta.active_slot,
4025                mid,
4026                &format!("{name_prefix}_{step}"),
4027            )?;
4028            let at_or_after_base = self
4029                .builder
4030                .build_int_compare(
4031                    inkwell::IntPredicate::UGE,
4032                    raw_ip,
4033                    range.base,
4034                    &format!("{name_prefix}_{step}_after_base"),
4035                )
4036                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4037            let before_end = self
4038                .builder
4039                .build_int_compare(
4040                    inkwell::IntPredicate::ULT,
4041                    raw_ip,
4042                    range.end,
4043                    &format!("{name_prefix}_{step}_before_end"),
4044                )
4045                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4046            let in_bounds = self
4047                .builder
4048                .build_and(
4049                    at_or_after_base,
4050                    before_end,
4051                    &format!("{name_prefix}_{step}_in_bounds"),
4052                )
4053                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4054            let in_range = self
4055                .builder
4056                .build_and(
4057                    range.found,
4058                    in_bounds,
4059                    &format!("{name_prefix}_{step}_in_range"),
4060                )
4061                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4062            let before_range = self
4063                .builder
4064                .build_int_compare(
4065                    inkwell::IntPredicate::ULT,
4066                    raw_ip,
4067                    range.base,
4068                    &format!("{name_prefix}_{step}_before_range"),
4069                )
4070                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4071            let after_range = self
4072                .builder
4073                .build_int_compare(
4074                    inkwell::IntPredicate::UGE,
4075                    raw_ip,
4076                    range.end,
4077                    &format!("{name_prefix}_{step}_after_range"),
4078                )
4079                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4080            let found_next = self
4081                .builder
4082                .build_or(found, in_range, &format!("{name_prefix}_{step}_found_next"))
4083                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4084            let selected_cookie = self
4085                .builder
4086                .build_select::<BasicValueEnum<'ctx>, _>(
4087                    in_range,
4088                    range.cookie.into(),
4089                    cookie.into(),
4090                    &format!("{name_prefix}_{step}_selected_cookie"),
4091                )
4092                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4093                .into_int_value();
4094            let selected_bias = self
4095                .builder
4096                .build_select::<BasicValueEnum<'ctx>, _>(
4097                    in_range,
4098                    range.text.into(),
4099                    bias.into(),
4100                    &format!("{name_prefix}_{step}_selected_bias"),
4101                )
4102                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4103                .into_int_value();
4104            let mid_plus_one = self
4105                .builder
4106                .build_int_add(
4107                    mid,
4108                    i32_type.const_int(1, false),
4109                    &format!("{name_prefix}_{step}_mid_plus_one"),
4110                )
4111                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4112            let range_missing = self
4113                .builder
4114                .build_not(range.found, &format!("{name_prefix}_{step}_range_missing"))
4115                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4116            let hi_next = self
4117                .builder
4118                .build_select::<BasicValueEnum<'ctx>, _>(
4119                    before_range,
4120                    mid.into(),
4121                    hi.into(),
4122                    &format!("{name_prefix}_{step}_hi_next"),
4123                )
4124                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4125                .into_int_value();
4126            let lo_after = self
4127                .builder
4128                .build_select::<BasicValueEnum<'ctx>, _>(
4129                    after_range,
4130                    mid_plus_one.into(),
4131                    lo.into(),
4132                    &format!("{name_prefix}_{step}_lo_after"),
4133                )
4134                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4135                .into_int_value();
4136            let lo_next = self
4137                .builder
4138                .build_select::<BasicValueEnum<'ctx>, _>(
4139                    range_missing,
4140                    hi.into(),
4141                    lo_after.into(),
4142                    &format!("{name_prefix}_{step}_lo_next"),
4143                )
4144                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4145                .into_int_value();
4146            self.builder
4147                .build_unconditional_branch(after_block)
4148                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4149            let search_end = self.current_insert_block("finish module range search block")?;
4150
4151            self.builder.position_at_end(after_block);
4152
4153            let found_phi = self
4154                .builder
4155                .build_phi(bool_type, &format!("{name_prefix}_{step}_found_phi"))
4156                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4157            found_phi.add_incoming(&[(&found_next, search_end), (&found, skip_end)]);
4158            found = found_phi.as_basic_value().into_int_value();
4159
4160            let cookie_phi = self
4161                .builder
4162                .build_phi(i64_type, &format!("{name_prefix}_{step}_cookie_phi"))
4163                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4164            cookie_phi.add_incoming(&[(&selected_cookie, search_end), (&cookie, skip_end)]);
4165            cookie = cookie_phi.as_basic_value().into_int_value();
4166
4167            let bias_phi = self
4168                .builder
4169                .build_phi(i64_type, &format!("{name_prefix}_{step}_bias_phi"))
4170                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4171            bias_phi.add_incoming(&[(&selected_bias, search_end), (&bias, skip_end)]);
4172            bias = bias_phi.as_basic_value().into_int_value();
4173
4174            let lo_phi = self
4175                .builder
4176                .build_phi(i32_type, &format!("{name_prefix}_{step}_lo_phi"))
4177                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4178            lo_phi.add_incoming(&[(&lo_next, search_end), (&lo, skip_end)]);
4179            lo = lo_phi.as_basic_value().into_int_value();
4180
4181            let hi_phi = self
4182                .builder
4183                .build_phi(i32_type, &format!("{name_prefix}_{step}_hi_phi"))
4184                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4185            hi_phi.add_incoming(&[(&hi_next, search_end), (&hi, skip_end)]);
4186            hi = hi_phi.as_basic_value().into_int_value();
4187        }
4188
4189        Ok(BtFrameModule {
4190            cookie,
4191            bias,
4192            found,
4193        })
4194    }
4195
4196    fn resolve_backtrace_frame_module(
4197        &mut self,
4198        raw_ip: IntValue<'ctx>,
4199        fallback_cookie: IntValue<'ctx>,
4200        fallback_bias: IntValue<'ctx>,
4201        fallback_found: IntValue<'ctx>,
4202        name_prefix: &str,
4203    ) -> Result<BtFrameModule<'ctx>> {
4204        if self.backtrace_module_row_ranges.is_empty() {
4205            return Ok(BtFrameModule {
4206                cookie: fallback_cookie,
4207                bias: fallback_bias,
4208                found: fallback_found,
4209            });
4210        }
4211
4212        let pid = self.proc_module_pid_key(name_prefix)?;
4213        let meta = self.lookup_proc_module_range_meta(pid, name_prefix)?;
4214        self.lookup_backtrace_frame_module_in_ranges(
4215            raw_ip,
4216            pid,
4217            meta,
4218            fallback_cookie,
4219            fallback_bias,
4220            name_prefix,
4221        )
4222    }
4223
4224    fn backtrace_module_fallback_found(&self, found: IntValue<'ctx>) -> IntValue<'ctx> {
4225        if self.backtrace_module_row_ranges.is_empty() {
4226            found
4227        } else {
4228            self.context.bool_type().const_zero()
4229        }
4230    }
4231
4232    fn backtrace_lookup_pc_from_raw(
4233        &self,
4234        raw_ip: IntValue<'ctx>,
4235        module_bias: IntValue<'ctx>,
4236        offsets_found: IntValue<'ctx>,
4237    ) -> Result<IntValue<'ctx>> {
4238        self.normalized_pc_from_raw(raw_ip, module_bias, offsets_found)
4239    }
4240
4241    fn bool_to_u8(&self, value: IntValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
4242        self.builder
4243            .build_select::<BasicValueEnum<'ctx>, _>(
4244                value,
4245                self.context.i8_type().const_int(1, false).into(),
4246                self.context.i8_type().const_zero().into(),
4247                name,
4248            )
4249            .map(|value| value.into_int_value())
4250            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
4251    }
4252
4253    fn status_or_offsets_unavailable(
4254        &self,
4255        status: BacktraceStatus,
4256        offsets_found: IntValue<'ctx>,
4257    ) -> Result<IntValue<'ctx>> {
4258        self.builder
4259            .build_select::<BasicValueEnum<'ctx>, _>(
4260                offsets_found,
4261                self.context
4262                    .i8_type()
4263                    .const_int(status as u64, false)
4264                    .into(),
4265                self.context
4266                    .i8_type()
4267                    .const_int(BacktraceStatus::OffsetsUnavailable as u64, false)
4268                    .into(),
4269                "bt_status_or_offsets",
4270            )
4271            .map(|value| value.into_int_value())
4272            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
4273    }
4274
4275    fn status_for_backtrace_stop(
4276        &self,
4277        complete: IntValue<'ctx>,
4278        error_code: IntValue<'ctx>,
4279        offsets_found: IntValue<'ctx>,
4280    ) -> Result<IntValue<'ctx>> {
4281        let i8_type = self.context.i8_type();
4282        let i16_type = self.context.i16_type();
4283        let ra_read_error = self
4284            .builder
4285            .build_int_compare(
4286                inkwell::IntPredicate::EQ,
4287                error_code,
4288                i16_type.const_int(BACKTRACE_ERROR_RETURN_ADDRESS_READ as u64, false),
4289                "bt_status_ra_read_error",
4290            )
4291            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4292        let rbp_read_error = self
4293            .builder
4294            .build_int_compare(
4295                inkwell::IntPredicate::EQ,
4296                error_code,
4297                i16_type.const_int(BACKTRACE_ERROR_FRAME_POINTER_READ as u64, false),
4298                "bt_status_rbp_read_error",
4299            )
4300            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4301        let read_error = self
4302            .builder
4303            .build_or(ra_read_error, rbp_read_error, "bt_status_read_error_flag")
4304            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4305        let error_status = self
4306            .builder
4307            .build_select::<BasicValueEnum<'ctx>, _>(
4308                read_error,
4309                i8_type
4310                    .const_int(BacktraceStatus::ReadError as u64, false)
4311                    .into(),
4312                i8_type
4313                    .const_int(BacktraceStatus::InvalidFrame as u64, false)
4314                    .into(),
4315                "bt_status_for_error_code",
4316            )
4317            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4318        let backtrace_status = self
4319            .builder
4320            .build_select::<BasicValueEnum<'ctx>, _>(
4321                complete,
4322                i8_type
4323                    .const_int(BacktraceStatus::Complete as u64, false)
4324                    .into(),
4325                error_status,
4326                "bt_status_for_stop",
4327            )
4328            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4329        self.builder
4330            .build_select::<BasicValueEnum<'ctx>, _>(
4331                offsets_found,
4332                backtrace_status,
4333                i8_type
4334                    .const_int(BacktraceStatus::OffsetsUnavailable as u64, false)
4335                    .into(),
4336                "bt_status_or_offsets_for_error_code",
4337            )
4338            .map(|value| value.into_int_value())
4339            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
4340    }
4341
4342    fn store_backtrace_frame(
4343        &self,
4344        inst_buffer: PointerValue<'ctx>,
4345        frame_index: usize,
4346        module_cookie: IntValue<'ctx>,
4347        pc: IntValue<'ctx>,
4348        raw_ip: IntValue<'ctx>,
4349        flags: u16,
4350    ) -> Result<()> {
4351        let frame_base =
4352            INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_SIZE + frame_index * BACKTRACE_FRAME_DATA_SIZE;
4353        self.store_u64_value(
4354            inst_buffer,
4355            frame_base + BACKTRACE_FRAME_MODULE_COOKIE_OFFSET,
4356            module_cookie,
4357            "bt_frame_cookie",
4358        )?;
4359        self.store_u64_value(
4360            inst_buffer,
4361            frame_base + BACKTRACE_FRAME_PC_OFFSET,
4362            pc,
4363            "bt_frame_pc",
4364        )?;
4365        self.store_u64_value(
4366            inst_buffer,
4367            frame_base + BACKTRACE_FRAME_RAW_IP_OFFSET,
4368            raw_ip,
4369            "bt_frame_raw_ip",
4370        )?;
4371        self.store_u16_const(
4372            inst_buffer,
4373            frame_base + BACKTRACE_FRAME_FLAGS_OFFSET,
4374            flags,
4375            "bt_frame_flags",
4376        )
4377    }
4378
4379    fn store_backtrace_frame_dynamic(
4380        &self,
4381        inst_buffer: PointerValue<'ctx>,
4382        frame_index: IntValue<'ctx>,
4383        max_frame_index: u8,
4384        module_cookie: IntValue<'ctx>,
4385        pc: IntValue<'ctx>,
4386        raw_ip: IntValue<'ctx>,
4387    ) -> Result<()> {
4388        let i64_type = self.context.i64_type();
4389        let frame_index_i64 = if frame_index.get_type().get_bit_width() == i64_type.get_bit_width()
4390        {
4391            frame_index
4392        } else {
4393            self.builder
4394                .build_int_z_extend(frame_index, i64_type, "bt_frame_index_i64")
4395                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4396        };
4397        let max_frame_index = i64_type.const_int(max_frame_index as u64, false);
4398        let frame_index_in_bounds = self
4399            .builder
4400            .build_int_compare(
4401                inkwell::IntPredicate::ULE,
4402                frame_index_i64,
4403                max_frame_index,
4404                "bt_frame_index_in_bounds",
4405            )
4406            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4407        let frame_index_i64 = self
4408            .builder
4409            .build_select::<BasicValueEnum<'ctx>, _>(
4410                frame_index_in_bounds,
4411                frame_index_i64.into(),
4412                max_frame_index.into(),
4413                "bt_frame_index_bounded",
4414            )
4415            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
4416            .into_int_value();
4417        let frame_stride = i64_type.const_int(BACKTRACE_FRAME_DATA_SIZE as u64, false);
4418        let frame_offset = self
4419            .builder
4420            .build_int_mul(frame_index_i64, frame_stride, "bt_dynamic_frame_offset")
4421            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4422        let base_offset = i64_type.const_int(
4423            (INSTRUCTION_HEADER_SIZE + BACKTRACE_DATA_SIZE) as u64,
4424            false,
4425        );
4426        let frame_base_offset = self
4427            .builder
4428            .build_int_add(base_offset, frame_offset, "bt_dynamic_frame_base_offset")
4429            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4430        let frame_base =
4431            self.dynamic_byte_gep(inst_buffer, frame_base_offset, "bt_dynamic_frame")?;
4432
4433        self.store_u64_value(
4434            frame_base,
4435            BACKTRACE_FRAME_MODULE_COOKIE_OFFSET,
4436            module_cookie,
4437            "bt_frame_cookie",
4438        )?;
4439        self.store_u64_value(frame_base, BACKTRACE_FRAME_PC_OFFSET, pc, "bt_frame_pc")?;
4440        self.store_u64_value(
4441            frame_base,
4442            BACKTRACE_FRAME_RAW_IP_OFFSET,
4443            raw_ip,
4444            "bt_frame_raw_ip",
4445        )?;
4446        self.store_u16_const(
4447            frame_base,
4448            BACKTRACE_FRAME_FLAGS_OFFSET,
4449            0,
4450            "bt_frame_flags",
4451        )
4452    }
4453
4454    fn store_u8_const(
4455        &self,
4456        base: PointerValue<'ctx>,
4457        offset: usize,
4458        value: u8,
4459        name: &str,
4460    ) -> Result<()> {
4461        self.store_u8_value(
4462            base,
4463            offset,
4464            self.context.i8_type().const_int(value as u64, false),
4465            name,
4466        )
4467    }
4468
4469    fn store_u8_value(
4470        &self,
4471        base: PointerValue<'ctx>,
4472        offset: usize,
4473        value: IntValue<'ctx>,
4474        name: &str,
4475    ) -> Result<()> {
4476        let ptr = self.byte_gep(base, offset, name)?;
4477        self.builder
4478            .build_store(ptr, value)
4479            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4480        Ok(())
4481    }
4482
4483    fn store_u16_const(
4484        &self,
4485        base: PointerValue<'ctx>,
4486        offset: usize,
4487        value: u16,
4488        name: &str,
4489    ) -> Result<()> {
4490        self.store_u16_value(
4491            base,
4492            offset,
4493            self.context.i16_type().const_int(value as u64, false),
4494            name,
4495        )
4496    }
4497
4498    fn store_u16_value(
4499        &self,
4500        base: PointerValue<'ctx>,
4501        offset: usize,
4502        value: IntValue<'ctx>,
4503        name: &str,
4504    ) -> Result<()> {
4505        let ptr = self.byte_gep(base, offset, name)?;
4506        let ptr = self
4507            .builder
4508            .build_pointer_cast(
4509                ptr,
4510                self.context.ptr_type(AddressSpace::default()),
4511                &format!("{name}_u16_ptr"),
4512            )
4513            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4514        self.builder
4515            .build_store(ptr, value)
4516            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4517        Ok(())
4518    }
4519
4520    fn store_u64_value(
4521        &self,
4522        base: PointerValue<'ctx>,
4523        offset: usize,
4524        value: IntValue<'ctx>,
4525        name: &str,
4526    ) -> Result<()> {
4527        let ptr = self.byte_gep(base, offset, name)?;
4528        let ptr = self
4529            .builder
4530            .build_pointer_cast(
4531                ptr,
4532                self.context.ptr_type(AddressSpace::default()),
4533                &format!("{name}_u64_ptr"),
4534            )
4535            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4536        self.builder
4537            .build_store(ptr, value)
4538            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4539        Ok(())
4540    }
4541
4542    fn byte_gep(
4543        &self,
4544        base: PointerValue<'ctx>,
4545        offset: usize,
4546        name: &str,
4547    ) -> Result<PointerValue<'ctx>> {
4548        // SAFETY: callers pass offsets within the instruction region reserved for
4549        // this Backtrace instruction.
4550        unsafe {
4551            self.builder
4552                .build_gep(
4553                    self.context.i8_type(),
4554                    base,
4555                    &[self.context.i32_type().const_int(offset as u64, false)],
4556                    &format!("{name}_ptr"),
4557                )
4558                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
4559        }
4560    }
4561
4562    fn dynamic_byte_gep(
4563        &self,
4564        base: PointerValue<'ctx>,
4565        offset: IntValue<'ctx>,
4566        name: &str,
4567    ) -> Result<PointerValue<'ctx>> {
4568        // SAFETY: callers guard the dynamic offset against the per-CPU buffer
4569        // size before using the returned pointer.
4570        unsafe {
4571            self.builder
4572                .build_gep(
4573                    self.context.i8_type(),
4574                    base,
4575                    &[offset],
4576                    &format!("{name}_ptr"),
4577                )
4578                .map_err(|e| CodeGenError::LLVMError(e.to_string()))
4579        }
4580    }
4581
4582    fn build_entry_alloca<T>(&self, ty: T, name: &str) -> Result<PointerValue<'ctx>>
4583    where
4584        T: inkwell::types::BasicType<'ctx>,
4585    {
4586        let current_block = self.builder.get_insert_block().ok_or_else(|| {
4587            CodeGenError::LLVMError("no current block for bt stack allocation".to_string())
4588        })?;
4589        let current_fn = self.current_function("allocate bt scratch")?;
4590        let entry_block = current_fn.get_first_basic_block().ok_or_else(|| {
4591            CodeGenError::LLVMError("no entry block for bt stack allocation".to_string())
4592        })?;
4593
4594        if let Some(first_instruction) = entry_block.get_first_instruction() {
4595            self.builder.position_before(&first_instruction);
4596        } else {
4597            self.builder.position_at_end(entry_block);
4598        }
4599        let alloca = self
4600            .builder
4601            .build_alloca(ty, name)
4602            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4603        self.builder.position_at_end(current_block);
4604        Ok(alloca)
4605    }
4606}
4607
4608fn backtrace_row_binary_search_steps(row_count: usize) -> usize {
4609    if row_count <= 1 {
4610        1
4611    } else {
4612        (usize::BITS - (row_count - 1).leading_zeros()) as usize + 1
4613    }
4614}
4615
4616#[cfg(test)]
4617mod tests {
4618    use super::*;
4619    use crate::ebpf::context::BacktraceModuleRowRangeEntry;
4620    use crate::CompileOptions;
4621    use inkwell::AddressSpace;
4622
4623    #[test]
4624    fn binary_search_steps_cover_power_of_two_row_counts() {
4625        assert_eq!(backtrace_row_binary_search_steps(0), 1);
4626        assert_eq!(backtrace_row_binary_search_steps(1), 1);
4627        assert_eq!(backtrace_row_binary_search_steps(2), 2);
4628        assert_eq!(backtrace_row_binary_search_steps(4), 3);
4629        assert_eq!(backtrace_row_binary_search_steps(8), 4);
4630    }
4631
4632    #[test]
4633    fn binary_search_steps_cover_non_power_of_two_row_counts() {
4634        assert_eq!(backtrace_row_binary_search_steps(3), 3);
4635        assert_eq!(backtrace_row_binary_search_steps(5), 4);
4636        assert_eq!(backtrace_row_binary_search_steps(9), 5);
4637    }
4638
4639    #[test]
4640    fn runtime_backtrace_frame_module_resolution_generates_range_lookups() {
4641        let context = inkwell::context::Context::create();
4642        let opts = CompileOptions::default();
4643        let mut ctx =
4644            EbpfContext::new(&context, "bt_frame_module_test", Some(0), &opts).expect("ctx");
4645        let i64_type = context.i64_type();
4646        for map_name in ["proc_module_range_meta", "proc_module_ranges"] {
4647            let map_global =
4648                ctx.module
4649                    .add_global(i64_type, Some(AddressSpace::default()), map_name);
4650            map_global.set_initializer(&i64_type.const_zero());
4651        }
4652
4653        let fn_type = context.i32_type().fn_type(&[], false);
4654        let function = ctx.module.add_function("bt_frame_module", fn_type, None);
4655        let entry = context.append_basic_block(function, "entry");
4656        ctx.builder.position_at_end(entry);
4657        let key_type = context.i32_type().array_type(4);
4658        ctx.pm_key_alloca = Some(
4659            ctx.builder
4660                .build_alloca(key_type, "pm_key")
4661                .expect("pm_key alloca"),
4662        );
4663        ctx.backtrace_module_row_ranges = vec![
4664            BacktraceModuleRowRangeEntry {
4665                cookie: 0x1111,
4666                range: ghostscope_protocol::BacktraceModuleRowRange {
4667                    row_start: 0,
4668                    row_end: 1,
4669                },
4670            },
4671            BacktraceModuleRowRangeEntry {
4672                cookie: 0x2222,
4673                range: ghostscope_protocol::BacktraceModuleRowRange {
4674                    row_start: 1,
4675                    row_end: 2,
4676                },
4677            },
4678        ];
4679
4680        let frame_module = ctx
4681            .resolve_backtrace_frame_module(
4682                i64_type.const_int(0x7f00_1234, false),
4683                i64_type.const_int(0x1111, false),
4684                i64_type.const_zero(),
4685                context.bool_type().const_zero(),
4686                "test_bt_frame_module",
4687            )
4688            .expect("resolve frame module");
4689        ctx.store_u64_value(
4690            ctx.pm_key_alloca.expect("pm key"),
4691            0,
4692            frame_module.cookie,
4693            "selected_cookie",
4694        )
4695        .expect("store selected cookie");
4696
4697        let ir = ctx.module.print_to_string().to_string();
4698        assert!(
4699            ir.contains("proc_module_range_meta")
4700                && ir.contains("proc_module_ranges")
4701                && ir.contains("test_bt_frame_module_0_range_base")
4702                && ir.contains("test_bt_frame_module_0_range_end"),
4703            "resolver should use the per-PID module range index\nIR:\n{ir}"
4704        );
4705        assert!(
4706            ir.contains("test_bt_frame_module_0_range_cookie")
4707                && !ir.contains("proc_module_offsets"),
4708            "resolver should select a module cookie without scanning offsets\nIR:\n{ir}"
4709        );
4710    }
4711
4712    #[test]
4713    fn runtime_backtrace_row_bounds_cover_all_prepared_modules() {
4714        let context = inkwell::context::Context::create();
4715        let opts = CompileOptions::default();
4716        let mut ctx =
4717            EbpfContext::new(&context, "bt_row_bounds_test", Some(0), &opts).expect("ctx");
4718
4719        let fn_type = context
4720            .i32_type()
4721            .fn_type(&[context.i64_type().into()], false);
4722        let function = ctx.module.add_function("bt_row_bounds", fn_type, None);
4723        let entry = context.append_basic_block(function, "entry");
4724        ctx.builder.position_at_end(entry);
4725        let map_global = ctx.module.add_global(
4726            context.i64_type(),
4727            Some(AddressSpace::default()),
4728            "bt_module_row_ranges",
4729        );
4730        map_global.set_initializer(&context.i64_type().const_zero());
4731        let key_type = context.i32_type().array_type(4);
4732        ctx.pm_key_alloca = Some(
4733            ctx.builder
4734                .build_alloca(key_type, "pm_key")
4735                .expect("pm_key alloca"),
4736        );
4737
4738        ctx.backtrace_module_row_ranges = (0..=32)
4739            .map(|idx| BacktraceModuleRowRangeEntry {
4740                cookie: 0x1000 + idx as u64,
4741                range: ghostscope_protocol::BacktraceModuleRowRange {
4742                    row_start: (idx * 2) as u32,
4743                    row_end: (idx * 2 + 2) as u32,
4744                },
4745            })
4746            .collect();
4747
4748        let bounds = ctx
4749            .backtrace_unwind_row_bounds_for_module(
4750                function
4751                    .get_first_param()
4752                    .expect("module cookie param")
4753                    .into_int_value(),
4754                "test_bt_row_bounds",
4755            )
4756            .expect("row bounds");
4757        ctx.builder
4758            .build_return(Some(&bounds.end))
4759            .expect("return bounds end");
4760
4761        let ir = ctx.module.print_to_string().to_string();
4762        assert!(
4763            ir.contains("bt_module_row_ranges")
4764                && ir.contains("test_bt_row_bounds_row_range_lookup"),
4765            "row bounds lookup should use the module range map\nIR:\n{ir}"
4766        );
4767        assert!(
4768            !ir.contains("module_matches"),
4769            "row bounds lookup should not use static candidate comparisons\nIR:\n{ir}"
4770        );
4771        assert!(
4772            !ir.contains("row_range_cookie_lo") && !ir.contains("row_range_cookie_hi"),
4773            "row bounds lookup should store the native u64 module cookie\nIR:\n{ir}"
4774        );
4775    }
4776}