Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
387 changes: 346 additions & 41 deletions ghostscope-compiler/src/ebpf/codegen/backtrace.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion ghostscope-compiler/src/ebpf/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ pub struct BacktraceTailCallProgram {
#[derive(Debug, Clone)]
pub(crate) struct PendingBacktraceTailCall {
pub step_program_name: String,
pub module_cookie: u64,
pub depth: u8,
pub instruction_size: usize,
}
Expand Down Expand Up @@ -162,6 +161,7 @@ pub struct EbpfContext<'ctx, 'dw> {

// === DWARF compact unwind rows for bt ===
pub backtrace_unwind_rows: Vec<ghostscope_protocol::BacktraceUnwindRow>,
pub(crate) backtrace_module_cookies: Vec<u64>,
pub(crate) backtrace_unwind_rows_use_runtime_pcs: bool,
pub(crate) backtrace_tail_call_slots: u8,
pub(crate) next_backtrace_tail_call_slot: u8,
Expand Down Expand Up @@ -273,6 +273,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
string_vars: HashMap::new(),
// Backtrace compact unwind rows
backtrace_unwind_rows: Vec::new(),
backtrace_module_cookies: Vec::new(),
backtrace_unwind_rows_use_runtime_pcs: false,
backtrace_tail_call_slots: 1,
next_backtrace_tail_call_slot: 0,
Expand Down
222 changes: 156 additions & 66 deletions ghostscope-compiler/src/ebpf/helper_functions.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ghostscope-compiler/src/ebpf/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ impl<'ctx> MapManager<'ctx> {
max_entries: u64,
) -> Result<()> {
// Key: {pid:u32, pad:u32, cookie:u64} => 16 bytes => 128 bits
// Value: {text, rodata, data, bss: u64} => 32 bytes => 256 bits
// Value: {text, rodata, data, bss, base, size: u64} => 48 bytes => 384 bits
self.create_map_definition(
module,
di_builder,
Expand All @@ -268,7 +268,7 @@ impl<'ctx> MapManager<'ctx> {
BpfMapType::Hash,
max_entries,
SizedType::integer(128),
SizedType::integer(256),
SizedType::integer(384),
)
}

Expand Down
21 changes: 18 additions & 3 deletions ghostscope-process/src/offsets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct CachedEntry {
pid: u32,
cookie: u64,
offsets: SectionOffsets,
base: u64,
size: u64,
}

#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -255,11 +257,13 @@ impl ProcessManager {
3,
std::time::Duration::from_millis(75),
) {
Ok((cookie, offsets, _base, _size)) => {
Ok((cookie, offsets, base, size)) => {
cached.push(CachedEntry {
pid,
cookie,
offsets,
base,
size,
});
new_count += 1;
}
Expand All @@ -276,10 +280,17 @@ impl ProcessManager {
Ok(new_count)
}

pub fn cached_offsets_for_module(&self, module_path: &str) -> Vec<(u32, u64, SectionOffsets)> {
pub fn cached_offsets_for_module(
&self,
module_path: &str,
) -> Vec<(u32, u64, SectionOffsets, u64, u64)> {
self.module_cache
.get(module_path)
.map(|v| v.iter().map(|e| (e.pid, e.cookie, e.offsets)).collect())
.map(|v| {
v.iter()
.map(|e| (e.pid, e.cookie, e.offsets, e.base, e.size))
.collect()
})
.unwrap_or_default()
}

Expand Down Expand Up @@ -649,11 +660,15 @@ mod tests {
pid: 42,
cookie: 1,
offsets: SectionOffsets::default(),
base: 0,
size: 0,
},
CachedEntry {
pid: 7,
cookie: 2,
offsets: SectionOffsets::default(),
base: 0,
size: 0,
},
],
);
Expand Down
52 changes: 38 additions & 14 deletions ghostscope-process/src/pinned_bpf_maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,19 @@ pub fn bpffs_mount_hint_for_pin_path(pin_path: &Path) -> Option<String> {

pub use ghostscope_protocol::{PidAliasValue, ProcModuleKey, ProcModuleOffsetsValue};

fn proc_offsets_pin_layout_matches(map: &MapData) -> bool {
match map.info() {
Ok(info) => {
info.key_size() == ghostscope_protocol::PROC_MODULE_KEY_SIZE as u32
&& info.value_size() == ghostscope_protocol::PROC_MODULE_OFFSETS_VALUE_SIZE as u32
}
Err(e) => {
warn!("Unable to inspect pinned proc_module_offsets map layout: {e}");
false
}
}
}

fn ensure_pin_dir(path: &Path) -> std::io::Result<()> {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir)
Expand All @@ -232,17 +245,28 @@ pub fn ensure_pinned_proc_offsets_exists(max_entries: u32) -> anyhow::Result<()>
)
})?;

// If pinned file already exists, try to reuse it directly (idempotent)
// If pinned file already exists, reuse it only when the ABI layout matches.
if pin_path.exists() {
if MapData::from_pin(&pin_path).is_ok() {
info!(
"Reusing existing pinned map at {} (no recreate)",
pin_path.display()
);
return Ok(());
} else {
// Stale/corrupted pin path, remove and recreate
let _ = std::fs::remove_file(&pin_path);
match MapData::from_pin(&pin_path) {
Ok(map) if proc_offsets_pin_layout_matches(&map) => {
info!(
"Reusing existing pinned map at {} (layout ok)",
pin_path.display()
);
return Ok(());
}
Ok(_) => {
warn!(
"Pinned {} at {} has stale ABI layout; recreating",
PROC_OFFSETS_MAP_NAME,
pin_path.display()
);
let _ = std::fs::remove_file(&pin_path);
}
Err(_) => {
// Stale/corrupted pin path, remove and recreate
let _ = std::fs::remove_file(&pin_path);
}
}
}

Expand Down Expand Up @@ -280,15 +304,15 @@ pub fn ensure_pinned_proc_offsets_exists(max_entries: u32) -> anyhow::Result<()>
Err(e) => {
// If another thread/process pinned concurrently, reuse the existing pin
match MapData::from_pin(&pin_path) {
Ok(_) => {
Ok(map) if proc_offsets_pin_layout_matches(&map) => {
info!(
"Pin path {} already exists; reusing existing map ({}).",
pin_path.display(),
e
);
Ok(())
}
Err(_) => {
Ok(_) | Err(_) => {
// Best-effort cleanup and propagate error
let _ = std::fs::remove_file(&pin_path);
let hint = bpffs_mount_hint_for_pin_path(&pin_path)
Expand Down Expand Up @@ -580,8 +604,8 @@ pub fn insert_offsets_for_pid(
match map.insert(key, *off, 0) {
Ok(()) => {
tracing::debug!(
"proc_module_offsets insert ok: pid={} cookie=0x{:08x}{:08x} text=0x{:x} rodata=0x{:x} data=0x{:x} bss=0x{:x}",
pid, key.cookie_hi, key.cookie_lo, off.text, off.rodata, off.data, off.bss
"proc_module_offsets insert ok: pid={} cookie=0x{:08x}{:08x} text=0x{:x} rodata=0x{:x} data=0x{:x} bss=0x{:x} base=0x{:x} size=0x{:x}",
pid, key.cookie_hi, key.cookie_lo, off.text, off.rodata, off.data, off.bss, off.base, off.size
);
inserted += 1
}
Expand Down
20 changes: 14 additions & 6 deletions ghostscope-process/src/sysmon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,10 +634,12 @@ fn run_sysmon_loop(
use std::collections::HashMap;
let mut by_pid: HashMap<u32, Vec<(u64, ProcModuleOffsetsValue)>> =
HashMap::new();
for (pid, cookie, off) in entries {
for (pid, cookie, off, base, size) in entries {
by_pid.entry(pid).or_default().push((
cookie,
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss),
ProcModuleOffsetsValue::new(
off.text, off.rodata, off.data, off.bss, base, size,
),
));
}
let mut total = 0usize;
Expand Down Expand Up @@ -954,10 +956,14 @@ fn prefill_offsets_for_pid(
}
let mut by_pid: HashMap<u32, Vec<(u64, ProcModuleOffsetsValue)>> =
HashMap::new();
for (pid, cookie, off) in guard.cached_offsets_for_module(&module_path) {
for (pid, cookie, off, base, size) in
guard.cached_offsets_for_module(&module_path)
{
by_pid.entry(pid).or_default().push((
cookie,
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss),
ProcModuleOffsetsValue::new(
off.text, off.rodata, off.data, off.bss, base, size,
),
));
}
for (pid, items) in by_pid {
Expand Down Expand Up @@ -1023,6 +1029,8 @@ fn prefill_offsets_for_pid(
e.offsets.rodata,
e.offsets.data,
e.offsets.bss,
e.base,
e.size,
),
)
})
Expand Down Expand Up @@ -1094,10 +1102,10 @@ fn refresh_target_module_offsets(
);
return;
}
for (pid, cookie, off) in guard.cached_offsets_for_module(&module_path) {
for (pid, cookie, off, base, size) in guard.cached_offsets_for_module(&module_path) {
by_pid.entry(pid).or_default().push((
cookie,
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss),
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss, base, size),
));
}
}
Expand Down
14 changes: 12 additions & 2 deletions ghostscope-protocol/src/bpf_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,19 @@ pub struct ProcModuleOffsetsValue {
pub rodata: u64,
pub data: u64,
pub bss: u64,
pub base: u64,
pub size: u64,
}

impl ProcModuleOffsetsValue {
pub fn new(text: u64, rodata: u64, data: u64, bss: u64) -> Self {
pub fn new(text: u64, rodata: u64, data: u64, bss: u64, base: u64, size: u64) -> Self {
Self {
text,
rodata,
data,
bss,
base,
size,
}
}
}
Expand All @@ -59,6 +63,10 @@ pub const PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET: usize =
std::mem::offset_of!(ProcModuleOffsetsValue, data);
pub const PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET: usize =
std::mem::offset_of!(ProcModuleOffsetsValue, bss);
pub const PROC_MODULE_OFFSETS_VALUE_BASE_OFFSET: usize =
std::mem::offset_of!(ProcModuleOffsetsValue, base);
pub const PROC_MODULE_OFFSETS_VALUE_SIZE_OFFSET: usize =
std::mem::offset_of!(ProcModuleOffsetsValue, size);
pub const PROC_MODULE_OFFSETS_VALUE_SIZE: usize = std::mem::size_of::<ProcModuleOffsetsValue>();

/// Value for the pinned `pid_aliases` map.
Expand Down Expand Up @@ -263,11 +271,13 @@ mod tests {
assert_eq!(PROC_MODULE_KEY_COOKIE_LO_OFFSET, 8);
assert_eq!(PROC_MODULE_KEY_COOKIE_HI_OFFSET, 12);

assert_eq!(PROC_MODULE_OFFSETS_VALUE_SIZE, 32);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_SIZE, 48);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET, 0);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET, 8);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET, 16);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET, 24);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_BASE_OFFSET, 32);
assert_eq!(PROC_MODULE_OFFSETS_VALUE_SIZE_OFFSET, 40);

assert_eq!(PID_ALIAS_VALUE_SIZE, 4);
assert_eq!(PID_ALIAS_VALUE_PROC_PID_OFFSET, 0);
Expand Down
7 changes: 4 additions & 3 deletions ghostscope-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ pub use bpf_abi::{
BACKTRACE_UNWIND_WORD_RBP_OFFSET, BACKTRACE_UNWIND_WORD_REGISTERS,
PID_ALIAS_VALUE_PROC_PID_OFFSET, PID_ALIAS_VALUE_SIZE, PROC_MODULE_KEY_COOKIE_HI_OFFSET,
PROC_MODULE_KEY_COOKIE_LO_OFFSET, PROC_MODULE_KEY_PAD_OFFSET, PROC_MODULE_KEY_PID_OFFSET,
PROC_MODULE_KEY_SIZE, PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET,
PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET, PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET,
PROC_MODULE_OFFSETS_VALUE_SIZE, PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET,
PROC_MODULE_KEY_SIZE, PROC_MODULE_OFFSETS_VALUE_BASE_OFFSET,
PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET, PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET,
PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET, PROC_MODULE_OFFSETS_VALUE_SIZE,
PROC_MODULE_OFFSETS_VALUE_SIZE_OFFSET, PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET,
};

pub use trace_context::TraceContext;
Expand Down
23 changes: 17 additions & 6 deletions ghostscope/src/script/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,25 @@ fn apply_cached_offsets_for_session_pid(
.coordinator
.lock()
.expect("coordinator mutex poisoned");
coordinator.cached_offsets_pairs_for_pid(proc_pid)
coordinator
.cached_offsets_with_paths_for_pid(proc_pid)
.map(|entries| entries.to_vec())
};
if let Some(items) = items {
use ghostscope_process::pinned_bpf_maps::ProcModuleOffsetsValue;
let adapted: Vec<(u64, ProcModuleOffsetsValue)> = items
.iter()
.map(|(cookie, off)| {
.map(|entry| {
(
*cookie,
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss),
entry.cookie,
ProcModuleOffsetsValue::new(
entry.offsets.text,
entry.offsets.rodata,
entry.offsets.data,
entry.offsets.bss,
entry.base,
entry.size,
),
)
})
.collect();
Expand Down Expand Up @@ -214,10 +223,12 @@ async fn create_and_attach_loader(
// Group by pid for efficient batch insert
use std::collections::HashMap;
let mut by_pid: HashMap<u32, Vec<(u64, ProcModuleOffsetsValue)>> = HashMap::new();
for (pid, cookie, off) in entries {
for (pid, cookie, off, base, size) in entries {
by_pid.entry(pid).or_default().push((
cookie,
ProcModuleOffsetsValue::new(off.text, off.rodata, off.data, off.bss),
ProcModuleOffsetsValue::new(
off.text, off.rodata, off.data, off.bss, base, size,
),
));
}
let mut total = 0usize;
Expand Down
Loading