@@ -10,10 +10,32 @@ import (
10
10
"sync"
11
11
)
12
12
13
- // Call records a single function invocation from a goroutine stack. It is a
14
- // wrapper for the program counter values returned by runtime.Caller and
15
- // runtime.Callers and consumed by runtime.FuncForPC.
16
- type Call uintptr
13
+ // Call records a single function invocation from a goroutine stack.
14
+ type Call struct {
15
+ fn * runtime.Func
16
+ pc uintptr
17
+ }
18
+
19
+ // Caller returns a Call from the stack of the current goroutine. The argument
20
+ // skip is the number of stack frames to ascend, with 0 identifying the
21
+ // calling function.
22
+ func Caller (skip int ) Call {
23
+ var pcs [2 ]uintptr
24
+ n := runtime .Callers (skip + 1 , pcs [:])
25
+
26
+ var c Call
27
+
28
+ if n < 2 {
29
+ return c
30
+ }
31
+
32
+ c .pc = pcs [1 ]
33
+ if runtime .FuncForPC (pcs [0 ]) != sigpanic {
34
+ c .pc --
35
+ }
36
+ c .fn = runtime .FuncForPC (c .pc )
37
+ return c
38
+ }
17
39
18
40
// Format implements fmt.Formatter with support for the following verbs.
19
41
//
@@ -29,22 +51,21 @@ type Call uintptr
29
51
// %+n import path qualified function name
30
52
// %+v equivalent to %+s:%d
31
53
// %#v equivalent to %#s:%d
32
- func (pc Call ) Format (s fmt.State , c rune ) {
33
- fn := runtime .FuncForPC (uintptr (pc ))
34
- if fn == nil {
35
- fmt .Fprintf (s , "%%!%c(NOFUNC)" , c )
54
+ func (c Call ) Format (s fmt.State , verb rune ) {
55
+ if c .fn == nil {
56
+ fmt .Fprintf (s , "%%!%c(NOFUNC)" , verb )
36
57
return
37
58
}
38
59
39
- switch c {
60
+ switch verb {
40
61
case 's' , 'v' :
41
- file , line := fn .FileLine (uintptr (pc ))
62
+ file , line := c . fn .FileLine (uintptr (c . pc ))
42
63
switch {
43
64
case s .Flag ('#' ):
44
65
// done
45
66
case s .Flag ('+' ):
46
67
// Here we want to get the source file path relative to the
47
- // compile time GOPATH. As of Go 1.3 .x there is no direct way to
68
+ // compile time GOPATH. As of Go 1.4 .x there is no direct way to
48
69
// know the compiled GOPATH at runtime, but we can infer the
49
70
// number of path segments in the GOPATH. We note that fn.Name()
50
71
// returns the function name qualified by the import path, which
@@ -64,7 +85,7 @@ func (pc Call) Format(s fmt.State, c rune) {
64
85
// From this we can easily see that fn.Name() has one less path
65
86
// separator than our desired output.
66
87
const sep = "/"
67
- impCnt := strings .Count (fn .Name (), sep ) + 1
88
+ impCnt := strings .Count (c . fn .Name (), sep ) + 1
68
89
pathCnt := strings .Count (file , sep )
69
90
for pathCnt > impCnt {
70
91
i := strings .Index (file , sep )
@@ -81,16 +102,16 @@ func (pc Call) Format(s fmt.State, c rune) {
81
102
}
82
103
}
83
104
fmt .Fprint (s , file )
84
- if c == 'v' {
105
+ if verb == 'v' {
85
106
fmt .Fprint (s , ":" , line )
86
107
}
87
108
88
109
case 'd' :
89
- _ , line := fn .FileLine (uintptr (pc ))
110
+ _ , line := c . fn .FileLine (uintptr (c . pc ))
90
111
fmt .Fprint (s , line )
91
112
92
113
case 'n' :
93
- name := fn .Name ()
114
+ name := c . fn .Name ()
94
115
if ! s .Flag ('+' ) {
95
116
const pathSep = "/"
96
117
if i := strings .LastIndex (name , pathSep ); i != - 1 {
@@ -107,42 +128,41 @@ func (pc Call) Format(s fmt.State, c rune) {
107
128
108
129
// name returns the import path qualified name of the function containing the
109
130
// call.
110
- func (pc Call ) name () string {
111
- fn := runtime .FuncForPC (uintptr (pc ))
112
- if fn == nil {
131
+ func (c Call ) name () string {
132
+ if c .fn == nil {
113
133
return "???"
114
134
}
115
- return fn .Name ()
135
+ return c . fn .Name ()
116
136
}
117
137
118
- func (pc Call ) file () string {
119
- fn := runtime .FuncForPC (uintptr (pc ))
120
- if fn == nil {
138
+ func (c Call ) file () string {
139
+ if c .fn == nil {
121
140
return "???"
122
141
}
123
- file , _ := fn .FileLine (uintptr (pc ))
142
+ file , _ := c . fn .FileLine (uintptr (c . pc ))
124
143
return file
125
144
}
126
145
127
- // CallStack records a sequence of function invocations from a goroutine stack.
146
+ // CallStack records a sequence of function invocations from a goroutine
147
+ // stack.
128
148
type CallStack []Call
129
149
130
- // Format implements fmt.Formatter by printing the CallStack as square brackes ([,
131
- // ]) surrounding a space separated list of Calls each formatted with the
150
+ // Format implements fmt.Formatter by printing the CallStack as square brackes
151
+ // ([, ]) surrounding a space separated list of Calls each formatted with the
132
152
// supplied verb and options.
133
- func (pcs CallStack ) Format (s fmt.State , c rune ) {
153
+ func (cs CallStack ) Format (s fmt.State , verb rune ) {
134
154
s .Write ([]byte ("[" ))
135
- for i , pc := range pcs {
155
+ for i , pc := range cs {
136
156
if i > 0 {
137
157
s .Write ([]byte (" " ))
138
158
}
139
- pc .Format (s , c )
159
+ pc .Format (s , verb )
140
160
}
141
161
s .Write ([]byte ("]" ))
142
162
}
143
163
144
- // findSigpanic intentially executes faulting code to generate a stack
145
- // trace containing an entry for runtime.sigpanic.
164
+ // findSigpanic intentially executes faulting code to generate a stack trace
165
+ // containing an entry for runtime.sigpanic.
146
166
func findSigpanic () * runtime.Func {
147
167
var fn * runtime.Func
148
168
func () int {
@@ -190,53 +210,38 @@ func Trace() CallStack {
190
210
n := runtime .Callers (2 , pcs )
191
211
cs := make ([]Call , n )
192
212
193
- var prevFn * runtime.Func
194
213
for i , pc := range pcs [:n ] {
195
214
pcFix := pc
196
- if prevFn != sigpanic {
215
+ if i > 0 && cs [ i - 1 ]. fn != sigpanic {
197
216
pcFix --
198
217
}
199
- cs [i ] = Call (pcFix )
200
- prevFn = runtime .FuncForPC (pc )
218
+ cs [i ] = Call {
219
+ fn : runtime .FuncForPC (pcFix ),
220
+ pc : pcFix ,
221
+ }
201
222
}
202
223
203
224
pcStackPool .Put (pcs )
204
225
205
226
return cs
206
227
}
207
228
208
- // TrimBelow returns a slice of the CallStack with all entries below pc removed.
209
- func (pcs CallStack ) TrimBelow (pc Call ) CallStack {
210
- for len (pcs ) > 0 && pcs [0 ] != pc {
211
- pcs = pcs [1 :]
229
+ // TrimBelow returns a slice of the CallStack with all entries below pc
230
+ // removed.
231
+ func (cs CallStack ) TrimBelow (c Call ) CallStack {
232
+ for len (cs ) > 0 && cs [0 ].pc != c .pc {
233
+ cs = cs [1 :]
212
234
}
213
- return pcs
214
- }
215
-
216
- // TrimAbove returns a slice of the CallStack with all entries above pc removed.
217
- func (pcs CallStack ) TrimAbove (pc Call ) CallStack {
218
- for len (pcs ) > 0 && pcs [len (pcs )- 1 ] != pc {
219
- pcs = pcs [:len (pcs )- 1 ]
220
- }
221
- return pcs
222
- }
223
-
224
- // TrimBelowName returns a slice of the CallStack with all entries below the
225
- // lowest with function name name removed.
226
- func (pcs CallStack ) TrimBelowName (name string ) CallStack {
227
- for len (pcs ) > 0 && pcs [0 ].name () != name {
228
- pcs = pcs [1 :]
229
- }
230
- return pcs
235
+ return cs
231
236
}
232
237
233
- // TrimAboveName returns a slice of the CallStack with all entries above the
234
- // highest with function name name removed.
235
- func (pcs CallStack ) TrimAboveName ( name string ) CallStack {
236
- for len (pcs ) > 0 && pcs [len (pcs )- 1 ].name () != name {
237
- pcs = pcs [:len (pcs )- 1 ]
238
+ // TrimAbove returns a slice of the CallStack with all entries above pc
239
+ // removed.
240
+ func (cs CallStack ) TrimAbove ( c Call ) CallStack {
241
+ for len (cs ) > 0 && cs [len (cs )- 1 ].pc != c . pc {
242
+ cs = cs [:len (cs )- 1 ]
238
243
}
239
- return pcs
244
+ return cs
240
245
}
241
246
242
247
var goroot string
@@ -255,12 +260,12 @@ func inGoroot(path string) bool {
255
260
return strings .HasPrefix (path , goroot )
256
261
}
257
262
258
- // TrimRuntime returns a slice of the CallStack with the topmost entries from the
259
- // go runtime removed. It considers any calls originating from files under
263
+ // TrimRuntime returns a slice of the CallStack with the topmost entries from
264
+ // the go runtime removed. It considers any calls originating from files under
260
265
// GOROOT as part of the runtime.
261
- func (pcs CallStack ) TrimRuntime () CallStack {
262
- for len (pcs ) > 0 && inGoroot (pcs [len (pcs )- 1 ].file ()) {
263
- pcs = pcs [:len (pcs )- 1 ]
266
+ func (cs CallStack ) TrimRuntime () CallStack {
267
+ for len (cs ) > 0 && inGoroot (cs [len (cs )- 1 ].file ()) {
268
+ cs = cs [:len (cs )- 1 ]
264
269
}
265
- return pcs
270
+ return cs
266
271
}
0 commit comments