diff --git a/stack.go b/stack.go index ac3b93b..961aa42 100644 --- a/stack.go +++ b/stack.go @@ -208,14 +208,24 @@ func (cs CallStack) Format(s fmt.State, verb rune) { s.Write(closeBracketBytes) } +// Convenience wrapper around runtime.Callers() +func Callers(skip int) []uintptr { + var pcs [512]uintptr + n := runtime.Callers(skip+1, pcs[:]) + return pcs[:n] +} + // Trace returns a CallStack for the current goroutine with element 0 // identifying the calling function. func Trace() CallStack { - var pcs [512]uintptr - n := runtime.Callers(1, pcs[:]) + return TraceFrom(Callers(1)) +} - frames := runtime.CallersFrames(pcs[:n]) - cs := make(CallStack, 0, n) +// TraceFrom creates a CallStack from the given program counters (as generated +// by runtime.Callers) +func TraceFrom(pcs []uintptr) CallStack { + frames := runtime.CallersFrames(pcs) + cs := make(CallStack, 0, len(pcs)) // Skip extra frame retrieved just to make sure the runtime.sigpanic // special case is handled. diff --git a/stack_test.go b/stack_test.go index 44f3a7d..e974686 100644 --- a/stack_test.go +++ b/stack_test.go @@ -497,6 +497,14 @@ func deepStack(depth int, b *testing.B) stack.CallStack { return s } +func deepCallers(depth int, b *testing.B) []uintptr { + if depth > 0 { + return deepCallers(depth-1, b) + } + b.StartTimer() + return stack.Callers(1) +} + func BenchmarkTrace10(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() @@ -518,6 +526,33 @@ func BenchmarkTrace100(b *testing.B) { } } +func BenchmarkCallers(b *testing.B) { + for i := 0; i < b.N; i++ { + stack.Callers(1) + } +} + +func BenchmarkCallers10(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + deepCallers(10, b) + } +} + +func BenchmarkCallers50(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + deepCallers(50, b) + } +} + +func BenchmarkCallers100(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + deepCallers(100, b) + } +} + //////////////// // Benchmark functions followed by formatting ////////////////