1+ // +build go1.7
2+
13// Package stack implements utilities to capture, manipulate, and format call
24// stacks. It provides a simpler API than package runtime.
35//
@@ -21,23 +23,31 @@ import (
2123
2224// Call records a single function invocation from a goroutine stack.
2325type Call struct {
24- pcs [ 2 ] uintptr
26+ frame runtime. Frame
2527}
2628
2729// Caller returns a Call from the stack of the current goroutine. The argument
2830// skip is the number of stack frames to ascend, with 0 identifying the
2931// calling function.
3032func Caller (skip int ) Call {
31- var pcs [2 ]uintptr
33+ // As of Go 1.9 we need room for up to three PC entries.
34+ //
35+ // 0. An entry for the stack frame prior to the target to check for
36+ // special handling needed if that prior entry is runtime.sigpanic.
37+ // 1. A possible second entry to hold metadata about skipped inlined
38+ // functions. If inline functions were not skipped the target frame
39+ // PC will be here.
40+ // 2. A third entry for the target frame PC when the second entry
41+ // is used for skipped inline functions.
42+ var pcs [3 ]uintptr
3243 n := runtime .Callers (skip + 1 , pcs [:])
44+ frames := runtime .CallersFrames (pcs [:n ])
45+ frame , _ := frames .Next ()
46+ frame , _ = frames .Next ()
3347
34- var c Call
35- if n < 2 {
36- return c
48+ return Call {
49+ frame : frame ,
3750 }
38-
39- c .pcs = pcs
40- return c
4151}
4252
4353// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
@@ -48,7 +58,7 @@ func (c Call) String() string {
4858// MarshalText implements encoding.TextMarshaler. It formats the Call the same
4959// as fmt.Sprintf("%v", c).
5060func (c Call ) MarshalText () ([]byte , error ) {
51- if c .pcs [ 0 ] == 0 {
61+ if c .frame == (runtime. Frame {}) {
5262 return nil , ErrNoFunc
5363 }
5464
@@ -78,31 +88,19 @@ var ErrNoFunc = errors.New("no call stack information")
7888// %+v equivalent to %+s:%d
7989// %#v equivalent to %#s:%d
8090func (c Call ) Format (s fmt.State , verb rune ) {
81- frames := runtime .CallersFrames (c .pcs [:])
82- head , _ := frames .Next ()
83- frame , _ := frames .Next ()
84-
85- if head .Function == "runtime.signpanic" {
86- frame , _ = frames .Next ()
87- }
88-
89- format (& frame , s , verb )
90- }
91-
92- func format (frame * runtime.Frame , s fmt.State , verb rune ) {
93- if frame .Func == nil {
91+ if c .frame == (runtime.Frame {}) {
9492 fmt .Fprintf (s , "%%!%c(NOFUNC)" , verb )
9593 return
9694 }
9795
9896 switch verb {
9997 case 's' , 'v' :
100- file , line := frame . File , frame .Line
98+ file := c . frame .File
10199 switch {
102100 case s .Flag ('#' ):
103101 // done
104102 case s .Flag ('+' ):
105- file = file [pkgIndex (file , frame .Function ):]
103+ file = file [pkgIndex (file , c . frame .Function ):]
106104 default :
107105 const sep = "/"
108106 if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -112,16 +110,15 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
112110 io .WriteString (s , file )
113111 if verb == 'v' {
114112 buf := [7 ]byte {':' }
115- s .Write (strconv .AppendInt (buf [:1 ], int64 (line ), 10 ))
113+ s .Write (strconv .AppendInt (buf [:1 ], int64 (c . frame . Line ), 10 ))
116114 }
117115
118116 case 'd' :
119- line := frame .Line
120117 buf := [6 ]byte {}
121- s .Write (strconv .AppendInt (buf [:0 ], int64 (line ), 10 ))
118+ s .Write (strconv .AppendInt (buf [:0 ], int64 (c . frame . Line ), 10 ))
122119
123120 case 'k' :
124- name := frame .Function
121+ name := c . frame .Function
125122 const pathSep = "/"
126123 start , end := 0 , len (name )
127124 if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -137,7 +134,7 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
137134 io .WriteString (s , name [start :end ])
138135
139136 case 'n' :
140- name := frame .Function
137+ name := c . frame .Function
141138 if ! s .Flag ('+' ) {
142139 const pathSep = "/"
143140 if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -152,43 +149,17 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
152149 }
153150}
154151
152+ // Frame returns the call frame infomation for the Call.
153+ func (c Call ) Frame () runtime.Frame {
154+ return c .frame
155+ }
156+
155157// PC returns the program counter for this call frame; multiple frames may
156158// have the same PC value.
159+ //
160+ // Deprecated: Use Call.Frame instead.
157161func (c Call ) PC () uintptr {
158- return c .pcs [1 ]
159- }
160-
161- func (c Call ) frame () * runtime.Frame {
162- frames := runtime .CallersFrames (c .pcs [:])
163- frame , _ := frames .Next ()
164- frame , _ = frames .Next ()
165- return & frame
166- }
167-
168- // name returns the import path qualified name of the function containing the
169- // call.
170- func (c Call ) name () string {
171- frame := c .frame ()
172- if frame .Func == nil {
173- return "???"
174- }
175- return frame .Function
176- }
177-
178- func (c Call ) file () string {
179- frame := c .frame ()
180- if frame .Func == nil {
181- return "???"
182- }
183- return frame .File
184- }
185-
186- func (c Call ) line () int {
187- frame := c .frame ()
188- if frame .Func == nil {
189- return 0
190- }
191- return frame .Line
162+ return c .frame .PC
192163}
193164
194165// CallStack records a sequence of function invocations from a goroutine
@@ -226,31 +197,12 @@ func (cs CallStack) MarshalText() ([]byte, error) {
226197// supplied verb and options.
227198func (cs CallStack ) Format (s fmt.State , verb rune ) {
228199 s .Write (openBracketBytes )
229-
230- var pcs []uintptr
231- for _ , c := range cs {
232- pcs = append (pcs , c .pcs [:]... )
233- }
234-
235- frames := runtime .CallersFrames (pcs [:])
236-
237- initial := true
238- for frame , more := frames .Next (); more ; frame , more = frames .Next () {
239- if ! initial {
200+ for i , pc := range cs {
201+ if i > 0 {
240202 s .Write (spaceBytes )
241203 }
242-
243- next , _ := frames .Next ()
244- if frame .Function == "runtime.sigpanic" {
245- frame , _ = runtime .CallersFrames ([]uintptr {next .PC - 1 }).Next ()
246- } else {
247- frame = next
248- }
249-
250- format (& frame , s , verb )
251- initial = false
204+ pc .Format (s , verb )
252205 }
253-
254206 s .Write (closeBracketBytes )
255207}
256208
@@ -259,12 +211,17 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
259211func Trace () CallStack {
260212 var pcs [512 ]uintptr
261213 n := runtime .Callers (1 , pcs [:])
262- cs := make ([]Call , n )
263214
264- for i := range pcs [:n ] {
265- cs [i ] = Call {
266- pcs : [2 ]uintptr {pcs [i ], pcs [i + 1 ]},
267- }
215+ frames := runtime .CallersFrames (pcs [:n ])
216+ cs := make (CallStack , 0 , n )
217+
218+ // Skip extra frame retrieved just to make sure the runtime.sigpanic
219+ // special case is handled.
220+ frame , more := frames .Next ()
221+
222+ for more {
223+ frame , more = frames .Next ()
224+ cs = append (cs , Call {frame : frame })
268225 }
269226
270227 return cs
@@ -273,7 +230,7 @@ func Trace() CallStack {
273230// TrimBelow returns a slice of the CallStack with all entries below c
274231// removed.
275232func (cs CallStack ) TrimBelow (c Call ) CallStack {
276- for len (cs ) > 0 && cs [0 ]. pcs [ 1 ] != c . pcs [ 1 ] {
233+ for len (cs ) > 0 && cs [0 ] != c {
277234 cs = cs [1 :]
278235 }
279236 return cs
@@ -282,7 +239,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
282239// TrimAbove returns a slice of the CallStack with all entries above c
283240// removed.
284241func (cs CallStack ) TrimAbove (c Call ) CallStack {
285- for len (cs ) > 0 && cs [len (cs )- 1 ]. pcs [ 1 ] != c . pcs [ 1 ] {
242+ for len (cs ) > 0 && cs [len (cs )- 1 ] != c {
286243 cs = cs [:len (cs )- 1 ]
287244 }
288245 return cs
@@ -331,12 +288,13 @@ func pkgIndex(file, funcName string) int {
331288var runtimePath string
332289
333290func init () {
334- var pcs [1 ]uintptr
291+ var pcs [3 ]uintptr
335292 runtime .Callers (0 , pcs [:])
336- fn := runtime .FuncForPC (pcs [0 ])
337- file , _ := fn .FileLine (pcs [0 ])
293+ frames := runtime .CallersFrames (pcs [:])
294+ frame , _ := frames .Next ()
295+ file := frame .File
338296
339- idx := pkgIndex (file , fn . Name () )
297+ idx := pkgIndex (frame . File , frame . Function )
340298
341299 runtimePath = file [:idx ]
342300 if runtime .GOOS == "windows" {
@@ -345,7 +303,7 @@ func init() {
345303}
346304
347305func inGoroot (c Call ) bool {
348- file := c .file ()
306+ file := c .frame . File
349307 if len (file ) == 0 || file [0 ] == '?' {
350308 return true
351309 }
0 commit comments