Skip to content

DO NOT MERGE: diagnose a GPU memory fault on the retry that follows it - #1800

Open
sbryngelson wants to merge 18 commits into
masterfrom
ci/gpu-fault-diagnostics
Open

DO NOT MERGE: diagnose a GPU memory fault on the retry that follows it#1800
sbryngelson wants to merge 18 commits into
masterfrom
ci/gpu-fault-diagnostics

Conversation

@sbryngelson

Copy link
Copy Markdown
Member

Draft, and the top commit is a deliberate bug. 3ef4c282 injects an out-of-bounds device write so CI produces a real GPU memory fault and the diagnostics below can be seen working in an actual job log. Revert it before this is considered for merge.

The problem

A GPU memory fault reaches CI as an address and nothing else:

Memory access fault by GPU node-9 (Agent handle: 0x32463c0) on address 0x15443991c000

That is not actionable. Twelve jobs failed this way in a two-week sample — all Frontier CCE, the largest non-infrastructure failure class on that machine — and none produced anything anyone could act on.

What the runtimes will say if asked

Measured on a Frontier compute node with MFC's own module set (cpe/25.03, rocm/6.3.1, craype-accel-amd-gfx90a), using a deliberate out-of-bounds write:

ACC: Execute kernel fault_$ck_L8_1 async(auto) from fault.f90:8
Memory access fault by GPU node-4 ...

CRAY_ACC_DEBUG=1 names the kernel and the source line of the launch that faulted. CRAY_ACC_DEBUG=2 adds allocate 'arr(?:?)' (800 bytes) / present 'arr(:)' (800 bytes), which separates an out-of-bounds access from an unmapped one. Level 3 is unusable noise.

On the AFAR toolchain frontier_amd uses, verified on the identical compiler drop (therock-afar-23.2.1-gfx90a), OFFLOAD_TRACK_ALLOCATION_TRACES=true gives:

Device pointer 0x7f158f7e2000 does not point into any (current or prior) host-issued allocation

Why it goes on the retry

Neither can be on for a whole run: CRAY_ACC_DEBUG=1 emits 142,777 lines for one 800-cell 1D case, one per kernel launch and per transfer.

But MFC already retries a failed case up to three times, and those retries rescue almost nothing — 0 of 235 in bench, with every recorded failed test showing the full attempt count. That last fact is what makes this work: when a case fails it fails all its attempts, so the retry is a reproduction of the fault that has already been paid for and currently produces nothing. On a GPU memory fault, the next attempt now re-runs with the diagnostics on and keeps the tail.

Both variables are set rather than detecting the cluster: each runtime provably ignores the other's, checked on both toolchains.

Everything here was measured, not assumed

question answer how
Does the env reach the binary through mfc.sh run → mako → srun? yes 142,777 ACC: lines on Frontier
Volume 142,777 lines for one 1D case → capped to the last 80 same run
Slowdown 1.27x (13.7s → 17.3s) timed on Frontier
Timeout risk none — a case would need ~2800s unaided to be pushed past TEST_TIMEOUT_SECONDS; the slowest seen in CI is ~1000s derived
Does the AMD variable do anything under CCE? no, ignored tested on Frontier
Blast radius one case, one retry; case_env is local to handle_case, so a suite with no GPU faults is bit-for-bit unaffected code

Deliberately not done

  • Nothing in the .mako templates. Those generate job scripts for every ./mfc.sh run on all 18 supported clusters, so anything set there would follow users into production runs. The environment is built per subprocess in the test harness instead — also why it is a fresh dict rather than os.environ, since cases run in worker threads and a mutated global would leak per-kernel logging into every concurrent case.
  • No GPU core dumps. They do land on Frontier when the working directory is node-local, but one faulting run wrote 1.1 GB of core plus fifteen gpucore files of ~157 MB each. The CI message GPU core dump failed / Failed to allocate file: Bad file descriptor is the runner workspace being on Lustre, and it is accidentally protective.

Unverified, and it cannot be forced

That these faults reproduce on the retry. It is inferred from every recorded failed test showing Attempts: 3, but there has been no occurrence in the last twelve Test Suite runs, so history cannot confirm it. If they do not reproduce, this costs nothing — it only fires after a failure — but it buys nothing either.

The injected bug in 3ef4c282 is what turns that from an assumption into an observation.

https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy

A GPU memory fault reaches CI as an address and nothing else:

  Memory access fault by GPU node-9 (Agent handle: 0x...) on address 0x...

Measured on a Frontier compute node with MFC's own module set, the offload
runtimes will say considerably more than that. Under CCE, CRAY_ACC_DEBUG=1
names the kernel and the source line of the launch that faulted:

  ACC: Execute kernel fault_$ck_L8_1 async(auto) from fault.f90:8
  Memory access fault by GPU node-4 ...

Under the AFAR toolchain frontier_amd uses, OFFLOAD_TRACK_ALLOCATION_TRACES
states whether the address ever belonged to a host-issued allocation, which
separates an out-of-bounds write from an unmapped one.

Neither can be on for a whole run: CRAY_ACC_DEBUG prints per kernel launch
and per transfer, and MFC launches thousands per timestep.

So spend a retry on it. MFC already retries a failed case up to three times,
and those retries rescue almost nothing -- 0 of 235 in bench, with every
recorded failed test showing the full attempt count. That last fact is what
makes this work: when a case fails it fails all its attempts, so the retry is
a reproduction of the fault that has already been paid for and currently
produces nothing. On a GPU memory fault the next attempt now re-runs with both
variables set. Both, rather than detecting the cluster: each runtime ignores
the other's, verified on both toolchains.

Nothing changes for any other failure, and nothing changes on the happy path.

Not placed in the .mako templates. Those generate job scripts for every
./mfc.sh run on all 18 supported clusters, so anything set there would follow
users into production runs. The environment is built per subprocess in the
test harness instead -- also the reason it is a fresh dict rather than
os.environ, since cases run in worker threads and a mutated global would leak
per-kernel logging into every concurrent case.

Measured while establishing the above, on the same AFAR drop Frontier uses:
allocation tracking costs 10.5x on a loop that maps and unmaps every
iteration, and nothing measurable on MFC's shape (map once, then kernels and
target updates: 2.606s -> 2.620s over 2000 iterations).

Also learned and deliberately not acted on: GPU core dumps do land on Frontier
when the working directory is node-local, but a single faulting run wrote
1.1 GB of core plus 15 gpucore files of ~157 MB each. The CI failure
"GPU core dump failed / Failed to allocate file: Bad file descriptor" is the
runner workspace being on Lustre, and it is accidentally protective.

510 tests pass.
Measured on Frontier: CRAY_ACC_DEBUG=1 emits 142,777 "ACC:" lines for a
single 800-cell 1D case, one per kernel launch and per transfer. The
previous commit echoed a failing attempt's output whole, so a diagnostic
retry would have buried the failure it exists to explain under six
figures of runtime chatter.

Only the tail is worth keeping. The fault comes last, and the launch
immediately before it is what names the kernel and source line:

  ACC: Execute kernel syscheck_$ck_L89_1 from .../syscheck.fpp:89
  Memory access fault by GPU node-4 ...

Ordinary failures still print in full -- they are short and the whole
thing is useful. Only the diagnostic retry is capped, and the complete
capture remains in out_pre_sim.txt for anyone who wants it.

The same session settled the two things this design rested on:

  chain  the variable does reach the binary through ./mfc.sh run ->
         frontier.mako -> srun -> binary, so the change is live, not inert

  cost   13.7s -> 17.3s (1.27x) on the case that produced those 142,777
         lines. Against the 1 hour TEST_TIMEOUT_SECONDS a case would need
         to take ~2800s unaided before a diagnostic retry could push it
         over, and the slowest case seen in CI is around 1000s. So the
         retry cannot convert a fault into a timeout, which would have
         hidden the very thing it is meant to surface.

The logging is per case and per retry -- case_env is local to
handle_case and only set once that case has faulted -- so a suite with no
GPU faults is bit-for-bit unaffected, and one with a fault pays 1.27x on
exactly one case.

511 tests pass.
A deliberate out-of-bounds device write in the RK update, so CI produces a
real GPU memory fault and the retry diagnostics from the preceding two
commits can be seen end to end in a job log rather than argued about.

Scoped to the igr branch, which a handful of tests exercise, rather than
firing for every GPU case and burning the whole matrix. Not in syscheck:
a faulting syscheck would trip the preflight, which would then start
excluding perfectly healthy nodes.

Expected in the log of a Frontier or Phoenix GPU leg:

  <case> failed, Memory access fault by GPU node-N ...
  GPU memory fault: retrying <case> with offload diagnostics enabled
  ACC: Execute kernel <name> from src/simulation/m_time_steppers.fpp:<line>
  Memory access fault by GPU node-N ...

Revert this commit before the PR is considered for merge.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Claude Code Review

Head SHA: 3ef4c28

Files changed:

  • 4
  • src/simulation/m_time_steppers.fpp
  • toolchain/mfc/test/case.py
  • toolchain/mfc/test/test.py
  • toolchain/mfc/test/test_gpu_fault_diagnostics.py

Findings:

  • src/simulation/m_time_steppers.fpp (around the new lines after if (igr) then): deliberate out-of-bounds GPU write left in the diff, explicitly labeled "DO NOT MERGE". The added line q_cons_ts(1)%vf(i)%sf(j + 100000000, k, l) = 1._wp is an intentional out-of-bounds device write, per the surrounding comment, meant only to trigger a GPU fault for testing CI diagnostics. As written this is memory-corrupting code inside the simulation time-stepper's hot path and must not be merged into master — it will crash or silently corrupt every IGR run once built. This needs to be removed before merge regardless of the CI-diagnostics feature's value.

  • toolchain/mfc/test/test.py: the retry loop's GPU-fault detection can never fire, so the new diagnostic-retry feature is dead code. In _handle_case (line ~648), a detected fault is re-raised as MFCException(f"Test {case}: Failed to execute MFC. [gpu-memory-fault]") — a synthetic message that does not contain the raw fault text. In handle_case's except block (line ~884), the retry logic then calls is_gpu_memory_fault(str(exc)), which matches only against GPU_FAULT_SIGNATURES ("memory access fault by gpu", "offload error: memory access fault"). Since str(exc) is the synthetic "...[gpu-memory-fault]" message and never contains either signature string, is_gpu_memory_fault(str(exc)) will always evaluate False, so case_env is never populated and the diagnostic retry (CRAY_ACC_DEBUG/OFFLOAD_TRACK_ALLOCATION_TRACES) never activates on a real GPU fault. The added unit tests in test_gpu_fault_diagnostics.py don't catch this because they check that is_gpu_memory_fault and gpu-memory-fault/diagnostic_env/case_env each appear somewhere in the source independently, rather than exercising the actual exception round-trip from _handle_case into handle_case.

@sbryngelson
sbryngelson marked this pull request as ready for review September 2, 2026 01:42
Copilot AI lite review requested due to automatic review settings September 2, 2026 01:42
@sbryngelson sbryngelson changed the title DRAFT: diagnose a GPU memory fault on the retry that follows it DO NOT MERGE: diagnose a GPU memory fault on the retry that follows it Sep 2, 2026
GPU_PARALLEL_LOOP expands to nothing on CPU builds, so the deliberate
out-of-bounds write was also executing on the host in every CPU igr test.
That is undefined behaviour rather than the clean device fault this is
meant to produce, and it would have made the CPU legs fail for a reason
unrelated to what is being demonstrated.

Gated on MFC_GPU so only the GPU legs fault.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy

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.

🟡 Changes recommended

The diagnostic retry currently won’t trigger because the retry loop checks the wrong text for the GPU-fault signature, and the PR also contains an intentional out-of-bounds device write that must be removed before merge.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This draft PR aims to make GPU memory faults actionable in CI by using the test harness’ existing retry mechanism: when a run fails with a GPU memory fault, the next attempt re-runs with offload-runtime diagnostics enabled and prints only the bounded tail of the output.

Changes:

  • Thread a per-subprocess env through the test harness so retries can enable offload diagnostics without mutating global os.environ.
  • Detect GPU memory fault signatures and (intended to) trigger a diagnostic retry that prints only the last N lines to avoid log flooding.
  • Add unit tests for the GPU-fault detection/diagnostic-env logic, and (intentionally, per PR description) inject an out-of-bounds device write to force a real GPU fault.
File summaries
File Description
toolchain/mfc/test/test.py Adds GPU-fault detection + diagnostic retry env plumbing and bounds CI log output on diagnostic retries.
toolchain/mfc/test/case.py Allows passing a per-subprocess environment to mfc.sh run via common.system(..., env=...).
toolchain/mfc/test/test_gpu_fault_diagnostics.py Adds unit tests covering GPU fault signature matching and diagnostic env composition/usage.
src/simulation/m_time_steppers.fpp Injects a deliberate out-of-bounds device write to force a GPU memory fault (must be removed before merge).
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/simulation/m_time_steppers.fpp Outdated
Comment on lines +495 to +503
! ############ DO NOT MERGE ############
! Deliberate out-of-bounds device write, to make
! CI produce a real GPU memory fault so the retry
! diagnostics can be seen end to end in a job log.
! Scoped to igr so only a handful of tests fault
! instead of the whole GPU matrix.
! Revert with: git revert <this commit>
q_cons_ts(1)%vf(i)%sf(j + 100000000, k, l) = 1._wp
! ######################################
Comment thread toolchain/mfc/test/test.py Outdated
Comment on lines +884 to +886
if is_gpu_memory_fault(str(exc)) and case_env is None:
case_env = diagnostic_env(dict(os.environ))
cons.print(f" [yellow]GPU memory fault[/yellow]: retrying {case.trace} with offload diagnostics enabled")
Comment on lines +94 to +101
def test_diagnostics_are_not_enabled_for_ordinary_failures():
import inspect

from mfc.test import test as t

src = inspect.getsource(t.handle_case)
# the enabling is guarded by the fault check, not unconditional
assert "if is_gpu_memory_fault(" in src
The first attempt used j + 1e8 (762 MB past the array base) and produced
no fault at all: the Frontier benchmark leg ran the igr case five times
and passed.

Reproduced on an MI210 with the same AFAR toolchain. The offset has to
clear MFC's *whole* device footprint, not one array:

  32 MB allocated,  offset 1e8  -> faults      (why the first local test misled me)
  4 GB allocated,   offset 1e8  -> NO fault    (lands in the next allocation)
  4 GB allocated,   offset 2e9  -> memory access fault
  4 GB allocated,   offset 1e11 -> HSA_STATUS_ERROR_MEMORY_APERTURE_VIOLATION

So there is a window. Too small and it silently corrupts a neighbouring
array; too large and the runtime reports an aperture violation, which is
a different message that is_gpu_memory_fault does not match and which
would not exercise the diagnostic path either.

2e9 elements is 16 GB, clear of MFC's allocations and still an ordinary
memory access fault. It also stays inside a default 4-byte integer.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
The failure site raised "[gpu-memory-fault]" while the retry searched for "memory access fault by gpu", so the two never matched and the diagnostic could not fire. CI proved it: a Frontier gpu-omp shard hit 216 memory access faults and enabled diagnostics zero times.

The marker is now one of the signatures verbatim, and parenthesised rather than bracketed -- Rich parses "[...]" as a style tag and deletes it, which is why that shard logged a bare "Failed to execute MFC. " with the marker missing.

The three tests this replaces asserted only that the source text contained certain identifiers, which cannot detect a mismatch between the string one side writes and the string the other side reads. The two new tests exercise the hand-off and the Rich rendering; both are verified red against the respective bugs.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
The diagnostic fired correctly on Frontier CCE (39 retries, 4413 CRAY_ACC_DEBUG lines) and then pointed at the wrong kernel. With a known out-of-bounds write injected into m_time_steppers, the last kernel logged before each fault was s_write_run_time_information in 111 of 140 faults, s_igr_riemann_solver in 23, and m_time_steppers in none.

Dispatches are asynchronous, so the fault is reported long after the launch that caused it and the trace's tail is whatever ran next. A trace that confidently accuses the wrong kernel is worse than no trace, so the retry now sets AMD_SERIALIZE_KERNEL/COPY=3.

Also corrects the docstring claim that CRAY_ACC_DEBUG=1 names the launch that faulted; this run falsified it. Whether CCE's offload runtime honours the HIP serialization vars is what the next run measures.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
Measured on the CCE gpu-acc shard at 45ec609: with AMD_SERIALIZE_KERNEL/COPY=3 set, the same injected fault still blamed s_write_run_time_information in 168 of 213 faults and m_time_steppers in none -- the distribution is unchanged from before serialization. They are HIP runtime variables; CCE's offload runtime is not HIP.

This also removes a claim I had no measurement for: the previous docstring said serialization was "verified to be honoured on the AFAR/HIP path". It was inferred from the variables being HIP's, not measured. The AMD lanes are still pending.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
The fault-injection experiment measured what each variable was worth, and most were worth nothing.

AFAR/OpenMP already names the faulting kernel unaided -- "Kernel 0: omp target in _QMm_time_steppersPs_tvd_rk @ 486", correct in 90 of 90 faults and printed on the FIRST attempt, before any diagnostic is enabled. That falsifies the premise this retry was built on, that a fault reports only an address.

CCE/OpenACC cannot name it at all: across three runs CRAY_ACC_DEBUG blamed s_write_run_time_information 386 times and the true culprit 0 of 473, because dispatch is asynchronous and its log's tail is whatever ran next. AMD_SERIALIZE_* changed neither lane. Both are removed -- a confidently wrong suspect is worse than no diagnostic.

OFFLOAD_TRACK_ALLOCATION_TRACES stays: it reports whether the faulting address was ever a real host allocation (60 retried faults, 0 unretried), which the runtime does not volunteer.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.69%. Comparing base (ac08ffe) to head (ce6c05f).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1800   +/-   ##
=======================================
  Coverage   61.69%   61.69%           
=======================================
  Files          84       84           
  Lines       21620    21622    +2     
  Branches     3196     3196           
=======================================
+ Hits        13338    13340    +2     
  Misses       6090     6090           
  Partials     2192     2192           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The diagnostics only emit when the runtime is already aborting on a memory fault, so they are inert in a healthy run and there is nothing to save by withholding them. Setting them on every run makes attempt 1 carry the evidence, which is what the retry existed to obtain.

Adds OFFLOAD_TRACK_NUM_KERNEL_LAUNCH_TRACES=8 -- host stack traces for recent launches, which the runtime advertises in its own fault message -- alongside the allocation verdict.

Adds NVHPC's wording to the fault signatures. It says "Accelerator Fatal Error / CUDA_ERROR_ILLEGAL_ADDRESS", nothing like AMD's "memory access fault by GPU", so 189 faults on a Phoenix gpu-acc shard were never recognised as GPU faults.

Still not setting CRAY_ACC_DEBUG: it streams a line per launch for the whole run and, because CCE dispatches async by default (acc_model=auto_async_kernel), its tail names whatever ran next -- the wrong kernel in 81 of 102 traced faults. The flag that would fix that, -h acc_model=auto_async_none, is a compile flag no retry can set.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
…tion

CCE defaults to acc_model=auto_async_kernel, so a memory fault surfaces at an unrelated sync point and its trace names the wrong kernel (81 of 102 traced faults blamed s_write_run_time_information, 0 named the culprit). auto_async_none executes kernels synchronously, which should make the abort land on the faulting kernel.

Scoped to Cray + OpenACC: acc_model is an OpenACC flag, so the OpenMP offload builds are unaffected by construction.

This is a measurement, not a proposal. Even if it works it should probably not ship in CI builds: it would stop the test suite exercising the asynchronous dispatch that production runs use.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
Removes the three DO NOT MERGE commits' effect on m_time_steppers.fpp (3ef4c28, cc4b52e, c2d0579). The file is now byte-identical to master.

The injection did its job: it is the only reason the diagnostics could be checked against a known ground truth, which is how the original design was found to be measuring the wrong thing on every lane.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
1. The detection path had no effect. is_gpu_memory_fault tagged the exception and nothing read the tag: classify_error bucketed anything containing "failed to execute" as a generic execution failure, so a GPU memory fault -- the one execution failure a retry provably cannot fix -- was indistinguishable from a transient launcher problem. It now gets its own bucket, which is what the detection was kept for.

2. "accelerator fatal error" was too broad. NVHPC uses that prefix for unrelated failures, including "call to cuMemAlloc returned error 2: Out of memory"; classifying an OOM as a memory fault would send the reader hunting a bad index that does not exist. cuda_error_illegal_address already matches the real thing.

3. Restart cases bypassed the diagnostics entirely -- run_restart never took an env, so a fault there produced none of the output this exists to provide.

4/5. Comments still described the retry that was removed, and one clause did not parse.

Findings 1 and 4 were both residue from deleting the retry: the mechanism went, its vocabulary stayed. The tests missed it because they asserted the marker round-trips, not that anything consumes it.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
Measured on Frontier (CCE 19.0.0, ROCm 6.3.1): HSA_TOOLS_LIB=librocm-debug-agent.so.2 prints "Disassembly for function s_tvd_rk$m_time_steppers_$ck_L486_6" -- subroutine, module and source line of the injected fault -- plus the faulting instruction and per-wave registers, straight to the job log.

The earlier conclusion looked only at CCE's own trace and generalised from it to the machine. The information was available one layer down, at ROCr. Env-only: no recompile, no execution-model change.

Not enabled yet: its cost on a healthy run is being measured, and that decides always-on versus a documented recipe.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
HSA_TOOLS_LIB=librocm-debug-agent.so.2 is the only thing that names a faulting kernel on CCE. Measured on Frontier (CCE 19.0.0, ROCm 6.3.1): it prints "Disassembly for function s_tvd_rk$m_time_steppers_$ck_L486_6" -- the exact injected fault site -- with the faulting instruction and per-wave registers, where no CRAY_ACC_* variable names it at all. Enabled wherever the library is reachable.

The gate is evaluated per call, never at import. On Frontier the library is on disk the whole time but only reaches LD_LIBRARY_PATH once mfc.sh load runs, so an import-time gate reports "absent" on the one machine this is for, indistinguishably from Phoenix where it truly is missing. It probes for the file rather than dlopen'ing it, so testing the subprocess's environment does not load a debug agent into the harness.

The agent emits ~14k lines per fault, almost all of it one disassembly and register dump repeated per wave. summarize_rocm_debug_agent collapses that to ~37 lines. A fixed tail cannot substitute: on the real report the first 80 lines are one wave's registers and the last 80 another's, and the kernel name appears in neither. The stop-PC histogram is kept because the modal PC was a load while the fault is a write, so a single PC would name the wrong instruction.

Cost on a healthy run: 4.5645 ns/gp/eq/rhs against an agent-free spread of 4.5301-4.5614 -- 0.07% above a range 0.69% wide. That is n=1 by decision, not by measurement, and the comment says so.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
Every command in the investigation was --gpu acc. The OpenMP-offload lane was never built or run, so listing it as working was an inference sitting in a table of measurements.

The agent hooks ROCr, below both OpenACC and OpenMP offload, so it should fire either way -- but the claim is attribution, not firing. s_tvd_rk$m_time_steppers_$ck_L486_6 is CCE's OpenACC symbol mangling, and whether module, subroutine and line survive in the OpenMP-offload form is unverified. The summarizer is unaffected: its regex takes whatever the symbol is.

Also upgrades the fixture to the real report's format -- the "(Agent handle: ...)" clause, "End of disassembly." as terminator, and the blank line plus "scalar registers:" header before a wave's dump -- and adds a test pinning the field set the summarizer produced from the genuine 14,635-line log. Structure only: pinning the wave counts or PC histogram would encode one fault instead of testing the code.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
The regexes were written against ROCm 6.3.1 and silently produced '' for 65,210 lines of real 7.2.0 output -- on the AFAR lane, the very one they were meant to serve, with no error to explain it. Two format changes:

  wave line: 7.2.0 inserts kernel_code_entry= and kernargs= BETWEEN the pc and "(stopped, reason:", which the adjacency-requiring regex rejected.

  fault line: "OFFLOAD ERROR: memory access fault ... at virtual address ... Reasons:" instead of "Memory access fault ... on address ... Reason:", and the lookup was case-sensitive on "Memory".

Fixed at three sites; the fault line now reuses is_gpu_memory_fault, which already knows every wording, instead of hardcoding one version's. Both formats are pinned by fixtures built from real reports, and neither may be fixed at the other's expense.

This is the failure a single-version fixture cannot catch: it passes while the lane produces nothing. Found only because someone ran it against the real file.

Claude-Session: https://claude.ai/code/session_013573Qr8zEMdYLkP4XyVfiy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants