From 7f213f5cd056c2e152b09602621bc8f4acaf7844 Mon Sep 17 00:00:00 2001 From: Greg Weber Date: Thu, 6 Sep 2018 12:22:47 -0700 Subject: [PATCH 1/3] add NewStack(), which generates a stack trace This avoids using hacky code to get a trace. --- stack.go | 18 ++++++++++++++++++ stack_test.go | 22 +++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/stack.go b/stack.go index f01dd86..c7c580f 100644 --- a/stack.go +++ b/stack.go @@ -151,3 +151,21 @@ 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 use AddStack to get an error with a stack trace. +// Stack trace information can be generated by calling this function. +// It can then be turned into a stack trace by calling .StackTrace() +// +// This function takes an optional argument for the position in the stack to start from. +// This avoids putting stack generation function calls in the stack trace. +// It defaults to 1, which removes the NewStack function from the stack trace. +func NewStack(position ...int) StackTracer { + stackPosition := 1 + if len(position) > 0 { + stackPosition = position[0] + } + + adjustedStack := (*callers())[stackPosition:] + return &adjustedStack +} diff --git a/stack_test.go b/stack_test.go index 0b4ee98..518b937 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().StackTrace() + want := NewStack().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) + } +} From a39f23b24153e5b599641be675f66cccb68b4895 Mon Sep 17 00:00:00 2001 From: Greg Weber Date: Fri, 7 Sep 2018 09:29:03 -0700 Subject: [PATCH 2/3] require that NewStack take an argument --- stack.go | 28 +++++++++++++--------------- stack_test.go | 4 ++-- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/stack.go b/stack.go index c7c580f..a2d74d1 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 } @@ -153,19 +157,13 @@ func funcname(name string) string { } // NewStack is for library implementers that want to generate a stack trace. -// Normally you should use AddStack to get an error with a stack trace. -// Stack trace information can be generated by calling this function. -// It can then be turned into a stack trace by calling .StackTrace() +// Normally you should insted use AddStack to get an error with a stack trace. // -// This function takes an optional argument for the position in the stack to start from. -// This avoids putting stack generation function calls in the stack trace. -// It defaults to 1, which removes the NewStack function from the stack trace. -func NewStack(position ...int) StackTracer { - stackPosition := 1 - if len(position) > 0 { - stackPosition = position[0] - } - - adjustedStack := (*callers())[stackPosition:] - return &adjustedStack +// 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. +// You probably want to give it a value of 1, which removes the NewStack function from the stack trace. +func NewStack(skip int) StackTracer { + return callersSkip(skip + 3) } diff --git a/stack_test.go b/stack_test.go index 518b937..5603425 100644 --- a/stack_test.go +++ b/stack_test.go @@ -277,8 +277,8 @@ func TestStackTraceFormat(t *testing.T) { } func TestNewStack(t *testing.T) { - got := NewStack().StackTrace() - want := NewStack().StackTrace() + got := NewStack(1).StackTrace() + want := NewStack(1).StackTrace() if got[0] != want[0] { t.Errorf("NewStack(remove NewStack): want: %v, got: %v", want, got) } From 5dc497ef83f0b859c0bafcdf815f3b522b60d06c Mon Sep 17 00:00:00 2001 From: Greg Weber Date: Fri, 7 Sep 2018 09:43:29 -0700 Subject: [PATCH 3/3] doc fix --- stack.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stack.go b/stack.go index a2d74d1..6edd7e5 100644 --- a/stack.go +++ b/stack.go @@ -163,7 +163,8 @@ func funcname(name string) string { // // 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. -// You probably want to give it a value of 1, which removes the NewStack function from 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) }