summaryrefslogtreecommitdiff
path: root/errors/debug_stack.go
diff options
context:
space:
mode:
Diffstat (limited to 'errors/debug_stack.go')
-rwxr-xr-xerrors/debug_stack.go116
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]
+}