Conversation
…, crashme A crash, hang or silent exit becomes a JSON report on disk (stack as module-relative addresses with build ids, fault, machine, breadcrumbs, state; a minidump on Windows), symbolicated on the player's machine from a build-time table when one is present (embedded in the binary by cf_symbols(target EMBED), beside it, or in a directory) and uploaded to any endpoint as one multipart POST, where any reply is the ack. libraries/cute/cute_crash.h (cc_): capture on Windows (SEH, stack guarantee, vectored keep-alive, abort/purecall/terminate) and macOS/Linux (signals on an alternate stack, backtrace), a no-allocation writer, hang watchdog, clean-exit marker, pending upload at init through send/ask callbacks, an uploader child from the handler, an optional watcher process. libraries/cute/cute_sym.h (sym_): the CUTESYM table, PDB (MSF, DBI, C13, inline annotations, IPI), ELF and Mach-O DWARF 2-5 including the debug map with object-file relocations, resolve/print, the slot patch, the cute-sym CLI. No toolchain dependencies. Design after crash-where by bullno1, credited in both headers.
…CF on macOS, signatures everywhere cute_crash.h compiles to no-ops under Emscripten (no signals, no processes, no files there). The crash and symbol tests no longer init and destroy cf_fs around their work, which pulled the file system out from under the video suite that ran next. The Mach-O debug-map reader follows N_OSO entries into ar archives, BSD and GNU name forms, so frames inside libcute.a resolve to file and line. Every report carries signature.raw, abnormal exits included, and a hang report spawns the uploader child under upload_on_crash. cf_symbols gives the cute library debug info as well as the target; the minidump leaves out data segments (tens of MB on a static build); the CF layer keeps header strings alive for the request and skips attachments over 8 MB.
A failed CF_ASSERT puts its expression, file and line into the report before the handler runs, and if the handler returns the assert becomes a report of its own: cc_report writes a report with a message and the caller's stack and returns, cf_crash_report is its CF face. A shared cute library gets its table beside it (cute.dll.sym) from CF's own CMake, so frames inside CF resolve in shared builds; cute-sym --optional makes that step a no-op without debug info. The web build compiles: the stub carries the size defaults, the CF layer leaves out HTTPS and the GPU device there.
…erence clang-cl leaves the statement bit clear on every C13 line row where MSVC sets it, and the reader had been keeping only statement rows, so lld-linked PDBs gave names without lines. Rows are kept regardless now, as dbghelp does; MSVC results are unchanged. One 64-to-32 narrowing in the ELF section reader for the wasm build, a logical-operand cleanup, and the assert macro named in prose where the docs parser took it for a reference.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 678e1348af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| SDL_MessageBoxData data = { SDL_MESSAGEBOX_INFORMATION, NULL, "Crash report", text.c_str(), 3, buttons, NULL }; | ||
| int id = 2; | ||
| if (!SDL_ShowMessageBox(&data, &id)) return CC_CONSENT_ASK; | ||
| return id == 0 ? CC_CONSENT_ASK : id == 1 ? CC_CONSENT_SEND : CC_CONSENT_NEVER; |
There was a problem hiding this comment.
Make the Send button authorize the pending upload
When the player chooses the default "Send" button, this returns CC_CONSENT_ASK. Both upload paths interpret that value as no permission, and s_upload_flow only transmits reports for CC_CONSENT_SEND, so the report remains on disk and the dialog reappears next launch instead of sending once. Preserve the distinction between one-time and persistent consent while allowing the current upload.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid. The Send button now returns CC_CONSENT_ONCE, a new value: the pending reports are sent and nothing is stored, so the question comes back for later ones; ASK sends nothing. Covered by the reporter's consent tests (ONCE/ASK/SEND/NEVER, including the uploader child). Fixed in 8863554.
| s.mu.Lock() | ||
| groups := s.idx.groupList(q.Get("sort"), q.Get("q"), q.Get("app")) | ||
| total := len(s.idx.reports) | ||
| s.mu.Unlock() |
There was a problem hiding this comment.
Snapshot group state before releasing the mutex
When a dashboard request overlaps an ingest or deletion, groups still contains pointers to the live group objects after this unlock. Template rendering subsequently reads fields and iterates Versions/Installs while idx.add or idx.remove can mutate the same maps and slices, which is a data race and can terminate the server with concurrent map iteration and map write; groupPage has the same pointer-after-unlock pattern. Copy the rendered state while locked or otherwise keep those accesses synchronized.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid. Both pages now render from a copy taken under the lock (groupView: the fields the templates read plus a copy of the report slice); the live group is never touched after Unlock. Fixed in 8863554.
| configure_file("${CF_SYM_SLOT_IN}" "${SLOT_SRC}" @ONLY) | ||
| target_sources(${TARGET} PRIVATE "${SLOT_SRC}") | ||
| add_custom_command(TARGET ${TARGET} POST_BUILD | ||
| COMMAND cute-sym "$<TARGET_FILE:${TARGET}>" --embed |
There was a problem hiding this comment.
Use a host-native cute-sym when cross-compiling
When cross-compiling for Android or iOS, cute-sym is built with the target toolchain, but this post-build command then tries to execute that target binary on the build host. The default crashme sample invokes cf_symbols, so such builds fail with an incompatible-executable error; a native host tool or configured cross-compiling emulator is required for this build step. This affects platforms explicitly supported by the framework.
AGENTS.md reference: AGENTS.md:L203-L209
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid. cf_symbols now checks CMAKE_CROSSCOMPILING: with CF_CUTE_SYM_HOST set to a host-built cute-sym it runs that, otherwise it logs a status line and skips the step, so Android and iOS builds configure and build; cute-sym itself is not built when cross-compiling. Fixed in 8863554.
| ss.ss_sp = malloc(64 * 1024); | ||
| ss.ss_size = 64 * 1024; | ||
| ss.ss_flags = 0; | ||
| if (ss.ss_sp) sigaltstack(&ss, NULL); |
There was a problem hiding this comment.
Reclaim alternate stacks when attached threads exit
On Linux and macOS, every active reporter attachment—including every thread created through cf_thread_create—allocates a 64 KiB alternate signal stack, but the pointer is neither retained nor freed when the thread function returns. Workloads that repeatedly create short-lived threads therefore leak 64 KiB per thread, and the accompanying monotonically increasing 64-entry stack registry eventually stops tracking new threads as well; add per-thread teardown that disables/frees the alternate stack and unregisters the thread.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid. Attach now has a teardown: on POSIX a pthread_key destructor disables and frees the thread's alternate stack and clears its registry slot, on Windows an FlsAlloc callback clears the slot; slots are reused (tid 0 is free), so neither the stack nor the table grows with short-lived threads. Verified with 500 attached and joined threads (working set flat) and a crash on the 501st still named. Fixed in 8863554.
| add_dependencies(tests crashme) | ||
| target_compile_definitions(tests PRIVATE CF_TEST_HAVE_CRASHME) | ||
| endif() | ||
| cf_symbols(tests FILE) |
There was a problem hiding this comment.
Guard built-in symbol steps with CF_CUTE_SYM
Configuring the top-level project with -DCF_CUTE_SYM=OFF while leaving the default tests enabled always reaches this call, and cf_symbols immediately raises a fatal error because the cute-sym target was intentionally not created. The default samples contain another unconditional call for crashme, so the advertised option cannot be disabled unless users also discover and disable both tests and samples; guard these calls or omit the symbol-specific targets when the option is off.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid. cf_symbols no longer fails when the tool is off: it logs a status line and returns, and the tests that need tables (the self test and the crashme suite) are compiled only when CF_CUTE_SYM is on. A configure with -DCF_CUTE_SYM=OFF now succeeds with tests and samples enabled. Fixed in 8863554.
…ool-off CMake, dashboard snapshots The consent box's Send button is CC_CONSENT_ONCE: what is pending goes now and the question comes back later; ASK sends nothing. Threads release what attach gave them: a pthread key destructor frees the alternate stack and clears the registry slot, FlsAlloc does the slot on Windows, and slots are reused, so short-lived threads neither leak nor exhaust the table. cf_symbols skips with a status line when cross-compiling without CF_CUTE_SYM_HOST or when CF_CUTE_SYM is off, and the tests that need tables are gated on the tool. crashbox renders groups from copies taken under the lock.
… the key destructor is declared before use
…d the server README
…d a step-by-step page for newcomers
Originally written by bullno1 as crash-where. This PR carries that design into Cute Framework as two self-contained single-file C libraries plus a thin CF layer, with his name at the top of both headers and in their licenses.
Crash reporting for CF games: when a shipped game crashes, hangs, or quits without saying goodbye, a report of what happened is written to disk and sent to an endpoint of the developer's choosing.
What the developer sees
Two lines before the app exists, one line of CMake:
That is a working reporter. The player sees one question the first time a report goes out (Send, Always send, Don't send) and nothing afterwards. The developer sees reports arrive with function names, files and lines.
Optional, where the game already logs:
A failed
CF_ASSERTrecords its expression, file and line in the report; if the assert handler returns instead of stopping, the assert becomes a report of its own.Under a debugger, or with
CC_DISABLE=1, nothing installs and a crash breaks into the debugger as always.What a report is
One JSON document per crash, written by the crashing process with no allocation, plus a minidump on Windows for locals:
Addresses are module-relative and every module carries its build id, so a report resolves anywhere the matching symbol table is, on the player's machine or the developer's.
The decisions, and why
CF_CRASH_MODE_WATCHERopts into an out-of-process reporter that exits with the game and never respawns.cute-sym resolve. A missing table means module+offset, never an error.tools/crashboxis the minimal real one.upload_on_crashspawns a short-lived child with a clean heap; the file stays as the fallback.Accepted: no locals outside Windows (the minidump); a build without debug info resolves to module+offset only.
Pieces
include/cute_crash.h: thecf_crash_*API above. CF supplies HTTPS, the consent box, the frame heartbeat, thread attachment and the machine section.libraries/cute/cute_crash.h(cc_): the reporter, usable without CF through two callbacks (send,ask).libraries/cute/cute_sym.h(sym_): the table, the readers,resolve,print, thecute-symCLI. Also a library, so a program that compiles code at runtime can build tables for its own modules.cmake/CuteSym.cmake:cf_symbols(target EMBED|FILE). Turns on debug info for the target and for CF, runs the tool after every link.samples/crashme.c: crashes on request so the whole path can be watched.tools/crashbox/: the minimal server, in Go with no dependencies: ingest, groups by signature sorted by count, report and dump pages, size/rate/disk limits on by default, with scripts that put it on a Linux box as a service. Not part of the CMake build.docs/topics/crash_reporting.md.Verification
crashmeon Windows, 180 checks: null write, stack overflow, abort, uncaught throw, crash on a worker thread, hang, in-process and watcher mode; every resolved top frame checked against the source line incrashme.c; the minidump's exception stream read back; abnormal exit after a kill; resolve at the next launch; upload at the next launch and from the child against a live server over HTTPS.// Unverified on this host:until it confirms them.