perf(error): check SourceFragmentsHidden before formatting sources#139
Conversation
ToMap, LogValue and formatVerbose evaluated the condition
if sources := o.Sources(); sources != "" && !SourceFragmentsHidden
left-to-right: Sources() formatted the full source-fragment output -
reading source files from disk and building per-line strings - and the
result was then discarded, because SourceFragmentsHidden is true by
default. Checking the flag first skips all of that work in the default
configuration; behavior with SourceFragmentsHidden=false is unchanged.
Also replaces fmt.Sprintf with direct concatenation in the per-line /
per-block builders (getSourceFromFrame, framesToStacktraceBlocks,
framesToSourceBlocks), benefiting the non-hidden path too.
benchstat vs main (darwin/arm64, Apple M3, -count=10 -cpu=1):
| before3.txt | after3.txt |
| sec/op | sec/op vs base |
ToMap 3.494u ± 5% 1.631u ± 15% -53.31% (p=0.002 n=10)
LogValue 3.499u ± 4% 1.478u ± 8% -57.77% (p=0.000 n=10)
geomean 3.496u 1.553u -55.60%
| before3.txt | after3.txt |
| B/op | B/op vs base |
ToMap 5.214Ki ± 0% 2.789Ki ± 0% -46.51% (p=0.000 n=10)
LogValue 5.357Ki ± 0% 2.745Ki ± 0% -48.76% (p=0.000 n=10)
geomean 5.285Ki 2.767Ki -47.65%
| before3.txt | after3.txt |
| allocs/op | allocs/op vs base |
ToMap 63.00 ± 0% 28.00 ± 0% -55.56% (p=0.000 n=10)
LogValue 61.00 ± 0% 24.00 ± 0% -60.66% (p=0.000 n=10)
geomean 61.99 25.92 -58.18%
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #139 +/- ##
==========================================
+ Coverage 90.50% 91.20% +0.70%
==========================================
Files 15 15
Lines 1243 1251 +8
==========================================
+ Hits 1125 1141 +16
+ Misses 93 88 -5
+ Partials 25 22 -3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR optimizes OopsError formatting paths by avoiding expensive source-fragment generation when SourceFragmentsHidden is enabled (the default), while preserving behavior when source fragments are explicitly enabled.
Changes:
- Reorders the
SourceFragmentsHiddencheck ahead ofSources()inLogValue,ToMap, andformatVerboseto skip unnecessary disk reads/string building by default. - Replaces some
fmt.Sprintfusages with direct string concatenation /strconv.Itoain per-line and per-block formatting helpers. - Removes now-unneeded
fmtimports in files where formatting no longer uses it.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
error.go |
Checks SourceFragmentsHidden before calling Sources() in LogValue, ToMap, and formatVerbose to avoid wasted work in the default configuration. |
sources.go |
Replaces fmt.Sprintf with strconv.Itoa + concatenation in per-line source formatting; removes fmt import. |
stacktrace.go |
Simplifies stacktrace/source block string assembly via concatenation; removes fmt import. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The previous commit gated Sources() behind SourceFragmentsHidden in ToMap, LogValue and formatVerbose, but no existing test ever set SourceFragmentsHidden=false, leaving oopsStacktrace.Source() and framesToSourceBlocks at 0% coverage and the new gated branches partially untested. Adds: - TestOopsSourceFragmentsVisible / TestOopsSourceFragmentsHiddenByDefault: verify ToMap, LogValue and formatVerbose include/omit "sources" depending on the flag. - TestOopsStacktraceSource: direct coverage of oopsStacktrace.Source(). - TestFramesToSourceBlocks: direct coverage of framesToSourceBlocks, including the no-body-skips-block and multi-block-reversal cases. Coverage: 90.8% -> 94.0%.
TestOopsSourceFragmentsHiddenByDefault asserts on the package-level SourceFragmentsHidden default, which TestOopsSourceFragmentsVisible temporarily flips. Both must run outside the parallel pool so the flip-and-restore in the latter cannot interleave with the former assumption; add the paralleltest lint suppression that comes with opting out of t.Parallel() for that reason.
Summary
ToMap,LogValueandformatVerboseevaluatedif sources := o.Sources(); sources != "" && !SourceFragmentsHiddenleft-to-right:Sources()formatted the full source-fragment output - reading source files from disk and building per-line strings - and the result was then discarded, becauseSourceFragmentsHiddenistrueby default. Checking the flag first skips all of that work in the default configuration; behavior withSourceFragmentsHidden=falseis unchanged.fmt.Sprintfwith direct concatenation in the per-line / per-block builders (getSourceFromFrame,framesToStacktraceBlocks,framesToSourceBlocks), benefiting the non-hidden path too.Benchmarks
benchstatvsmain(darwin/arm64, Apple M3,-count=10 -cpu=1):BenchmarkNewStacktracewas also checked as a control (this path never callsSources()): allocations and memory are byte-for-byte identical before/after (0% variance), confirming the change is isolated to theSources()-gated paths.Test coverage
The
SourceFragmentsHidden=falsepath (and, by extension,oopsStacktrace.Source()andframesToSourceBlocks) had no coverage at all before this PR — every existing test ran with the package default (true), so the new gated branches and the two previously-0%-covered functions were untested. Added:TestOopsSourceFragmentsVisible/TestOopsSourceFragmentsHiddenByDefault: assertToMap,LogValueandformatVerboseinclude/omit"sources"depending on the flag. Both run sequentially (not.Parallel(),//nolint:paralleltest) since the former temporarily flips the package-level flag and the latter asserts on its default.TestOopsStacktraceSource: direct coverage ofoopsStacktrace.Source()(empty-frames and with-frames cases).TestFramesToSourceBlocks: direct coverage offramesToSourceBlocks, including the no-body-skips-block and multi-block-reversal cases.Package coverage: 90.8% -> 94.0%.
SourceandframesToSourceBlocksare now at 100%.Test plan
Verified with
go build ./...,go vet ./...,go test ./...,golangci-lint run ./...(0 issues), andgo test -bench=BenchmarkToMap -bench=BenchmarkLogValue -benchmem -count=10 -cpu=1 ./benchmarksrun before/after the change and compared withbenchstat.