Skip to content

Commit e5d1434

Browse files
authored
Merge pull request #2 from romanyx/feature/improve
Use uintptr instead of runtime.Frame
2 parents e3c770b + 9acb6dc commit e5d1434

2 files changed

Lines changed: 73 additions & 39 deletions

File tree

error.go

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ import (
55
"runtime"
66
)
77

8-
const defaultStackCap = 20
9-
10-
type withStack struct {
11-
origin error
12-
stack []runtime.Frame
13-
}
14-
158
func (e *withStack) Error() string {
169
return e.origin.Error()
1710
}
@@ -21,48 +14,88 @@ func (e *withStack) Unwrap() error {
2114
}
2215

2316
func (e *withStack) StackTrace() []runtime.Frame {
24-
return e.stack
17+
return e.stack.StackTrace()
2518
}
2619

27-
func trace(err error, offset int) *withStack {
28-
e := withStack{
29-
origin: err,
30-
stack: make([]runtime.Frame, 0, defaultStackCap),
31-
}
20+
type withStack struct {
21+
origin error
22+
*stack
23+
}
3224

33-
for {
34-
if f, ok := getFrame(offset); ok {
35-
e.stack = append(e.stack, f)
36-
offset++
37-
continue
38-
}
25+
// frame represents a program counter inside a stack frame.
26+
// For historical reasons if Frame is interpreted as a uintptr
27+
// its value represents the program counter + 1.
28+
type frame uintptr
3929

40-
break
30+
// pc returns the program counter for this frame;
31+
// multiple frames may have the same PC value.
32+
func (f frame) pc() uintptr { return uintptr(f) - 1 }
33+
34+
// file returns the full path to the file that contains the
35+
// function for this Frame's pc.
36+
func (f frame) file() string {
37+
fn := runtime.FuncForPC(f.pc())
38+
if fn == nil {
39+
return "unknown"
4140
}
41+
file, _ := fn.FileLine(f.pc())
42+
return file
43+
}
4244

43-
return &e
45+
// line returns the line number of source code of the
46+
// function for this Frame's pc.
47+
func (f frame) line() int {
48+
fn := runtime.FuncForPC(f.pc())
49+
if fn == nil {
50+
return 0
51+
}
52+
_, line := fn.FileLine(f.pc())
53+
return line
4454
}
4555

46-
func getFrame(caller int) (runtime.Frame, bool) {
47-
pc, file, line, ok := runtime.Caller(caller)
48-
if !ok {
49-
return runtime.Frame{}, false
56+
// name returns the name of this function, if known.
57+
func (f frame) name() string {
58+
fn := runtime.FuncForPC(f.pc())
59+
if fn == nil {
60+
return "unknown"
5061
}
62+
return fn.Name()
63+
}
5164

52-
f := runtime.Frame{
53-
PC: pc,
54-
File: file,
55-
Line: line,
65+
func (f frame) runtime() runtime.Frame {
66+
rf := runtime.Frame{
67+
PC: f.pc(),
68+
File: f.file(),
69+
Line: f.line(),
5670
}
5771

58-
fn := runtime.FuncForPC(pc)
72+
fn := runtime.FuncForPC(f.pc())
5973
if fn == nil {
60-
return f, true
74+
return rf
6175
}
6276

63-
f.Func = fn
64-
f.Function = fn.Name()
65-
f.Entry = fn.Entry()
77+
rf.Func = fn
78+
rf.Function = fn.Name()
79+
rf.Entry = fn.Entry()
80+
81+
return rf
82+
}
83+
84+
// stack represents a stack of program counters.
85+
type stack []uintptr
86+
87+
func (s *stack) StackTrace() []runtime.Frame {
88+
f := make([]runtime.Frame, len(*s))
89+
for i := 0; i < len(f); i++ {
90+
f[i] = frame((*s)[i]).runtime()
91+
}
92+
return f
93+
}
6694

67-
return f, true
95+
func callers() *stack {
96+
const depth = 32
97+
var pcs [depth]uintptr
98+
n := runtime.Callers(3, pcs[:])
99+
var st stack = pcs[0:n]
100+
return &st
68101
}

stack.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import (
77
"strconv"
88
)
99

10-
const offset = 3
11-
1210
// Errorf saves stack trace and pass arguments to
1311
// fmt.Errorf.
1412
func Errorf(format string, a ...interface{}) error {
15-
return trace(fmt.Errorf(format, a...), offset)
13+
return &withStack{
14+
fmt.Errorf(format, a...),
15+
callers(),
16+
}
1617
}
1718

1819
// Origin returns unwrapped origin of the error.
@@ -32,7 +33,7 @@ func Origin(err error) error {
3233

3334
// Trace returns stack trace for error.
3435
func Trace(err error) []runtime.Frame {
35-
stack := make([]runtime.Frame, 0, defaultStackCap)
36+
stack := make([]runtime.Frame, 0, 30)
3637

3738
for {
3839
if err == nil {

0 commit comments

Comments
 (0)