diff options
author | Valery Piashchynski <[email protected]> | 2020-10-22 10:57:48 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-10-22 10:57:48 +0300 |
commit | 77726578db6d539b151a5c4d36300b11e54a0bee (patch) | |
tree | 9c5247e50900bf6ab1b6ea2d54d11046be600ae0 /errors/debug_stack.go | |
parent | 1102a5c1faf17ec3153b62b25749fafafd2c98eb (diff) |
A
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] +} |