Files
OneCAD-Simulation/docs/crash-reports.md
jyvikoandClaude Opus 5.5 af9b4bb222 app: startup steps in the crash journal, and a Windows exception filter (#299)
egui-wgpu's startup hook and the app's own steps (window, app setup, viewport
pipelines, first frame, running) go into the journal. On Windows,
native_crash.rs (adapted from SolidCraft, the only module allowed unsafe code;
the app crate now denies instead of forbids it) writes the exception code,
faulting module and offset and a minidump. A nightly/on-demand CI job runs the
app's tests on Windows, including a child that faults on purpose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:11:58 -04:00

117 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Crash reports, the log file and Report a Bug
## Privacy
No crash report, log line or model is uploaded automatically. Crash reports and the log stay on
this computer (anonymous usage, if you agreed to share it, never carries them: see
[privacy.md](privacy.md)). The only way they leave it is **Help › Report a Bug** (or **Report It…** after a crash), which opens a GitHub issue
form in your browser; you read it there and submit it, or not.
**Help › Crash Reports…** lists every saved report (when, what, how many times), each with
**Report…**, **Copy Path** and **Delete**, and **Delete All** (`diagnostics.crash_reports`,
`diagnostics.crash_reports.delete`).
What a bug report carries:
| Always (you can edit it) | Only with **Share my session** | Only with **Include the model file** as well |
|---|---|---|
| the version and OS, what happened (the last error by default), for a crash its message, place and backtrace top; your home folder is shown as `~` | the commands that built the model (`fieldcraft-session.json`, replayable with `fieldcraft-cli run`); they can name your files | the model (`model.fieldcraft`), which you attach yourself |
The form shows exactly what will be filled in before you click **Open GitHub Issue**. GitHub
refuses long new-issue links, so a session that does not fit goes out as a file instead: it is
saved to `bug-reports/<time>/` in the data folder below and copied to the clipboard, and the issue
text says to attach or paste it. The model is always a file you attach.
## Where things are
The app's data folder holds `recovery/`, `crashes/`, `logs/` and `bug-reports/`:
| System | Folder |
|---|---|
| macOS | `~/Library/Application Support/FieldCraft` |
| Linux | `~/.local/share/fieldcraft` (or `$XDG_DATA_HOME/fieldcraft`) |
| Windows | `%LOCALAPPDATA%\FieldCraft` |
Runs driven by automation (`--control`, or `CI` set) use `fieldcraft-automation/` in the
temporary folder instead, so test runs never reach your folders. `FIELDCRAFT_CRASH_DIR` moves the
crash folder, `FIELDCRAFT_RECOVERY_DIR` the recovery folder. The browser build keeps no crash
folder and logs to the browser console.
## The log file
`logs/fieldcraft.log`: one line per record, `time LEVEL [pid] target: message`. Past 2 MB it
becomes `fieldcraft.log.1` and a new one starts. Warnings and errors also go to the standard
error stream.
## Crash reports
`crashes/<YYYYMMDD-HHMMSS>-<signature>/`:
- `report.json` (`"format": "fieldcraft-crash/1"`): `kind` (`command_panic`: a command hit an
internal error, caught, the model left unchanged; `recovered_panic`: one caught inside a command;
`panic`: an internal error outside commands; `unclean_exit`: the app ended without a clean exit
— an abort, a stack overflow, a crash in a graphics driver, the process being killed outright
(SIGKILL) — found at the next start), `message`, `location`, `thread`, `backtrace`, `app`, `system`, `command`,
`recent_commands` (up to 32), `last_activity` (the last control request, such as
`control ui.capture`), `startup_phase` (`{step, ms}`: the step a run that never drew its first
frame died in), `native` (Windows: what the exception filter caught), `base`, `count`,
`first_seen`, `last_seen`, `notes`; `system.environment` names the renderer, GPU and driver
- `model.fieldcraft`: the model the replay starts from
- `replay.json`: `{"commands": [{command, params}, …]}`
- `log.txt`: the crashed run's lines of the log
- `minidump.dmp` (Windows, a crash in native code): open it with WinDbg or Visual Studio
### Startup steps and native crashes
While the app gets up, the journal keeps the step it is in: the app's own (`window`, `app setup`,
`viewport pipelines (shader compile)`, `first frame`, and `running` once a frame was presented)
and egui-wgpu's in between (`graphics instance`, `window surface`, `adapter request`, `adapter
chosen`, when the renderer is recorded, `device creation`, `egui renderer (shader compile)`,
`surface configure`, `first frame (present)`). A run that dies before `running` is reported "during startup (device creation, 1.2 s
after launch)", so a graphics driver crash names its step.
On Windows an unhandled-exception filter (`apps/fieldcraft/src/native_crash.rs`) catches a crash
in native code (an access violation in a graphics driver, a stack overflow): before the process
goes it writes the exception code, the faulting module and the offset into it, and a minidump,
next to the journal. The next start puts them into the report: "crashed with
EXCEPTION_ACCESS_VIOLATION (0xC0000005) in igvk64.dll+0x64EA72 during startup (device
creation…)", `backtrace` the code, the module and offset, and the thread, `native` the raw fields,
and `minidump.dmp`. After a stack overflow there is no stack left for a minidump; the rest is
still written.
The next start after a crash asks "FieldCraft closed unexpectedly": **Report It…** opens Report a
Bug for that report.
### For agents
| From | List | One report | Replay |
|---|---|---|---|
| Command (MCP `execute`, control channel, scripts) | `diagnostics.crash_reports` | `diagnostics.crash_report {id}` | |
| CLI | `fieldcraft-cli crashes [--json]` | `fieldcraft-cli crashes show <id>` | `fieldcraft-cli crashes replay <id>` |
`crashes replay` opens the report's model, runs its commands (files they write can only go to a
scratch folder) and says whether the same internal error happens again. `crashes dir` prints the
folder, `crashes clear [<id>]` deletes reports. `diagnostics.bug_report` returns the draft (link,
fields, attachments) without opening anything; over the control channel `ui.start
diagnostics.bug_report` opens the form, `ui.prompt {"answer": "set", …}` sets its fields, and
`ui.inspect` shows the draft. Only a click opens the browser.
**Check that reporting works:** with `FIELDCRAFT_CRASH_TEST=1`, `diagnostics.crash_test {mode}`
crashes on purpose: `panic`, `recovered` or `abort` (reported at the next start).
A request to terminate is no crash: on Linux and macOS, SIGTERM (`kill PID`) and SIGINT (Ctrl-C)
close the window as **Close** does (unsaved work is autosaved for recovery, the
journal ends, no report). A second signal ends the app at once.
### How it stays cheap and small
The app keeps a journal of the window's top-level commands (`crashes/sessions/*.jsonl`, at most
512 KB, two small writes per command that changes the model, none for commands that only read),
restarted from each autosave and each model opened. A clean exit deletes it; it is locked while its
app runs, so a journal nobody holds belongs to an app that died. A crash is identified by its
place and message, numbers ignored: repeats only raise `count`. At most 5 new reports per run,
20 reports, 100 MB, 90 days; a model over 20 MB is not copied. Tests, the CLI and the browser build
never install the hook.
Adapted from SolidCraft's crash reports (MIT OR Apache-2.0, see ATTRIBUTION.md).