diff options
Diffstat (limited to 'errors/debug_stack.go')
-rwxr-xr-x | errors/debug_stack.go | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/errors/debug_stack.go b/errors/debug_stack.go new file mode 100755 index 00000000..fa77ddc8 --- /dev/null +++ b/errors/debug_stack.go @@ -0,0 +1,116 @@ +// +build debug + +package errors + +import ( + "bytes" + "fmt" + "runtime" + "strings" +) + +type stack struct { + callers []uintptr + // TODO(adg): add time of creation +} + +func (e *Error) populateStack() { + e.callers = callers() + + e2, ok := e.Err.(*Error) + if !ok { + return + } + + i := 0 + + ok = false + for ; i < len(e.callers) && i < len(e2.callers); i++ { + // check for similar + if e.callers[len(e.callers)-1-i] != e2.callers[len(e2.callers)-1-i] { + break + } + ok = true + } + + if ok { //we have common PCs + e2Head := e2.callers[:len(e2.callers)-i] + eTail := e.callers + + e.callers = make([]uintptr, len(e2Head)+len(eTail)) + + copy(e.callers, e2Head) + copy(e.callers[len(e2Head):], eTail) + + e2.callers = nil + } +} + +// frame returns the nth frame, with the frame at top of stack being 0. +func frame(callers []uintptr, n int) runtime.Frame { + frames := runtime.CallersFrames(callers) + var f runtime.Frame + for i := len(callers) - 1; i >= n; i-- { + var ok bool + f, ok = frames.Next() + if !ok { + break + } + } + return f +} + +func (e *Error) printStack(b *bytes.Buffer) { + c := callers() + + var prev string + var diff bool + for i := 0; i < len(e.callers); i++ { + pc := e.callers[len(e.callers)-i-1] // get current PC + fn := runtime.FuncForPC(pc) // get function by pc + name := fn.Name() + + if !diff && i < len(c) { + ppc := c[len(c)-i-1] + pname := runtime.FuncForPC(ppc).Name() + if name == pname { + continue + } + diff = true + } + + if name == prev { + continue + } + + trim := 0 + for { + j := strings.IndexAny(name[trim:], "./") + if j < 0 { + break + } + if !strings.HasPrefix(prev, name[:j+trim]) { + break + } + trim += j + 1 // skip over the separator + } + + // Do the printing. + appendStrToBuf(b, Separator) + file, line := fn.FileLine(pc) + fmt.Fprintf(b, "%v:%d: ", file, line) + if trim > 0 { + b.WriteString("...") + } + b.WriteString(name[trim:]) + + prev = name + } +} + +func callers() []uintptr { + var stk [64]uintptr + const skip = 4 + n := runtime.Callers(skip, stk[:]) + return stk[:n] +} |