-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdebug.go
121 lines (115 loc) · 3.46 KB
/
debug.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package errors
import (
"bytes"
"fmt"
"runtime"
"strings"
)
// stack is a type that is embedded in an Error struct, and contains
// information about the stack that created that Error.
type stack struct {
// nolint:gas
callers []uintptr
// TODO(adg): add time of creation
}
// populateStack uses the runtime to populate the Error's stack struct with
// information about the current stack. It should be called from the E
// function, when the Error is being created.
// If the Error has another Error value in its Err field, populateStack
// coalesces the stack from the inner error (if any) with the current stack,
// so that any given Error value only prints one stack.
func (e *Error) populateStack() {
e.callers = callers()
e2, ok := e.Err.(*Error)
if !ok {
return
}
// Move distinct callers from inner error to outer error
// (and throw the common callers away)
// so that we only print the stack trace once.
i := 0
ok = false
for ; i < len(e.callers) && i < len(e2.callers); i++ {
if e.callers[len(e.callers)-1-i] != e2.callers[len(e2.callers)-1-i] {
break
}
ok = true
}
if ok { // The stacks have some PCs in common.
head := e2.callers[:len(e2.callers)-i]
tail := e.callers
e.callers = make([]uintptr, len(head)+len(tail))
copy(e.callers, head)
copy(e.callers[len(head):], tail)
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 // Should never happen, and this is just debugging.
}
}
return &f
}
// printStack formats and prints the stack for this Error to the given buffer.
// It should be called from the Error's Error method.
func (e *Error) printStack(b *bytes.Buffer) {
printCallers := callers()
// Iterate backward through e.callers (the last in the stack is the
// earliest call, such as main) skipping over the PCs that are shared
// by the error stack and by this function call stack, printing the
// names of the functions and their file names and line numbers.
var prev string // the name of the last-seen function
var diff bool // do the print and error call stacks differ now?
for i := 0; i < len(e.callers); i++ {
thisFrame := frame(e.callers, i)
name := thisFrame.Func.Name()
if !diff && i < len(printCallers) {
if name == frame(printCallers, i).Func.Name() {
// both stacks share this PC, skip it.
continue
}
// No match, don't consider printCallers again.
diff = true
}
// Don't print the same function twice.
// (Can happen when multiple error stacks have been coalesced.)
if name == prev {
continue
}
// Find the uncommon prefix between this and the previous
// function name, separating by dots and slashes.
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.
pad(b, Separator)
fmt.Fprintf(b, "%v:%d: ", thisFrame.File, thisFrame.Line)
if trim > 0 {
b.WriteString("...")
}
b.WriteString(name[trim:])
prev = name
}
}
// callers is a wrapper for runtime.Callers that allocates a slice.
func callers() []uintptr {
var stk [64]uintptr
const skip = 4 // Skip 4 stack frames; ok for both E and Error funcs.
n := runtime.Callers(skip, stk[:])
return stk[:n]
}