This repository has been archived by the owner on Sep 22, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
stack.go
98 lines (86 loc) · 2.58 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
package apperrors
import (
"runtime"
"strings"
)
const (
stackMaxSize = 32
stackBaseOffset = 3
)
// StackTrace is a stack of Frame from innermost to outermost
type StackTrace []Frame
// Frame represents a single frame of stack trace
type Frame struct {
Func string
File string
Line int64
}
// newStackTrace creates StackTrace by callers
func newStackTrace(offset int) StackTrace {
pcs := make([]uintptr, stackMaxSize)
n := runtime.Callers(stackBaseOffset+offset, pcs[:])
i := 0
frames := make([]Frame, n)
for _, pc := range pcs[0:n] {
f := runtime.FuncForPC(pc)
if f == nil {
continue
}
file, line := f.FileLine(pc)
frames[i] = Frame{
Func: funcname(f.Name()),
File: trimGOPATH(f.Name(), file),
Line: int64(line),
}
i++
}
return frames[:i]
}
// funcname removes the path prefix component of a function's name reported by func.Name().
// Copied from https://github.com/pkg/errors/blob/master/stack.go
func funcname(name string) string {
i := strings.LastIndex(name, "/")
name = name[i+1:]
i = strings.Index(name, ".")
return name[i+1:]
}
// Copied from https://github.com/pkg/errors/blob/master/stack.go
func trimGOPATH(name, file string) string {
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(name, sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file
}