use anyhow::Result;
use ghostscope_loader::GhostScopeLoader;
use tracing::{error, info, warn};
#[derive(Debug)]
pub struct TraceInstance {
pub trace_id: u32,
pub target: String, pub script_content: String, pub binary_path: String, pub target_display: String, pub pc: u64, pub target_pid: Option<u32>, pub is_enabled: bool, pub loader: Option<GhostScopeLoader>, pub ebpf_function_name: String, pub address_global_index: Option<usize>, }
pub struct TraceInstanceArgs {
pub trace_id: u32,
pub target: String,
pub script_content: String,
pub pc: u64,
pub binary_path: String,
pub target_display: String,
pub target_pid: Option<u32>,
pub loader: Option<ghostscope_loader::GhostScopeLoader>,
pub ebpf_function_name: String,
pub address_global_index: Option<usize>,
}
impl TraceInstance {
pub fn new(args: TraceInstanceArgs) -> Self {
Self {
trace_id: args.trace_id,
target: args.target,
script_content: args.script_content,
pc: args.pc,
binary_path: args.binary_path,
target_display: args.target_display,
target_pid: args.target_pid,
is_enabled: false,
loader: args.loader,
ebpf_function_name: args.ebpf_function_name,
address_global_index: args.address_global_index,
}
}
pub fn enable(&mut self) -> Result<()> {
if self.is_enabled {
info!("Trace {} is already enabled", self.trace_id);
Ok(())
} else if let Some(ref mut loader) = self.loader {
let already_attached = loader.is_uprobe_attached();
if !already_attached {
info!(
"Enabling trace {} for target '{}' at PC 0x{:x} in binary '{}'",
self.trace_id, self.target_display, self.pc, self.binary_path
);
}
if already_attached {
self.is_enabled = true;
Ok(())
} else if loader.get_attachment_info().is_some() {
info!(
"Re-attaching uprobe for trace {} (program already loaded)",
self.trace_id
);
match loader.reattach_uprobe() {
Ok(_) => {
info!(
"✓ Successfully re-attached uprobe for trace {}",
self.trace_id
);
self.is_enabled = true;
Ok(())
}
Err(e) => {
error!(
"❌ Failed to re-attach uprobe for trace {}: {}",
self.trace_id, e
);
Err(anyhow::anyhow!("Failed to re-attach uprobe: {}", e))
}
}
} else {
info!(
"Attaching uprobe for trace {} at offset 0x{:x} using program '{}'",
self.trace_id, self.pc, self.ebpf_function_name
);
match loader.attach_uprobe(
&self.binary_path,
&self.ebpf_function_name,
Some(self.pc),
self.target_pid.map(|pid| pid as i32),
) {
Ok(_) => {
info!(
"✓ Successfully attached uprobe for trace {} at offset 0x{:x}",
self.trace_id, self.pc
);
self.is_enabled = true;
Ok(())
}
Err(e) => {
error!(
"❌ Failed to attach uprobe for trace {}: {}",
self.trace_id, e
);
Err(anyhow::anyhow!("Failed to attach uprobe: {}", e))
}
}
}
} else {
error!("No eBPF loader available for trace {}", self.trace_id);
Err(anyhow::anyhow!("No eBPF loader available"))
}
}
pub fn disable(&mut self) -> Result<()> {
if !self.is_enabled {
info!("Trace {} is already disabled", self.trace_id);
return Ok(());
}
info!(
"Disabling trace {} for target '{}' at PC 0x{:x}",
self.trace_id, self.target_display, self.pc
);
if let Some(ref mut loader) = self.loader {
match loader.detach_uprobe() {
Ok(_) => {
info!("✓ Successfully detached uprobe for trace {}", self.trace_id);
self.is_enabled = false;
Ok(())
}
Err(e) => {
error!(
"❌ Failed to detach uprobe for trace {}: {}",
self.trace_id, e
);
Err(anyhow::anyhow!("Failed to detach uprobe: {}", e))
}
}
} else {
warn!(
"No eBPF loader available for trace {}, marking as disabled",
self.trace_id
);
self.is_enabled = false;
Ok(())
}
}
pub async fn wait_for_events_async(
&mut self,
) -> Result<Vec<ghostscope_protocol::ParsedTraceEvent>> {
if !self.is_enabled {
return Ok(Vec::new());
}
if let Some(ref mut loader) = self.loader {
match loader.wait_for_events_async().await {
Ok(events) => Ok(events),
Err(e) => {
warn!(
"Error waiting for events from trace {}: {}",
self.trace_id, e
);
Err(e.into())
}
}
} else {
error!("No eBPF loader available for trace {}", self.trace_id);
Err(anyhow::anyhow!("No eBPF loader available"))
}
}
}