Linux process tree — snapshot from /proc, incremental maintenance via fork/exec/exit events, ancestry chain queries, and pstree-style display.
proc-tree provides a unified interface for maintaining a Linux process tree with O(1) child lookups, incremental updates from process events, and ancestry chain queries. It supports one-shot snapshots from /proc and incremental updates via fork/exec/exit events, with thread-safe storage.
Status: zero-compat redesign (ADR-007): source-agnostic process topology state machine with four orthogonal input protocols (
LifecycleEvent/Snapshot/ProcessMetadata/SourceHealth), explicit source capability contract, generation-safe identity and zero-allocation batch paths. Seedocs/for the specification set:SEMANTICS.md,ARCHITECTURE.md, ADR-00x,API-0.6.md,RELEASE-CHECKLIST.md,BASELINE-0.5.md(history).
Unlike simple process listing tools that only show a point-in-time snapshot,
proc-tree maintains an in-memory process topology state machine that is
incrementally updated from source-agnostic inputs — /proc snapshots,
lifecycle events, offline replays — with generation-safe identity (the same
raw PID's old and new life never collide), deterministic semantics and
zero-allocation batch paths.
Add to your Cargo.toml:
[dependencies]
proc-tree = "0.6" # default features: procf (polling) + print (pstree)- Linux with
/procfilesystem (the core itself never touches/proc; theprocfadapter does) - No special capabilities required
This crate is Linux-only and will fail to compile on other platforms.
use proc_tree::{
BootstrapBuilder, ProcessTracker, TrackerConfig,
procf::{ProcScanConfig, scan},
};
let config = TrackerConfig::default();
let mut tracker = ProcessTracker::new(config);
// Adopt a snapshot atomically (bootstrap), then keep calibrating.
let result = scan(&ProcScanConfig { proc_root: "/proc".into(), ..Default::default() });
let builder = BootstrapBuilder::new(config);
let (tracker, report, _) = builder.build(result.entries, &result.meta).expect("bootstrap");
// Poll loop: reconcile new snapshots, apply lifecycle events.
let result = scan(&ProcScanConfig { proc_root: "/proc".into(), ..Default::default() });
let changes = tracker.reconcile(result.entries, &result.meta).1;use proc_tree::{LifecycleEvent, EventMeta, SourceId, StartTicks, Tid, Tgid, IdentityEvidence};
tracker.apply(&LifecycleEvent::Spawn {
meta: EventMeta { src: SourceId(1), epoch: 0, timestamp: None,
clock: proc_tree::ClockDomain::BootMonotonic, cursor: None },
child_tid: Tid(100), child_tgid: Tgid(100),
parent_tid: Tid(1), parent_tgid: Tgid(1),
evidence: IdentityEvidence::Strong(StartTicks(1234)),
});let view = tracker.view(); // one committed revision
let key = view.current(Tgid(100)).expect("current");
let (chain, end) = view.ancestors(key, proc_tree::AncestorMode::CurrentParent);
let owned = view.export_owned(key); // owned snapshot for hand-offlet shared = proc_tree::SharedProcessTracker::with_config(config);
let read = shared.read(); // guard-bound revision-pinned view