-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstacktracez.go
More file actions
113 lines (100 loc) · 2.22 KB
/
Copy pathstacktracez.go
File metadata and controls
113 lines (100 loc) · 2.22 KB
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
package stacktracez // import "ezpkg.io/stacktracez"
import (
"fmt"
"path"
"runtime"
"strings"
"sync"
"ezpkg.io/fmtz"
)
const maxFrames = 32
type Frames struct {
frames *runtime.Frames
mutex sync.RWMutex
cached []Frame
}
type Frame runtime.Frame
type StackTracerZ interface {
StackTraceZ() *Frames
}
func (fz *Frames) StackTraceZ() *Frames { return fz }
func StackTrace() *Frames {
var pc [maxFrames]uintptr
runtime.Callers(2, pc[:])
frames := runtime.CallersFrames(pc[:])
return &Frames{frames: frames}
}
func StackTraceSkip(skip int) *Frames {
var pc [maxFrames]uintptr
n := runtime.Callers(skip+2, pc[:])
frames := runtime.CallersFrames(pc[:n])
return &Frames{frames: frames}
}
func (fz *Frames) Format(s0 fmt.State, verb rune) {
s := fmtz.WrapState(s0)
if fz == nil {
s.WriteStringZ("<nil>")
return
}
switch verb {
case 's', 'v':
formatFrames(s, verb, fz.GetFrames())
}
}
func (fz *Frames) GetFrames() []Frame {
if fz == nil {
return nil
}
fz.mutex.RLock()
if cached := fz.cached; cached != nil {
fz.mutex.RUnlock()
return cached
}
fz.mutex.RUnlock()
fz.mutex.Lock()
defer fz.mutex.Unlock()
if cached := fz.cached; cached != nil {
return cached
}
fz.cached = make([]Frame, 0, maxFrames)
for fr, ok := fz.frames.Next(); ok; fr, ok = fz.frames.Next() {
fz.cached = append(fz.cached, Frame(fr))
}
return fz.cached
}
func (f Frame) Format(s0 fmt.State, verb rune) {
s := fmtz.WrapState(s0)
switch verb {
case 's', 'v':
switch {
case s.Flag('+'):
s.Printf("%s\n\t%s:%d", f.Function, f.File, f.Line)
default:
pkg, file, line, fn := f.Components()
s.Printf("%s/%s:%d · %s", pkg, file, line, fn)
}
case 'd':
s.Printf("%d", f.Line)
}
}
func (f Frame) Components() (pkg, file string, line int, fn string) {
sepIdx := strings.LastIndexByte(f.Function, '/')
if sepIdx < 0 {
sepIdx = 0
}
dotIdx := strings.IndexByte(f.Function[sepIdx:], '.')
if dotIdx <= 0 {
dotIdx = -1
}
pkg, fn = f.Function[:sepIdx+dotIdx], f.Function[sepIdx+dotIdx+1:]
return pkg, path.Base(f.File), f.Line, fn
}
func formatFrames(s fmtz.State, verb rune, frames []Frame) {
if len(frames) == 0 {
s.WriteStringZ("[]")
}
for _, frame := range frames {
frame.Format(s, verb)
s.WriteStringZ("\n")
}
}