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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Dockerfile linguist-vendored
# Exclude installer scripts from language statistics
scripts/*.sh linguist-vendored
ghostscope-process/ebpf/build_sysmon_bpf.sh linguist-vendored
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Configuration follows this priority order (highest to lowest):
3. Configuration file (~/.ghostscope/config.toml or ./ghostscope.toml)
4. Default values

> **Note**: GhostScope typically runs under sudo to access tracing functionality. When executed as root, `$HOME` points to `/root` by default, so the user-level config at `~/.ghostscope/config.toml` will not be picked up automatically. Use `ghostscope --config /home/<user>/.ghostscope/config.toml` to load your personal configuration when running with sudo.

## Command-Line Arguments

### Target Selection
Expand Down
40 changes: 24 additions & 16 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
# GhostScope Roadmap (Milestones)

## Global variables in `-t <exec_path>` mode (planned)
- Background: resolving globals requires per-module ASLR offsets computed from `/proc/<pid>/maps`, while `-t` mode has no PID context.
- Direction: introduce PID discovery/subscription in the attach flow, or compute and populate offsets once a PID is known at trigger time; ensure safety and ordering.
- Until this is available, globals remain disabled in `-t` mode (see “Limitations”).
GhostScope is still evolving quickly. The milestones below are ordered from “strengthen core capabilities” to “polish experience” and finally “expand language and deployment coverage”.

## Chained array access (`a.b[idx].c`) and dynamic indices (planned)
- Support constant indices first; later extend to expression-based indices within eBPF verifier limits.
## Chained array access & dynamic indices
- First unlock constant-index access such as `a.b[idx].c`.
- Once verifier limits are well understood, roll out expression-based/dynamic indices in staged fashion.

## Container tracing enhancements
- PID-based tracing inside containerized environments (Docker/WSL) still faces soft limitations; see [Limitations](limitations.md#9-container--wsl-limitations-for--p-pid-mode).
- Once foundational work settles, we’ll revisit improving compatibility in these isolated setups.

## Stack Unwinding
- Capture full call stacks at trace points, implemented via `.eh_frame` parsing.

Reference: https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way#h5.1-parsing-eh_frame-and-eh_frame_hdr-with-gimli
- Capture full call stacks at each trace point by parsing `.eh_frame`/`.eh_frame_hdr`.
- Surface the stack in the TUI with symbol/source awareness.
Reference: <https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way#h5.1-parsing-eh_frame-and-eh_frame_hdr-with-gimli>

## Stability & accuracy
- Keep fixing defects, hardening error handling, and ensuring data consistency.
- Grow automated and regression coverage so the core workflows stay dependable.

## Stability and Accuracy Improvements
- As a debugging tool, continue fixing defects, improving error handling and data consistency, and raising overall reliability.
## Performance via bpftime
- Evaluate switching from kernel uprobes to userspace eBPF with [bpftime](https://github.com/eunomia-bpf/bpftime) to cut context-switch overhead.

## Performance Optimization with bpftime
- Evaluate migrating from kernel uprobe to userspace eBPF via [bpftime](https://github.com/eunomia-bpf/bpftime) to reduce context switches.
## Advanced language features
- Compiled languages: prioritize modern Rust features (async functions, trait objects, etc.).
- Interpreted languages: explore cooperation with runtimes such as Lua to surface variables/stack state.

## Advanced Language Features
- Compiled direction: prioritize Rust advanced features (async functions, trait objects, etc.)
- Interpreted direction: explore tracing support for specific interpreted languages (e.g., Lua)
## Client-server execution model
- Typical scenario: sources and debug info live in the cloud, while binaries run on test rigs or local hosts.
- Goal: keep GhostScope’s TUI on the control side and run the eBPF agent on the target machine, similar to `gdb`/`gdbserver`.
- This will come after the core functionality stabilizes to avoid diluting near-term focus.
2 changes: 2 additions & 0 deletions docs/zh/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ GhostScope 可以通过命令行参数、配置文件和环境变量进行配置
3. 配置文件(~/.ghostscope/config.toml 或 ./ghostscope.toml)
4. 默认值

> **注意**:GhostScope 通常需要以 sudo/root 身份运行以访问跟踪能力。当以 root 运行时,默认的 `$HOME` 指向 `/root`,因此用户目录下的 `~/.ghostscope/config.toml` 不会自动被读取。请使用 `ghostscope --config /home/<用户名>/.ghostscope/config.toml` 来显式指定个人配置文件。

## 命令行参数

### 目标选择
Expand Down
41 changes: 26 additions & 15 deletions docs/zh/roadmap.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
# GhostScope 路线图(里程碑)

## `-t <exec_path>` 模式支持全局变量(计划中):
- 背景:全局变量需要结合 `/proc/<pid>/maps` 计算模块偏移,`-t` 启动时尚无 PID 上下文。
- 方向:在 attach 流程中引入 PID 发现/订阅机制,或在触发时机得到 PID 后再计算并下发偏移;同时确保安全与时序正确。
- 在该能力落地前,`-t` 模式下全局变量保持禁用(见“使用限制”)。
GhostScope 仍处在快速演进阶段,以下里程碑按照“优先修补基础能力 → 打磨使用体验 → 拓展更多语言与部署形态”的顺序规划。

## 链式数组访问(`a.b[idx].c`)与动态下标(计划中):
- 先支持常量下标,后续扩展到表达式下标,在验证 eBPF 验证器可接受的范围内分阶段推出。
## 链式数组访问与动态下标
- 先解锁 `a.b[idx].c` 这类链式访问的常量下标能力。
- 在验证 eBPF 验证器限制后,逐步引入表达式/动态下标,确保性能与安全达标。

## 栈回溯(Stack Unwinding):
- 在追踪点捕获完整调用栈,基于 `.eh_frame` 解析实现。
## 容器追踪增强
- 容器(Docker/WSL 等)环境下的 `-p <pid>` 模式仍存在软限制,详见[限制列表](limitations.md#9-容器-wsl-场景下--pid-pid-模式的软限制)。
- 待核心能力稳定后,会评估如何改进在这些隔离场景中的兼容性。

## 稳定性与准确性改进:
- 作为调试工具,持续修复缺陷、改进错误处理与数据一致性,提升可用性。
## 栈回溯(Stack Unwinding)
- 在每个追踪点捕获完整调用栈,基于 `.eh_frame`/`.eh_frame_hdr` 信息做好解析。
- 结合符号/源信息,TUI 中提供直观的栈帧浏览。
参考资料:[Unwinding the stack the hard way](https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way#h5.1-parsing-eh_frame-and-eh_frame_hdr-with-gimli)

## 使用 bpftime 优化性能:
- 评估从内核 uprobe 迁移到用户态 eBPF(bpftime)的可行性,以降低上下文切换开销。
## 稳定性与准确性
- 作为调试工具,持续修复缺陷、改进错误处理,确保数据一致性。
- 加强自动化测试与回归验证,让关键路径“可依赖”。

## 高级语言特性:
- 编译型方向:优先支持 Rust 高级特性(async、trait objects 等)
- 解释型方向:探索对特定解释型语言(如 Lua)的追踪支持
## 利用 bpftime 优化性能
- 评估从内核 uprobe 迁移到用户态 eBPF([bpftime](https://github.com/eunomia-bpf/bpftime))的可行性。
- 目标是降低上下文切换与 probe 开销,为高频追踪提供更好的采样能力。

## 高级语言特性
- 编译型语言方向:优先支持 Rust 的 async/trait 对象等高级语义。
- 解释型语言方向:探索与 Lua 等运行时协同抓取变量/栈信息的方案。

## Client-Server 调用模式
- 典型场景:代码与调试信息在云端,目标二进制运行在测试机或本地。
- 规划让 GhostScope TUI 仅运行在控制端,eBPF Agent 独立驻留在目标主机,参考 `gdb/gdbserver` 模式。
- 待核心能力稳定后再启动该特性,以免分散目前的开发精力。
151 changes: 92 additions & 59 deletions ghostscope-loader/src/kernel_caps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,32 @@ use aya_obj::{
maps::PinningType,
EbpfSectionKind, Map,
};
use std::sync::OnceLock;
use std::{fmt, sync::OnceLock};
use tracing::{error, info, warn};

/// Global kernel capabilities cache
static KERNEL_CAPS: OnceLock<KernelCapabilities> = OnceLock::new();
static KERNEL_CAPS: OnceLock<Result<KernelCapabilities, KernelCapabilityError>> = OnceLock::new();

#[derive(Debug, Clone)]
pub struct KernelCapabilityError {
message: String,
}

impl KernelCapabilityError {
fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}

impl fmt::Display for KernelCapabilityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}

impl std::error::Error for KernelCapabilityError {}

/// Kernel eBPF capabilities detection
#[derive(Debug, Clone, Copy)]
Expand All @@ -21,76 +42,88 @@ pub struct KernelCapabilities {

impl KernelCapabilities {
/// Get global kernel capabilities (detected once on first call)
/// Panics if neither RingBuf nor PerfEventArray is supported
pub fn get() -> &'static Self {
KERNEL_CAPS.get_or_init(|| {
let supports_ringbuf = detect_ringbuf_support();
let supports_perf_event_array = if !supports_ringbuf {
// Only check PerfEventArray if RingBuf failed
detect_perf_event_array_support()
} else {
// Assume PerfEventArray is supported if RingBuf is (5.8 > 4.3)
true
};

if supports_ringbuf {
info!("✓ Kernel supports RingBuf (>= 5.8)");
} else if supports_perf_event_array {
warn!("⚠️ Kernel does not support RingBuf (< 5.8)");
warn!("⚠️ Will use PerfEventArray as fallback");
info!("✓ Kernel supports PerfEventArray (>= 4.3)");
} else {
error!("❌ Kernel supports neither RingBuf nor PerfEventArray");
error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
error!("❌ Current kernel appears to be older or eBPF is disabled");
panic!(
"Unsupported kernel: Neither RingBuf nor PerfEventArray is available. \
Please upgrade to Linux kernel >= 4.3 or enable eBPF support."
);
}

Self {
supports_ringbuf,
supports_perf_event_array,
}
})
/// Returns an error if neither RingBuf nor PerfEventArray is supported
pub fn get() -> Result<&'static Self, KernelCapabilityError> {
match KERNEL_CAPS.get_or_init(detect_full_capabilities) {
Ok(capabilities) => Ok(capabilities),
Err(err) => Err(err.clone()),
}
}

/// Get kernel capabilities with PerfEventArray-only detection (for testing mode)
/// Skips RingBuf detection and only validates PerfEventArray support
/// Panics if PerfEventArray is not supported
pub fn get_perf_only() -> &'static Self {
KERNEL_CAPS.get_or_init(|| {
info!("Testing mode: Only detecting PerfEventArray support");
let supports_perf_event_array = detect_perf_event_array_support();

if !supports_perf_event_array {
error!("❌ Kernel does not support PerfEventArray");
error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
panic!(
"Unsupported kernel: PerfEventArray is not available. \
Please upgrade to Linux kernel >= 4.3 or enable eBPF support."
);
}

info!("✓ Kernel supports PerfEventArray (>= 4.3)");

Self {
supports_ringbuf: false, // Not detected in testing mode
supports_perf_event_array,
}
})
/// Returns an error if PerfEventArray is not supported
pub fn get_perf_only() -> Result<&'static Self, KernelCapabilityError> {
match KERNEL_CAPS.get_or_init(detect_perf_only_capabilities) {
Ok(capabilities) => Ok(capabilities),
Err(err) => Err(err.clone()),
}
}

/// Check if RingBuf is supported (convenience method)
pub fn ringbuf_supported() -> bool {
Self::get().supports_ringbuf
Self::get()
.map(|caps| caps.supports_ringbuf)
.unwrap_or(false)
}

/// Check if PerfEventArray is supported (convenience method)
pub fn perf_event_array_supported() -> bool {
Self::get().supports_perf_event_array
Self::get()
.map(|caps| caps.supports_perf_event_array)
.unwrap_or(false)
}
}

fn detect_full_capabilities() -> Result<KernelCapabilities, KernelCapabilityError> {
let supports_ringbuf = detect_ringbuf_support();
let supports_perf_event_array = if !supports_ringbuf {
detect_perf_event_array_support()
} else {
true
};

if supports_ringbuf {
info!("✓ Kernel supports RingBuf (>= 5.8)");
} else if supports_perf_event_array {
warn!("⚠️ Kernel does not support RingBuf (< 5.8)");
warn!("⚠️ Will use PerfEventArray as fallback");
info!("✓ Kernel supports PerfEventArray (>= 4.3)");
} else {
error!("❌ Kernel supports neither RingBuf nor PerfEventArray");
error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
error!("❌ Current kernel appears to be older or eBPF is disabled");
return Err(KernelCapabilityError::new(
"Kernel lacks both RingBuf (>=5.8) and PerfEventArray (>=4.3) support. \
Please upgrade the kernel or enable eBPF features.",
));
}

Ok(KernelCapabilities {
supports_ringbuf,
supports_perf_event_array,
})
}

fn detect_perf_only_capabilities() -> Result<KernelCapabilities, KernelCapabilityError> {
info!("Testing mode: Only detecting PerfEventArray support");
let supports_perf_event_array = detect_perf_event_array_support();

if !supports_perf_event_array {
error!("❌ Kernel does not support PerfEventArray");
error!("❌ GhostScope requires kernel >= 4.3 for eBPF event output");
return Err(KernelCapabilityError::new(
"Kernel lacks PerfEventArray support (>=4.3 required). \
Please upgrade the kernel or enable eBPF features.",
));
}

info!("✓ Kernel supports PerfEventArray (>= 4.3)");

Ok(KernelCapabilities {
supports_ringbuf: false,
supports_perf_event_array,
})
}

/// Detect RingBuf support by attempting to create a minimal map
Expand Down
2 changes: 1 addition & 1 deletion ghostscope-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use tracing::{debug, error, info, warn};

// Export kernel capabilities detection
mod kernel_caps;
pub use kernel_caps::KernelCapabilities;
pub use kernel_caps::{KernelCapabilities, KernelCapabilityError};

// Export error types
mod error;
Expand Down
6 changes: 4 additions & 2 deletions ghostscope/src/config/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,11 @@ impl Args {
impl ParsedArgs {
/// Validate command line arguments for consistency and completeness
pub fn validate(&self) -> Result<()> {
// Must have either PID or target path for meaningful operation
// Require either PID (-p) or target file (-t)
if self.pid.is_none() && self.target_path.is_none() {
warn!("No target PID or target file specified - running in standalone mode");
return Err(anyhow::anyhow!(
"No target specified. Please provide either --pid <PID> or --target <PATH>."
));
}

// Target path validation
Expand Down
8 changes: 7 additions & 1 deletion ghostscope/src/config/settings.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use tracing::{debug, info};
Expand Down Expand Up @@ -709,7 +710,12 @@ impl Config {
let mut paths = Vec::new();

// 1. ~/.ghostscope/config.toml (user-level config)
if let Some(home_dir) = dirs::home_dir() {
if let Some(home_dir) = env::var("HOME")
.ok()
.filter(|p| !p.is_empty())
.map(PathBuf::from)
.or_else(dirs::home_dir)
{
paths.push(home_dir.join(".ghostscope").join("config.toml"));
}

Expand Down
Loading