diff --git a/stack.go b/stack.go index f01dd86..6edd7e5 100644 --- a/stack.go +++ b/stack.go @@ -137,9 +137,13 @@ func (s *stack) StackTrace() StackTrace { } func callers() *stack { + return callersSkip(4) +} + +func callersSkip(skip int) *stack { const depth = 32 var pcs [depth]uintptr - n := runtime.Callers(3, pcs[:]) + n := runtime.Callers(skip, pcs[:]) var st stack = pcs[0:n] return &st } @@ -151,3 +155,16 @@ func funcname(name string) string { i = strings.Index(name, ".") return name[i+1:] } + +// NewStack is for library implementers that want to generate a stack trace. +// Normally you should insted use AddStack to get an error with a stack trace. +// +// The result of this function can be turned into a stack trace by calling .StackTrace() +// +// This function takes an argument for the number of stack frames to skip. +// This avoids putting stack generation function calls like this one in the stack trace. +// A value of 0 will give you the line that called NewStack(0) +// A library author wrapping this in their own function will want to use a value of at least 1. +func NewStack(skip int) StackTracer { + return callersSkip(skip + 3) +} diff --git a/stack_test.go b/stack_test.go index 0b4ee98..5603425 100644 --- a/stack_test.go +++ b/stack_test.go @@ -204,12 +204,12 @@ func TestStackTrace(t *testing.T) { } } +// This comment helps to maintain original line numbers +// Perhaps this test is too fragile :) func stackTrace() StackTrace { - const depth = 8 - var pcs [depth]uintptr - n := runtime.Callers(1, pcs[:]) - var st stack = pcs[0:n] - return st.StackTrace() + return NewStack(0).StackTrace() + // This comment helps to maintain original line numbers + // Perhaps this test is too fragile :) } func TestStackTraceFormat(t *testing.T) { @@ -275,3 +275,15 @@ func TestStackTraceFormat(t *testing.T) { testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) } } + +func TestNewStack(t *testing.T) { + got := NewStack(1).StackTrace() + want := NewStack(1).StackTrace() + if got[0] != want[0] { + t.Errorf("NewStack(remove NewStack): want: %v, got: %v", want, got) + } + gotFirst := fmt.Sprintf("%+v", got[0])[0:15] + if gotFirst != "testing.tRunner" { + t.Errorf("NewStack(): want: %v, got: %+v", "testing.tRunner", gotFirst) + } +}