ghostscope_compiler/
lib.rs1pub mod ebpf;
5pub mod script; use crate::script::compiler::AstCompiler;
11use ebpf::context::CodeGenError;
12pub use ghostscope_process::{PidFilterSpec, PidNamespaceId};
13use script::parser::ParseError;
14use tracing::info;
15
16pub fn hello() -> &'static str {
17 "Hello from ghostscope-compiler!"
18}
19
20#[derive(Debug, thiserror::Error)]
21pub enum CompileError {
22 #[error("Parse error: {0}")]
23 Parse(#[from] Box<ParseError>),
24
25 #[error("Code generation error: {0}")]
26 CodeGen(#[from] CodeGenError),
27
28 #[error("LLVM error: {0}")]
29 LLVM(String),
30
31 #[error("{0}")]
32 Other(String),
33}
34
35pub type Result<T> = std::result::Result<T, CompileError>;
36
37impl From<ParseError> for CompileError {
38 fn from(err: ParseError) -> Self {
39 CompileError::Parse(Box::new(err))
40 }
41}
42
43pub use script::compiler::{CompilationResult, UProbeConfig};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum EventMapType {
49 RingBuf,
51 PerfEventArray,
53}
54
55#[derive(Debug, Clone)]
57pub struct CompileOptions {
58 pub save_llvm_ir: bool,
59 pub save_ebpf: bool,
60 pub save_ast: bool,
61 pub binary_path_hint: Option<String>,
62 pub ringbuf_size: u64,
63 pub proc_module_offsets_max_entries: u64,
64 pub perf_page_count: u32,
65 pub event_map_type: EventMapType,
66 pub mem_dump_cap: u32,
68 pub compare_cap: u32,
70 pub max_trace_event_size: u32,
72 pub selected_index: Option<usize>,
75 pub pid_filter_spec: Option<PidFilterSpec>,
78 pub special_pid_ns: Option<PidNamespaceId>,
81 pub proc_offsets_pid_ns: Option<PidNamespaceId>,
92 pub input_pid: Option<u32>,
95}
96
97impl Default for CompileOptions {
98 fn default() -> Self {
99 Self {
100 save_llvm_ir: false,
101 save_ebpf: false,
102 save_ast: false,
103 binary_path_hint: None,
104 ringbuf_size: 262144, proc_module_offsets_max_entries: 4096, perf_page_count: 64, event_map_type: EventMapType::RingBuf, mem_dump_cap: 256, compare_cap: 64, max_trace_event_size: 32768, selected_index: None,
112 pid_filter_spec: None,
113 special_pid_ns: None,
114 proc_offsets_pid_ns: None,
115 input_pid: None,
116 }
117 }
118}
119
120pub fn compile_script(
125 script_source: &str,
126 process_analyzer: &ghostscope_dwarf::DwarfAnalyzer,
127 pid: Option<u32>,
128 trace_id: Option<u32>,
129 compile_options: &CompileOptions,
130) -> Result<CompilationResult> {
131 info!("Starting unified script compilation with DwarfAnalyzer (multi-module support)");
132
133 let program = script::parser::parse(script_source)?;
135 info!("Parsed script with {} statements", program.statements.len());
136
137 let mut compiler = AstCompiler::new(
139 Some(process_analyzer),
140 compile_options.binary_path_hint.clone(),
141 trace_id.unwrap_or(0), compile_options.clone(),
143 );
144
145 let result = compiler.compile_program(&program, pid)?;
147
148 if result.uprobe_configs.is_empty() {
149 if !result.failed_targets.is_empty() {
150 tracing::warn!(
151 "Compilation produced 0 uprobe configs; {} target(s) failed to compile",
152 result.failed_targets.len()
153 );
154 } else {
155 tracing::warn!(
156 "Compilation completed with 0 uprobe configs (no attachable targets resolved)"
157 );
158 }
159 } else {
160 info!(
161 "Successfully compiled script: {} trace points, {} uprobe configs",
162 result.trace_count,
163 result.uprobe_configs.len()
164 );
165 }
166
167 info!(
169 "Compilation summary: trace_points={}, uprobe_configs={}, failed_targets={}",
170 result.trace_count,
171 result.uprobe_configs.len(),
172 result.failed_targets.len()
173 );
174
175 Ok(result)
176}
177
178pub fn print_ast(program: &crate::script::Program) {
180 info!("\n=== AST Tree ===");
181 info!("Program:");
182 for (i, stmt) in program.statements.iter().enumerate() {
183 info!(" Statement {}: {:?}", i, stmt);
184 }
185 info!("=== End AST Tree ===\n");
186}
187
188pub fn save_ast_to_file(program: &crate::script::Program, filename: &str) -> Result<()> {
190 let mut ast_content = String::new();
191 ast_content.push_str("=== AST Tree ===\n");
192 ast_content.push_str("Program:\n");
193 for (i, stmt) in program.statements.iter().enumerate() {
194 ast_content.push_str(&format!(" Statement {i}: {stmt:?}\n"));
195 }
196 ast_content.push_str("=== End AST Tree ===\n");
197
198 let file_path = format!("{filename}.txt");
199 std::fs::write(&file_path, ast_content)
200 .map_err(|e| CompileError::Other(format!("Failed to save AST file '{file_path}': {e}")))?;
201
202 Ok(())
203}
204
205pub fn format_ebpf_bytecode(bytecode: &[u8]) -> String {
207 bytecode
208 .iter()
209 .map(|byte| format!("{byte:02x}"))
210 .collect::<Vec<String>>()
211 .join(" ")
212}
213
214pub fn generate_file_name_for_ast(pid: Option<u32>, binary_path: Option<&str>) -> String {
216 let pid_part = pid
217 .map(|p| p.to_string())
218 .unwrap_or_else(|| "unknown".to_string());
219 let exec_part = binary_path
220 .and_then(|path| std::path::Path::new(path).file_name())
221 .and_then(|name| name.to_str())
222 .unwrap_or("unknown");
223
224 format!("gs_{pid_part}_{exec_part}_ast")
225}