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 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 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 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 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 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 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 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 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 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 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 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}