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
18 changes: 10 additions & 8 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,12 @@ ghostscope --source-panel # Show source panel
# WARNING: Testing purposes only. Forces PerfEventArray even on kernels >= 5.8
ghostscope --force-perf-event-array

# Start sysmon for standalone -t when target is a shared library (.so).
# Not needed when -t is combined with -p.
# Standalone -t starts sysmon by default. It is not used when -t is
# combined with -p because the PID already provides concrete process mappings.
# WARNING: Attaches system-wide sched tracepoints (exec/fork/exit) and may add
# overhead on hosts with high process churn. Default is OFF.
ghostscope --enable-sysmon-shared-lib
# overhead on hosts with high process churn. Set enable_sysmon_for_target=false
# in config to disable it; this flag can re-enable it for one run.
ghostscope --enable-sysmon-for-target
```

### BPFFS Maintenance
Expand Down Expand Up @@ -252,7 +253,7 @@ Behavior:
| `--source-panel` | | Show source panel | On |
| `--config <PATH>` | | Custom config file | Auto-detect |
| `--force-perf-event-array` | | Force PerfEventArray (testing) | Off |
| `--enable-sysmon-shared-lib` | | Start sysmon for standalone `-t` shared library, so globals can be traced in future processes. Not needed with `-t -p`. | Off |
| `--enable-sysmon-for-target` | | Re-enable sysmon for standalone `-t` when config disables it. Standalone `-t` enables sysmon by default; `-t -p` does not use it. | Off |
| `[BINARY] [ARGS...]` | | Launch target program with positional arguments | None |
| `--args <PROGRAM> [ARGS...]` | | Separate GhostScope options from target program arguments | None |

Expand Down Expand Up @@ -485,13 +486,13 @@ max_trace_event_size = 32768
# overhead compared to RingBuf and should only be used for compatibility testing.
force_perf_event_array = false # Default (auto-detect based on kernel version)

# Start sysmon eBPF for standalone -t when the target is a shared library (.so).
# Maintains ASLR offsets for late-start processes loading the library.
# Start sysmon eBPF for standalone -t targets.
# Maintains ASLR offsets for late-start processes loading the target.
# Not used when -t is combined with -p, because the PID already provides the
# concrete process mappings.
# This enables system-wide sched tracepoints and may impact performance
# when process churn is high.
enable_sysmon_for_shared_lib = false # Default
enable_sysmon_for_target = true # Default
```

### Configuration Examples
Expand Down Expand Up @@ -573,6 +574,7 @@ mem_dump_cap = 512
compare_cap = 32 # Smaller compare cap for minimal overhead
max_trace_event_size = 16384
proc_module_offsets_max_entries = 1024 # Single process only
enable_sysmon_for_target = false # Disable standalone -t lifecycle tracking

[general]
log_level = "error"
Expand Down
2 changes: 1 addition & 1 deletion docs/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ It mainly listens for:
- `fork`
- `exit`

In the current implementation, `-p` mode does not start this pipeline. A combined `-t <path> -p <pid>` run also does not start `sysmon`: `-t` scopes function/source/address target resolution to one module, while `-p` supplies the concrete process mappings and PID filter. `sysmon` mainly serves standalone `-t` mode, especially when GhostScope needs to keep module offsets, allowlists, and exit cleanup up to date after the target starts.
In the current implementation, standalone `-t` starts this pipeline by default unless `enable_sysmon_for_target = false` is set in config. `-p` mode does not start it. A combined `-t <path> -p <pid>` run also does not start `sysmon`: `-t` scopes function/source/address target resolution to one module, while `-p` supplies the concrete process mappings and PID filter. `sysmon` mainly serves standalone `-t` mode, especially when GhostScope needs to keep module offsets, allowlists, and exit cleanup up to date after the target starts.

#### Which PID View sysmon Depends On

Expand Down
2 changes: 1 addition & 1 deletion docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ GhostScope scans `/proc/PID/maps` at startup to obtain loaded dynamic library in

- **Executable targets**: When `-t` points to an executable (`-t /path/to/app`), GhostScope treats that binary as the primary module and globals are supported by default.
- **Shared-library targets (existing processes)**: If GhostScope starts after the library has already been mapped (e.g., tracing a running process that loaded `libfoo.so` earlier), globals work without extra steps.
- **Shared-library targets (new processes)**: For processes that start after GhostScope, enable `--enable-sysmon-shared-lib` (or the matching config option) so globals can be resolved. This incurs extra system-wide work, so expect higher overhead on hosts with frequent process churn.
- **Shared-library targets (new processes)**: Standalone `-t` starts sysmon by default so globals can be resolved for later-started processes. This incurs extra system-wide work, so expect higher overhead on hosts with frequent process churn; set `enable_sysmon_for_target = false` in config to disable it.
- **Target-scoped PID runs (`-t ... -p ...`)**: sysmon is not needed or started. `-t` chooses the module used for function/source/address target resolution, while `-p` supplies the concrete process mappings and PID filter.

> **Note**: The current sysmon pipeline still assumes the library is mapped when the exec event is handled; if a loader pulls it in much later, offsets are not retried yet.
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ sudo ghostscope -t /usr/lib/libexample.so
```
- **When to use**: You want to catch process startup events or trace multiple instances
- **Advantage**: Can capture events from process initialization, perfect for debugging startup issues
- **Note**: Will trace ALL processes using this binary/library, which may generate more events
- **Note**: Standalone `-t` starts sysmon by default and will trace ALL processes using this binary/library, which may generate more events. Set `enable_sysmon_for_target = false` in config to disable sysmon for standalone `-t`.
- **Target-scoped PID variant**: Use `-t /path/to/module -p <PID>` when you want `-t` target resolution for one module but only one running process's events. In this variant, `-t` takes precedence and `sysmon` is not started.

#### Important Notes
Expand Down
18 changes: 10 additions & 8 deletions docs/zh/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,12 @@ ghostscope --source-panel # 显示源码面板
# 警告:仅用于测试目的。即使在内核 >= 5.8 上也强制使用 PerfEventArray
ghostscope --force-perf-event-array

# 当独立 -t 目标是共享库(.so)时启动 sysmon。
# -t 与 -p 同时使用时不需要
# 独立 -t 默认启动 sysmon。-t 与 -p 同时使用时不会使用 sysmon,
# 因为 PID 已经提供了具体进程映射
# 警告:该选项会全局附加 sched 的 exec/fork/exit tracepoint,在进程频繁
# 启动/退出的主机上可能带来一定性能开销。默认关闭。
ghostscope --enable-sysmon-shared-lib
# 启动/退出的主机上可能带来一定性能开销。可在配置中设置
# enable_sysmon_for_target=false 关闭;该选项可为单次运行重新开启。
ghostscope --enable-sysmon-for-target
```

### BPFFS 维护
Expand Down Expand Up @@ -253,7 +254,7 @@ ghostscope bpffs prune --dry-run --json
| `--source-panel` | | 显示源码面板 | 开 |
| `--config <PATH>` | | 自定义配置文件 | 自动检测 |
| `--force-perf-event-array` | | 强制 PerfEventArray(测试) | 关 |
| `--enable-sysmon-shared-lib` | | 独立 `-t` 目标为共享库时启动 sysmon,以支持未来进程中的全局变量探测。`-t -p` 不需要。 | 关 |
| `--enable-sysmon-for-target` | | 当配置关闭 sysmon 时,重新为独立 `-t` 开启 sysmon。独立 `-t` 默认开启;`-t -p` 不使用 sysmon。 | 关 |
| `[BINARY] [ARGS...]` | | 启动目标程序并传递位置参数 | 无 |
| `--args <PROGRAM> [ARGS...]` | | 分隔 GhostScope 选项和目标程序参数 | 无 |

Expand Down Expand Up @@ -480,11 +481,11 @@ max_trace_event_size = 32768
# 有性能开销,仅应用于兼容性测试。
force_perf_event_array = false # 默认(根据内核版本自动检测)

# 当独立 -t 目标为共享库(.so)时启动 sysmon eBPF,
# 用于维护后续启动进程里动态库的 ASLR 偏移。
# 为独立 -t 目标启动 sysmon eBPF,
# 用于维护后续启动进程里目标模块的 ASLR 偏移。
# -t 与 -p 同时使用时不会使用 sysmon,因为 PID 已经提供具体进程映射。
# 启用后会注册系统范围的 sched tracepoint,进程频繁创建/退出的环境下可能带来性能开销。
enable_sysmon_for_shared_lib = false # 默认关闭
enable_sysmon_for_target = true # 默认开启
```

### 配置示例
Expand Down Expand Up @@ -566,6 +567,7 @@ mem_dump_cap = 512
compare_cap = 32 # 降低内置比较上限以减小开销
max_trace_event_size = 16384
proc_module_offsets_max_entries = 1024 # 仅单进程
enable_sysmon_for_target = false # 关闭独立 -t 生命周期跟踪

[general]
log_level = "error"
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ proc_offsets_runtime_pid -> pid_aliases -> proc_offsets_lookup_pid (= 通常就
- `fork`
- `exit`

当前实现里,`-p` 模式不启动这条链路。组合使用 `-t <path> -p <pid>` 时也不会启动 `sysmon`:`-t` 只负责把函数/源码行/地址目标解析限定到一个模块,`-p` 提供具体进程映射和 PID 过滤。`sysmon` 主要服务于独立 `-t` 模式,尤其是需要在目标进程启动后持续维护模块 offsets、allowlist 和退出清理的时候。
当前实现里,独立 `-t` 默认启动这条链路,除非配置中设置了 `enable_sysmon_for_target = false`。`-p` 模式不启动这条链路。组合使用 `-t <path> -p <pid>` 时也不会启动 `sysmon`:`-t` 只负责把函数/源码行/地址目标解析限定到一个模块,`-p` 提供具体进程映射和 PID 过滤。`sysmon` 主要服务于独立 `-t` 模式,尤其是需要在目标进程启动后持续维护模块 offsets、allowlist 和退出清理的时候。

#### `sysmon` 依赖什么 PID 视角

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ GhostScope 启动时会扫描进程的 `/proc/PID/maps` 获取已加载的动态

- **可执行目标**:当 `-t` 指向可执行文件(`-t /path/to/app`)时,会以该二进制作为主模块,默认支持全局变量。
- **共享库目标(已有进程)**:若 GhostScope 启动时,目标库已经被现有进程加载,例如追踪一个已运行但早先加载好 `libfoo.so` 的进程,能够直接解析全局变量。
- **共享库目标(新启动进程)**:如果目标进程在 GhostScope 启动之后才运行,为确保全局变量可用,需要开启 `--enable-sysmon-shared-lib`(或在配置文件中启用)。该功能会带来额外的系统负载,进程频繁启动/退出的环境下开销会更明显。
- **共享库目标(新启动进程)**:独立 `-t` 默认启动 sysmon,因此能为后续启动的进程解析全局变量。该功能会带来额外的系统负载,进程频繁启动/退出的环境下开销会更明显;可在配置文件中设置 `enable_sysmon_for_target = false` 关闭
- **限定 PID 的目标模式(`-t ... -p ...`)**:不需要也不会启动 sysmon。`-t` 决定函数/源码行/地址目标解析使用哪个模块,`-p` 提供具体进程映射和 PID 过滤。

提示:`-p <pid>` 模式下仍会自动计算并下发模块偏移,全局变量始终可用。
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ sudo ghostscope -t /usr/lib/libexample.so
```
- **使用场景**:您想捕获进程启动事件或追踪多个实例
- **优势**:可以捕获进程初始化的事件,非常适合调试启动问题
- **注意**:将追踪所有使用此二进制文件/库的进程,可能会产生更多事件
- **注意**:独立 `-t` 默认启动 sysmon,并会追踪所有使用此二进制文件/库的进程,可能会产生更多事件。可在配置中设置 `enable_sysmon_for_target = false` 关闭独立 `-t` 的 sysmon。
- **限定 PID 的目标模式**:如果想使用 `-t /path/to/module` 的模块目标解析,但只观察一个运行中进程的事件,可以使用 `-t /path/to/module -p <PID>`。这种形式下以 `-t` 为准,且不会启动 sysmon。

#### 重要说明
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/c_multithread_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn run_ghostscope_with_script_for_target(
.with_script(script_content)
.attach_to(target)
.timeout_secs(timeout_secs)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub fn init() {
.with_target("/")
.with_cli_args([std::ffi::OsString::from("--help")])
.force_perf_event_array(false)
.enable_sysmon_shared_lib(false);
.enable_sysmon_for_target(false);

let _use_pid_check: fn(u32) -> bool = host_pid_is_running;
let _use_compiler_check: fn(FixtureCompiler) -> bool = fixture_compiler_available;
Expand Down
46 changes: 40 additions & 6 deletions e2e-tests/tests/common/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ pub struct GhostscopeRunner {
timeout_secs: u64,
force_perf_event_array: bool,
log_level: Option<String>,
enable_sysmon_shared_lib: bool,
enable_sysmon_for_target: bool,
disable_sysmon_for_target: bool,
enable_file_logging: bool,
enable_console_logging: bool,
sandbox: Option<SandboxHandle>,
Expand All @@ -66,7 +67,8 @@ impl Default for GhostscopeRunner {
timeout_secs: 3,
force_perf_event_array: false,
log_level: None,
enable_sysmon_shared_lib: false,
enable_sysmon_for_target: false,
disable_sysmon_for_target: false,
enable_file_logging: false,
enable_console_logging: false,
sandbox: None,
Expand Down Expand Up @@ -118,8 +120,13 @@ impl GhostscopeRunner {
self
}

pub fn enable_sysmon_shared_lib(mut self, yes: bool) -> Self {
self.enable_sysmon_shared_lib = yes;
pub fn enable_sysmon_for_target(mut self, yes: bool) -> Self {
self.enable_sysmon_for_target = yes;
self
}

pub fn disable_sysmon_for_target(mut self, yes: bool) -> Self {
self.disable_sysmon_for_target = yes;
self
}

Expand Down Expand Up @@ -227,6 +234,13 @@ impl GhostscopeRunner {
use std::io::Write as _;
script_file.write_all(self.script_content.as_bytes())?;
let script_path = sandbox.path_in_sandbox(script_file.path())?;
let sysmon_config_file = if self.disable_sysmon_for_target {
let mut file = create_config_file()?;
file.write_all(b"[ebpf]\nenable_sysmon_for_target = false\n")?;
Some(file)
} else {
None
};

let mut args: Vec<OsString> = Vec::new();
if let Some(ref target) = self.target {
Expand All @@ -244,6 +258,14 @@ impl GhostscopeRunner {

args.push(OsString::from("--script-file"));
args.push(script_path.into_os_string());
if let Some(config_file) = sysmon_config_file.as_ref() {
args.push(OsString::from("--config"));
args.push(
sandbox
.path_in_sandbox(config_file.path())?
.into_os_string(),
);
}

if let Some(level) = &logging.level {
args.push(OsString::from("--log-level"));
Expand All @@ -256,8 +278,8 @@ impl GhostscopeRunner {
args.push(OsString::from("--emit-ready-marker"));
args.push(OsString::from(marker));
}
if self.enable_sysmon_shared_lib {
args.push(OsString::from("--enable-sysmon-shared-lib"));
if self.enable_sysmon_for_target {
args.push(OsString::from("--enable-sysmon-for-target"));
}
if logging.enable_logging {
args.push(OsString::from("--log"));
Expand Down Expand Up @@ -721,6 +743,18 @@ fn create_script_file() -> Result<NamedTempFile> {
.map_err(Into::into)
}

fn create_config_file() -> Result<NamedTempFile> {
let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.ok_or_else(|| anyhow::anyhow!("failed to resolve repo root for temp config file"))?
.to_path_buf();
Builder::new()
.prefix(".ghostscope-test-config-")
.suffix(".toml")
.tempfile_in(repo_root)
.map_err(Into::into)
}

fn env_bool(name: &str) -> Option<bool> {
let raw = env::var(name).ok()?;
let value = raw.trim();
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/tests/complex_types_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async fn run_ghostscope_with_script_for_target(
.with_script(script_content)
.attach_to(target)
.timeout_secs(timeout_secs)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand All @@ -32,7 +32,7 @@ async fn run_ghostscope_with_script_for_target_perf(
.attach_to(target)
.timeout_secs(timeout_secs)
.force_perf_event_array(true)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/cpp_script_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn run_ghostscope_with_script_for_target(
.with_script(script_content)
.attach_to(target)
.timeout_secs(timeout_secs)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/debuginfod_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ trace add_numbers {
.with_script(script_content)
.attach_to(&target)
.timeout_secs(4)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.with_cli_args(cli_args)
.run()
.await?;
Expand Down
8 changes: 4 additions & 4 deletions e2e-tests/tests/entry_value_recovery_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ async fn test_non_inline_entry_value_recovers_touch_parameters_at_runtime() -> a
.with_script(&script)
.attach_to(&target.target)
.timeout_secs(4)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await?;
let (target_stdout, target_stderr) = target.terminate_and_collect().await?;
Expand Down Expand Up @@ -647,7 +647,7 @@ async fn test_post_call_entry_value_recovers_state_members_at_runtime() -> anyho
.with_script(&script)
.attach_to(&target.target)
.timeout_secs(4)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await?;
let (target_stdout, target_stderr) = target.terminate_and_collect().await?;
Expand Down Expand Up @@ -778,7 +778,7 @@ async fn test_post_call_entry_value_state_pointer_memory_formats() -> anyhow::Re
.with_script(&script)
.attach_to(&target.target)
.timeout_secs(4)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await?;
let (_target_stdout, target_stderr) = target.terminate_and_collect().await?;
Expand Down Expand Up @@ -837,7 +837,7 @@ async fn test_entry_value_breg_stack_parameter_recovers_at_runtime() -> anyhow::
.with_script(&script)
.attach_to(&target.target)
.timeout_secs(4)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await?;
let (target_stdout, target_stderr) = target.terminate_and_collect().await?;
Expand Down
6 changes: 3 additions & 3 deletions e2e-tests/tests/globals_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn run_ghostscope_with_script_for_target(
.with_script(script_content)
.attach_to(target)
.timeout_secs(timeout_secs)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand All @@ -33,7 +33,7 @@ async fn run_ghostscope_with_script_for_target_perf(
.attach_to(target)
.timeout_secs(timeout_secs)
.force_perf_event_array(true)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand All @@ -48,7 +48,7 @@ async fn run_ghostscope_with_script_for_target_with_log(
.with_script(script_content)
.attach_to(target)
.timeout_secs(timeout_secs)
.enable_sysmon_shared_lib(false)
.enable_sysmon_for_target(false)
.run()
.await
}
Expand Down
Loading
Loading