forked from mailgun/holster
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcallstack.go
104 lines (92 loc) · 2.38 KB
/
callstack.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
package callstack
import (
"bytes"
"fmt"
"runtime"
"strconv"
"strings"
pkgerrors "github.com/pkg/errors" //nolint:gomodguard // Legacy code requires deprecated package.
)
type FrameInfo struct {
CallStack string
Func string
File string
LineNo int
}
func GetCallStack(frames pkgerrors.StackTrace) string {
var trace []string
for i := len(frames) - 1; i >= 0; i-- {
trace = append(trace, fmt.Sprintf("%v", frames[i]))
}
return strings.Join(trace, " ")
}
// GetLastFrame returns Caller information on the first frame in the stack trace.
func GetLastFrame(frames pkgerrors.StackTrace) FrameInfo {
if len(frames) == 0 {
return FrameInfo{}
}
pc := uintptr(frames[0]) - 1
fn := runtime.FuncForPC(pc)
if fn == nil {
return FrameInfo{Func: fmt.Sprintf("unknown func at %v", pc)}
}
filePath, lineNo := fn.FileLine(pc)
return FrameInfo{
CallStack: GetCallStack(frames),
Func: FuncName(fn),
File: filePath,
LineNo: lineNo,
}
}
// FuncName given a runtime function spec returns a short function name in
// format `<package name>.<function name>` or if the function has a receiver
// in format `<package name>.(<receiver>).<function name>`.
func FuncName(fn *runtime.Func) string {
if fn == nil {
return ""
}
funcPath := fn.Name()
idx := strings.LastIndex(funcPath, "/")
if idx == -1 {
return funcPath
}
return funcPath[idx+1:]
}
type HasStackTrace interface {
StackTrace() pkgerrors.StackTrace
}
// CallStack represents a stack of program counters.
type CallStack []uintptr
func (cs *CallStack) Format(st fmt.State, verb rune) {
if verb == 'v' && st.Flag('+') {
for _, pc := range *cs {
f := pkgerrors.Frame(pc)
_, _ = fmt.Fprintf(st, "\n%+v", f)
}
}
}
func (cs *CallStack) StackTrace() pkgerrors.StackTrace {
f := make([]pkgerrors.Frame, len(*cs))
for i := 0; i < len(f); i++ {
f[i] = pkgerrors.Frame((*cs)[i])
}
return f
}
// New creates a new CallStack struct from current stack minus 'skip' number of frames.
func New(skip int) *CallStack {
skip += 2
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(skip, pcs[:])
var st CallStack = pcs[0:n]
return &st
}
// GoRoutineID returns the current goroutine id.
func GoRoutineID() uint64 {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, _ := strconv.ParseUint(string(b), 10, 64)
return n
}