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,29 +23,31 @@ import (
2123
2224// Call records a single function invocation from a goroutine stack.
2325type Call struct {
24- fn * runtime.Func
25- pc uintptr
26+ frame runtime.Frame
2627}
2728
2829// Caller returns a Call from the stack of the current goroutine. The argument
2930// skip is the number of stack frames to ascend, with 0 identifying the
3031// calling function.
3132func Caller (skip int ) Call {
32- 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
3343 n := runtime .Callers (skip + 1 , pcs [:])
44+ frames := runtime .CallersFrames (pcs [:n ])
45+ frame , _ := frames .Next ()
46+ frame , _ = frames .Next ()
3447
35- var c Call
36-
37- if n < 2 {
38- return c
48+ return Call {
49+ frame : frame ,
3950 }
40-
41- c .pc = pcs [1 ]
42- if runtime .FuncForPC (pcs [0 ]).Name () != "runtime.sigpanic" {
43- c .pc --
44- }
45- c .fn = runtime .FuncForPC (c .pc )
46- return c
4751}
4852
4953// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
@@ -54,9 +58,10 @@ func (c Call) String() string {
5458// MarshalText implements encoding.TextMarshaler. It formats the Call the same
5559// as fmt.Sprintf("%v", c).
5660func (c Call ) MarshalText () ([]byte , error ) {
57- if c .fn == nil {
61+ if c .frame == (runtime. Frame {}) {
5862 return nil , ErrNoFunc
5963 }
64+
6065 buf := bytes.Buffer {}
6166 fmt .Fprint (& buf , c )
6267 return buf .Bytes (), nil
@@ -83,19 +88,19 @@ var ErrNoFunc = errors.New("no call stack information")
8388// %+v equivalent to %+s:%d
8489// %#v equivalent to %#s:%d
8590func (c Call ) Format (s fmt.State , verb rune ) {
86- if c .fn == nil {
91+ if c .frame == (runtime. Frame {}) {
8792 fmt .Fprintf (s , "%%!%c(NOFUNC)" , verb )
8893 return
8994 }
9095
9196 switch verb {
9297 case 's' , 'v' :
93- file , line := c .fn . FileLine ( c . pc )
98+ file := c .frame . File
9499 switch {
95100 case s .Flag ('#' ):
96101 // done
97102 case s .Flag ('+' ):
98- file = file [pkgIndex (file , c .fn . Name () ):]
103+ file = file [pkgIndex (file , c .frame . Function ):]
99104 default :
100105 const sep = "/"
101106 if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -105,16 +110,15 @@ func (c Call) Format(s fmt.State, verb rune) {
105110 io .WriteString (s , file )
106111 if verb == 'v' {
107112 buf := [7 ]byte {':' }
108- s .Write (strconv .AppendInt (buf [:1 ], int64 (line ), 10 ))
113+ s .Write (strconv .AppendInt (buf [:1 ], int64 (c . frame . Line ), 10 ))
109114 }
110115
111116 case 'd' :
112- _ , line := c .fn .FileLine (c .pc )
113117 buf := [6 ]byte {}
114- s .Write (strconv .AppendInt (buf [:0 ], int64 (line ), 10 ))
118+ s .Write (strconv .AppendInt (buf [:0 ], int64 (c . frame . Line ), 10 ))
115119
116120 case 'k' :
117- name := c .fn . Name ()
121+ name := c .frame . Function
118122 const pathSep = "/"
119123 start , end := 0 , len (name )
120124 if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -130,7 +134,7 @@ func (c Call) Format(s fmt.State, verb rune) {
130134 io .WriteString (s , name [start :end ])
131135
132136 case 'n' :
133- name := c .fn . Name ()
137+ name := c .frame . Function
134138 if ! s .Flag ('+' ) {
135139 const pathSep = "/"
136140 if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -145,35 +149,17 @@ func (c Call) Format(s fmt.State, verb rune) {
145149 }
146150}
147151
152+ // Frame returns the call frame infomation for the Call.
153+ func (c Call ) Frame () runtime.Frame {
154+ return c .frame
155+ }
156+
148157// PC returns the program counter for this call frame; multiple frames may
149158// have the same PC value.
159+ //
160+ // Deprecated: Use Call.Frame instead.
150161func (c Call ) PC () uintptr {
151- return c .pc
152- }
153-
154- // name returns the import path qualified name of the function containing the
155- // call.
156- func (c Call ) name () string {
157- if c .fn == nil {
158- return "???"
159- }
160- return c .fn .Name ()
161- }
162-
163- func (c Call ) file () string {
164- if c .fn == nil {
165- return "???"
166- }
167- file , _ := c .fn .FileLine (c .pc )
168- return file
169- }
170-
171- func (c Call ) line () int {
172- if c .fn == nil {
173- return 0
174- }
175- _ , line := c .fn .FileLine (c .pc )
176- return line
162+ return c .frame .PC
177163}
178164
179165// CallStack records a sequence of function invocations from a goroutine
@@ -197,9 +183,6 @@ func (cs CallStack) MarshalText() ([]byte, error) {
197183 buf := bytes.Buffer {}
198184 buf .Write (openBracketBytes )
199185 for i , pc := range cs {
200- if pc .fn == nil {
201- return nil , ErrNoFunc
202- }
203186 if i > 0 {
204187 buf .Write (spaceBytes )
205188 }
@@ -227,18 +210,18 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
227210// identifying the calling function.
228211func Trace () CallStack {
229212 var pcs [512 ]uintptr
230- n := runtime .Callers (2 , pcs [:])
231- cs := make ([]Call , n )
213+ n := runtime .Callers (1 , pcs [:])
232214
233- for i , pc := range pcs [:n ] {
234- pcFix := pc
235- if i > 0 && cs [i - 1 ].fn .Name () != "runtime.sigpanic" {
236- pcFix --
237- }
238- cs [i ] = Call {
239- fn : runtime .FuncForPC (pcFix ),
240- pc : pcFix ,
241- }
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 })
242225 }
243226
244227 return cs
@@ -247,7 +230,7 @@ func Trace() CallStack {
247230// TrimBelow returns a slice of the CallStack with all entries below c
248231// removed.
249232func (cs CallStack ) TrimBelow (c Call ) CallStack {
250- for len (cs ) > 0 && cs [0 ]. pc != c . pc {
233+ for len (cs ) > 0 && cs [0 ] != c {
251234 cs = cs [1 :]
252235 }
253236 return cs
@@ -256,7 +239,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
256239// TrimAbove returns a slice of the CallStack with all entries above c
257240// removed.
258241func (cs CallStack ) TrimAbove (c Call ) CallStack {
259- for len (cs ) > 0 && cs [len (cs )- 1 ]. pc != c . pc {
242+ for len (cs ) > 0 && cs [len (cs )- 1 ] != c {
260243 cs = cs [:len (cs )- 1 ]
261244 }
262245 return cs
@@ -305,12 +288,13 @@ func pkgIndex(file, funcName string) int {
305288var runtimePath string
306289
307290func init () {
308- var pcs [1 ]uintptr
291+ var pcs [3 ]uintptr
309292 runtime .Callers (0 , pcs [:])
310- fn := runtime .FuncForPC (pcs [0 ])
311- file , _ := fn .FileLine (pcs [0 ])
293+ frames := runtime .CallersFrames (pcs [:])
294+ frame , _ := frames .Next ()
295+ file := frame .File
312296
313- idx := pkgIndex (file , fn . Name () )
297+ idx := pkgIndex (frame . File , frame . Function )
314298
315299 runtimePath = file [:idx ]
316300 if runtime .GOOS == "windows" {
@@ -319,7 +303,7 @@ func init() {
319303}
320304
321305func inGoroot (c Call ) bool {
322- file := c .file ()
306+ file := c .frame . File
323307 if len (file ) == 0 || file [0 ] == '?' {
324308 return true
325309 }
0 commit comments