11package errors
22
33import (
4- "bytes"
54 "fmt"
65 "io"
76 "path"
@@ -11,7 +10,42 @@ import (
1110)
1211
1312// Frame represents a program counter inside a stack frame.
14- type Frame runtime.Frame
13+ type Frame uintptr
14+
15+ // pc returns the program counter for this frame;
16+ // multiple frames may have the same PC value.
17+ func (f Frame ) pc () uintptr { return uintptr (f ) - 1 }
18+
19+ // file returns the full path to the file that contains the
20+ // function for this Frame's pc.
21+ func (f Frame ) file () string {
22+ fn := runtime .FuncForPC (f .pc ())
23+ if fn == nil {
24+ return "unknown"
25+ }
26+ file , _ := fn .FileLine (f .pc ())
27+ return file
28+ }
29+
30+ // line returns the line number of source code of the
31+ // function for this Frame's pc.
32+ func (f Frame ) line () int {
33+ fn := runtime .FuncForPC (f .pc ())
34+ if fn == nil {
35+ return 0
36+ }
37+ _ , line := fn .FileLine (f .pc ())
38+ return line
39+ }
40+
41+ // name returns the name of this function, if known.
42+ func (f Frame ) name () string {
43+ fn := runtime .FuncForPC (f .pc ())
44+ if fn == nil {
45+ return "unknown"
46+ }
47+ return fn .Name ()
48+ }
1549
1650// Format formats the frame according to the fmt.Formatter interface.
1751//
@@ -35,25 +69,16 @@ func (f Frame) format(w io.Writer, s fmt.State, verb rune) {
3569 case 's' :
3670 switch {
3771 case s .Flag ('+' ):
38- if f .Function == "" {
39- io .WriteString (w , "unknown" )
40- } else {
41- io .WriteString (w , f .Function )
42- io .WriteString (w , "\n \t " )
43- io .WriteString (w , f .File )
44- }
72+ io .WriteString (w , f .name ())
73+ io .WriteString (w , "\n \t " )
74+ io .WriteString (w , f .file ())
4575 default :
46- file := f .File
47- if file == "" {
48- file = "unknown"
49- }
50- io .WriteString (w , path .Base (file ))
76+ io .WriteString (w , path .Base (f .file ()))
5177 }
5278 case 'd' :
53- io .WriteString (w , strconv .Itoa (f .Line ))
79+ io .WriteString (w , strconv .Itoa (f .line () ))
5480 case 'n' :
55- name := f .Function
56- io .WriteString (s , funcname (name ))
81+ io .WriteString (w , funcname (f .name ()))
5782 case 'v' :
5883 f .format (w , s , 's' )
5984 io .WriteString (w , ":" )
@@ -73,50 +98,23 @@ type StackTrace []Frame
7398//
7499// %+v Prints filename, function, and line number for each Frame in the stack.
75100func (st StackTrace ) Format (s fmt.State , verb rune ) {
76- var b bytes.Buffer
77101 switch verb {
78102 case 'v' :
79103 switch {
80104 case s .Flag ('+' ):
81- b .Grow (len (st ) * stackMinLen )
82- for _ , fr := range st {
83- b .WriteByte ('\n' )
84- fr .format (& b , s , verb )
105+ for _ , f := range st {
106+ fmt .Fprintf (s , "\n %+v" , f )
85107 }
86108 case s .Flag ('#' ):
87- fmt .Fprintf (& b , "%#v" , []Frame (st ))
109+ fmt .Fprintf (s , "%#v" , []Frame (st ))
88110 default :
89- st . formatSlice ( & b , s , verb )
111+ fmt . Fprintf ( s , "%v" , [] Frame ( st ) )
90112 }
91113 case 's' :
92- st . formatSlice ( & b , s , verb )
114+ fmt . Fprintf ( s , "%s" , [] Frame ( st ) )
93115 }
94- io .Copy (s , & b )
95116}
96117
97- // formatSlice will format this StackTrace into the given buffer as a slice of
98- // Frame, only valid when called with '%s' or '%v'.
99- func (st StackTrace ) formatSlice (b * bytes.Buffer , s fmt.State , verb rune ) {
100- b .WriteByte ('[' )
101- if len (st ) == 0 {
102- b .WriteByte (']' )
103- return
104- }
105-
106- b .Grow (len (st ) * (stackMinLen / 4 ))
107- st [0 ].format (b , s , verb )
108- for _ , fr := range st [1 :] {
109- b .WriteByte (' ' )
110- fr .format (b , s , verb )
111- }
112- b .WriteByte (']' )
113- }
114-
115- // stackMinLen is a best-guess at the minimum length of a stack trace. It
116- // doesn't need to be exact, just give a good enough head start for the buffer
117- // to avoid the expensive early growth.
118- const stackMinLen = 96
119-
120118// stack represents a stack of program counters.
121119type stack []uintptr
122120
@@ -125,29 +123,20 @@ func (s *stack) Format(st fmt.State, verb rune) {
125123 case 'v' :
126124 switch {
127125 case st .Flag ('+' ):
128- frames := runtime .CallersFrames (* s )
129- for {
130- frame , more := frames .Next ()
131- fmt .Fprintf (st , "\n %+v" , Frame (frame ))
132- if ! more {
133- break
134- }
126+ for _ , pc := range * s {
127+ f := Frame (pc )
128+ fmt .Fprintf (st , "\n %+v" , f )
135129 }
136130 }
137131 }
138132}
139133
140134func (s * stack ) StackTrace () StackTrace {
141- var st []Frame
142- frames := runtime .CallersFrames (* s )
143- for {
144- frame , more := frames .Next ()
145- st = append (st , Frame (frame ))
146- if ! more {
147- break
148- }
135+ f := make ([]Frame , len (* s ))
136+ for i := 0 ; i < len (f ); i ++ {
137+ f [i ] = Frame ((* s )[i ])
149138 }
150- return st
139+ return f
151140}
152141
153142func callers () * stack {
0 commit comments