1
+ // +build go1.7
2
+
1
3
// Package stack implements utilities to capture, manipulate, and format call
2
4
// stacks. It provides a simpler API than package runtime.
3
5
//
@@ -21,23 +23,31 @@ import (
21
23
22
24
// Call records a single function invocation from a goroutine stack.
23
25
type Call struct {
24
- pcs [ 2 ] uintptr
26
+ frame runtime. Frame
25
27
}
26
28
27
29
// Caller returns a Call from the stack of the current goroutine. The argument
28
30
// skip is the number of stack frames to ascend, with 0 identifying the
29
31
// calling function.
30
32
func 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
32
43
n := runtime .Callers (skip + 1 , pcs [:])
44
+ frames := runtime .CallersFrames (pcs [:n ])
45
+ frame , _ := frames .Next ()
46
+ frame , _ = frames .Next ()
33
47
34
- var c Call
35
- if n < 2 {
36
- return c
48
+ return Call {
49
+ frame : frame ,
37
50
}
38
-
39
- c .pcs = pcs
40
- return c
41
51
}
42
52
43
53
// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
@@ -48,7 +58,7 @@ func (c Call) String() string {
48
58
// MarshalText implements encoding.TextMarshaler. It formats the Call the same
49
59
// as fmt.Sprintf("%v", c).
50
60
func (c Call ) MarshalText () ([]byte , error ) {
51
- if c .pcs [ 0 ] == 0 {
61
+ if c .frame == (runtime. Frame {}) {
52
62
return nil , ErrNoFunc
53
63
}
54
64
@@ -78,31 +88,19 @@ var ErrNoFunc = errors.New("no call stack information")
78
88
// %+v equivalent to %+s:%d
79
89
// %#v equivalent to %#s:%d
80
90
func (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 {}) {
94
92
fmt .Fprintf (s , "%%!%c(NOFUNC)" , verb )
95
93
return
96
94
}
97
95
98
96
switch verb {
99
97
case 's' , 'v' :
100
- file , line := frame . File , frame .Line
98
+ file := c . frame .File
101
99
switch {
102
100
case s .Flag ('#' ):
103
101
// done
104
102
case s .Flag ('+' ):
105
- file = file [pkgIndex (file , frame .Function ):]
103
+ file = file [pkgIndex (file , c . frame .Function ):]
106
104
default :
107
105
const sep = "/"
108
106
if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -112,16 +110,15 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
112
110
io .WriteString (s , file )
113
111
if verb == 'v' {
114
112
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 ))
116
114
}
117
115
118
116
case 'd' :
119
- line := frame .Line
120
117
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 ))
122
119
123
120
case 'k' :
124
- name := frame .Function
121
+ name := c . frame .Function
125
122
const pathSep = "/"
126
123
start , end := 0 , len (name )
127
124
if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -137,7 +134,7 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
137
134
io .WriteString (s , name [start :end ])
138
135
139
136
case 'n' :
140
- name := frame .Function
137
+ name := c . frame .Function
141
138
if ! s .Flag ('+' ) {
142
139
const pathSep = "/"
143
140
if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -152,43 +149,17 @@ func format(frame *runtime.Frame, s fmt.State, verb rune) {
152
149
}
153
150
}
154
151
152
+ // Frame returns the call frame infomation for the Call.
153
+ func (c Call ) Frame () runtime.Frame {
154
+ return c .frame
155
+ }
156
+
155
157
// PC returns the program counter for this call frame; multiple frames may
156
158
// have the same PC value.
159
+ //
160
+ // Deprecated: Use Call.Frame instead.
157
161
func (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
192
163
}
193
164
194
165
// CallStack records a sequence of function invocations from a goroutine
@@ -226,31 +197,12 @@ func (cs CallStack) MarshalText() ([]byte, error) {
226
197
// supplied verb and options.
227
198
func (cs CallStack ) Format (s fmt.State , verb rune ) {
228
199
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 {
240
202
s .Write (spaceBytes )
241
203
}
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 )
252
205
}
253
-
254
206
s .Write (closeBracketBytes )
255
207
}
256
208
@@ -259,12 +211,17 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
259
211
func Trace () CallStack {
260
212
var pcs [512 ]uintptr
261
213
n := runtime .Callers (1 , pcs [:])
262
- cs := make ([]Call , n )
263
214
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 })
268
225
}
269
226
270
227
return cs
@@ -273,7 +230,7 @@ func Trace() CallStack {
273
230
// TrimBelow returns a slice of the CallStack with all entries below c
274
231
// removed.
275
232
func (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 {
277
234
cs = cs [1 :]
278
235
}
279
236
return cs
@@ -282,7 +239,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
282
239
// TrimAbove returns a slice of the CallStack with all entries above c
283
240
// removed.
284
241
func (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 {
286
243
cs = cs [:len (cs )- 1 ]
287
244
}
288
245
return cs
@@ -331,12 +288,13 @@ func pkgIndex(file, funcName string) int {
331
288
var runtimePath string
332
289
333
290
func init () {
334
- var pcs [1 ]uintptr
291
+ var pcs [3 ]uintptr
335
292
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
338
296
339
- idx := pkgIndex (file , fn . Name () )
297
+ idx := pkgIndex (frame . File , frame . Function )
340
298
341
299
runtimePath = file [:idx ]
342
300
if runtime .GOOS == "windows" {
@@ -345,7 +303,7 @@ func init() {
345
303
}
346
304
347
305
func inGoroot (c Call ) bool {
348
- file := c .file ()
306
+ file := c .frame . File
349
307
if len (file ) == 0 || file [0 ] == '?' {
350
308
return true
351
309
}
0 commit comments