11package errors
22
33import (
4+ "bytes"
45 "fmt"
56 "io"
67 "path"
78 "runtime"
9+ "strconv"
810 "strings"
911)
1012
@@ -24,33 +26,40 @@ type Frame runtime.Frame
2426// GOPATH separated by \n\t (<funcname>\n\t<path>)
2527// %+v equivalent to %+s:%d
2628func (f Frame ) Format (s fmt.State , verb rune ) {
29+ f .format (s , s , verb )
30+ }
31+
32+ // format allows stack trace printing calls to be made with a bytes.Buffer.
33+ func (f Frame ) format (w io.Writer , s fmt.State , verb rune ) {
2734 switch verb {
2835 case 's' :
2936 switch {
3037 case s .Flag ('+' ):
3138 fn := runtime .Frame (f ).Func
3239 if fn == nil {
33- io .WriteString (s , "unknown" )
40+ io .WriteString (w , "unknown" )
3441 } else {
3542 file := runtime .Frame (f ).File
36- fmt .Fprintf (s , "%s\n \t %s" , fn .Name (), file )
43+ io .WriteString (w , fn .Name ())
44+ io .WriteString (w , "\n \t " )
45+ io .WriteString (w , file )
3746 }
3847 default :
3948 file := runtime .Frame (f ).File
4049 if file == "" {
4150 file = "unknown"
4251 }
43- io .WriteString (s , path .Base (file ))
52+ io .WriteString (w , path .Base (file ))
4453 }
4554 case 'd' :
46- fmt . Fprintf ( s , "%d" , runtime .Frame (f ).Line )
55+ io . WriteString ( w , strconv . Itoa ( runtime .Frame (f ).Line ) )
4756 case 'n' :
4857 name := runtime .Frame (f ).Function
4958 io .WriteString (s , funcname (name ))
5059 case 'v' :
51- f .Format ( s , 's' )
52- io .WriteString (s , ":" )
53- f .Format ( s , 'd' )
60+ f .format ( w , s , 's' )
61+ io .WriteString (w , ":" )
62+ f .format ( w , s , 'd' )
5463 }
5564}
5665
@@ -66,23 +75,50 @@ type StackTrace []Frame
6675//
6776// %+v Prints filename, function, and line number for each Frame in the stack.
6877func (st StackTrace ) Format (s fmt.State , verb rune ) {
78+ var b bytes.Buffer
6979 switch verb {
7080 case 'v' :
7181 switch {
7282 case s .Flag ('+' ):
73- for _ , f := range st {
74- fmt .Fprintf (s , "\n %+v" , f )
83+ b .Grow (len (st ) * stackMinLen )
84+ for _ , fr := range st {
85+ b .WriteByte ('\n' )
86+ fr .format (& b , s , verb )
7587 }
7688 case s .Flag ('#' ):
77- fmt .Fprintf (s , "%#v" , []Frame (st ))
89+ fmt .Fprintf (& b , "%#v" , []Frame (st ))
7890 default :
79- fmt . Fprintf ( s , "%v" , [] Frame ( st ) )
91+ st . formatSlice ( & b , s , verb )
8092 }
8193 case 's' :
82- fmt .Fprintf (s , "%s" , []Frame (st ))
94+ st .formatSlice (& b , s , verb )
95+ }
96+ io .Copy (s , & b )
97+ }
98+
99+ // formatSlice will format this StackTrace into the given buffer as a slice of
100+ // Frame, only valid when called with '%s' or '%v'.
101+ func (st StackTrace ) formatSlice (b * bytes.Buffer , s fmt.State , verb rune ) {
102+ b .WriteByte ('[' )
103+ if len (st ) == 0 {
104+ b .WriteByte (']' )
105+ return
106+ }
107+
108+ b .Grow (len (st ) * (stackMinLen / 4 ))
109+ st [0 ].format (b , s , verb )
110+ for _ , fr := range st [1 :] {
111+ b .WriteByte (' ' )
112+ fr .format (b , s , verb )
83113 }
114+ b .WriteByte (']' )
84115}
85116
117+ // stackMinLen is a best-guess at the minimum length of a stack trace. It
118+ // doesn't need to be exact, just give a good enough head start for the buffer
119+ // to avoid the expensive early growth.
120+ const stackMinLen = 96
121+
86122// stack represents a stack of program counters.
87123type stack []uintptr
88124
0 commit comments