-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcallframes.go
138 lines (115 loc) · 2.9 KB
/
callframes.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
package elk
import (
"bytes"
"fmt"
"io"
"runtime"
"strconv"
)
// CallFrame is a type alias for runtime.Frame with additional formatting
// functionailty used in downstream functions.
type CallFrame runtime.Frame
func (t CallFrame) String() string {
return fmt.Sprintf("%s", t)
}
func (t CallFrame) Format(s fmt.State, verb rune) {
width, hasWidth := s.Width()
switch verb {
case 's':
format := "%s %s:%d"
if hasWidth {
format = "%-" + strconv.Itoa(width) + "s\t%s:%d"
}
fmt.Fprintf(s, format, t.Function, t.File, t.Line)
case 'v':
fmt.Fprintf(s, "%v", runtime.Frame(t))
}
}
// CallStack contains the list of called
// runtime.Frames in the call chain with
// an offset from which frames are
// reported.
type CallStack struct {
ptrs []uintptr
frames []CallFrame
offset int
}
// Frames returns the offset slice of called
// runtime.Frame's in the recorded call stack.
func (t *CallStack) Frames() []CallFrame {
if t.frames == nil {
t.fetchCallFrames()
}
if len(t.frames) < t.offset {
return nil
}
return t.frames[t.offset:]
}
// WriteIndent is an alias for write with the given
// indent string attached before each line of output.
func (t *CallStack) WriteIndent(w io.Writer, max int, indent string) {
frames := t.Frames()
if max > 0 && len(frames) > max {
frames = frames[:max]
}
maxLenFName := 0
for _, frame := range frames {
if l := len(frame.Function); l > maxLenFName {
maxLenFName = l
}
}
for _, frame := range frames {
fmt.Fprintf(w, "%s%"+strconv.Itoa(maxLenFName)+"s\n", indent, frame)
}
}
// Write formats the call stack into a table of called
// function and the file plus line number and writes
// the result into the writer w.
//
// max defines the number of stack frames which are
// printed starting from the original caller.
func (t *CallStack) Write(w io.Writer, max int) {
t.WriteIndent(w, max, "")
}
// String returns the formatted output of the callstack
// as string.
func (t *CallStack) String() string {
var b bytes.Buffer
t.Write(&b, 0)
return b.String()
}
// At returns the formatted call frame at the given position n
// if existent.
func (t *CallStack) At(n int) (s string, ok bool) {
frames := t.Frames()
if n >= len(frames) || n < 0 {
return "", false
}
frame := frames[n]
return frame.String(), true
}
// First is shorthand for At(0) and returns the first frame in
// the CallStack, if available.
func (t *CallStack) First() (s string, ok bool) {
return t.At(0)
}
func newCallStack(offset int, n int) *CallStack {
callerPtrs := make([]uintptr, n)
nPtrs := runtime.Callers(2, callerPtrs)
return &CallStack{
ptrs: callerPtrs[:nPtrs],
offset: offset,
}
}
func (t *CallStack) fetchCallFrames() {
frameCursor := runtime.CallersFrames(t.ptrs)
callFrames := make([]CallFrame, 0, len(t.ptrs))
for {
frame, more := frameCursor.Next()
callFrames = append(callFrames, CallFrame(frame))
if !more {
break
}
}
t.frames = callFrames
}