Skip to content

fix: report reversible source sites for tiny panics - #1154

Open
luames wants to merge 4 commits into
burrowers:masterfrom
luames:fix/tiny-panic-message
Open

luames wants to merge 4 commits into
burrowers:masterfrom
luames:fix/tiny-panic-message

Conversation

@luames

@luames luames commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #604. Tiny mode should distinguish an unrecovered panic from a silent exit and let the source owner identify the failing site, without printing the panic value or a stack trace.

Print one line such as panic: p_abc123.go:1. garble -tiny reverse maps the token back to its original filename and line using the same source, build flags, and seed. The token hashes the source site, not the message, so dynamic values and user-defined formatting methods are never needed.

Capture the origin before deferred calls run. This covers explicit panics, nil dereferences, bounds panics, inlined calls, and deferred panics. Panic values and recovery are unchanged, including both panic(nil) settings. Fatal runtime errors remain silent, and GOTRACEBACK=crash still crashes the process. If no source token is available, print panic: hidden.

The linker retains opaque PC-to-file mappings but removes physical filenames, line tables, and function start lines. Public runtime source-location APIs remain stripped. A dense global file table replaces sparse per-CU lookup tables. PC-to-file data is rewritten per function without mutating shared object symbols, omitted when no opaque sites are present, and deduplicated after rewriting. Hidden ranges coalesce. Each hash is stored without the repeated p_ prefix or .go suffix; the panic printer restores them.

On the unchanged existing tiny fixture, with Go 1.27.0 and fixed seed AAAAAAAAAAA, the generic-diagnostic baseline is 2,211,964 bytes. The initial reversible version was 2,683,004 bytes; the compact version is 2,494,588 bytes. This removes 188,416 bytes, or 40% of the added size, reducing the overhead from 21.3% to 12.8%. Repeated builds of all three versions are byte-identical, and each binary executes with its expected diagnostic. The remaining overhead is retained source-site metadata.

Extend the existing tiny fixture with exact reversed-output checks, recovery checks, a -literals build, and binary-table assertions for compact metadata. No separate script fixture or extra fixture build is added. The README documents reversal and the metadata tradeoff.

Tests

  • go test -run '^TestScript$/^(tiny|reverse|position|debugdir)$|^TestRuntime|^TestReverse' -count=1 passed without short mode.
  • All six CI jobs passed on head 1851c8d: Test run.
  • go test ./internal/patcher -count=1 passed.
  • go vet ./... passed.
  • Go formatting, including archived fixture sources, passed. Source whitespace checks passed; generated patch context whitespace was excluded.
  • The compaction patch is byte-identical to its readable toolchain commit's format-patch output. Applying all eight ordered patches to fresh Go sources reproduces the same tree.

Implemented by Hermes Agent using gpt-6.1-sol, acting on behalf of @luantak.

Tiny mode suppresses panic values and tracebacks to avoid leaking runtime information, but hiding the entire diagnostic makes a crashing binary look as if it exited without running. Keep a single panic: hidden line for unrecovered panics while leaving fatal runtime diagnostics suppressed.

Replace printpanics after the generic runtime print stripping pass. Do not inspect or replace panic values, and keep preprintpanics empty so user-defined formatting methods cannot expose information. Recovery, nil-panic behavior, and GOTRACEBACK crash handling remain unchanged.

Extend the existing tiny fixture without adding builds. Check nil panics with both panicnil settings, runtime panics, custom values, nil-panic recovery, and default-mode diagnostics. The pre-fix test failed with no match for ^panic: hidden$ in stderr; the fixed non-short fixture and runtime unit tests pass, as does go vet ./....

Fixes burrowers#604.
@mvdan

mvdan commented Oct 5, 2026

Copy link
Copy Markdown
Member

if we're showing a panic message, we might as well hash the original panic message, so that the user can reverse that to figure out what the original underlying panic was?

@mvdan

mvdan commented Oct 5, 2026

Copy link
Copy Markdown
Member

e.g. panic("internal error: deadbeef012345...")

@luames
luames marked this pull request as ready for review October 5, 2026 08:43
@luantak

luantak commented Oct 5, 2026

Copy link
Copy Markdown
Member

@luames mvdan is right here

@mvdan

mvdan commented Oct 5, 2026

Copy link
Copy Markdown
Member

I fully agree with not completely hiding panics btw. That is confusing.

@luames

luames commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, a hash would be more useful than panic: hidden: it could distinguish failures without printing the original message.

The missing piece is reversal. garble reverse currently maps names and source positions, not panic messages. It could match a hash against compile-time string values, but it cannot reconstruct an arbitrary runtime-generated message from its hash. Formatting custom panic values also means invoking their Error or String methods, which tiny mode currently skips.

Should the initial implementation cover statically known string messages with reverse support, keeping the generic diagnostic for other panic values? That would make the reversibility guarantee explicit rather than printing a hash we cannot resolve.

@luantak

luantak commented Oct 5, 2026

Copy link
Copy Markdown
Member

I think what needs to be able to be reconstructed is where the original panic came from, the message can then be read from source.

We don't want stack traces, nor do we care about dynamic values for -tiny

@luames please implement

@mvdan

mvdan commented Oct 5, 2026

Copy link
Copy Markdown
Member

Yeah good idea. The location is more useful than the message, because the message could be repeated with other call sites.

The generic diagnostic for burrowers#604 identifies a panic instead of a silent
exit, but it cannot identify the failing source. Record an opaque source
site before panic unwinding and print only that token for an unrecovered
panic. Do not format panic values or print a stack trace.

Reuse salted source-position hashes and teach reverse about tiny tokens.
Retain PC-to-file and CU lookup metadata while stripping physical paths,
line tables and function start lines. Public runtime source-location
APIs remain stripped. Capture on the system stack so implicit runtime
panics, inlining and deferred panics identify their originating site.
Keep the generic diagnostic when no source token is available.

Extend the existing tiny fixture with exact reversed-output assertions,
both nil-panic settings, custom-value recovery and a literals build.
The pre-fix test failed because stderr contained panic: hidden rather
than a source token. Tiny, reverse, position and runtime tests, patcher
tests, and go vet pass. Regenerated patch bytes and the freshly applied
ordered patch-series tree match the readable toolchain commit.

Retaining origin mappings has a size cost: on the unchanged tiny fixture
with Go 1.27.0 and seed AAAAAAAAAAA, binaries grow from 2,211,964 to
2,683,004 bytes. Repeated builds of both versions are byte-identical.

Fixes burrowers#604.
@luames luames changed the title fix: print a generic diagnostic for tiny panics fix: report reversible source sites for tiny panics Oct 5, 2026
Tiny panic reversal intentionally retains salted source-site directives while stripping physical paths and public source locations. The debugdir fixture still asserted the previous absence of all line directives and failed on the full Linux, macOS and Windows CI runs.

Assert that tiny output contains opaque panic-site directives and does not expose the physical main filename. The non-short debugdir fixture passes locally.

Refs burrowers#604.
@luames

luames commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Implemented. -tiny now prints a single opaque source-site token, such as panic: p_abc123.go:1. garble -tiny reverse reconstructs the original filename and line, so the message can be read from source. No panic values or stack traces are printed, and custom formatting methods are not called.

The tests cover explicit and implicit panics, inlined and deferred panics, recovery, both nil-panic settings, and reversal with -literals. All six CI jobs passed.

The tradeoff is retained opaque origin metadata: the existing fixture binary grows by 21.3% compared with the generic-diagnostic version. That measurement is documented in the PR. Physical filenames and line tables remain stripped, as do the public runtime source-location APIs.

@luantak

luantak commented Oct 5, 2026

Copy link
Copy Markdown
Member

@luames as tiny should be tiny can we get the increase in file size down

Reversible panic locations for burrowers#604 retained the original CU lookup
tables, including dead-file holes, and repeated the same token prefix
and suffix thousands of times. Compact that metadata without dropping
source coverage or changing the diagnostic format.

Append a linker patch which assigns dense global file indices and
rewrites PC-to-file data per function without mutating shared object
symbols. Omit tables without opaque sites, coalesce hidden ranges, and
deduplicate rewritten tables. Store token hashes alone and restore the
common p_ and .go text only when printing an unrecovered panic.

Extend the existing tiny build with binary-table assertions. Before the
fix, the table check failed with 146,984 bytes for 13,276 files; after
compaction, a second check caught the repeated token suffixes. Both
checks now pass, as do non-short tiny, reverse, position and debugdir
scripts, runtime/reverse unit tests, patcher tests and go vet.

On the unchanged fixture with Go 1.27.0 and seed AAAAAAAAAAA, file size
drops from 2,683,004 to 2,494,588 bytes. The generic-diagnostic baseline
is 2,211,964 bytes, so the increase falls from 21.3% to 12.8%, removing
40% of the added size. Repeated builds are byte-identical and all three
binaries execute with the expected diagnostics. The new patch matches
its readable toolchain commit, and fresh ordered application reproduces
the complete toolchain tree.

Refs burrowers#604.
@luames

luames commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Yes. The fixture binary now drops from 2,683,004 to 2,494,588 bytes, removing 40% of the added size. Compared with the 2,211,964-byte generic-diagnostic baseline, the overhead falls from 21.3% to 12.8%. These are matching Go 1.27.0 builds with the same source and fixed seed; repeated builds are byte-identical.

I replaced sparse per-CU tables with a dense global table, removed unused file mappings, deduplicated rewritten PC tables, and stopped storing the token prefix and suffix thousands of times. Panic-site coverage and the printed/reversed format are unchanged. The remaining overhead is the source-site metadata itself.

Pushed the compaction and updated the measurements in the PR. The focused non-short tests and all six CI jobs pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

-tiny can cause confusion when hiding panics entirely

3 participants