Skip to content

feat(hrr): cap capture size and blob size at a limit the user sets - #13895

Merged
andraghetti merged 9 commits into
developfrom
users/andraghetti/hrr-capture-size-limits
Nov 2, 2026
Merged

andraghetti merged 9 commits into
developfrom
users/andraghetti/hrr-capture-size-limits

Conversation

@andraghetti

@andraghetti andraghetti commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

HRR capture grows its archive for as long as the process runs. Today only the free-space reserve from #12256 bounds it. That reserve protects the file system, not the user's budget. The security-gap review asked for a size the user can set, and for a cap on a single blob. Both stop the same way the reserve does: the archive is marked incomplete and the application keeps running. The sliding-window ring buffer stays the mechanism for long runs. These limits apply whether or not it has landed.

Technical Details

  • Two new variables, both in MiB and both 0 (no limit) by default: HIP_HRR_CAPTURE_MAX_MB and HIP_HRR_CAPTURE_MAX_BLOB_MB. They are not CLR flags: writer::open() reads them from the environment once per process, in hip_capture_writer.cpp, so a forked child keeps its parent's limits.
  • HIP_HRR_CAPTURE_MAX_MB caps each process archive (pid-<pid>/). It counts events.bin, every blob and every code object. Blobs and code objects are counted in whole file system blocks, as the reserve counts them. Manifests and the trailer are not counted.
  • The cap is checked before each write, holding the writer's file mutex. The write that would pass the cap is not made, and capture stops under the same hold of the mutex that flush() takes to decide on the trailer: a cap-stopped archive is never finalized as complete. So, manifests aside, the archive never exceeds the limit.
  • At the cap, capture stops through the same path as the free-space stop. events.bin is flushed and closed, the active marker is removed, the archive is marked incomplete, and one [HRR capture] Capture stopped line goes to stderr.
  • The space stop and the cap stop share stop_capture_locked(). The stop flag is renamed from g_out_of_space to g_capture_stopped, because it now has two causes, and g_stopped_at_limit records which one it was. The incomplete reason names no path; only the stderr line names the archive, read under the writer's file mutex.
  • HIP_HRR_CAPTURE_MAX_BLOB_MB caps one blob. A larger copy is not hashed or stored. Its event is recorded with a zero blob hash, which is the existing "no blob" convention. The archive is marked incomplete, one line goes to stderr per archive, and capture continues. Replay refuses work recorded without its contents and returns hipErrorNotFound. That covers a host-to-device hipMemcpy, hipMemcpyAsync, hipMemcpyHtoD, hipMemcpyHtoDAsync or hipMemcpyWithStream, checked in replay_memcpy_impl(); a 2D or 3D host-to-device copy (hipMemcpy2D/2DAsync, hipMemcpy3D/3DAsync and their _spt forms, hipDrvMemcpy3D/Async, hipDrvMemcpy2DUnaligned, hipMemcpyParam2D/Async), checked in drvmemcpy_h2d_src_blob(); a hipHostRegister, checked in its handler; and a launch whose pinned host snapshot the cap left out, checked in restore_host_snapshots() unless the launch goes into a stream capture. A to-symbol graph copy from host memory (hipGraphAddMemcpyNodeToSymbol, hipGraphMemcpyNodeSetParamsToSymbol, hipGraphExecMemcpyNodeSetParamsToSymbol) is refused too, in to_symbol_source(), and returns hipErrorInvalidValue. A copy or registration refusal names the API and the size.
  • Code objects and fat binaries are exempt from the per-blob cap, because replay needs every one. Fat binaries go through writer::write_blob() with capped false, stored as blobs as before. The archive cap still bounds both. A hipLinkAddData image over the per-blob cap is left out, and replay refuses its link.
  • Counters reset in open(), so a forked child gets its own limit. A child forked after a cap stop still opens its own pid-<pid> archive with a fresh budget. After a space stop it opens none, as the file system is shared.
  • The cap applies to each pid-<pid> archive, not to the archive root across processes.
  • projects/hrr/README.md documents both variables.
  • Three new integration tests in hrr_disk_space_test.cc reuse its workload driver, with free space left at its default so only the limit can stop capture:
    • Unit_HRR_CaptureLimit_StopsAtSize: a 64 MiB cap and 128 distinct 1 MiB copies. Capture stops once and the archive is incomplete but not torn. Every recorded blob exists, and the archive holds at most 64 MiB and more than 62 MiB. The application still reads back correct data.
    • Unit_HRR_CaptureLimit_SkipsLargeBlob: a 1 MiB blob cap. Two 4 MiB copies are recorded without a blob, with one stderr line. A 4 MiB hipMemcpy2D, a 4 MiB hipHostRegister and two 4 MiB hipGraphAddMemcpyNodeToSymbol copies from pinned memory (hipMemcpyHostToDevice and hipMemcpyDefault) go over the cap too. A 256 KiB copy and its read-back are stored, and capture does not stop. Replaying the archive with --continue-on-error fails, with 2 hipMemcpy refusals, 1 hipMemcpy2D refusal, 1 hipHostRegister refusal and 2 hipGraphAddMemcpyNodeToSymbol refusals.
    • Unit_HRR_CaptureLimit_ForkAfterStop: the parent reaches a 64 MiB cap, then forks a child that records once. The child opens and finalizes its own pid-<child> archive, and the root manifest lists two processes.
  • Unit_HRR_FatBinHeaderChecks in hrr_fatbin_header_test.cc now runs with a 1 MiB blob cap. A 2 MiB bundle registered before HIP initialises is stored whole, and the archive stays complete.
  • Unit_HRR_PinnedHostBlobRefused in hrr_pinned_host_test.cc also captures its workload with HIP_HRR_CAPTURE_MAX_BLOB_MB=1. Replay must fail with the pinned host snapshot refusal and launch no kernel.
  • In hrr_host_budget_test.cc, the hipHostRegister record of Unit_HRR_HostBudget_HostAllocationHandlersRefuseOverBudget gets a non-zero blob hash, as capture gives it, so it still reaches the budget refusal.
  • Their hidden _Direct workloads skip when run without their driver's environment, as Unit_HRR_CaptureCapabilitiesImport_Direct does.

Issue Tracking

JIRA ID: AIRUNTIME-2732

Test Plan

  • HRR build with capture compiled into CLR, unit and integration ctest, then every Catch2 case one at a time, hidden cases included, on MI350X (gfx950). Head tested: ac2005c752, which merges develop cd8bec0684 and reads the two limits in HRR instead of flags.hpp; gate p13895-ac2005c GREEN against develop gdevcd8bec, with every non-passing case also failing on that develop. The table below and the negative controls are from the earlier head 671081f (gate p13895-671081f, against develop 4eb1b0f, gdev4eb1b0f).
  • Negative controls, same gate: 2b33af2df8, the integration tests of this head with the hipHostRegister and to-symbol refusals taken out of replay (p13895-neg-2b33af2), which fails only CaptureLimit_SkipsLargeBlob, at hrr_disk_space_test.cc:1177-1178; ffe85b0 with the runtime files (hip_capture_writer.cpp, hip_capture_writer.h and the two limits) and the README put back to develop (airuntime-2732-u-neg-dd23c1c), which fails CaptureLimit_SkipsLargeBlob and CaptureLimit_StopsAtSize; 30bf275 with fat binaries sent back through write_blob() (airuntime-2732-cf-neg-b715ebc), which fails FatBinOverBlobLimit; 4880be9 without the stop reset in a forked child (p13895-neg-b34523d), which fails CaptureLimit_ForkAfterStop; 0468fa5 without the replay refusal (p13895-neg-46a8317), which fails CaptureLimit_SkipsLargeBlob; 4277328 with fat binaries capped and no stop reset in a forked child (p13895-trimneg-bd8415c), which fails FatBinHeaderChecks and CaptureLimit_ForkAfterStop; 4277328 with a fat binary over the cap marking the archive incomplete (p13895-trimneg-e350e57), which fails FatBinHeaderChecks; afea389a43, 302841e with its fix reverted and the tests kept (nc13895c-afea389), which fails CaptureLimit_SkipsLargeBlob and PinnedHostBlobRefused.
  • Baseline: develop at 4eb1b0fa37, merged into this branch, same runner and same scripts (gdev4eb1b0f).
  • Not tested: Windows. The writer change is portable code, but it has not been built or run on Windows. A crash after a cap stop is not tested separately; it takes the same path as the space stop, which Unit_HRR_DiskSpace_CrashAfterStop covers. A flush() racing a cap stop has no test: no call a test can hook runs between the refusal and the stop, and they now share one critical section.
  • Not in this PR: the ring buffer and the on-fault window, and measuring a bounded archive on a long-running workload. Those stay with the sliding-window work.

Test Result

Check This PR (671081f) Negative control: no hipHostRegister or to-symbol refusal develop 4eb1b0f
Build, unit ctest pass pass pass
Integration ctest pass fails: CaptureLimit_SkipsLargeBlob pass
Unit Catch2 cases 81 of 81 pass 81 of 81 pass 81 of 81 pass
Visible integration cases 135 pass, 4 skip 134 pass, 1 fail, 4 skip 132 pass, 4 skip
Hidden integration cases 125 pass, 26 fail, 8 skip 125 pass, 26 fail, 8 skip 125 pass, 26 fail, 5 skip

Apart from the six new cases, every case has the same status as on develop. The 26 hidden cases that do not pass also fail on develop. Most are _Direct workloads that need their driver's setup. The three extra hidden skips are the new _Direct workloads, which skip without their driver's environment.

The gate also runs the hrr-generator-rules ctest. It passes on this head and on develop 4eb1b0f.

Submission Checklist

@andraghetti
andraghetti requested review from a team as code owners October 21, 2026 09:25
Copilot AI balanced review requested due to automatic review settings October 21, 2026 09:25
@therock-pr-bot

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

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

Fat-binary exclusion and shutdown synchronization need correction to preserve replay data and accurate archive completeness.

2 open issues
What changed in this PR

Adds user-configurable HRR capture limits alongside the existing free-space reserve.

Changes:

  • Adds per-process archive and per-blob size settings.
  • Reuses the capture-stop path for archive limits and skips oversized blobs.
  • Documents the settings and adds integration coverage.
File Description
projects/​hrr/​tests/​integration/​hrr_disk_space_test.cc Tests archive limits and oversized-blob handling.
projects/​hrr/​README.md Documents both size settings.
projects/​clr/​rocclr/​utils/​flags.hpp Declares the new runtime flags.
projects/​clr/​hipamd/​src/​hrr/​hip_capture_writer.h Updates writer contract comments.
projects/​clr/​hipamd/​src/​hrr/​hip_capture_writer.cpp Implements accounting, limits, and shared stop handling.

Review effort: Balanced

@danielbolsson danielbolsson 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.

Checked ffe85b0: events are charged by full record size, blobs and code objects by whole blocks, a write that would pass HIP_HRR_CAPTURE_MAX_MB is not made and capture stops through the free-space path, and HIP_HRR_CAPTURE_MAX_BLOB_MB skips a blob (not code objects) and marks the archive incomplete. Nothing blocking; red NVIDIA build is the develop check_config rule, not this PR.

@mangupta mangupta 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.

🤖 AI-assisted pre-review (for @mangupta)

@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (4880be9af0a7) — no new findings at or above the posting threshold.

@danielbolsson danielbolsson 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.

Re-approving the new head: fat binaries are exempt from the blob cap, the size-cap stop and trailer decision share one lock hold, and a child forked after the stop gets a fresh archive budget.

@andraghetti
andraghetti requested a review from a team October 22, 2026 11:46
Two runtime flags, both 0 (no limit) by default, bound how large a capture
grows, on top of the free-space reserve:

- HIP_HRR_CAPTURE_MAX_MB caps each process archive: events.bin plus every
  blob and code object, counted in file system blocks as the reserve
  counts them. The write that would pass the limit is not made and, under
  the same hold of g_file_mu, capture stops as it does for space
  (events.bin flushed and closed, active marker removed, archive marked
  incomplete, one stderr line); the application runs on. A child forked
  after that stop opens an archive of its own, held to the same limit,
  while a stop for space carries over to the child, as the file system is
  shared.
- HIP_HRR_CAPTURE_MAX_BLOB_MB caps one blob. A larger copy is recorded
  without its contents (zero hash), the archive is marked incomplete with
  one stderr line, and capture goes on. Code objects and the fat binaries
  HIP registers are not held to it; the archive limit still counts them.
  Replay refuses a host-to-device hipMemcpy, hipMemcpyAsync,
  hipMemcpyHtoD, hipMemcpyHtoDAsync or hipMemcpyWithStream recorded
  without its contents, naming the API and the size, with
  hipErrorNotFound.

Both stops go through stop_capture_locked(), whose incomplete reason
names no path.

New integration cases, added to integration.yaml:
Unit_HRR_CaptureLimit_StopsAtSize, Unit_HRR_CaptureLimit_ForkAfterStop,
Unit_HRR_CaptureLimit_SkipsLargeBlob and Unit_HRR_FatBinOverBlobLimit,
each with its _Direct workload. Their capture directories are
per-process (hrr_temp_dir()).

Jira: AIRUNTIME-2732
@andraghetti
andraghetti force-pushed the users/andraghetti/hrr-capture-size-limits branch from 4880be9 to 0468fa5 Compare October 23, 2026 12:18
# Conflicts:
#	projects/hrr/playback/hip_playback.cpp
@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (8971e5634d77) — no new findings at or above the posting threshold.

@danielbolsson danielbolsson 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.

Checked 8971e56: capture is unchanged since my approval of 4880be9 apart from comments, and replay now refuses a host-to-device copy recorded without contents, by API name with hipErrorNotFound; the new test pins the message and the exit code. Nothing blocking.

…checks

- write_blob() and write_fat_binary() only forwarded to store_blob().
  write_blob() now takes `capped`, true by default, and the fat binary
  path passes false.
- Unit_HRR_FatBinOverBlobLimit was a near-copy of
  Unit_HRR_FatBinHeaderChecks. That driver now sets
  HIP_HRR_CAPTURE_MAX_BLOB_MB=1, and its workload registers the 2 MiB
  bundle before HIP initialises. The bundle registered after went through
  the same write, so it is gone.
- Unit_HRR_CaptureLimit_ForkAfterStop: the child's events.bin and
  manifest.json checks go. The driver's count of two processes needs both.
- Unit_HRR_CaptureLimit_StopsAtSize: `copies > 0` goes. It passes with
  the limit reverted.

AIRUNTIME-2732: scope unchanged; one writer entry point and one test
case fewer, every limit and its negative control kept.
@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (4277328ba1e2) — no new findings at or above the posting threshold.

@andraghetti
andraghetti requested a review from saleelk October 26, 2026 15:46

@danielbolsson danielbolsson 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.

Re-checked 4277328 on top of my approval of 8971e56: folding write_fat_binary into write_blob(..., capped=false) leaves the fat-binary path uncapped and every other caller capped, with no reference to the old names left. Nothing blocking.

With HIP_HRR_CAPTURE_MAX_BLOB_MB set, replay skipped a 2D or 3D
host-to-device copy whose contents capture had left out and returned
hipSuccess, and a launch silently lost the pinned host snapshot the cap
dropped. Replay now refuses such a copy by name, as it already did for
the 1D copies, and capture records a left-out snapshot with a zero hash
so replay refuses the launch that reads it. The hip_capture.cpp comment
on which capture knobs live in flags.hpp matches the new flags.

AIRUNTIME-2732
@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (302841e387ec) — no new findings at or above the posting threshold.

@danielbolsson danielbolsson 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.

Checked the delta since 4277328: replay now refuses 2D/3D H2D copies, pinned snapshots, hipHostRegister and to-symbol graph copies whose blob the cap left out, matching what capture records (zero hash, same memcpy_src_is_host rule), and hipLinkAddData still refuses via its missing blob. The rocprofiler-sdk failure is unrelated.

@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (671081faeee6) — no new findings at or above the posting threshold.

…OB_MB in HRR instead of the CLR flag registry

The two size limits leave rocclr/utils/flags.hpp. writer::open() reads them
with getenv, parsed as the registry parses a size_t, once per process, as
hip_capture.cpp reads HIP_HRR_HOST_SNAPSHOTS and HIP_HRR_HOST_SNAPSHOT_MAX_MB.
Same names, same default of 0 (no limit), same meaning.

JIRA ID: AIRUNTIME-2732
# Conflicts:
#	projects/hrr/tests/include/hrr_test_common.hh

@danielbolsson danielbolsson 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.

Review — needs work

Review of changes since my earlier review at 671081f (one new PR commit, 4011ace, plus develop merges), with the PR's CLR diff against its merge-base also read in full. I did not re-read hip_playback.cpp or the test files.
Intent: AIRUNTIME-2732, implemented in full by the code. The description's account of how it is implemented is out of date (first Blocking item).

Blocking

  • The description states the old design: "Two new CLR flags in flags.hpp" — PR body, Technical Details first bullet, Test Plan, Test Result.
    • 4011ace deletes both release(size_t, ...) entries from projects/clr/rocclr/utils/flags.hpp. The PR no longer touches flags.hpp at all. writer::open() now reads the two variables with getenv/atol in env_limit_bytes() (hip_capture_writer.cpp:1041-1047, 1270-1271).
    • The body still says the flags are in flags.hpp. It also names flags.hpp among the runtime files in the ffe85b0bba negative control, which now describes a diff that does not exist.
    • The Test Plan and Test Result table are pinned to head 671081faee and gate p13895-671081f. The current head is ac2005c752. The "GREEN, no new failures" claim does not cover the reworked read path (static const once-per-process parsing).
    • Fix: rewrite the first bullet to say the limits are read from the environment by the writer. Drop flags.hpp from the control descriptions. Re-run the gate on the current head, or say the results are for 671081f and that 4011ace is untested. Confidence 90 · logic.
  • Check rocprofiler-sdk (gfx94X-dcgpu) failed, and therock-pr-bot failed on this commit — statusCheckRollup.
    • I read no log that attributes either failure, and the HRR checks pass. I could not tell whether the failures are related to this change.
    • Method rule: a failing check is blocking until shown unrelated. Show it is a baseline failure on develop, or fix it. The rccl gfx94X test was still pending, so it is not counted. Confidence 70 · mechanical.

Non-blocking

  • The limits are fixed at the first open() and cannot change in-process — hip_capture_writer.cpp:1270-1271.
    • Behaviour changed from the flag registry. A test or tool that sets HIP_HRR_CAPTURE_MAX_MB in-process after the first open() is silently ignored. Every use I found sets it in a subprocess environment, so nothing breaks today. The comment says only "forked child keeps the limits", not that later environment changes are ignored. Confidence 70 · logic. This is a documentation point, not a defect.

Decisions for the author

  • Environment read instead of a registered flag — tradeoff.
    • For: keeps HRR-only knobs in HRR code, consistent with HIP_HRR_HOST_SNAPSHOTS and HIP_HRR_HOST_SNAPSHOT_MAX_MB in hip_capture.cpp:246-254. It also keeps the PR out of rocclr.
    • Against: the knobs lose the registry's single place for flags, listing and config-file handling.
    • atol matches the registry's Tsize_t parsing (flags.cpp:183), so a negative or garbage value behaves as before: it clamps to the largest limit, effectively none. Confirm this was intended.

Positive signals

  • The stale-comment sweep in 4011ace is complete. hip_capture.cpp:135-142 is reworded, and git grep finds no remaining reference to the limits living in flags.hpp.
  • reserve_space() and write_event_raw() charge the limit and call stop_at_limit_locked() under the same g_file_mu hold that flush() takes to decide on the trailer, so an archive that dropped a write is never finalized as complete.

Deployment notes

  • HIP_HRR_CAPTURE_MAX_MB and HIP_HRR_CAPTURE_MAX_BLOB_MB no longer appear in flags.hpp, so they are not in the runtime's flag listing. The name, default (0, no limit) and meaning are unchanged, and the README table documents them.

What this covered

  • Read: the full CLR-side PR diff (hip_capture.cpp, hip_capture_writer.cpp, hip_capture_writer.h) against merge-base cd8bec0 through head ac2005c. I also read commit 4011ace and the PR description, and compared the changed files against the pinned results.
  • Degraded: hip_playback.cpp, the tests and DESIGN.md were not re-read this round. No worker fan-out was done, so this is a single-context pass. The earlier findings are not carried into it.
  • Not run: the history and prior-changes passes; I attempted no build or test.
  • Check not made: I could not attribute the rocprofiler-sdk or therock-pr-bot failures; the pulled logs were not useful.
  • Not reconciled against the PR discussion or the earlier report. I could not check whether my earlier findings were answered, so whether the new commit resolves them is open.

@mangupta

Copy link
Copy Markdown
Contributor

🤖 AI-assisted pre-review (for @mangupta)

Re-reviewed the updated head (ac2005c75232) — no new findings at or above the posting threshold.

@danielbolsson danielbolsson 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.

Not approving yet: the PR description is still stale and I could not tie the failing checks to develop. Two items:

  • PR body, Technical Details bullet 1, Test Plan, Test Result: still says the limits are CLR flags in projects/clr/rocclr/utils/flags.hpp and pins the gate to 671081f; the PR no longer touches flags.hpp (hip_capture_writer.cpp:1041-1047, :1270-1271 read them with getenv) and the head is ac2005c. Fix: say the writer reads the env vars, drop flags.hpp from the ffe85b0 control, and re-run the gate on the current head or state 4011ace is untested.
  • rocprofiler-sdk (gfx94X) and therock-pr-bot fail at this head; show they also fail on develop (the HRR checks pass).

@andraghetti
andraghetti merged commit 6b8765e into develop Nov 2, 2026
413 of 417 checks passed
@andraghetti
andraghetti deleted the users/andraghetti/hrr-capture-size-limits branch November 2, 2026 11:44
@peymanr

peymanr commented Nov 2, 2026

Copy link
Copy Markdown

PR Pundit review

(Testing PR Pundit Code Review agent. Not all comments may be relevant. This is a one-time automated pass: please address what is useful, no need to request a re-review. Please reply to each point, whether it applies or not: we harvest these replies, and both kinds are enormously helpful for tuning the agent. Reach out to @peymanr for suggestions.)

# Finding Where
1 🟠 Replay silently skips D2H read-back validation: this PR capped D2H blobs with hash {0,0} projects/clr/hipamd/src/hrr/hip_capture.cpp:893
2 🟠 Default-kind pageable to-symbol copy over blob cap reports misleading 'source not mapped' and continues: this PR added left-out check after translate_ptr projects/hrr/playback/hip_playback.cpp:1984-2001
3 🟠 Mistyped cap env value runs capture unbounded with no warning: this PR parses with unchecked std::atol projects/clr/hipamd/src/hrr/hip_capture_writer.cpp:1043
4 🟠 Capture stops early at HIP_HRR_CAPTURE_MAX_MB after failed writes: this PR charges g_archive_bytes with no refund projects/clr/hipamd/src/hrr/hip_capture_writer.cpp:1052
5 🟠 Each capped launch wastes a pinned-memory copy under g_pinned_mu: this PR enforces the blob cap only inside write_blob projects/clr/hipamd/src/hrr/hip_capture.cpp:321-329

1. 🟠 Should fix: Replay silently skips D2H read-back validation: this PR capped D2H blobs with hash {0,0}

projects/clr/hipamd/src/hrr/hip_capture.cpp:893

With capped=true as the default, HIP_HRR_CAPTURE_MAX_BLOB_MB also drops device-to-host read-back blobs (hipMemcpy/hipMemcpyAsync D2H, hipMemcpyDtoH/hipMemcpyDtoHAsync), recording them with hash {0,0}. Capture reports this once on stderr and marks the archive incomplete. Replay treats {0,0} as 'no check expected': the read-back is not compared and not counted in d2h_attempted, and replay itself says nothing about it, so it can exit 0 having validated fewer outputs. The README lists only the host-to-device copies replay refuses and is silent on read-backs. The async paths also still run sync_for_d2h_snapshot for a blob that is then dropped.

Who is affected: Users who set HIP_HRR_CAPTURE_MAX_BLOB_MB and capture device-to-host copies larger than it, then rely on replay's validation result.

Checked against the code by an independent pass.

2. 🟠 Should fix: Default-kind pageable to-symbol copy over blob cap reports misleading 'source not mapped' and continues: this PR added left-out check after translate_ptr

projects/hrr/playback/hip_playback.cpp:1984-2001

In to_symbol_source(), a to-symbol graph copy from pageable host memory whose blob was left out over HIP_HRR_CAPTURE_MAX_BLOB_MB is not refused as the README says. translate_ptr(a->src) runs before the left-out check, fails on the host address and notes a miss. With hipMemcpyDefault, from_host needs a non-null src, so the message is the misleading 'device source ... is not mapped'. With hipMemcpyHostToDevice the 'captured without their contents' line prints, but the noted miss still makes dispatch_event treat the event as unmapped: by default it is discarded and replay continues, where the other left-out copies return hipErrorNotFound and stop replay unless --continue-on-error is set. The new SkipsLargeBlob workload copies only from hipHostMalloc memory, so this path is untested.

Who is affected: Replays of archives captured with HIP_HRR_CAPTURE_MAX_BLOB_MB set, where a graph to-symbol copy from pageable host memory exceeded the limit.

Checked against the code by an independent pass.

3. 🟠 Should fix: Mistyped cap env value runs capture unbounded with no warning: this PR parses with unchecked std::atol

projects/clr/hipamd/src/hrr/hip_capture_writer.cpp:1043

env_limit_bytes() parses HIP_HRR_CAPTURE_MAX_MB / HIP_HRR_CAPTURE_MAX_BLOB_MB with std::atol, as the CLR flags registry parses a size_t, without validation. A negative or non-numeric value (-1, abc, 0.5) silently gives no limit, and a unit suffix is ignored (1G means 1 MiB). A too-small limit is not silent: capture stops with a [HRR capture] Capture stopped message naming the limit. Rejecting or warning about a value that does not parse as a non-negative integer would make a mistyped cap visible.

Who is affected: Users who set HIP_HRR_CAPTURE_MAX_MB or HIP_HRR_CAPTURE_MAX_BLOB_MB to a negative, fractional or non-numeric value.

Checked against the code by an independent pass.

4. 🟠 Should fix: Capture stops early at HIP_HRR_CAPTURE_MAX_MB after failed writes: this PR charges g_archive_bytes with no refund

projects/clr/hipamd/src/hrr/hip_capture_writer.cpp:1052

charge_archive() counts a blob or code object toward HIP_HRR_CAPTURE_MAX_MB in reserve_space() before the write, and g_archive_bytes is never reduced. When atomic_write_file or claim_sub_dir then fails, finish_claimed_write unpublishes the key and capture goes on. A later copy with the same hash is charged again, so the counter can exceed the bytes on disk and capture can stop at the limit early. The other paths named (free-space refusal, an over-limit charge, the archive finalized after the reserve) stop or finalize capture, so the extra count there has no effect. Consider refunding the charge when a claimed write fails.

Who is affected: Users who set HIP_HRR_CAPTURE_MAX_MB and whose blob or code-object writes fail with I/O errors during capture.

Checked against the code by an independent pass.

5. 🟠 Should fix: Each capped launch wastes a pinned-memory copy under g_pinned_mu: this PR enforces the blob cap only inside write_blob

projects/clr/hipamd/src/hrr/hip_capture.cpp:321-329

With HIP_HRR_CAPTURE_MAX_BLOB_MB set below HIP_HRR_HOST_SNAPSHOT_MAX_MB, pinned_scan_locked() still copies a pinned allocation between the two caps under g_pinned_mu on every launch that references it. write_blob() then refuses the copy for being over the blob cap, so the copy is wasted. This is not a regression: the same copy was made before this PR, and the capped path now skips the hashing and writing that the uncapped path does. Checking the blob cap in pinned_scan_locked() before allocating would avoid the copy. Re-recording {0,0} on each launch is intended, so that replay can refuse those launches.

Who is affected: Users who opt into HIP_HRR_CAPTURE_MAX_BLOB_MB below the pinned snapshot cap and launch kernels that reference pinned allocations sized between the two caps.

Checked against the code by an independent pass.

Shared from a pilot of PR Pundit, an agentic code reviewer that builds and tests the change and reads the project's past reviews. Reviewed at ac2005c7; findings checked and posted by @peymanr; points already raised on this PR are left out.

@systems-assistant

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants