Skip to content

A provisional profile's heap check that grows the block keeps the profile's alias region out of the heap region (Ocarina of Time, issue #1361) - #1373

Closed
enjoyerofslop wants to merge 2 commits into
seedds:mainfrom
enjoyerofslop:fix/heap-check-keeps-alias
Closed

enjoyerofslop wants to merge 2 commits into
seedds:mainfrom
enjoyerofslop:fix/heap-check-keeps-alias

Conversation

@enjoyerofslop

@enjoyerofslop enjoyerofslop commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Fixes #1361 for builds made after it.

Symptom: the provisional profile's heap check reports "the heap request did not fit; set mem_reserve" and the build finishes. The first run still stops at SetHeapSize OOM: req 0x18ce00000 > cap 0xb8000000, then CRASH sig=10 (SIGBUS) in nninitStartup, on the heap it never got. Found on The Legend of Zelda: Ocarina of Time (Switch 2, 0400486024DEC000, update v131072, SDK 22.2.8).

Cause: sometimes the block has less than 1 GB left for the alias region beside the requested heap region. In that case heap_signals (dm/games/profile_guess.py) grew mem_reserve to 256 MB + 1 GB + the stack region + the request, as if the alias region were 1 GB, but it set only mem_reserve. The heap region is what follows the profile's alias region and stacks (hle_svc.c mem_init), so any alias region above 1 GB came off the heap region.

  • The Switch 2 guess for a game with a system resource is a 4.375 GB alias region (0x118000000).
  • Ocarina of Time asks for 0x18ce00000, and the check set mem_reserve 0x1f0000000.
  • That leaves the heap region at 0x1f0000000 − 0x10000000 − 0x118000000 − 0x10000000 = 0xb8000000.

Change: the grown block keeps the profile's alias region, at least 1 GB as before:
mem_reserve = 256 MB + max(mem_alias, 1 GB) + mem_stack + the request rounded up to 128 MB.

  • A profile whose alias region is 1 GB or less gets the same keys as before.
  • The branch that shrinks the alias region when the block has room (Tears of the Kingdom's 0x98000000) is unchanged.
  • Ocarina of Time's check now sets 0x2c8000000.

I kept the alias region rather than setting it to 1 GB: Ocarina of Time maps it up to +1.15 GB within two minutes of its title movie.

Reference entry: The heap check grows the block around the profile's alias region. The symptom index is regenerated.

Second commit, CI only: lint-python has been red on main since dd6ce5b, which added three more rule['cuts'][0] = … lines to test_nso_match.py (6 pyright reportIndexIssue findings against the baseline's 3). nso_match.recipe() is now annotated -> dict, as patch_recipe() beside it already is. All 6 findings go and the baseline shrinks by 3 (lint_python.py --update); no behavior changes. It's a separate commit, so it can be dropped if main fixes this first.

Checked

  • Tests:
    • python3 -m unittest dm/tools/tests/test_nso_match.py: 27 tests OK after the second commit, and lint_python.py --against main then reports 0 new findings.
    • python3 -m unittest dm/tools/tests/test_profile_guess.py: 31 tests OK. The new HeapSignals covers three cases: the Ocarina layout, a small alias region (old keys) and Tears of the Kingdom's shrink branch. Its Ocarina case fails on the old code with the 0xb8000000 heap region.
    • dm/tools/tests/test_docs.py and dm/tools/tests/test_nsp_incremental.py pass.
    • Not run: make -C dm/host/tests (no host code changed), so test_memory_profile.py's heap probe, which takes the other branch, was not run either.
  • Game run: The Legend of Zelda: Ocarina of Time, 0400486024DEC000, update v131072.
  • Devices and OS: M5 Max (Mac17,6), macOS 27.0.1.
  • regress.py: not run. The change only touches what a provisional profile's heap check writes. Hand-made profiles, the host and the shader chain are untouched.

Docs

  • The finding is in its docs/reference/memory.md entry, and python3 dm/tools/docs_symptoms.py was run (the symptom index)
  • No game files, keys or data extracted from a game in the change or the attachments

🤖 Generated with Claude Code

…file's alias region out of the heap region: Ocarina of Time's 6.20 GB request got 0xb8000000 (issue seedds#1361)

When a heap check run's request leaves the block not even 1 GB for the alias region, heap_signals grew mem_reserve to 256 MB + 1 GB + the stack region + the request, as if the alias region were 1 GB, but set only mem_reserve. The heap region is what follows the profile's alias region and stacks (hle_svc.c mem_init), so an alias region above 1 GB came off the heap region. The Switch 2 guess for a game with a system resource is 4.375 GB (0x118000000): The Legend of Zelda: Ocarina of Time (0400486024DEC000, update v131072, SDK 22.2.8) asks nninitStartup for 0x18ce00000, the check set mem_reserve 0x1f0000000, and the build's first run stopped at SetHeapSize OOM: req 0x18ce00000 > cap 0xb8000000, then SIGBUS in nninitStartup.

The grown block now keeps the profile's alias region, at least 1 GB as before: mem_reserve = 256 MB + max(mem_alias, 1 GB) + mem_stack + the request rounded up to 128 MB. A profile whose alias region is 1 GB or less gets the same keys as before, and the branch that shrinks the alias region when the block has room (Tears of the Kingdom) is unchanged. Ocarina of Time's check now sets 0x2c8000000: a host built with it and the 4.375 GB alias region logs [mem] heap 6.20 GB (heap region 6.25 GB) and reaches nnMain. test_profile_guess.py's HeapSignals checks the layout the keys give; its Ocarina case fails on the old code with the 0xb8000000 heap region.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added tools Tools and scripts (dm/tools, scripts) docs Documentation labels Nov 5, 2026
…, as patch_recipe() does

dd6ce5b added three more rule['cuts'][0] = ... assignments to test_nso_match.py. Pyright infers recipe()'s return from dict(name=..., cuts=[...]) as a dict whose values may be str, so each assignment is reportIndexIssue "__setitem__" method not defined on type "str": 6 against the baseline's 3, and the ratchet failed on main and on every pull request. recipe() is annotated -> dict like patch_recipe() beside it; no behavior changes. All 6 findings go, and the baseline shrinks by the 3 it listed (lint_python.py --update).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the rewriter The NSO rewriter and per-build patches label Nov 5, 2026
@bogdanandreifilip

Copy link
Copy Markdown

Thank you very much !

gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 5, 2026
…t grows the block keeps the profile's alias region out of the heap region (Ocarina of Time, issue seedds#1361)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
@seedds

seedds commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Landed as e9d1ef2 (lint) and eab0aaa (the fix), with your code and diagnosis and a Co-authored-by trailer. Thank you.

Three text changes on top, because #1373 predates e9d71e2, which put the heap and stacks before the alias region:

  • The test docstring and the memory.md entry now state the formula instead of the old region order. The formula itself holds on both layouts.
  • Since e9d71e2 the Mac's alias region is 64 GB whatever the profile says, so keeping the 4.375 GB alias and cutting it to 1 GB give the same layout on the Mac. The comment and the entry now say that it matters only to the region check and the iPhone, where a block this size is over the limit either way.
  • The recovery advice now says to delete the provisional profile. Deleting only the # heap check: line re-solves from the old block and gives a 1 GB alias region.

Checked on main: the real hle_svc.c compiled with the old keys logs SetHeapSize OOM: req 0x18ce00000 > cap 0xb8000000, and with the new ones grants the request ([mem] heap 6.20 GB (heap region 6.25 GB)). The host memory tests pass. No game was booted on the current layout.

@seedds seedds closed this Nov 6, 2026
fpedrolucas95 pushed a commit to fpedrolucas95/nxnative that referenced this pull request Nov 6, 2026
…ck that grows the block keeps the profile's alias region out of the heap region (Ocarina of Time, issue seedds#1361)
fpedrolucas95 added a commit to fpedrolucas95/nxnative that referenced this pull request Nov 6, 2026
…pool table checks kept, upstream's final seedds#1373 text taken

Taking upstream's side for its files in the replayed merges dropped memory.md's "The Mac's alias region is the Switch's 64 GB" and set t_pool_table back to the PR's checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
… layers, seedds#1345 and seedds#1373 landed)

Upstream's c83c48f makes tex_fit's views from layer counts, which covers the cube read as a 2D array, so NXForge's own version of that (f17b3e7) is reverted before this merge. hle_audren.c and its notes take upstream's landed seedds#1345. The failed command buffer report keeps both seedds#1382's segment names (cbn_close) and upstream's shader keys (cbs_feedback), and seedds#1332's gpu_lost_check. The present keeps NXForge's sidebar inset on upstream's present_fit. The translator binaries, lock and chain header are upstream's until the translator is rebuilt on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
) replaces the pull request heads

Upstream landed Ocarina of Time in main (chain adec7d81d970: "holds 60 fps in
Kokiri Forest, 4 GB less") and closed the pull requests it folded in, with
reworded and partly different code. Mixing a pull request head with its landed
version hunk by hunk silently broke things (two GlobalToStorage tables, a
duplicate AF2ip, two mtk_pso definitions), so the rule here is one version per
feature:

- Pull requests upstream closed (seedds#1299, seedds#1300, seedds#1312, seedds#1343, seedds#1345, seedds#1353,
  seedds#1354, seedds#1359, seedds#1360, seedds#1373, seedds#1428, seedds#1430, seedds#1434): their hunks are taken out
  of our side before the three-way merge, so upstream's landed version stands
  alone. Gone with them: bindless pool table patching (reg_bump), the ghost
  table's per-submission hold, the growing raw pool table and its write-log
  sizing, seedds#1428's pool_buf spares (upstream's pool_table_take is the same
  idea), the ixc-based indirect mesh draw (upstream uses mdx), and their tests
  (t_pool_table, t_mesh_indirect, t_gmem_raw, finalized_bindless_pool,
  test_bnsh_nvn2, test_ndve).
- Open pull requests that upstream's landing supersedes: seedds#1394 (upstream's
  mtl_pool_addressed) and seedds#1393 pool_skip_retired (upstream's budgeted pool
  walk; the profile does not use it) are dropped with their tests and docs.
  seedds#1368's mesh pipeline prewarm is dropped too: upstream kept the background
  build in nvn_geometry.inc, and the prewarm's mtk_build / mtk_claim /
  g_warm_mps went with it.
- Kept, as upstream does not have them: NXForge's own features (GPU watchdog,
  present filters and anti-aliasing, aniso floor, quick settings, pipeline
  archive compaction, upload breakdown, memory report lines) and the still
  open pull requests seedds#1304, seedds#1305, seedds#1308, seedds#1320, seedds#1331, seedds#1332, seedds#1355, seedds#1356,
  seedds#1369, seedds#1376, seedds#1377, seedds#1379, seedds#1382, seedds#1390, seedds#1391, seedds#1392, seedds#1414, seedds#1420,
  seedds#1436, seedds#1439, seedds#1446, seedds#1453.
- Combined where both sides changed the same lines: GW_N 1 << 16 with seedds#1320's
  wide-write intervals (GW_PAGES = SBW_WPAGES, 128 slots), the CPU upload log
  with upstream's linear upload route, arch_open_end before the compaction
  limit, cbn_prog in geo_gs_one (seedds#1382).
- Translator: upstream's sources and binaries, plus seedds#1414's
  InstEmitAmpereMem diff and the LDSM SubgroupBarrier in
  InstEmitAmpereTensor.cs. The binaries and dmsh_chain.lock are upstream's
  until the Mac rebuilds them (dotnet publish, tool_lock --update-lock,
  dmsh_chain --header); dmsh_chain.h is regenerated for now.
- RED_ONCE keys: upstream took 153 and 154, so the present anti-aliasing lines
  move to 156 and 157 and seedds#1420's packaged-entry line to 158 (array 159).
- Host: hle_net.c and t_net_eventfd.c are upstream's (its eventfd poll waits
  out the whole timeout, which our 5d7920c fixed another way).
- Kept ours: dm/tools/romfs_image.py and its test, so the 26 GB RomFS image is
  not packed again.

dm/tools tests on Linux: same failures as on 875449e (Linux-only quirks),
plus test_nlg4 and test_dmsh_pool_arrays, which fail on upstream main too.
Not yet built on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation rewriter The NSO rewriter and per-build patches tools Tools and scripts (dm/tools, scripts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ocarina of Time (0400486024DEC000) error [dm] SetHeapSize OOM: req 0x18ce00000 > cap 0xb8000000

3 participants