feat(scripts): memtour.py, a memory tour through the control bridge #570

Open
tonesi wants to merge 1 commit from tonesi/Longshot:feat/memtour into main
Contributor

Type: feat · Risk: low · Tested on: Linux, RTX 3070 Ti · Needs: none

Why

"The game outgrows my RAM and the swap fills" (issue 388) can only be answered, and a fix only checked, with the same tour run the same way on each PC and build. This is that tour as one command, on top of the control bridge, so a tester needs no private harness. See #388. The tracker was searched (issues and pull requests, all states, for memory, RAM, swap, tour, sampler, control bridge): none adds a memory tour. Related, not the same: #562 shows the game's own CPU and RAM in an FPS-counter page and writes them to a file from inside the game, with no swap, no shared-memory split and no tour; this reads /proc from outside, over a scripted route. #416 is the change that was measured with this tour first.

What changed

A new script starts a game folder through the control bridge, loads a save, walks through some areas and writes down the game's memory as it goes. Its test needs no game.

  • scripts/memtour.py (new): starts the game with longshot_ctl.py's start_session (test profile: headless, a copy of the save, the source only read), presses A until the save is loaded (Tour.to_play), teleports through --dests (default 13,1,2,3,0,7,9,11, or all) with a walk and camera swings in each, then quits. Sampler writes mem.csv every 2 s from /proc/<pid>: RssAnon, RssShmem, swap (anonymous and shared), game_total (resident + swapped, file pages left out), MemAvailable, swap in use. marks.csv has the tour's events; summary.md (summarize) the peaks and each area's peak, end value and lowest MemAvailable. Linux only: it reads /proc and says so elsewhere.
  • tests/test_memtour.py (new): the sampler against a made-up /proc; the tour against a fake bridge (A presses until the save loads, a refused teleport retried, one refused for good skipped, a save that never loads, an error reply); the summary's numbers per area.
  • docs/development.md: one row in the commands table.

Measurements

Verdict: nothing to measure: a script and its test, and no game code changes. (One real run of the script is under Testing.)

Flags and settings

None.

Testing

Tested: on Linux (RTX 3070 Ti 8 GB, 15 GB RAM), python -I -B tests/run_all.py: 483 passed, 5 failed, 84 skipped (no clang, Vulkan or SPIR-V headers here). Four of the failures also fail on a clean forge/main (test_oot_saves, test_bake_portability, test_cpm_discovery, test_walk_key); test_crash_record failed once under 8 parallel checks and passes alone. test_memtour.py, test_longshot_ctl.py and test_control.py pass (after the rebase onto 8b619764, the first two again). In the game, one run: python -I scripts/memtour.py <game folder built from main 0caf0618> --save <Zora's River save> --dests 13,1 --dwell 15 --settle 10 loaded the save, teleported to Zora's Domain and Hyrule Field, quit cleanly and wrote the three files (peak game total 9,491 MB, lowest MemAvailable 840 MB, 39 samples).

Not tested: Windows (the script refuses, by design); a first start that prepares shaders before the first frame (--launch-timeout is there for it); runs of more than a few minutes through this script (the long tours behind issue 388 used a private harness that this replaces).

Must be tested: None.

Risks

What could break: nothing the game or the build uses: a new script, its test and a docs row. The test is discovered by run_all.py and runs in under a second.

Turning it off: revert the commit.

Compatibility: Nothing changes.

Issues

See #388

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A game-free test added for the fix, failing on main and passing here.
  • Docs updated.

🤖 Generated with Claude Code

**Type:** feat · **Risk:** low · **Tested on:** Linux, RTX 3070 Ti · **Needs:** none ## Why "The game outgrows my RAM and the swap fills" (issue 388) can only be answered, and a fix only checked, with the same tour run the same way on each PC and build. This is that tour as one command, on top of the control bridge, so a tester needs no private harness. See https://git.magicbean.dev/MagicBeanDev/Longshot/issues/388. The tracker was searched (issues and pull requests, all states, for memory, RAM, swap, tour, sampler, control bridge): none adds a memory tour. Related, not the same: https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/562 shows the game's own CPU and RAM in an FPS-counter page and writes them to a file from inside the game, with no swap, no shared-memory split and no tour; this reads `/proc` from outside, over a scripted route. https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/416 is the change that was measured with this tour first. ## What changed A new script starts a game folder through the control bridge, loads a save, walks through some areas and writes down the game's memory as it goes. Its test needs no game. - `scripts/memtour.py` (new): starts the game with `longshot_ctl.py`'s `start_session` (test profile: headless, a copy of the save, the source only read), presses A until the save is loaded (`Tour.to_play`), teleports through `--dests` (default `13,1,2,3,0,7,9,11`, or `all`) with a walk and camera swings in each, then quits. `Sampler` writes `mem.csv` every 2 s from `/proc/<pid>`: RssAnon, RssShmem, swap (anonymous and shared), `game_total` (resident + swapped, file pages left out), MemAvailable, swap in use. `marks.csv` has the tour's events; `summary.md` (`summarize`) the peaks and each area's peak, end value and lowest MemAvailable. Linux only: it reads `/proc` and says so elsewhere. - `tests/test_memtour.py` (new): the sampler against a made-up `/proc`; the tour against a fake bridge (A presses until the save loads, a refused teleport retried, one refused for good skipped, a save that never loads, an error reply); the summary's numbers per area. - `docs/development.md`: one row in the commands table. ## Measurements **Verdict:** nothing to measure: a script and its test, and no game code changes. (One real run of the script is under Testing.) ### Flags and settings None. ## Testing **Tested:** on Linux (RTX 3070 Ti 8 GB, 15 GB RAM), `python -I -B tests/run_all.py`: 483 passed, 5 failed, 84 skipped (no clang, Vulkan or SPIR-V headers here). Four of the failures also fail on a clean `forge/main` (`test_oot_saves`, `test_bake_portability`, `test_cpm_discovery`, `test_walk_key`); `test_crash_record` failed once under 8 parallel checks and passes alone. `test_memtour.py`, `test_longshot_ctl.py` and `test_control.py` pass (after the rebase onto `8b619764`, the first two again). In the game, one run: `python -I scripts/memtour.py <game folder built from main 0caf0618> --save <Zora's River save> --dests 13,1 --dwell 15 --settle 10` loaded the save, teleported to Zora's Domain and Hyrule Field, quit cleanly and wrote the three files (peak game total 9,491 MB, lowest MemAvailable 840 MB, 39 samples). **Not tested:** Windows (the script refuses, by design); a first start that prepares shaders before the first frame (`--launch-timeout` is there for it); runs of more than a few minutes through this script (the long tours behind issue 388 used a private harness that this replaces). **Must be tested:** None. ## Risks **What could break:** nothing the game or the build uses: a new script, its test and a docs row. The test is discovered by `run_all.py` and runs in under a second. **Turning it off:** revert the commit. **Compatibility:** Nothing changes. ## Issues See https://git.magicbean.dev/MagicBeanDev/Longshot/issues/388 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(scripts): memtour.py, a memory tour through the control bridge
Some checks failed
beanstalk/approval awaiting maintainer approval
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
ef5809ac42
Starts a game folder through longshot_ctl.py (test profile, a copy of a save),
presses A until the save is loaded, teleports through areas with a walk in
each, and samples the game's resident + swapped memory, MemAvailable and swap
every 2 s. Writes mem.csv, marks.csv and a summary.md with the peaks and each
area's numbers. For "the game outgrows my RAM" reports (issue 388) and for A/B
runs of a change that should move it. Linux only (reads /proc).

tests/test_memtour.py covers the sampler (a made-up /proc), the tour (a fake
bridge: A presses, refused teleports, a save that never loads) and the summary,
with no game.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Author
Contributor

Measurements section filled in (a script: measured by its game-free checks and one real run), 'Measured' ticked, no testers needed.

Measurements section filled in (a script: measured by its game-free checks and one real run), 'Measured' ticked, no testers needed.
tonesi force-pushed feat/memtour from ef5809ac42
Some checks failed
beanstalk/approval awaiting maintainer approval
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 33da09baab
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 15s
beanstalk/approval Test farm not requested
Website / Website on Cloudflare (pull_request_target) Has been skipped
2026-11-14 20:19:29 +00:00
Compare
Author
Contributor

Rebased onto 8b619764 and the description rewritten to the new template. Measured: a script, so its verdict says there is nothing to measure; no testers needed. Tracker searched; PR 562 (in-game system readings) named as related, not overlapping.

Rebased onto 8b619764 and the description rewritten to the new template. Measured: a script, so its verdict says there is nothing to measure; no testers needed. Tracker searched; PR 562 (in-game system readings) named as related, not overlapping.
tonesi changed title from WIP: feat(scripts): memtour.py, a memory tour through the control bridge to feat(scripts): memtour.py, a memory tour through the control bridge 2026-11-14 20:21:10 +00:00
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 15s
beanstalk/approval Test farm not requested
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/memtour:tonesi-feat/memtour'
git switch 'tonesi-feat/memtour'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!570
No description provided.