Skip to content

Commit 301ad14

Browse files
committed
add TraceFrom(pcs) for "on-demand" StackTrace instantiation
Allows to record just the pcs from runtime.Callers when an error occurs and convert them to a StackTrace only if the trace is actually needed, e.g. when printing to a log. Improves performance about 4x.
1 parent f66500b commit 301ad14

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

stack.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,24 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
208208
s.Write(closeBracketBytes)
209209
}
210210

211+
// Convenience wrapper around runtime.Callers()
212+
func Callers(skip int) []uintptr {
213+
var pcs [512]uintptr
214+
n := runtime.Callers(skip+1, pcs[:])
215+
return pcs[:n]
216+
}
217+
211218
// Trace returns a CallStack for the current goroutine with element 0
212219
// identifying the calling function.
213220
func Trace() CallStack {
214-
var pcs [512]uintptr
215-
n := runtime.Callers(1, pcs[:])
221+
return TraceFrom(Callers(1))
222+
}
216223

217-
frames := runtime.CallersFrames(pcs[:n])
218-
cs := make(CallStack, 0, n)
224+
// TraceFrom creates a CallStack from the given program counters (as generated
225+
// by runtime.Callers)
226+
func TraceFrom(pcs []uintptr) CallStack {
227+
frames := runtime.CallersFrames(pcs)
228+
cs := make(CallStack, 0, len(pcs))
219229

220230
// Skip extra frame retrieved just to make sure the runtime.sigpanic
221231
// special case is handled.

stack_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,14 @@ func deepStack(depth int, b *testing.B) stack.CallStack {
497497
return s
498498
}
499499

500+
func deepCallers(depth int, b *testing.B) []uintptr {
501+
if depth > 0 {
502+
return deepCallers(depth-1, b)
503+
}
504+
b.StartTimer()
505+
return stack.Callers(1)
506+
}
507+
500508
func BenchmarkTrace10(b *testing.B) {
501509
for i := 0; i < b.N; i++ {
502510
b.StopTimer()
@@ -518,6 +526,33 @@ func BenchmarkTrace100(b *testing.B) {
518526
}
519527
}
520528

529+
func BenchmarkCallers(b *testing.B) {
530+
for i := 0; i < b.N; i++ {
531+
stack.Callers(1)
532+
}
533+
}
534+
535+
func BenchmarkCallers10(b *testing.B) {
536+
for i := 0; i < b.N; i++ {
537+
b.StopTimer()
538+
deepCallers(10, b)
539+
}
540+
}
541+
542+
func BenchmarkCallers50(b *testing.B) {
543+
b.StopTimer()
544+
for i := 0; i < b.N; i++ {
545+
deepCallers(50, b)
546+
}
547+
}
548+
549+
func BenchmarkCallers100(b *testing.B) {
550+
b.StopTimer()
551+
for i := 0; i < b.N; i++ {
552+
deepCallers(100, b)
553+
}
554+
}
555+
521556
////////////////
522557
// Benchmark functions followed by formatting
523558
////////////////

0 commit comments

Comments
 (0)