[COD-2349] feat(callgrind): track subprocesses across fork and exec#25
Open
GuillaumeLagrange wants to merge 2 commits into
Conversation
Greptile SummaryThis PR tracks benchmark subprocesses across forks and traced execs. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
|
Merging this PR will not alter performance
Comparing Footnotes
|
GuillaumeLagrange
force-pushed
the
cod-2349-support-subprocesses-in-valgrind
branch
from
July 22, 2026 21:23
ce72315 to
69b355e
Compare
GuillaumeLagrange
force-pushed
the
cod-3196-fix-valgrind-separate-threadsyes-behavior-to-represent-os
branch
from
July 22, 2026 21:23
5cae7e2 to
bf37129
Compare
A process spawned via exec under --trace-children=yes gets a fresh valgrind whose instrumentation state resets to --instr-atstart, so a benchmark reached through an exec chain (e.g. `cargo run`) was measured from the wrong point. A plain fork inherits the state with the address space, but an exec does not. Add a VG_(needs_child_exec_args) tool need: the core asks the tool for extra valgrind arguments while building the child's argv at exec time and appends them after VG_(args_for_valgrind), where later options override earlier ones. Callgrind uses it to forward --instr-atstart=<current state> so the exec'd image is instrumented from its first instruction. Wired into the exec argv construction on Linux (generic), Darwin, and Solaris. Closes COD-2349 Co-Authored-By: Claude <noreply@anthropic.com>
When a benchmark spawns a subprocess, the resulting profile had no way to attribute that subprocess back to the exact benchmark (dump part) that spawned it, so the spawn tree could not be rebuilt. Track fork edges and emit them in the dump: on fork the parent records the new child pid against the part currently being measured, and each part's header lists the children spawned during it as "desc: Spawned pid:" lines (a desc field so kcachegrind and callgrind_annotate ignore it). A fork child restarts its part counter at 1 and drops the inherited edges so it only reports children it spawns itself, and zeroes cost so work before the fork stays attributed to the parent. To carry the child pid to the fork hook, VG_(atfork)'s parent callback now takes the pid of the just-created child; the pre/child callbacks are unchanged. All fork/clone sites (Linux, FreeBSD, Solaris, generic) pass it through. The child hook also re-stamps the in-flight fork syscall's start time from the child's clocks, since the thread CPU clock restarts in the child and the exit delta would otherwise underflow. Closes COD-2349 Co-Authored-By: Claude <noreply@anthropic.com>
GuillaumeLagrange
force-pushed
the
cod-2349-support-subprocesses-in-valgrind
branch
from
July 22, 2026 21:56
69b355e to
002990c
Compare
GuillaumeLagrange
force-pushed
the
cod-3196-fix-valgrind-separate-threadsyes-behavior-to-represent-os
branch
from
July 22, 2026 21:56
bf37129 to
bb4fb76
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Track subprocesses spawned by a benchmark so they can be measured and
attributed back to the benchmark that spawned them, across both
forkandexec.Two independent pieces, one per commit:
1. Forward instrumentation state across a traced exec. A process spawned
via
execunder--trace-children=yesgets a fresh valgrind whoseinstrumentation state resets to
--instr-atstart, so a benchmark reachedthrough an exec chain (e.g.
cargo run) was measured from the wrong point. Aplain
forkinherits the state with the address space, but an exec does not.This adds a
VG_(needs_child_exec_args)tool need: the core asks the tool forextra valgrind arguments while building the child's argv at exec time and
appends them after
VG_(args_for_valgrind)(later options win). Callgrind usesit to forward
--instr-atstart=<current state>. Wired into the exec argvconstruction on Linux (generic), Darwin, and Solaris.
2. Record spawned subprocesses per dump part. When a benchmark spawns a
subprocess, the profile had no way to attribute that subprocess to the exact
benchmark (dump part) that spawned it. On
forkthe parent now records the newchild pid against the part currently being measured, and each part's header
lists the children spawned during it as
desc: Spawned pid:lines (adescfield so kcachegrind and callgrind_annotate ignore it). A fork child restarts
its part counter at 1 and drops the inherited edges so it only reports children
it spawns itself, and zeroes cost so work before the fork stays attributed to
the parent.
To carry the child pid to the fork hook,
VG_(atfork)'s parent callback nowtakes the pid of the just-created child; the pre/child callbacks are unchanged.
All fork/clone sites (Linux, FreeBSD, Solaris, generic) pass it through. The
child hook also re-stamps the in-flight fork syscall's start time from the
child's clocks, since the thread CPU clock restarts in the child and the exit
delta would otherwise underflow.
The spawn edge is recorded parent→child rather than child→parent: at fork time
the parent already knows both the child pid and its own live part, so no spawn
identity needs to cross the exec boundary — only the instrumentation state
does. This is a change from the earlier squashed design that emitted
desc: Spawned by: <pid>:<part>from the child; see the COD-2349 discussion forthe full rationale.
Stacked on top of #24 (COD-3196, per-thread dump support), which this builds
on for the retired-thread handling in the fork/exec paths.
Behavior verified with fork/exec integration tests in the valgrind-helpers repo
(subprocess measured and recorded across fork, across a traced exec, through a
deep fork tree, and one child recorded per part across a two-part run).
Closes COD-2349