-
Notifications
You must be signed in to change notification settings - Fork 0
/
debug.go
122 lines (112 loc) · 2.98 KB
/
debug.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
package debug
import (
"bytes"
"fmt"
"runtime"
"strings"
)
const (
mMAX_DEPTH = 1000
mFILTER_KEY = "/x/core/debug/stack.go"
)
var (
// goRootForFilter is used for stack filtering purpose.
goRootForFilter = runtime.GOROOT()
)
func init() {
if goRootForFilter != "" {
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
}
}
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
func PrintStack(skip ...int) {
fmt.Print(Stack(skip...))
}
// Stack returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
func Stack(skip ...int) string {
return StackWithFilter("", skip...)
}
// StackWithFilter returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
//
// The parameter <filter> is used to filter the path of the caller.
func StackWithFilter(filter string, skip ...int) string {
number := 0
if len(skip) > 0 {
number = skip[0]
}
name := ""
space := " "
index := 1
buffer := bytes.NewBuffer(nil)
for i := callerFromIndex(filter) + number; i < mMAX_DEPTH; i++ {
if pc, file, line, ok := runtime.Caller(i); ok {
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, mFILTER_KEY) {
continue
}
if fn := runtime.FuncForPC(pc); fn == nil {
name = "unknown"
} else {
name = fn.Name()
}
if index > 9 {
space = " "
}
buffer.WriteString(fmt.Sprintf("%d.%s%s\n %s:%d\n", index, space, name, file, line))
index++
} else {
break
}
}
return buffer.String()
}
// CallerPath returns the absolute file path along with its line number of the caller.
func Caller(skip ...int) string {
return CallerWithFilter("", skip...)
}
// CallerPathWithFilter returns the absolute file path along with its line number of the caller.
//
// The parameter <filter> is used to filter the path of the caller.
func CallerWithFilter(filter string, skip ...int) string {
number := 0
if len(skip) > 0 {
number = skip[0]
}
for i := callerFromIndex(filter) + number; i < mMAX_DEPTH; i++ {
if _, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, mFILTER_KEY) {
continue
}
return fmt.Sprintf(`%s:%d`, file, line)
} else {
break
}
}
return ""
}
// callerFromIndex returns the caller position exclusive of the debug package.
func callerFromIndex(filter string) int {
for i := 0; i < mMAX_DEPTH; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, mFILTER_KEY) {
continue
}
// exclude the depth from the function of current package.
return i - 1
}
}
return 0
}