-
Notifications
You must be signed in to change notification settings - Fork 0
/
stack.go
83 lines (70 loc) · 1.95 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
package errors
import (
"fmt"
"io"
"os"
"runtime"
"strings"
)
var workingDir string
func init() {
s, _ := os.Getwd()
workingDir = s
}
// StackTrace represents a stack trace, which is a collection of Frames.
// It provides methods for formatting and appending stack traces.
type StackTrace []Frame
// Format formats the StackTrace according to the fmt.State and verb.
// It iterates over each Frame in the StackTrace and calls its Format method.
func (s StackTrace) Format(state fmt.State, verb rune) {
if verb == 'v' {
if state.Flag('+') {
for _, f := range s {
f.Format(state, verb)
}
}
}
}
func appendStackTrace(s, ss StackTrace) StackTrace {
appended := make([]Frame, len(s)+len(ss))
for i, frame := range s {
appended[i] = frame
}
for i, frame := range ss {
appended[i+len(s)] = frame
}
return appended
}
// Frame represents a single frame in a stack trace.
type Frame struct {
pc uintptr
}
// String returns a formatted string representation of the Frame.
// It includes the function name, file path (relative to the working directory), and line number.
func (f Frame) String() string {
file := "unknown"
funcName := "unknown"
var line int
fn := runtime.FuncForPC(f.pc)
if fn != nil {
funcName = fn.Name()
file, line = fn.FileLine(f.pc)
}
return fmt.Sprintf("%s(%s:%d)", funcName, strings.TrimPrefix(file, workingDir+"/"), line)
}
// Format formats the Frame according to the fmt.State and verb.
// If the verb is 'v' and the '+' flag is set in the fmt.State, it writes a newline,
// a tab character, and the formatted Frame. Otherwise, it writes the formatted Frame.
func (f Frame) Format(state fmt.State, verb rune) {
if verb == 'v' {
if state.Flag('+') {
io.WriteString(state, "\n\tat ")
}
io.WriteString(state, f.String())
}
}
// caller returns a new StackTrace starting from the specified number of frames to skip.
func caller(skip int) StackTrace {
pc, _, _, _ := runtime.Caller(skip)
return []Frame{{pc: pc}}
}