-
Notifications
You must be signed in to change notification settings - Fork 42
/
stack.go
104 lines (87 loc) · 2.2 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
package gobrake
import (
"runtime"
"strings"
"github.com/pkg/errors"
)
// getBacktrace returns the stacktrace associated with e. If e is an
// error from the errors package its stacktrace is extracted, otherwise
// the current stacktrace is collected end returned.
func getBacktrace(e interface{}, skip int) (string, []StackFrame) {
if err, ok := e.(stackTracer); ok {
return backtraceFromErrorWithStackTrace(err)
}
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(skip+1, pcs[:])
ff := runtime.CallersFrames(pcs[:n])
var firstPkg string
frames := make([]StackFrame, 0)
for {
f, ok := ff.Next()
if !ok {
break
}
pkg, fn := splitPackageFuncName(f.Function)
if firstPkg == "" && pkg != "runtime" {
firstPkg = pkg
}
if stackFilter(pkg, fn, f.File, f.Line) {
frames = frames[:0]
continue
}
frames = append(frames, StackFrame{
File: f.File,
Line: f.Line,
Func: fn,
})
}
return firstPkg, frames
}
func splitPackageFuncName(funcName string) (string, string) {
var packageName string
if ind := strings.LastIndex(funcName, "/"); ind > 0 {
packageName += funcName[:ind+1]
funcName = funcName[ind+1:]
}
if ind := strings.Index(funcName, "."); ind > 0 {
packageName += funcName[:ind]
funcName = funcName[ind+1:]
}
return packageName, funcName
}
func stackFilter(packageName, funcName string, file string, line int) bool {
return packageName == "runtime" && funcName == "panic"
}
// stackTraces returns the stackTrace of an error.
// It is part of the errors package public interface.
type stackTracer interface {
StackTrace() errors.StackTrace
}
// backtraceFromErrorWithStackTrace extracts the stacktrace from e.
func backtraceFromErrorWithStackTrace(e stackTracer) (string, []StackFrame) {
stackTrace := e.StackTrace()
var pcs []uintptr
for _, f := range stackTrace {
pcs = append(pcs, uintptr(f))
}
ff := runtime.CallersFrames(pcs)
var firstPkg string
frames := make([]StackFrame, 0)
for {
f, ok := ff.Next()
if !ok {
break
}
pkg, fn := splitPackageFuncName(f.Function)
if firstPkg == "" {
firstPkg = pkg
}
frames = append(frames, StackFrame{
File: f.File,
Line: f.Line,
Func: fn,
})
}
return firstPkg, frames
}