feat: crash reports sent from the game to a bot (with consent) #81

Open
opened 2026-11-13 02:23:14 +00:00 by HumanBean · 1 comment
Owner

Why

When Longshot crashes, the player has to find the log, open an issue or a Discord message, and paste it by hand. Most
don't, so we only hear about a fraction of crashes, often without the version, the GPU or the backtrace. A bot that
receives a crash report straight from the game would tell us how often each crash happens and on which hardware.

What we already have

On a crash, the handler in host/mkw_glue.c (report_crash) writes to the log:

  • CRASH: exception <code> (<read|write> <addr>, guest <addr>) at <module>+<offset>
  • CRASH: host backtrace: <module>+<offset> ... (x64 unwind data on Windows, backtrace() on Linux)

and log_crash_filter (host/hos_thread.c) drains the log queue before the process dies. So the summary of a crash
is already there; it never leaves the PC.

Proposal

  1. In the game, after a crash: on the next start (not in the crashing process), offer to send the report:
    "Longshot closed unexpectedly last time. Send a crash report?" with Send, Don't send and Always send.
    Nothing is sent without that consent, and it can be turned off in the settings.
  2. What a report holds, shown to the player before sending:
    • the CRASH: lines and the last ~200 lines of the log before them
    • Longshot's version and commit, the OS, the GPU, its driver and VRAM, the settings that differ from the defaults
    • an optional comment ("what were you doing?") and an optional contact (Discord name)
  3. What it never holds: game files or anything made from them (CONTRIBUTING.md), save data, keys, or personal
    paths: user names in paths become <user>.
  4. Where it goes: a small endpoint on the Magic Bean server (next to the relay; MagicBeanDev/Infra), which:
    • rate-limits by IP and caps the report size
    • groups reports by a signature: the exception code plus the top frames of the backtrace
    • posts the first report of each signature to a Discord #crash-reports channel, and counts the rest on the same
      message (versions, GPUs and OSes seen)
    • opens a Forgejo issue (label crash) once a signature reaches a few reports, linking the Discord message, and
      comments on it when a new version still crashes the same way
  5. Symbols: the backtrace is module+offset. Each release keeps its symbol map so the endpoint (or a script) can
    turn the top frames into function names for the post.

Open questions

  • Minidumps: send one too (much more useful for debugging, but larger and may hold memory from the game), or the log
    only?
  • Should nightlies report as well (most crashes would come from them), and should they be told apart in the channel?
  • Sign the request with a per-build key, or accept anonymous reports and rely on rate limits?

Done when

  • A crash in a test build, then a restart and Send, shows up in #crash-reports with its version, hardware and
    backtrace, and nothing is sent without consent.
  • A second report of the same crash updates the count instead of posting again.
  • tests/ has a check that the report builder leaves out paths and game data (no game needed).
## Why When Longshot crashes, the player has to find the log, open an issue or a Discord message, and paste it by hand. Most don't, so we only hear about a fraction of crashes, often without the version, the GPU or the backtrace. A bot that receives a crash report straight from the game would tell us how often each crash happens and on which hardware. ## What we already have On a crash, the handler in `host/mkw_glue.c` (`report_crash`) writes to the log: - `CRASH: exception <code> (<read|write> <addr>, guest <addr>) at <module>+<offset>` - `CRASH: host backtrace: <module>+<offset> ...` (x64 unwind data on Windows, `backtrace()` on Linux) and `log_crash_filter` (`host/hos_thread.c`) drains the log queue before the process dies. So the summary of a crash is already there; it never leaves the PC. ## Proposal 1. **In the game, after a crash:** on the next start (not in the crashing process), offer to send the report: "Longshot closed unexpectedly last time. Send a crash report?" with *Send*, *Don't send* and *Always send*. Nothing is sent without that consent, and it can be turned off in the settings. 2. **What a report holds**, shown to the player before sending: - the `CRASH:` lines and the last ~200 lines of the log before them - Longshot's version and commit, the OS, the GPU, its driver and VRAM, the settings that differ from the defaults - an optional comment ("what were you doing?") and an optional contact (Discord name) 3. **What it never holds:** game files or anything made from them (CONTRIBUTING.md), save data, keys, or personal paths: user names in paths become `<user>`. 4. **Where it goes:** a small endpoint on the Magic Bean server (next to the relay; `MagicBeanDev/Infra`), which: - rate-limits by IP and caps the report size - groups reports by a signature: the exception code plus the top frames of the backtrace - posts the first report of each signature to a Discord `#crash-reports` channel, and counts the rest on the same message (versions, GPUs and OSes seen) - opens a Forgejo issue (label `crash`) once a signature reaches a few reports, linking the Discord message, and comments on it when a new version still crashes the same way 5. **Symbols:** the backtrace is module+offset. Each release keeps its symbol map so the endpoint (or a script) can turn the top frames into function names for the post. ## Open questions - Minidumps: send one too (much more useful for debugging, but larger and may hold memory from the game), or the log only? - Should nightlies report as well (most crashes would come from them), and should they be told apart in the channel? - Sign the request with a per-build key, or accept anonymous reports and rely on rate limits? ## Done when - A crash in a test build, then a restart and *Send*, shows up in `#crash-reports` with its version, hardware and backtrace, and nothing is sent without consent. - A second report of the same crash updates the count instead of posting again. - `tests/` has a check that the report builder leaves out paths and game data (no game needed).
Collaborator

Crash: 0xc0000005 (read) at livetest.exe+0xdead in 0.0.0-livetest

8184a6f2e233 · seen 3 times · first 2026-11-13 02:39 UTC · last 2026-11-13 02:41 UTC

Seen on
Versions 0.0.0-livetest (3)
Operating systems Live test from Bean-Ops (Linux) (2), Live test from Bean-Ops (Windows 11) (1)
Renderers Vulkan (2), WebGPU (1)
GPUs Test GPU (not a real crash) (3)

First report

Version 0.0.0-livetest
Operating system Live test from Bean-Ops (Linux)
Renderer Vulkan
Map none (live test)
Graphics card and driver Test GPU (not a real crash)
CPU and RAM Test CPU

Crash

CRASH: exception 0xc0000005 (read 0x0, guest 0x0) at livetest.exe+0xdead
CRASH: this is a Bean-Ops live test, not a real crash
The log before it
Bean-Ops crash-relay live test
open ~/Games/Longshot/save.dat
### Crash: 0xc0000005 (read) at `livetest.exe+0xdead` in `0.0.0-livetest` `8184a6f2e233` · **seen 3 times** · first 2026-11-13 02:39 UTC · last 2026-11-13 02:41 UTC | Seen on | | |---|---| | Versions | `0.0.0-livetest` (3) | | Operating systems | `Live test from Bean-Ops (Linux)` (2), `Live test from Bean-Ops (Windows 11)` (1) | | Renderers | `Vulkan` (2), `WebGPU` (1) | | GPUs | `Test GPU (not a real crash)` (3) | <!-- first-report --> **First report** | | | |---|---| | Version | `0.0.0-livetest` | | Operating system | `Live test from Bean-Ops (Linux)` | | Renderer | `Vulkan` | | Map | `none (live test)` | | Graphics card and driver | `Test GPU (not a real crash)` | | CPU and RAM | `Test CPU` | **Crash** ``` CRASH: exception 0xc0000005 (read 0x0, guest 0x0) at livetest.exe+0xdead CRASH: this is a Bean-Ops live test, not a real crash ``` <details><summary>The log before it</summary> ``` Bean-Ops crash-relay live test open ~/Games/Longshot/save.dat ``` </details> <!-- longshot-crash:eyJhY2Nlc3MiOiAicmVhZCIsICJjb2RlIjogIjB4YzAwMDAwMDUiLCAiY291bnQiOiAzLCAiZmlyc3QiOiAxNzk0NTM3NTcxLCAiZ3B1cyI6IHsiVGVzdCBHUFUgKG5vdCBhIHJlYWwgY3Jhc2gpIjogM30sICJsYXN0IjogMTc5NDUzNzY5OSwgIm9zIjogeyJMaXZlIHRlc3QgZnJvbSBCZWFuLU9wcyAoTGludXgpIjogMiwgIkxpdmUgdGVzdCBmcm9tIEJlYW4tT3BzIChXaW5kb3dzIDExKSI6IDF9LCAicmVuZGVyZXJzIjogeyJWdWxrYW4iOiAyLCAiV2ViR1BVIjogMX0sICJzaWciOiAiODE4NGE2ZjJlMjMzIiwgInZlcnNpb24iOiAiMC4wLjAtbGl2ZXRlc3QiLCAidmVyc2lvbnMiOiB7IjAuMC4wLWxpdmV0ZXN0IjogM30sICJ3aGVyZSI6ICJsaXZldGVzdC5leGUrMHhkZWFkIn0=.1c504b4bce53a3bc756bb912c040f138 -->
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#81
No description provided.