Skip to main content

ghostscope_compiler/ebpf/
context.rs

1//! eBPF LLVM context and core infrastructure
2//!
3//! This module provides the main code generation context and basic LLVM
4//! infrastructure for eBPF program generation.
5
6use super::maps::MapManager;
7use crate::script::{VarType, VariableContext};
8use ghostscope_dwarf::DwarfAnalyzer;
9use inkwell::basic_block::BasicBlock;
10use inkwell::builder::Builder;
11use inkwell::context::Context;
12use inkwell::debug_info::DebugInfoBuilder;
13use inkwell::module::Module;
14use inkwell::targets::{Target, TargetTriple};
15use inkwell::values::{FunctionValue, IntValue, PointerValue};
16use inkwell::AddressSpace;
17use inkwell::OptimizationLevel;
18use std::collections::HashMap;
19use thiserror::Error;
20use tracing::info;
21
22/// Compile-time context containing PC address and module information for DWARF queries
23#[derive(Debug, Clone)]
24pub struct CompileTimeContext {
25    pub pc_address: u64,
26    pub module_path: String,
27}
28
29#[derive(Debug, Clone)]
30pub struct BacktraceTailCallProgram {
31    pub step_program_name: String,
32}
33
34#[derive(Debug, Clone)]
35pub(crate) struct PendingBacktraceTailCall {
36    pub step_program_name: String,
37    pub depth: u8,
38    pub instruction_size: usize,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub(crate) struct BacktraceModuleRowRangeEntry {
43    pub cookie: u64,
44    pub range: ghostscope_protocol::BacktraceModuleRowRange,
45}
46
47#[derive(Error, Debug)]
48pub enum CodeGenError {
49    #[error("LLVM compilation error: {0}")]
50    LLVMError(String),
51    #[error("Unsupported evaluation result: {0}")]
52    UnsupportedEvaluation(String),
53    #[error("Register mapping error: {0}")]
54    RegisterMappingError(String),
55    #[error("Memory access error: {0}")]
56    MemoryAccessError(String),
57    #[error("Builder error: {0}")]
58    Builder(String),
59
60    // === Variable lookup and availability errors ===
61    #[error("Variable not found: {0}")]
62    VariableNotFound(String),
63    #[error("Variable not in scope: {0}")]
64    VariableNotInScope(String),
65    #[error("Variable unavailable: {0}")]
66    VariableUnavailable(String),
67    #[error("Type error: {0}")]
68    TypeError(String),
69    #[error("Not implemented: {0}")]
70    NotImplemented(String),
71    #[error("DWARF expression error: {0}")]
72    DwarfError(String),
73    #[error("Type size not available for variable: {0}")]
74    TypeSizeNotAvailable(String),
75}
76
77pub type Result<T> = std::result::Result<T, CodeGenError>;
78
79/// Runtime address produced by lowering a DWARF `PlannedAddress`.
80///
81/// `value` is the address to use in generated eBPF code. `offsets_found` is an
82/// i1 guard that is false when a link-time address could not be rebased through
83/// `proc_module_offsets`. Runtime-derived addresses do not need that lookup, so
84/// their guard is always true.
85#[derive(Debug, Clone, Copy)]
86pub(crate) struct RuntimeAddress<'ctx> {
87    pub value: IntValue<'ctx>,
88    pub offsets_found: IntValue<'ctx>,
89}
90
91impl<'ctx> RuntimeAddress<'ctx> {
92    pub(crate) fn available(value: IntValue<'ctx>, context: &'ctx Context) -> Self {
93        Self {
94            value,
95            offsets_found: context.bool_type().const_int(1, false),
96        }
97    }
98
99    pub(crate) fn with_offsets_found(value: IntValue<'ctx>, offsets_found: IntValue<'ctx>) -> Self {
100        Self {
101            value,
102            offsets_found,
103        }
104    }
105
106    pub(crate) fn with_value(self, value: IntValue<'ctx>) -> Self {
107        Self { value, ..self }
108    }
109}
110
111/// eBPF LLVM code generation context
112pub struct EbpfContext<'ctx, 'dw> {
113    pub context: &'ctx Context,
114    pub module: Module<'ctx>,
115    pub builder: Builder<'ctx>,
116
117    // eBPF-specific function declarations
118    pub trace_printk_fn: FunctionValue<'ctx>,
119
120    // Map manager for eBPF maps
121    pub map_manager: MapManager<'ctx>,
122
123    // Debug infrastructure
124    pub di_builder: DebugInfoBuilder<'ctx>,
125    pub compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
126
127    // === Complete Variable Management System ===
128    pub variables: HashMap<String, PointerValue<'ctx>>, // Variable name -> LLVM pointer
129    pub var_types: HashMap<String, VarType>,            // Variable name -> type
130    pub optimized_out_vars: HashMap<String, bool>,      // Optimized out variables
131    pub var_pc_addresses: HashMap<String, u64>,         // Variable -> PC address
132    pub variable_context: Option<VariableContext>,      // Scope validation context
133    pub(super) process_analyzer: Option<&'dw DwarfAnalyzer>, // Multi-module DWARF analyzer
134    pub current_trace_id: Option<u32>,                  // Current trace_id being compiled
135    pub current_compile_time_context: Option<CompileTimeContext>, // PC address and module for DWARF queries
136
137    // === New instruction-based compilation system ===
138    pub trace_context: ghostscope_protocol::TraceContext, // Trace context for optimized transmission
139
140    // Per-invocation stack key for proc_module_offsets lookups (allocated in entry block)
141    // Backed by `[4 x i32]`, so consumers may only assume i32 alignment.
142    pub pm_key_alloca: Option<inkwell::values::PointerValue<'ctx>>,
143    // Per-invocation 8-byte scratch for thread-pointer/TLS helper reads,
144    // allocated lazily only for programs that actually lower TLS variables.
145    pub(super) tls_scratch_alloca: Option<inkwell::values::PointerValue<'ctx>>,
146    // Per-invocation event accumulation offset (u32) stored on stack (entry block)
147    pub event_offset_alloca: Option<inkwell::values::PointerValue<'ctx>>,
148    // Compile-time upper bound for bytes that may already be reserved in the current trace event.
149    // This is maintained across structured control flow so later instructions can budget against
150    // the worst-case path without double-counting sibling branches.
151    pub compile_time_event_bytes_upper_bound: usize,
152    // Compilation options (includes eBPF map configuration)
153    pub compile_options: crate::CompileOptions,
154
155    // === Control-flow expression error capture (soft abort) ===
156    pub condition_context_active: bool,
157
158    // === DWARF alias variables (script-level symbolic references) ===
159    // These variables do not store pointer values; instead they remember the RHS
160    // expression and are resolved to runtime addresses at use sites.
161    pub alias_vars: HashMap<String, crate::script::Expr>,
162
163    // === Script string variables (store literal bytes for content printing) ===
164    // When a variable is bound from a string literal (or copied from another string var),
165    // we keep its bytes (including optional NUL) here for content printing via ImmediateBytes.
166    pub string_vars: HashMap<String, Vec<u8>>,
167
168    // === DWARF compact unwind rows for bt ===
169    pub backtrace_unwind_rows: Vec<ghostscope_protocol::BacktraceUnwindRow>,
170    pub(crate) backtrace_module_row_ranges: Vec<BacktraceModuleRowRangeEntry>,
171    pub(crate) backtrace_tail_call_slots: u8,
172    pub(crate) next_backtrace_tail_call_slot: u8,
173    pub(crate) pending_backtrace_tail_call: Option<PendingBacktraceTailCall>,
174    pub(crate) backtrace_tail_enabled_alloca: Option<inkwell::values::PointerValue<'ctx>>,
175    pub(crate) backtrace_tail_last_slot_alloca: Option<inkwell::values::PointerValue<'ctx>>,
176
177    // === Lexical scoping for immutable variables ===
178    // Each scope frame records names declared in that scope.
179    pub scope_stack: Vec<std::collections::HashSet<String>>,
180}
181
182impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
183    pub(crate) fn backtrace_unwind_row_map_entries(&self) -> u64 {
184        (self.compile_options.backtrace_unwind_rows_max_entries as u64)
185            .max(self.backtrace_unwind_rows.len() as u64)
186            .max(1)
187    }
188
189    /// Create a new eBPF code generation context
190    pub fn new(
191        context: &'ctx Context,
192        module_name: &str,
193        trace_id: Option<u32>,
194        compile_options: &crate::CompileOptions,
195    ) -> Result<Self> {
196        let module = context.create_module(module_name);
197        let builder = context.create_builder();
198
199        // Initialize standard BPF target
200        Target::initialize_bpf(&Default::default());
201
202        // Create BPF target triple
203        let triple = TargetTriple::create("bpf-pc-linux");
204
205        // Get target and create target machine
206        let target = Target::from_triple(&triple).map_err(|e| {
207            CodeGenError::LLVMError(format!("Failed to get target from triple: {e}"))
208        })?;
209        let target_machine = target
210            .create_target_machine(
211                &triple,
212                "generic",
213                "+alu32",
214                OptimizationLevel::Default,
215                inkwell::targets::RelocMode::PIC,
216                inkwell::targets::CodeModel::Small,
217            )
218            .ok_or_else(|| {
219                CodeGenError::LLVMError("Failed to create target machine".to_string())
220            })?;
221
222        // Set module data layout and triple
223        let data_layout = target_machine.get_target_data().get_data_layout();
224        module.set_data_layout(&data_layout);
225        module.set_triple(&triple);
226
227        // Initialize debug info
228        let (di_builder, compile_unit) = module.create_debug_info_builder(
229            true,                                         // allow_unresolved
230            inkwell::debug_info::DWARFSourceLanguage::C,  // language
231            "ghostscope_generated.c",                     // filename
232            ".",                                          // directory
233            "ghostscope-compiler",                        // producer
234            false,                                        // is_optimized
235            "",                                           // flags
236            1,                                            // runtime_version
237            "",                                           // split_name
238            inkwell::debug_info::DWARFEmissionKind::Full, // kind
239            0,                                            // dwo_id
240            false,                                        // split_debug_inlining
241            false,                                        // debug_info_for_profiling
242            "",                                           // sysroot
243            "",                                           // sdk
244        );
245
246        let map_manager = MapManager::new(context);
247
248        // Declare eBPF helper functions
249        let trace_printk_fn = Self::declare_trace_printk(context, &module);
250
251        Ok(Self {
252            context,
253            module,
254            builder,
255            trace_printk_fn,
256            map_manager,
257            di_builder,
258            compile_unit,
259
260            // Initialize variable management system
261            variables: HashMap::new(),
262            var_types: HashMap::new(),
263            optimized_out_vars: HashMap::new(),
264            var_pc_addresses: HashMap::new(),
265            variable_context: None,
266            process_analyzer: None,
267            current_trace_id: trace_id,
268            current_compile_time_context: None,
269
270            // Initialize new instruction-based compilation system
271            trace_context: ghostscope_protocol::TraceContext::new(),
272            pm_key_alloca: None,
273            tls_scratch_alloca: None,
274            event_offset_alloca: None,
275            compile_time_event_bytes_upper_bound: 0,
276            compile_options: compile_options.clone(),
277
278            // Control-flow expression context
279            condition_context_active: false,
280
281            // Alias variables
282            alias_vars: HashMap::new(),
283            // String variables
284            string_vars: HashMap::new(),
285            // Backtrace compact unwind rows
286            backtrace_unwind_rows: Vec::new(),
287            backtrace_module_row_ranges: Vec::new(),
288            backtrace_tail_call_slots: 1,
289            next_backtrace_tail_call_slot: 0,
290            pending_backtrace_tail_call: None,
291            backtrace_tail_enabled_alloca: None,
292            backtrace_tail_last_slot_alloca: None,
293
294            // Scopes
295            scope_stack: Vec::new(),
296        })
297    }
298
299    /// Enter a new lexical scope
300    pub fn enter_scope(&mut self) {
301        self.scope_stack.push(std::collections::HashSet::new());
302    }
303
304    /// Exit current lexical scope and drop all names declared within
305    pub fn exit_scope(&mut self) {
306        if let Some(names) = self.scope_stack.pop() {
307            for name in names {
308                self.variables.remove(&name);
309                self.var_types.remove(&name);
310                self.alias_vars.remove(&name);
311                self.string_vars.remove(&name);
312                self.optimized_out_vars.remove(&name);
313                self.var_pc_addresses.remove(&name);
314            }
315        }
316    }
317
318    /// Check if a name exists in any active scope
319    pub fn is_name_in_any_scope(&self, name: &str) -> bool {
320        self.scope_stack.iter().any(|s| s.contains(name))
321    }
322
323    /// Check if a name exists in current (top) scope
324    pub fn is_name_in_current_scope(&self, name: &str) -> bool {
325        match self.scope_stack.last() {
326            Some(top) => top.contains(name),
327            None => false,
328        }
329    }
330
331    /// Declare a name in the current scope. Disallow same-scope redeclaration and shadowing.
332    pub fn declare_name_in_current_scope(&mut self, name: &str) -> Result<()> {
333        if self.scope_stack.is_empty() {
334            // Initialize a root scope if not present
335            self.enter_scope();
336        }
337        if self.is_name_in_current_scope(name) {
338            return Err(CodeGenError::TypeError(format!(
339                "Redeclaration in the same scope is not allowed: '{name}'"
340            )));
341        }
342        if self.is_name_in_any_scope(name) {
343            return Err(CodeGenError::TypeError(format!(
344                "Shadowing is not allowed for immutable variables: '{name}'"
345            )));
346        }
347        if let Some(top) = self.scope_stack.last_mut() {
348            top.insert(name.to_string());
349        }
350        Ok(())
351    }
352
353    /// Create a new code generator with DWARF analyzer support
354    pub fn new_with_process_analyzer(
355        context: &'ctx Context,
356        module_name: &str,
357        process_analyzer: Option<&'dw DwarfAnalyzer>,
358        trace_id: Option<u32>,
359        compile_options: &crate::CompileOptions,
360    ) -> Result<Self> {
361        let mut codegen = Self::new(context, module_name, trace_id, compile_options)?;
362        codegen.process_analyzer = process_analyzer;
363        Ok(codegen)
364    }
365
366    /// Set compile-time context for DWARF queries
367    pub fn set_compile_time_context(&mut self, pc_address: u64, module_path: String) {
368        self.current_compile_time_context = Some(CompileTimeContext {
369            pc_address,
370            module_path,
371        });
372    }
373
374    /// Get compile-time context for DWARF queries
375    pub fn get_compile_time_context(&self) -> Result<&CompileTimeContext> {
376        self.current_compile_time_context
377            .as_ref()
378            .ok_or_else(|| CodeGenError::DwarfError("No compile-time context set".to_string()))
379    }
380
381    /// Declare trace_printk eBPF helper function
382    fn declare_trace_printk(context: &'ctx Context, module: &Module<'ctx>) -> FunctionValue<'ctx> {
383        let i32_type = context.i32_type();
384        let ptr_type = context.ptr_type(AddressSpace::default());
385        let i64_type = context.i64_type();
386
387        // int bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
388        let fn_type = i32_type.fn_type(&[ptr_type.into(), i64_type.into()], true);
389
390        module.add_function("bpf_trace_printk", fn_type, None)
391    }
392
393    /// Create basic eBPF function with proper signature
394    pub fn create_basic_ebpf_function(&mut self, function_name: &str) -> Result<()> {
395        let i32_type = self.context.i32_type();
396        let ptr_type = self.context.ptr_type(AddressSpace::default());
397
398        // eBPF function signature: int function(struct pt_regs *ctx)
399        let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
400
401        let function = self.module.add_function(function_name, fn_type, None);
402
403        // Set section attribute for uprobe
404        function.add_attribute(
405            inkwell::attributes::AttributeLoc::Function,
406            self.context.create_string_attribute("section", "uprobe"),
407        );
408
409        // Create basic block
410        let basic_block = self.context.append_basic_block(function, "entry");
411        self.builder.position_at_end(basic_block);
412
413        info!("Created eBPF function: {}", function_name);
414        Ok(())
415    }
416
417    /// Test helper: ensure proc_module_offsets map exists in the module
418    #[cfg(test)]
419    pub fn __test_ensure_proc_offsets_map(&mut self) -> Result<()> {
420        self.map_manager
421            .create_proc_module_offsets_map(
422                &self.module,
423                &self.di_builder,
424                &self.compile_unit,
425                "proc_module_offsets",
426                self.compile_options.proc_module_offsets_max_entries,
427            )
428            .map_err(|e| {
429                CodeGenError::LLVMError(format!(
430                    "Failed to create proc_module_offsets map in test: {e}"
431                ))
432            })?;
433        self.map_manager
434            .create_pid_aliases_map(
435                &self.module,
436                &self.di_builder,
437                &self.compile_unit,
438                "pid_aliases",
439                self.compile_options.proc_module_offsets_max_entries,
440            )
441            .map_err(|e| {
442                CodeGenError::LLVMError(format!("Failed to create pid_aliases map in test: {e}"))
443            })?;
444        self.map_manager
445            .create_proc_module_range_meta_map(
446                &self.module,
447                &self.di_builder,
448                &self.compile_unit,
449                "proc_module_range_meta",
450                self.compile_options.proc_module_offsets_max_entries,
451            )
452            .map_err(|e| {
453                CodeGenError::LLVMError(format!(
454                    "Failed to create proc_module_range_meta map in test: {e}"
455                ))
456            })?;
457        self.map_manager
458            .create_proc_module_ranges_map(
459                &self.module,
460                &self.di_builder,
461                &self.compile_unit,
462                "proc_module_ranges",
463                self.compile_options
464                    .proc_module_offsets_max_entries
465                    .saturating_mul(2)
466                    .max(1),
467            )
468            .map_err(|e| {
469                CodeGenError::LLVMError(format!(
470                    "Failed to create proc_module_ranges map in test: {e}"
471                ))
472            })
473    }
474
475    /// Test helper: allocate per-invocation pm_key on the entry block like create_main_function
476    #[cfg(test)]
477    pub fn __test_alloc_pm_key(&mut self) -> Result<()> {
478        let i32_type = self.context.i32_type();
479        let key_arr_ty = i32_type.array_type(4);
480        let key_alloca = self
481            .builder
482            .build_alloca(key_arr_ty, "pm_key")
483            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
484        self.pm_key_alloca = Some(key_alloca);
485        Ok(())
486    }
487
488    /// Get the LLVM module reference
489    pub fn get_module(&self) -> &Module<'ctx> {
490        &self.module
491    }
492
493    /// Get the string table after compilation
494    pub fn get_trace_context(&self) -> ghostscope_protocol::TraceContext {
495        self.trace_context.clone()
496    }
497
498    pub fn backtrace_tail_call_program(&self) -> Option<BacktraceTailCallProgram> {
499        self.pending_backtrace_tail_call
500            .as_ref()
501            .map(|plan| BacktraceTailCallProgram {
502                step_program_name: plan.step_program_name.clone(),
503            })
504    }
505
506    pub(crate) fn current_insert_block(&self, op: &str) -> Result<BasicBlock<'ctx>> {
507        self.builder
508            .get_insert_block()
509            .ok_or_else(|| CodeGenError::Builder(format!("{op} requires an active insert block")))
510    }
511
512    pub(crate) fn current_function(&self, op: &str) -> Result<FunctionValue<'ctx>> {
513        self.current_insert_block(op)?
514            .get_parent()
515            .ok_or_else(|| CodeGenError::Builder(format!("{op} requires a parent function")))
516    }
517
518    /// Get pt_regs parameter from current function
519    pub fn get_pt_regs_parameter(&self) -> Result<PointerValue<'ctx>> {
520        let current_function = self.current_function("get pt_regs parameter")?;
521
522        let pt_regs_param = current_function
523            .get_first_param()
524            .ok_or_else(|| CodeGenError::Builder("Function has no parameters".to_string()))?
525            .into_pointer_value();
526
527        Ok(pt_regs_param)
528    }
529
530    /// Compile a complete program with statements
531    pub fn compile_program(
532        &mut self,
533        _program: &crate::script::Program,
534        function_name: &str,
535        trace_statements: &[crate::script::Statement],
536        target_pid: Option<u32>,
537        compile_time_pc: Option<u64>,
538        module_path: Option<&str>,
539    ) -> Result<(FunctionValue<'ctx>, ghostscope_protocol::TraceContext)> {
540        info!(
541            "Starting program compilation with function: {}",
542            function_name
543        );
544
545        // Set the current trace_id and compile-time context for code generation
546        self.current_compile_time_context =
547            if let (Some(pc), Some(path)) = (compile_time_pc, module_path) {
548                Some(CompileTimeContext {
549                    pc_address: pc,
550                    module_path: path.to_string(),
551                })
552            } else {
553                None
554            };
555        self.prepare_backtrace_unwind_rows(trace_statements);
556
557        // Create required maps - critical for eBPF loader
558        // Create event output map based on compile options
559        match self.compile_options.event_map_type {
560            crate::EventMapType::RingBuf => {
561                self.map_manager
562                    .create_ringbuf_map(
563                        &self.module,
564                        &self.di_builder,
565                        &self.compile_unit,
566                        "ringbuf",
567                        self.compile_options.ringbuf_size,
568                    )
569                    .map_err(|e| {
570                        CodeGenError::LLVMError(format!("Failed to create ringbuf map: {e}"))
571                    })?;
572            }
573            crate::EventMapType::PerfEventArray => {
574                self.map_manager
575                    .create_perf_event_array_map(
576                        &self.module,
577                        &self.di_builder,
578                        &self.compile_unit,
579                        "events",
580                    )
581                    .map_err(|e| {
582                        CodeGenError::LLVMError(format!(
583                            "Failed to create perf event array map: {e}"
584                        ))
585                    })?;
586            }
587        }
588
589        self.map_manager
590            .create_event_loss_counter_map(
591                &self.module,
592                &self.di_builder,
593                &self.compile_unit,
594                "event_loss_counters",
595                1,
596            )
597            .map_err(|e| {
598                CodeGenError::LLVMError(format!("Failed to create event_loss_counters map: {e}"))
599            })?;
600
601        // Create per-CPU accumulation maps for single-record event emission
602        //  - event_accum_buffer: value size = max_trace_event_size bytes, entries = 1
603        //  - event_accum_offset: value size = 4 bytes (u32), entries = 1
604        self.map_manager
605            .create_percpu_array_map(
606                &self.module,
607                &self.di_builder,
608                &self.compile_unit,
609                "event_accum_buffer",
610                1,
611                self.compile_options.max_trace_event_size as u64,
612            )
613            .map_err(|e| {
614                CodeGenError::LLVMError(format!("Failed to create event_accum_buffer: {e}"))
615            })?;
616
617        // Create ASLR offsets map for (pid,module) → section offsets
618        self.map_manager
619            .create_proc_module_offsets_map(
620                &self.module,
621                &self.di_builder,
622                &self.compile_unit,
623                "proc_module_offsets",
624                self.compile_options.proc_module_offsets_max_entries,
625            )
626            .map_err(|e| {
627                CodeGenError::LLVMError(format!("Failed to create proc_module_offsets map: {e}"))
628            })?;
629
630        self.map_manager
631            .create_pid_aliases_map(
632                &self.module,
633                &self.di_builder,
634                &self.compile_unit,
635                "pid_aliases",
636                self.compile_options.proc_module_offsets_max_entries,
637            )
638            .map_err(|e| {
639                CodeGenError::LLVMError(format!("Failed to create pid_aliases map: {e}"))
640            })?;
641
642        if !self.backtrace_unwind_rows.is_empty() {
643            let share_backtrace_maps = !self.backtrace_module_row_ranges.is_empty();
644            if share_backtrace_maps {
645                self.map_manager.mark_pinned_map("bt_unwind_rows");
646                self.map_manager.mark_pinned_map("bt_module_row_ranges");
647            }
648            self.map_manager
649                .create_array_map(
650                    &self.module,
651                    &self.di_builder,
652                    &self.compile_unit,
653                    "bt_unwind_rows",
654                    self.backtrace_unwind_row_map_entries(),
655                    crate::BACKTRACE_UNWIND_ROW_SIZE as u64,
656                )
657                .map_err(|e| {
658                    CodeGenError::LLVMError(format!("Failed to create bt_unwind_rows map: {e}"))
659                })?;
660            if share_backtrace_maps {
661                self.map_manager
662                    .create_proc_module_range_meta_map(
663                        &self.module,
664                        &self.di_builder,
665                        &self.compile_unit,
666                        "proc_module_range_meta",
667                        self.compile_options.proc_module_offsets_max_entries,
668                    )
669                    .map_err(|e| {
670                        CodeGenError::LLVMError(format!(
671                            "Failed to create proc_module_range_meta map: {e}"
672                        ))
673                    })?;
674                self.map_manager
675                    .create_proc_module_ranges_map(
676                        &self.module,
677                        &self.di_builder,
678                        &self.compile_unit,
679                        "proc_module_ranges",
680                        self.compile_options
681                            .proc_module_offsets_max_entries
682                            .saturating_mul(2)
683                            .max(1),
684                    )
685                    .map_err(|e| {
686                        CodeGenError::LLVMError(format!(
687                            "Failed to create proc_module_ranges map: {e}"
688                        ))
689                    })?;
690                self.map_manager
691                    .create_hash_map(
692                        &self.module,
693                        &self.di_builder,
694                        &self.compile_unit,
695                        "bt_module_row_ranges",
696                        self.compile_options.proc_module_offsets_max_entries.max(1),
697                        (
698                            std::mem::size_of::<u64>() as u64,
699                            ghostscope_protocol::BACKTRACE_MODULE_ROW_RANGE_SIZE as u64,
700                        ),
701                    )
702                    .map_err(|e| {
703                        CodeGenError::LLVMError(format!(
704                            "Failed to create bt_module_row_ranges map: {e}"
705                        ))
706                    })?;
707            }
708            self.map_manager
709                .create_percpu_array_map(
710                    &self.module,
711                    &self.di_builder,
712                    &self.compile_unit,
713                    "bt_state",
714                    self.backtrace_tail_call_slots.max(1) as u64,
715                    crate::BACKTRACE_TAIL_STATE_SIZE as u64,
716                )
717                .map_err(|e| {
718                    CodeGenError::LLVMError(format!("Failed to create bt_state map: {e}"))
719                })?;
720            self.map_manager
721                .create_program_array_map(
722                    &self.module,
723                    &self.di_builder,
724                    &self.compile_unit,
725                    "bt_prog_array",
726                    1,
727                )
728                .map_err(|e| {
729                    CodeGenError::LLVMError(format!("Failed to create bt_prog_array map: {e}"))
730                })?;
731        }
732
733        // Variables are now queried on-demand when accessed in expressions
734        // No need to pre-populate DWARF variables
735
736        // Create main function
737        let main_function = self.create_main_function(function_name)?;
738
739        // Add PID filtering:
740        // 1) explicit compile option override (namespace-aware)
741        // 2) fallback to legacy host TGID filter from target_pid
742        let pid_filter_spec = self
743            .compile_options
744            .pid_filter_spec
745            .or_else(|| target_pid.map(|pid| crate::PidFilterSpec::HostTgid { filter_pid: pid }));
746        if let Some(spec) = pid_filter_spec {
747            self.add_pid_filter(spec)?;
748        }
749
750        // Use new staged transmission system for all statements
751        let program = crate::script::ast::Program {
752            statements: trace_statements.to_vec(),
753        };
754
755        // Collect variable types from DWARF analysis
756        let variable_types = std::collections::HashMap::new(); // Empty for now, will be populated by codegen
757
758        // Generate staged transmission code using new architecture
759        let trace_context =
760            self.compile_program_with_staged_transmission(&program, variable_types)?;
761        info!(
762            "Generated TraceContext with {} strings",
763            trace_context.string_count()
764        );
765
766        // Return success
767        let i32_type = self.context.i32_type();
768        let return_value = i32_type.const_int(0, false);
769        self.builder
770            .build_return(Some(&return_value))
771            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
772
773        info!(
774            "Successfully compiled program with function: {} and TraceContext",
775            function_name
776        );
777        Ok((main_function, trace_context))
778    }
779
780    /// Create the main eBPF function
781    fn create_main_function(&mut self, function_name: &str) -> Result<FunctionValue<'ctx>> {
782        let i32_type = self.context.i32_type();
783        let ptr_type = self.context.ptr_type(AddressSpace::default());
784
785        // Create function type: int function_name(void *ctx)
786        let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
787        let function = self.module.add_function(function_name, fn_type, None);
788
789        // CRITICAL: Set section name for eBPF loader to find the function
790        function.set_section(Some("uprobe"));
791
792        // Create basic block and position builder
793        let basic_block = self.context.append_basic_block(function, "entry");
794        self.builder.position_at_end(basic_block);
795
796        // Allocate fixed-size per-invocation key buffer on the eBPF stack (entry block)
797        // Layout: [ pid:u32, pad:u32, cookie_lo:u32, cookie_hi:u32 ] to match struct {u32; u64}
798        // This remains an i32-aligned slot; probe-read scratch reuse must stay
799        // limited to <=4-byte scalar loads.
800        let key_arr_ty = i32_type.array_type(4);
801        let key_alloca = self
802            .builder
803            .build_alloca(key_arr_ty, "pm_key")
804            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
805        self.pm_key_alloca = Some(key_alloca);
806
807        // Allocate per-invocation event_offset (u32) and initialize to 0
808        let event_off_alloca = self
809            .builder
810            .build_alloca(i32_type, "event_offset")
811            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
812        self.builder
813            .build_store(event_off_alloca, i32_type.const_zero())
814            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
815        self.event_offset_alloca = Some(event_off_alloca);
816
817        info!("Created main function: {}", function_name);
818        Ok(function)
819    }
820
821    pub(crate) fn create_tail_call_function(
822        &mut self,
823        function_name: &str,
824    ) -> Result<FunctionValue<'ctx>> {
825        let i32_type = self.context.i32_type();
826        let ptr_type = self.context.ptr_type(AddressSpace::default());
827        let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
828        let function = self.module.add_function(function_name, fn_type, None);
829        function.set_section(Some("uprobe"));
830
831        let basic_block = self.context.append_basic_block(function, "entry");
832        self.builder.position_at_end(basic_block);
833
834        let key_arr_ty = i32_type.array_type(4);
835        let key_alloca = self
836            .builder
837            .build_alloca(key_arr_ty, "pm_key")
838            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
839        self.pm_key_alloca = Some(key_alloca);
840
841        info!("Created tail-call eBPF function: {}", function_name);
842        Ok(function)
843    }
844
845    /// Add PID filtering logic to the current function.
846    /// This generates LLVM IR to check PID and early-return if not matching.
847    fn add_pid_filter(&mut self, spec: crate::PidFilterSpec) -> Result<()> {
848        match spec {
849            crate::PidFilterSpec::HostTgid { filter_pid } => self.add_host_pid_filter(filter_pid),
850            crate::PidFilterSpec::NamespaceTgid { filter_pid, pid_ns } => {
851                let (pid_ns_dev, pid_ns_inode) = pid_ns.helper_dev_inode().ok_or_else(|| {
852                    CodeGenError::LLVMError(
853                        "Namespace TGID filter requires pid namespace device id".to_string(),
854                    )
855                })?;
856                self.add_namespace_pid_filter(filter_pid, pid_ns_dev, pid_ns_inode)
857            }
858        }
859    }
860
861    fn add_host_pid_filter(&mut self, filter_pid: u32) -> Result<()> {
862        info!("Adding host TGID filter for filter PID: {}", filter_pid);
863
864        // Get current function and entry block
865        let current_fn = self.current_function("add host pid filter")?;
866
867        // Create basic blocks for control flow
868        let continue_block = self
869            .context
870            .append_basic_block(current_fn, "continue_execution");
871        let early_return_block = self
872            .context
873            .append_basic_block(current_fn, "pid_mismatch_return");
874
875        // Get current PID/TID using bpf_get_current_pid_tgid helper
876        let pid_tgid_value = self.get_current_pid_tgid()?;
877
878        // Extract TGID (high 32 bits) by right shifting 32 bits
879        let shift_amount = self.context.i64_type().const_int(32, false);
880        let current_tgid = self
881            .builder
882            .build_right_shift(pid_tgid_value, shift_amount, false, "current_tgid")
883            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
884
885        // Convert filter_pid to i64 and compare
886        let target_pid_value = self.context.i64_type().const_int(filter_pid as u64, false);
887        let pid_matches = self
888            .builder
889            .build_int_compare(
890                inkwell::IntPredicate::EQ,
891                current_tgid,
892                target_pid_value,
893                "pid_matches",
894            )
895            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
896
897        // Conditional branch: if pid matches, continue; else early return
898        self.builder
899            .build_conditional_branch(pid_matches, continue_block, early_return_block)
900            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
901
902        // Early return block - just return 0
903        self.builder.position_at_end(early_return_block);
904        self.builder
905            .build_return(Some(&self.context.i32_type().const_int(0, false)))
906            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
907
908        // Position at continue block for the rest of the function
909        self.builder.position_at_end(continue_block);
910
911        info!(
912            "Host TGID filter added successfully for filter PID: {}",
913            filter_pid
914        );
915        Ok(())
916    }
917
918    fn add_namespace_pid_filter(
919        &mut self,
920        filter_pid: u32,
921        pid_ns_dev: u64,
922        pid_ns_inode: u64,
923    ) -> Result<()> {
924        const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
925        const BPF_PIDNS_INFO_SIZE: u64 = 8; // struct { u32 pid; u32 tgid; }
926
927        info!(
928            "Adding namespace TGID filter: filter_pid={} ns_dev={} ns_inode={}",
929            filter_pid, pid_ns_dev, pid_ns_inode
930        );
931
932        let current_fn = self
933            .builder
934            .get_insert_block()
935            .ok_or_else(|| CodeGenError::Builder("No current insert block".to_string()))?
936            .get_parent()
937            .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
938
939        let helper_ok_block = self
940            .context
941            .append_basic_block(current_fn, "pidns_helper_ok");
942        let continue_block = self
943            .context
944            .append_basic_block(current_fn, "continue_execution");
945        let early_return_block = self
946            .context
947            .append_basic_block(current_fn, "pid_mismatch_return");
948
949        let i32_type = self.context.i32_type();
950        let i64_type = self.context.i64_type();
951        let ptr_type = self.context.ptr_type(AddressSpace::default());
952
953        // Stack-allocate bpf_pidns_info-compatible storage: [pid:u32, tgid:u32].
954        let pidns_info_ty = i32_type.array_type(2);
955        let pidns_info_alloca = self
956            .builder
957            .build_alloca(pidns_info_ty, "pidns_info")
958            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
959        self.builder
960            .build_store(pidns_info_alloca, pidns_info_ty.const_zero())
961            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
962
963        let pidns_info_ptr = self
964            .builder
965            .build_bit_cast(pidns_info_alloca, ptr_type, "pidns_info_ptr")
966            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
967
968        let helper_args = [
969            i64_type.const_int(pid_ns_dev, false).into(),
970            i64_type.const_int(pid_ns_inode, false).into(),
971            pidns_info_ptr,
972            i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
973        ];
974        let helper_ret = self.create_bpf_helper_call(
975            BPF_FUNC_GET_NS_CURRENT_PID_TGID,
976            &helper_args,
977            i64_type.into(),
978            "ns_pid_tgid_ret",
979        )?;
980        let helper_ret = match helper_ret {
981            inkwell::values::BasicValueEnum::IntValue(v) => v,
982            _ => {
983                return Err(CodeGenError::LLVMError(
984                    "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
985                ));
986            }
987        };
988
989        let helper_ok = self
990            .builder
991            .build_int_compare(
992                inkwell::IntPredicate::EQ,
993                helper_ret,
994                i64_type.const_zero(),
995                "pidns_helper_ok",
996            )
997            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
998        self.builder
999            .build_conditional_branch(helper_ok, helper_ok_block, early_return_block)
1000            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1001
1002        self.builder.position_at_end(helper_ok_block);
1003        // SAFETY: pidns_info_alloca has the pid namespace helper result layout
1004        // [pid, tgid], so [0, 1] addresses the tgid field.
1005        let tgid_ptr = unsafe {
1006            self.builder.build_gep(
1007                pidns_info_ty,
1008                pidns_info_alloca,
1009                &[i32_type.const_zero(), i32_type.const_int(1, false)],
1010                "pidns_tgid_ptr",
1011            )
1012        }
1013        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1014        let ns_tgid = self
1015            .builder
1016            .build_load(i32_type, tgid_ptr, "ns_tgid")
1017            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1018            .into_int_value();
1019        let ns_tgid_i64 = self
1020            .builder
1021            .build_int_z_extend(ns_tgid, i64_type, "ns_tgid_i64")
1022            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1023        let target_pid_value = i64_type.const_int(filter_pid as u64, false);
1024        let pid_matches = self
1025            .builder
1026            .build_int_compare(
1027                inkwell::IntPredicate::EQ,
1028                ns_tgid_i64,
1029                target_pid_value,
1030                "pid_matches_ns",
1031            )
1032            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1033        self.builder
1034            .build_conditional_branch(pid_matches, continue_block, early_return_block)
1035            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1036
1037        self.builder.position_at_end(early_return_block);
1038        self.builder
1039            .build_return(Some(&self.context.i32_type().const_int(0, false)))
1040            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1041
1042        self.builder.position_at_end(continue_block);
1043        info!(
1044            "Namespace TGID filter added successfully for filter PID: {}",
1045            filter_pid
1046        );
1047        Ok(())
1048    }
1049
1050    /// Get or create a global i8 flag by name, initialized to 0
1051    pub fn get_or_create_flag_global(&mut self, name: &str) -> PointerValue<'ctx> {
1052        if let Some(g) = self.module.get_global(name) {
1053            return g.as_pointer_value();
1054        }
1055        let i8_type = self.context.i8_type();
1056        let global = self
1057            .module
1058            .add_global(i8_type, Some(AddressSpace::default()), name);
1059        global.set_initializer(&i8_type.const_zero());
1060        global.as_pointer_value()
1061    }
1062
1063    /// Set a flag global to a constant u8 value at runtime
1064    pub fn store_flag_value(&mut self, name: &str, value: u8) -> Result<()> {
1065        let ptr = self.get_or_create_flag_global(name);
1066        self.builder
1067            .build_store(ptr, self.context.i8_type().const_int(value as u64, false))
1068            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flag {name}: {e}")))?;
1069        Ok(())
1070    }
1071
1072    /// Mark that at least one variable succeeded (status==0)
1073    pub fn mark_any_success(&mut self) -> Result<()> {
1074        self.store_flag_value("_gs_any_success", 1)
1075    }
1076
1077    /// Mark that at least one variable failed (status!=0)
1078    pub fn mark_any_fail(&mut self) -> Result<()> {
1079        self.store_flag_value("_gs_any_fail", 1)
1080    }
1081
1082    /// Get or create global for condition error code (i8). Name: _gs_cond_error
1083    pub fn get_or_create_cond_error_global(&mut self) -> PointerValue<'ctx> {
1084        if let Some(g) = self.module.get_global("_gs_cond_error") {
1085            return g.as_pointer_value();
1086        }
1087        let i8_type = self.context.i8_type();
1088        let global =
1089            self.module
1090                .add_global(i8_type, Some(AddressSpace::default()), "_gs_cond_error");
1091        global.set_initializer(&i8_type.const_zero());
1092        global.as_pointer_value()
1093    }
1094
1095    /// Reset condition error to 0 (only meaningful when condition_context_active=true)
1096    pub fn reset_condition_error(&mut self) -> Result<()> {
1097        let ptr = self.get_or_create_cond_error_global();
1098        self.builder
1099            .build_store(ptr, self.context.i8_type().const_zero())
1100            .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error: {e}")))?;
1101        // Also reset error address
1102        let aptr = self.get_or_create_cond_error_addr_global();
1103        self.builder
1104            .build_store(aptr, self.context.i64_type().const_zero())
1105            .map_err(|e| {
1106                CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_addr: {e}"))
1107            })?;
1108        // Also reset flags
1109        let fptr = self.get_or_create_cond_error_flags_global();
1110        self.builder
1111            .build_store(fptr, self.context.i8_type().const_zero())
1112            .map_err(|e| {
1113                CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_flags: {e}"))
1114            })?;
1115        Ok(())
1116    }
1117
1118    /// Get or create global for condition error address (i64). Name: _gs_cond_error_addr
1119    pub fn get_or_create_cond_error_addr_global(&mut self) -> PointerValue<'ctx> {
1120        if let Some(g) = self.module.get_global("_gs_cond_error_addr") {
1121            return g.as_pointer_value();
1122        }
1123        let i64_type = self.context.i64_type();
1124        let global = self.module.add_global(
1125            i64_type,
1126            Some(AddressSpace::default()),
1127            "_gs_cond_error_addr",
1128        );
1129        global.set_initializer(&i64_type.const_zero());
1130        global.as_pointer_value()
1131    }
1132
1133    /// If in condition context, set error code when it's currently 0 (first error wins)
1134    pub fn set_condition_error_if_unset(&mut self, code: u8) -> Result<()> {
1135        if !self.condition_context_active {
1136            return Ok(());
1137        }
1138        let ptr = self.get_or_create_cond_error_global();
1139        let cur = self
1140            .builder
1141            .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1142            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1143            .into_int_value();
1144        let is_zero = self
1145            .builder
1146            .build_int_compare(
1147                inkwell::IntPredicate::EQ,
1148                cur,
1149                self.context.i8_type().const_zero(),
1150                "cond_err_is_zero",
1151            )
1152            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1153        let newv_bv: inkwell::values::BasicValueEnum =
1154            self.context.i8_type().const_int(code as u64, false).into();
1155        let sel = self
1156            .builder
1157            .build_select::<inkwell::values::BasicValueEnum, _>(
1158                is_zero,
1159                newv_bv,
1160                cur.into(),
1161                "cond_err_new",
1162            )
1163            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1164        self.builder
1165            .build_store(ptr, sel)
1166            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1167        Ok(())
1168    }
1169
1170    /// Read the current condition error as i1 predicate: (error != 0)
1171    pub fn build_condition_error_predicate(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
1172        let ptr = self.get_or_create_cond_error_global();
1173        let cur = self
1174            .builder
1175            .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1176            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1177            .into_int_value();
1178        self.builder
1179            .build_int_compare(
1180                inkwell::IntPredicate::NE,
1181                cur,
1182                self.context.i8_type().const_zero(),
1183                "cond_err_nonzero",
1184            )
1185            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
1186    }
1187
1188    /// Get or create global for condition error flags (i8). Name: _gs_cond_error_flags
1189    pub fn get_or_create_cond_error_flags_global(&mut self) -> PointerValue<'ctx> {
1190        if let Some(g) = self.module.get_global("_gs_cond_error_flags") {
1191            return g.as_pointer_value();
1192        }
1193        let i8_type = self.context.i8_type();
1194        let global = self.module.add_global(
1195            i8_type,
1196            Some(AddressSpace::default()),
1197            "_gs_cond_error_flags",
1198        );
1199        global.set_initializer(&i8_type.const_zero());
1200        global.as_pointer_value()
1201    }
1202
1203    /// OR into condition error flags (BV must be i8)
1204    pub fn or_condition_error_flags(&mut self, flags: IntValue<'ctx>) -> Result<()> {
1205        if !self.condition_context_active {
1206            return Ok(());
1207        }
1208        let ptr = self.get_or_create_cond_error_flags_global();
1209        let cur = self
1210            .builder
1211            .build_load(self.context.i8_type(), ptr, "cond_err_flags_cur")
1212            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1213            .into_int_value();
1214        let newv = self
1215            .builder
1216            .build_or(cur, flags, "cond_err_flags_or")
1217            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1218        self.builder
1219            .build_store(ptr, newv)
1220            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1221        Ok(())
1222    }
1223
1224    /// If in condition context, record failing address (first win). addr must be i64
1225    pub fn set_condition_error_addr_if_unset(&mut self, addr: IntValue<'ctx>) -> Result<()> {
1226        if !self.condition_context_active {
1227            return Ok(());
1228        }
1229        let ptr = self.get_or_create_cond_error_addr_global();
1230        let cur = self
1231            .builder
1232            .build_load(self.context.i64_type(), ptr, "cond_err_addr_cur")
1233            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1234            .into_int_value();
1235        let is_zero = self
1236            .builder
1237            .build_int_compare(
1238                inkwell::IntPredicate::EQ,
1239                cur,
1240                self.context.i64_type().const_zero(),
1241                "cond_err_addr_is_zero",
1242            )
1243            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1244        let sel = self
1245            .builder
1246            .build_select::<IntValue<'ctx>, _>(is_zero, addr, cur, "cond_err_addr_new")
1247            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1248        self.builder
1249            .build_store(ptr, sel)
1250            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1251        Ok(())
1252    }
1253}