Skip to content

Add syscall durations (-T) and process-name annotation for follow-forks#137

Merged
kov merged 3 commits into
mainfrom
syscall-times-comm
Jun 10, 2026
Merged

Add syscall durations (-T) and process-name annotation for follow-forks#137
kov merged 3 commits into
mainfrom
syscall-times-comm

Conversation

@kov

@kov kov commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Final PR from the review pass (after #132-#136): per-syscall durations and process-name annotations for follow-forks output.

Syscall durations (-T/--syscall-times) — the sys_enter program records a bpf_ktime_get_ns() timestamp in the enter map; submit_compact_payload() computes the elapsed time at exit and carries it in a new duration_ns wire header field (WIRE_VERSION bumped to 5). The enter-map entry is now removed at submit time rather than in get_args() so the timestamp survives until the event is built; entries from aborted paths are overwritten by the tid's next syscall enter. The client flag appends the time strace-style: ) = 0 (success) <0.000123>. Events with no matching enter (e.g. execve, which has its own enter tracepoint) just omit the suffix.

Process-name annotation for -f — the wire header gains a fixed-size comm field (16 bytes, the kernel's TASK_COMM_LEN), filled in place in the reserved ringbuf memory with the raw bpf_get_current_comm helper. Going through the safe aya wrapper would cost a stack buffer + copy in every monomorphization of the submit path, which pushed the eBPF programs past the BPF branch-offset range ("Branch target out of insn range" at link time); the in-place write keeps them small and is cheaper anyway.

With -f, one-line output annotates every line with a fixed-width bracketed name field for readable interleaving:

1317891 [ls      ] brk(addr: 0x0) = 0xaaab14497000 <0.000000>
1317892 [grep    ] write(fd: 1, buf: "Cargo.lock\n", count: 11) = 11 (bytes) <0.000007>
1316948 [sh      ] wait4(pid: -1, ...) = 1317892 <0.000086>

The field is 8 characters (COMM_LEN / 2); longer names truncate to keep columns aligned. The multi-line style instead puts the name unpadded on the header line (1322191 sh), bounded at 65 bytes for when richer process information (full command line, terminal-width awareness) uses the room later. Since execve exits are reported by the new program image, the name follows exec correctly: sh forking ls shows <sh> for the fork and ls from the exec onward.

🤖 Generated with Claude Code

The sys_enter program now records a bpf_ktime_get_ns() timestamp in
the enter map; submit_compact_payload() computes the elapsed time at
exit and carries it in a new duration_ns wire header field (wire
version bumped to 5). The enter map entry is now removed at submit
time rather than in get_args() so the timestamp survives until the
event is built; entries for aborted paths are overwritten by the
tid's next syscall enter.

The client's new -T/--syscall-times flag appends the time spent in
each syscall to the trace line, strace-style: ` <0.000123>`. Events
with no matching enter (e.g. execve, which uses its own enter
tracepoint) simply omit the suffix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support for syscall timing (-T/--syscall-times) and process-name (comm) annotations (used for -f/--follow-forks) by extending the wire header and updating both eBPF emission and userspace formatting.

Changes:

  • Extend the wire protocol (WIRE_VERSION → 5) to carry duration_ns and comm in WireEventHeader.
  • Record syscall entry timestamps in eBPF and compute elapsed time at exit; plumb through client formatting under -T.
  • Annotate follow-forks output with a fixed-width process name field derived from bpf_get_current_comm, with new unit/integration tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Documents -T syscall timing and -f comm annotations.
pinchy/tests/integration.rs Updates follow-forks expectations to include [comm] and adds an integration test for -T.
pinchy/src/tests/mod.rs Adds unit tests for duration suffix and comm annotation formatting.
pinchy/src/formatting.rs Adds formatter support for duration suffix and comm prefixing.
pinchy/src/client.rs Adds -T CLI flag and conditionally enables duration/comm formatting when relaying events.
pinchy-ebpf/src/util.rs Computes duration at submit time, removes enter-map entries, and fills comm in-place in ringbuf memory.
pinchy-ebpf/src/main.rs Records enter_ns on sys_enter for later duration computation.
pinchy-common/src/lib.rs Bumps wire version and extends WireEventHeader with duration_ns + comm.

Comment thread pinchy-ebpf/src/util.rs Outdated
Comment on lines +196 to +206
let tid = ctx.pid();
let mut duration_ns = 0u64;

if let Some(enter_data) = unsafe { ENTER_MAP.get(&tid) } {
if enter_data.syscall_nr == syscall_nr {
duration_ns = unsafe { aya_ebpf::helpers::bpf_ktime_get_ns() }
.saturating_sub(enter_data.enter_ns);

let _ = unsafe { ENTER_MAP.remove(&tid) };
}
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the ENTER_MAP entry removal moved back to get_args() (which runs before any reserve attempt) as part of reworking duration/comm capture into a per-CPU exit context — that rework also fixes the x86_64 linker failure, together with splitting the oversized trivial handler in two. comm bytes are now sanitized (non-printable bytes become '?') before being written to the terminal.

Comment thread pinchy/src/formatting.rs Outdated
Comment on lines +120 to +140
if let Some(comm) = comm {
let len = comm.iter().position(|&b| b == 0).unwrap_or(comm.len());

match self.style {
FormattingStyle::OneLine => {
let len = len.min(COMM_DISPLAY_WIDTH);
let mut field = [b' '; COMM_DISPLAY_WIDTH];
field[..len].copy_from_slice(&comm[..len]);

output.write_all(b" [").await?;
output.write_all(&field).await?;
output.write_all(b"]").await?;
}
FormattingStyle::MultiLine => {
output.write_all(b" ").await?;
output
.write_all(&comm[..len.min(MULTILINE_COMM_MAX)])
.await?;
}
}
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the ENTER_MAP entry removal moved back to get_args() (which runs before any reserve attempt) as part of reworking duration/comm capture into a per-CPU exit context — that rework also fixes the x86_64 linker failure, together with splitting the oversized trivial handler in two. comm bytes are now sanitized (non-printable bytes become '?') before being written to the terminal.

@kov kov force-pushed the syscall-times-comm branch from a83512c to c35da1f Compare June 10, 2026 17:06
kov and others added 2 commits June 10, 2026 14:20
The wire header gains a fixed-size comm field (16 bytes, the kernel's
TASK_COMM_LEN). With -f, the client annotates every line with the
process name next to the PID, so interleaved output from several
processes stays readable.

Capturing comm (and the syscall duration) in every monomorphization
of submit_compact_payload() pushed the larger eBPF programs past the
BPF branch offset range at link time ("Branch target out of insn
range"), x86_64 being the first to overflow. Both values are now
captured once per exit, in the dispatching exit program before the
tail call, into a per-CPU scratch map; the submit path only does a
cheap per-CPU read. Tracepoint programs are not preempted, so the
slot is stable across the tail call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One-line traces now show the process name as a fixed-width bracketed
field after the PID (`1317891 [ls      ] brk(...)`). The field is 8
characters wide to keep the columns compact; longer names are
truncated.

The multi-line style instead puts the name unpadded on the header
line (`1322191 sh`), bounded at 65 bytes for when richer process
information (full command line, terminal-width awareness) uses the
room later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kov kov force-pushed the syscall-times-comm branch from 8569536 to a5e492f Compare June 10, 2026 17:21
@kov kov merged commit 816edb7 into main Jun 10, 2026
1 check passed
@kov kov deleted the syscall-times-comm branch June 10, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants