@@ -21,8 +21,7 @@ import (
21
21
22
22
// Call records a single function invocation from a goroutine stack.
23
23
type Call struct {
24
- fn * runtime.Func
25
- pc uintptr
24
+ pcs [2 ]uintptr
26
25
}
27
26
28
27
// Caller returns a Call from the stack of the current goroutine. The argument
@@ -33,16 +32,11 @@ func Caller(skip int) Call {
33
32
n := runtime .Callers (skip + 1 , pcs [:])
34
33
35
34
var c Call
36
-
37
35
if n < 2 {
38
36
return c
39
37
}
40
38
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 )
39
+ c .pcs = pcs
46
40
return c
47
41
}
48
42
@@ -54,9 +48,10 @@ func (c Call) String() string {
54
48
// MarshalText implements encoding.TextMarshaler. It formats the Call the same
55
49
// as fmt.Sprintf("%v", c).
56
50
func (c Call ) MarshalText () ([]byte , error ) {
57
- if c .fn == nil {
51
+ if c .pcs [ 0 ] == 0 {
58
52
return nil , ErrNoFunc
59
53
}
54
+
60
55
buf := bytes.Buffer {}
61
56
fmt .Fprint (& buf , c )
62
57
return buf .Bytes (), nil
@@ -83,19 +78,31 @@ var ErrNoFunc = errors.New("no call stack information")
83
78
// %+v equivalent to %+s:%d
84
79
// %#v equivalent to %#s:%d
85
80
func (c Call ) Format (s fmt.State , verb rune ) {
86
- if c .fn == nil {
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 {
87
94
fmt .Fprintf (s , "%%!%c(NOFUNC)" , verb )
88
95
return
89
96
}
90
97
91
98
switch verb {
92
99
case 's' , 'v' :
93
- file , line := c . fn . FileLine ( c . pc )
100
+ file , line := frame . File , frame . Line
94
101
switch {
95
102
case s .Flag ('#' ):
96
103
// done
97
104
case s .Flag ('+' ):
98
- file = file [pkgIndex (file , c . fn . Name () ):]
105
+ file = file [pkgIndex (file , frame . Function ):]
99
106
default :
100
107
const sep = "/"
101
108
if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -109,12 +116,12 @@ func (c Call) Format(s fmt.State, verb rune) {
109
116
}
110
117
111
118
case 'd' :
112
- _ , line := c . fn . FileLine ( c . pc )
119
+ line := frame . Line
113
120
buf := [6 ]byte {}
114
121
s .Write (strconv .AppendInt (buf [:0 ], int64 (line ), 10 ))
115
122
116
123
case 'k' :
117
- name := c . fn . Name ()
124
+ name := frame . Function
118
125
const pathSep = "/"
119
126
start , end := 0 , len (name )
120
127
if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -130,7 +137,7 @@ func (c Call) Format(s fmt.State, verb rune) {
130
137
io .WriteString (s , name [start :end ])
131
138
132
139
case 'n' :
133
- name := c . fn . Name ()
140
+ name := frame . Function
134
141
if ! s .Flag ('+' ) {
135
142
const pathSep = "/"
136
143
if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -148,32 +155,47 @@ func (c Call) Format(s fmt.State, verb rune) {
148
155
// PC returns the program counter for this call frame; multiple frames may
149
156
// have the same PC value.
150
157
func (c Call ) PC () uintptr {
151
- return c .pc
158
+ return c .pcs [1 ]
159
+ }
160
+
161
+ func (c Call ) frame () (* runtime.Frame ) {
162
+ window := append (c .pcs [:], c .pcs [1 ] - 1 )
163
+ frames := runtime .CallersFrames (window )
164
+
165
+ head , _ := frames .Next ()
166
+ frame , _ := frames .Next ()
167
+
168
+ if head .Function == "runtime.sigpanic" {
169
+ frame , _ = frames .Next ()
170
+ }
171
+
172
+ return & frame
152
173
}
153
174
154
175
// name returns the import path qualified name of the function containing the
155
176
// call.
156
177
func (c Call ) name () string {
157
- if c .fn == nil {
178
+ frame := c .frame ()
179
+ if frame .Func == nil {
158
180
return "???"
159
181
}
160
- return c . fn . Name ()
182
+ return frame . Function
161
183
}
162
184
163
185
func (c Call ) file () string {
164
- if c .fn == nil {
186
+ frame := c .frame ()
187
+ if frame .Func == nil {
165
188
return "???"
166
189
}
167
- file , _ := c .fn .FileLine (c .pc )
168
- return file
190
+ return frame .File
169
191
}
170
192
171
193
func (c Call ) line () int {
172
- if c .fn == nil {
194
+ frame := c .frame ()
195
+ if frame .Func == nil {
173
196
return 0
174
197
}
175
- _ , line := c .fn .FileLine (c .pc )
176
- return line
198
+ return frame .Line
177
199
}
178
200
179
201
// CallStack records a sequence of function invocations from a goroutine
@@ -197,9 +219,6 @@ func (cs CallStack) MarshalText() ([]byte, error) {
197
219
buf := bytes.Buffer {}
198
220
buf .Write (openBracketBytes )
199
221
for i , pc := range cs {
200
- if pc .fn == nil {
201
- return nil , ErrNoFunc
202
- }
203
222
if i > 0 {
204
223
buf .Write (spaceBytes )
205
224
}
@@ -227,17 +246,12 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
227
246
// identifying the calling function.
228
247
func Trace () CallStack {
229
248
var pcs [512 ]uintptr
230
- n := runtime .Callers (2 , pcs [:])
249
+ n := runtime .Callers (1 , pcs [:])
231
250
cs := make ([]Call , n )
232
251
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 ,
252
+ for i := range pcs [:n ] {
253
+ cs [i ] = Call {
254
+ pcs : [2 ]uintptr {pcs [i ], pcs [i + 1 ]},
241
255
}
242
256
}
243
257
@@ -247,7 +261,7 @@ func Trace() CallStack {
247
261
// TrimBelow returns a slice of the CallStack with all entries below c
248
262
// removed.
249
263
func (cs CallStack ) TrimBelow (c Call ) CallStack {
250
- for len (cs ) > 0 && cs [0 ].pc != c .pc {
264
+ for len (cs ) > 0 && cs [0 ].pcs [ 1 ] != c .pcs [ 1 ] {
251
265
cs = cs [1 :]
252
266
}
253
267
return cs
@@ -256,7 +270,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
256
270
// TrimAbove returns a slice of the CallStack with all entries above c
257
271
// removed.
258
272
func (cs CallStack ) TrimAbove (c Call ) CallStack {
259
- for len (cs ) > 0 && cs [len (cs )- 1 ].pc != c .pc {
273
+ for len (cs ) > 0 && cs [len (cs )- 1 ].pcs [ 1 ] != c .pcs [ 1 ] {
260
274
cs = cs [:len (cs )- 1 ]
261
275
}
262
276
return cs
0 commit comments