This file provides instructions and context for AI coding agents working on this project.
From CONTRIBUTING.md:
cargo build # Build all binaries and examples
cargo test # Run all tests
cargo fmt --all -- --check # Check formatting
cargo clippy --all-targets --all-features # LintThe integration tests spawn example programs from examples/ as target
processes. The tests locate these example binaries via find_exec() in
tests/common/mod.rs, which expects them to already be built in the Cargo
output directory. You must build before running tests:
cargo build # Build binaries and examples first
cargo test # Then run the testsRunning cargo test alone may fail if the example binaries have not been
built yet.
You can also run individual tools directly:
./target/debug/ptree 1
./target/debug/pfiles $$ptools is a collection of Linux command-line utilities for inspecting process
state, inspired by the Solaris/illumos tools of the same name. Each tool is a
separate binary in src/bin/. All tools work on both live processes (via
/proc) and core dumps (via systemd-coredump journal entries), sharing the
same parsing logic through a common library.
src/
lib.rs Library root; re-exports the public API
bin/ One binary per tool (pargs.rs, pfiles.rs, ptree.rs, ...)
proc/ Process handle layer (parsing and structured access)
mod.rs ProcHandle, resolve_operand(), Error
auxv.rs Auxiliary vector parsing
cred.rs Credential parsing and UID/GID resolution
fd.rs File descriptor structures and classification
net.rs Socket metadata and peer process resolution
numa.rs NUMA node and CPU affinity
pidfd.rs pidfd utilities
signal.rs Signal set parsing
source/ Data source abstraction
mod.rs ProcSource trait
live.rs Live process backend (reads /proc)
coredump.rs Coredump backend (reads systemd journal + ELF notes)
dw.rs DWARF-based unwinding via elfutils libdw/libdwfl
stack/ Stack unwinding layer
mod.rs TraceOptions, Process, Thread, Frame types
display.rs Shared formatting helpers
examples/ Example programs used as test targets
tests/ Integration tests
common/mod.rs Test utilities (run_ptool, ReadySignal, etc.)
build.rs Man page generation (roff)
Within each .rs file, items should appear in this order:
- Module declarations (
mod foo;) - Imports (
usestatements) —stdfirst, then external crates, thencrate/super/self, separated by blank lines - Constants and statics
- Type definitions (
struct,enum,type) — simple/derived-like trait impls (From,Default,Drop,FromStr,Ord,PartialOrd,PartialEq,Eq,Borrow, etc.) should directly follow their type definition rather than being separated into the trait impls section - Trait definitions
- Trait impls (
impl Trait for Type) — for substantial impls with complex parsing or business logic (e.g.,FromBufRead) - Inherent impls (
impl Type) - Free functions (public before private)
- Tests (
#[cfg(test)] mod tests)
Within an impl block: constructors (new, with_*, from_*) first, then
public methods, then private helpers. Public items come before private items.
The codebase is organized into four layers with strict dependency rules:
-
Source layer (
src/source/): Abstracts where process data comes from. TheProcSourcetrait provides a uniform interface;LiveProcessreads/proc/[pid]/...whileCoredumpSourcereads from journal entries and ELF notes. Each source owns a lazily-initialized dwfl session and exposestrace_thread(tid, options)for stack unwinding -- no dwfl types or raw ELF pointers leak outside this layer. Only the process handle layer consumes it. -
Process handle layer (
src/proc/): Parses raw data from the source layer into typed Rust structures. The central type isProcHandle, an opaque handle through which all process queries flow. Types here should not implementDisplay(exceptError). Never format output in this layer -- it provides structured data only. All output formatting belongs in the presentation/display layer. -
Stack layer (
src/stack/): Orchestrates thread enumeration, ptrace attachment, and snapshot/rolling tracing strategies. Delegates actual frame walking to the source layer viaProcHandle::trace_thread(). This layer must not implementDisplay(exceptError). -
Presentation/display layer (
src/display.rsandsrc/bin/*.rs): Formats structured data from the proc-handle layer for terminal output. Shared formatting lives indisplay.rs; tool-specific formatting lives in each binary. This layer must never consume the source layer directly.
Tools should recover from errors and continue producing useful output. Debugging tools are expected to run on systems in unusual states, so do not panic on procfs inconsistencies. Assert only on purely internal invariants.
All tools use lexopt for argument parsing. No heavy framework (clap,
structopt) is used.
Integration tests live in tests/ and use a common helper module
(tests/common/mod.rs). The typical pattern:
- An example program in
examples/sets up the process state to be inspected (opens files, sets signal handlers, creates threads, etc.). - The example reads
PTOOLS_TEST_READY_FILEfrom its environment, creates that file to signal readiness, then loops until killed. - The test calls
common::run_ptool(), which spawns the example, waits for the ready signal, runs the tool against the example's PID, kills the example, and returns the tool's output. - The test asserts on the captured stdout/stderr.
To add a new test:
- If you need a new target process state, add an example in
examples/that follows the ready-signal convention (readPTOOLS_TEST_READY_FILE, create the file when ready, loop forever). - Add a test file in
tests/(or add a#[test]function to an existing one). Usecommon::run_ptool()to drive the tool. - Remember to run
cargo buildbeforecargo testso that the example binaries are available. - New functionality is expected to have corresponding tests.