-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstack.go
146 lines (124 loc) · 3.28 KB
/
stack.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package flaw
import (
"fmt"
"runtime"
"strconv"
)
// StackFrame represents a program counter inside a stack frame.
// For historical reasons if StackFrame is interpreted as a uintptr
// its value represents the program counter + 1.
type StackFrame runtime.Frame
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s source file full path
// %+v equivalent to %+s:%d (%n)
func (frame StackFrame) Format(state fmt.State, verb rune) {
switch verb {
case 'v':
frame.Format(state, 's')
fmt.Fprintf(state, ":")
frame.Format(state, 'd')
if state.Flag('+') {
fmt.Fprintf(state, " (")
frame.Format(state, 'n')
fmt.Fprintf(state, ")")
}
case 's':
switch {
case state.Flag('+'):
fmt.Fprint(state, frame.File)
default:
fmt.Fprint(state, relative(frame.File))
}
case 'd':
fmt.Fprint(state, strconv.Itoa(frame.Line))
case 'n':
fmt.Fprint(state, function(frame.Function))
}
}
// MarshalText formats a stacktrace StackFrame as a text string. The output is the
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
func (frame StackFrame) MarshalText() ([]byte, error) {
if name := frame.Function; name == "unknown" {
return []byte(name), nil
}
return []byte(fmt.Sprintf("%v", frame)), nil
}
// StackTrace is stack of StackFrames from innermost (newest) to outermost (oldest).
type StackTrace []StackFrame
// NewStackTrace creates a new StackTrace
func NewStackTrace() StackTrace {
var (
stack = make([]uintptr, 32)
size = runtime.Callers(3, stack[:])
frames = runtime.CallersFrames(stack[:size])
trace = StackTrace{}
)
for {
frame, ok := frames.Next()
if !ok {
return trace
}
trace = append(trace, StackFrame(frame))
}
}
// NewStackTraceAt creates a new stack trace at given position
func NewStackTraceAt(n int) StackTrace {
n = n + 1
stack := NewStackTrace()
count := len(stack)
if n > 0 && n < count {
stack = StackTrace(stack[n:])
}
return stack
}
// Format formats the stack of StackFrames according to the fmt.Formatter interface.
//
// %s lists source files for each StackFrame in the stack
// %v lists the source file and line number for each StackFrame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each StackFrame in the stack.
func (stack StackTrace) Format(state fmt.State, verb rune) {
switch verb {
case 's':
fallthrough
case 'v':
switch {
case state.Flag('+'):
stack.formatBullet(state, verb)
case state.Flag('#'):
fmt.Fprintf(state, "%#v", []StackFrame(stack))
default:
stack.formatSlice(state, verb)
}
}
}
func (stack StackTrace) formatBullet(state fmt.State, verb rune) {
count := len(stack)
for index, frame := range stack {
fmt.Fprint(state, " --- ")
frame.Format(state, verb)
if index < count-1 {
fmt.Fprint(state, "\n")
}
}
}
func (stack StackTrace) formatSlice(state fmt.State, verb rune) {
fmt.Fprint(state, "[")
for index, frame := range stack {
if index > 0 {
fmt.Fprint(state, ", ")
}
frame.Format(state, verb)
}
fmt.Fprint(state, "]")
}