-
Notifications
You must be signed in to change notification settings - Fork 25
/
stacktrace.go
119 lines (104 loc) · 2.75 KB
/
stacktrace.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
package panichandler
import (
"fmt"
"path"
"runtime"
"strconv"
"strings"
)
// Callstack is a full stacktrace.
type Callstack []uintptr
const stackLimit = 50
// Capture returns a full stacktrace.
func Capture() Callstack {
callers := make([]uintptr, stackLimit)
count := runtime.Callers(2, callers)
stack := callers[:count]
return Callstack(stack)
}
// Location holds the physical location of a stack entry.
type Location struct {
// Directory is the directory the source file is from.
Directory string
// File is the filename of the source file.
File string
// Line is the line index in the file.
Line int
}
// Function holds the logical location of a stack entry.
type Function struct {
// Package is the go package the stack entry is from.
Package string
// Name is the function name the stack entry is from.
Name string
}
// Entry holds the human understandable form of a StackTrace entry.
type Entry struct {
// Location holds the physical location for this entry.
Location Location
// Location holds the logical location for this entry.
Function Function
// PC is the program counter for this entry.
PC uintptr
}
// Entries returns all the entries for the stack trace.
func (c Callstack) Entries() []Entry {
frames := runtime.CallersFrames([]uintptr(c))
out := []Entry{}
for {
frame, more := frames.Next()
dir, file := path.Split(frame.File)
fullname := frame.Function
var pkg, name string
if i := strings.LastIndex(fullname, "/"); i > 0 {
i += strings.IndexRune(fullname[i+1:], '.')
// we find the last /, then find the next . to split the function name from the package name
pkg, name = fullname[:i+1], fullname[i+2:]
} else {
fullnameSplit := strings.Split(fullname, ".")
pkg, name = fullnameSplit[0], fullnameSplit[1]
}
out = append(out, Entry{
Location: Location{
Directory: dir,
File: file,
Line: frame.Line,
},
Function: Function{
Package: pkg,
Name: name,
},
PC: frame.PC,
})
if !more {
break
}
}
return out
}
// GetEntries returns stacktrace of Callstack in map[string]interface{} format.
func (c Callstack) GetEntries() map[string]interface{} {
entries := c.Entries()
lines := make(map[string]interface{})
for i, e := range entries {
lines["#"+strconv.Itoa(i+1)] = e.String()
}
return lines
}
// String returns stacktrace of Entry.
func (e Entry) String() string {
return fmt.Sprint(e.Location, ":", e.Function)
}
// String returns Location of stack entry.
func (l Location) String() string {
const strip = "fluxninja/"
dir := l.Directory
if i := strings.LastIndex(dir, strip); i > 0 {
dir = dir[i+len(strip):]
}
return fmt.Sprint(dir, l.File, "@", l.Line)
}
// String returns name of Function.
func (f Function) String() string {
return f.Name
}