ghostscope_compiler/ebpf/codegen/
mod.rs1use super::context::{CodeGenError, EbpfContext, Result, RuntimeAddress};
7use crate::script::{PrintStatement, Program, Statement};
8use aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user;
9use ghostscope_protocol::trace_event::{
10 BacktraceFrameData, BacktraceStatus, EndInstructionData, InstructionHeader,
11 PrintComplexFormatData, PrintComplexVariableData, PrintStringIndexData, PrintVariableIndexData,
12 VariableStatus, BACKTRACE_DATA_ERROR_CODE_OFFSET, BACKTRACE_DATA_FLAGS_OFFSET,
13 BACKTRACE_DATA_FRAME_COUNT_OFFSET, BACKTRACE_DATA_REQUESTED_DEPTH_OFFSET, BACKTRACE_DATA_SIZE,
14 BACKTRACE_DATA_STATUS_OFFSET, BACKTRACE_ERROR_FRAME_POINTER_READ,
15 BACKTRACE_ERROR_NEXT_CFA_NOT_ADVANCING, BACKTRACE_ERROR_NEXT_CFA_ZERO,
16 BACKTRACE_ERROR_NEXT_IP_BELOW_USER, BACKTRACE_ERROR_NEXT_IP_KERNEL_LIKE, BACKTRACE_ERROR_NONE,
17 BACKTRACE_ERROR_RETURN_ADDRESS_READ, BACKTRACE_FLAG_FULL, BACKTRACE_FLAG_INLINE,
18 BACKTRACE_FLAG_RAW, BACKTRACE_FRAME_DATA_SIZE, BACKTRACE_FRAME_FLAGS_OFFSET,
19 BACKTRACE_FRAME_MODULE_COOKIE_OFFSET, BACKTRACE_FRAME_PC_OFFSET, BACKTRACE_FRAME_RAW_IP_OFFSET,
20 EXPR_ERROR_DATA_ERROR_CODE_OFFSET, EXPR_ERROR_DATA_FAILING_ADDR_OFFSET,
21 EXPR_ERROR_DATA_FLAGS_OFFSET, EXPR_ERROR_DATA_SIZE, EXPR_ERROR_DATA_STRING_INDEX_OFFSET,
22 INSTRUCTION_HEADER_DATA_LENGTH_OFFSET, INSTRUCTION_HEADER_SIZE,
23 PRINT_COMPLEX_FORMAT_ARG_ACCESS_PATH_LEN_OFFSET, PRINT_COMPLEX_FORMAT_ARG_ACCESS_PATH_OFFSET,
24 PRINT_COMPLEX_FORMAT_ARG_FIXED_HEADER_LEN, PRINT_COMPLEX_FORMAT_ARG_STATUS_OFFSET,
25 PRINT_COMPLEX_FORMAT_ARG_TYPE_INDEX_OFFSET, PRINT_COMPLEX_FORMAT_DATA_ARG_COUNT_OFFSET,
26 VARIABLE_READ_ERROR_PAYLOAD_ADDR_OFFSET, VARIABLE_READ_ERROR_PAYLOAD_ERRNO_OFFSET,
27 VARIABLE_READ_ERROR_PAYLOAD_LEN,
28};
29use ghostscope_protocol::{InstructionType, TraceContext, TypeKind};
30use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
31use inkwell::AddressSpace;
32use std::collections::HashMap;
33use tracing::{debug, info, warn};
34
35#[derive(Debug, Clone)]
37struct PrintVarRuntimeMeta {
38 var_name_index: u16,
39 type_index: u16,
40 access_path: String,
41 data_len_limit: usize,
42}
43
44#[derive(Debug, Clone)]
46enum ComplexArgSource<'ctx> {
47 RuntimeRead {
48 address: ghostscope_dwarf::PlannedAddress,
49 dwarf_type: ghostscope_dwarf::TypeInfo,
50 module_for_offsets: Option<String>,
51 },
52 MemDump {
54 address: RuntimeAddress<'ctx>,
55 len: usize,
56 },
57 MemDumpDynamic {
59 address: RuntimeAddress<'ctx>,
60 len_value: inkwell::values::IntValue<'ctx>,
61 max_len: usize,
62 },
63 ImmediateBytes {
64 bytes: Vec<u8>,
65 },
66 AddressValue {
67 address: ghostscope_dwarf::PlannedAddress,
68 module_for_offsets: Option<String>,
69 },
70 ComputedAddress {
71 address: RuntimeAddress<'ctx>,
72 },
73 ComputedInt {
75 value: inkwell::values::IntValue<'ctx>,
76 byte_len: usize, },
78}
79
80#[derive(Debug, Clone)]
82struct ComplexArg<'ctx> {
83 var_name_index: u16,
84 type_index: u16,
85 access_path: Vec<u8>,
86 data_len: usize,
87 source: ComplexArgSource<'ctx>,
88}
89
90fn print_complex_format_instruction_budget(
91 max_trace_event_size: usize,
92 bytes_reserved_so_far: usize,
93) -> usize {
94 let end_instruction_size =
95 std::mem::size_of::<InstructionHeader>() + std::mem::size_of::<EndInstructionData>();
96 let event_budget = max_trace_event_size
97 .saturating_sub(bytes_reserved_so_far)
98 .saturating_sub(end_instruction_size);
99 let instruction_budget_cap = std::mem::size_of::<InstructionHeader>() + u16::MAX as usize;
100 event_budget.min(instruction_budget_cap)
101}
102
103fn distribute_budget_fairly(caps: &[usize], budget: usize) -> Vec<usize> {
104 let mut allocations = vec![0; caps.len()];
105 let mut active: Vec<usize> = caps
106 .iter()
107 .enumerate()
108 .filter_map(|(idx, cap)| (*cap > 0).then_some(idx))
109 .collect();
110 let mut remaining = budget;
111
112 while remaining > 0 && !active.is_empty() {
113 let share = remaining / active.len();
114 if share == 0 {
115 for &idx in active.iter().take(remaining) {
116 allocations[idx] += 1;
117 }
118 break;
119 }
120
121 let mut consumed = 0usize;
122 let mut next_active = Vec::with_capacity(active.len());
123 for idx in active {
124 let cap_left = caps[idx].saturating_sub(allocations[idx]);
125 let take = share.min(cap_left);
126 allocations[idx] += take;
127 consumed += take;
128 if allocations[idx] < caps[idx] {
129 next_active.push(idx);
130 }
131 }
132
133 if consumed == 0 {
134 break;
135 }
136
137 remaining = remaining.saturating_sub(consumed);
138 active = next_active;
139 }
140
141 allocations
142}
143
144fn allocate_dynamic_payload_reservations(max_lens: &[usize], available: usize) -> Vec<usize> {
145 if max_lens.is_empty() || available == 0 {
146 return vec![0; max_lens.len()];
147 }
148
149 let base_caps = vec![VARIABLE_READ_ERROR_PAYLOAD_LEN; max_lens.len()];
150 let base_budget = available.min(VARIABLE_READ_ERROR_PAYLOAD_LEN.saturating_mul(max_lens.len()));
151 let mut reservations = distribute_budget_fairly(&base_caps, base_budget);
152 let remaining_budget = available.saturating_sub(reservations.iter().sum::<usize>());
153 if remaining_budget == 0 {
154 return reservations;
155 }
156
157 let extra_caps: Vec<usize> = max_lens
158 .iter()
159 .zip(reservations.iter())
160 .map(|(max_len, reserved)| {
161 max_len
162 .max(&VARIABLE_READ_ERROR_PAYLOAD_LEN)
163 .saturating_sub(*reserved)
164 })
165 .collect();
166 let extras = distribute_budget_fairly(&extra_caps, remaining_budget);
167 for (reservation, extra) in reservations.iter_mut().zip(extras) {
168 *reservation += extra;
169 }
170
171 reservations
172}
173
174mod args;
175mod backtrace;
176mod backtrace_plan;
177mod expr_error;
178mod format;
179mod instruction_common;
180mod print_complex_variable;
181mod print_string_index;
182mod print_variable_index;
183mod statements;
184mod types;
185
186#[cfg(test)]
187mod tests;