@@ -30,23 +30,15 @@ type Call uintptr
30
30
// %+v equivalent to %+s:%d
31
31
// %#v equivalent to %#s:%d
32
32
func (pc Call ) Format (s fmt.State , c rune ) {
33
- // BUG(ChrisHines): Subtracting one from pc is a work around for
34
- // https://code.google.com/p/go/issues/detail?id=7690. The idea for this
35
- // work around comes from rsc's initial patch at
36
- // https://codereview.appspot.com/84100043/#ps20001, but as noted in the
37
- // issue discussion, it is not a complete fix since it doesn't handle some
38
- // cases involving signals. Just the same, it handles all of the other
39
- // cases I have tested.
40
- pcFix := uintptr (pc ) - 1
41
- fn := runtime .FuncForPC (pcFix )
33
+ fn := runtime .FuncForPC (uintptr (pc ))
42
34
if fn == nil {
43
35
fmt .Fprintf (s , "%%!%c(NOFUNC)" , c )
44
36
return
45
37
}
46
38
47
39
switch c {
48
40
case 's' , 'v' :
49
- file , line := fn .FileLine (pcFix )
41
+ file , line := fn .FileLine (uintptr ( pc ) )
50
42
switch {
51
43
case s .Flag ('#' ):
52
44
// done
@@ -94,7 +86,7 @@ func (pc Call) Format(s fmt.State, c rune) {
94
86
}
95
87
96
88
case 'd' :
97
- _ , line := fn .FileLine (pcFix )
89
+ _ , line := fn .FileLine (uintptr ( pc ) )
98
90
fmt .Fprint (s , line )
99
91
100
92
case 'n' :
@@ -116,31 +108,29 @@ func (pc Call) Format(s fmt.State, c rune) {
116
108
// name returns the import path qualified name of the function containing the
117
109
// call.
118
110
func (pc Call ) name () string {
119
- pcFix := uintptr (pc ) - 1 // work around for go issue #7690
120
- fn := runtime .FuncForPC (pcFix )
111
+ fn := runtime .FuncForPC (uintptr (pc ))
121
112
if fn == nil {
122
113
return "???"
123
114
}
124
115
return fn .Name ()
125
116
}
126
117
127
118
func (pc Call ) file () string {
128
- pcFix := uintptr (pc ) - 1 // work around for go issue #7690
129
- fn := runtime .FuncForPC (pcFix )
119
+ fn := runtime .FuncForPC (uintptr (pc ))
130
120
if fn == nil {
131
121
return "???"
132
122
}
133
- file , _ := fn .FileLine (pcFix )
123
+ file , _ := fn .FileLine (uintptr ( pc ) )
134
124
return file
135
125
}
136
126
137
- // Trace records a sequence of function invocations from a goroutine stack.
138
- type Trace []Call
127
+ // CallStack records a sequence of function invocations from a goroutine stack.
128
+ type CallStack []Call
139
129
140
- // Format implements fmt.Formatter by printing the Trace as square brackes ([,
130
+ // Format implements fmt.Formatter by printing the CallStack as square brackes ([,
141
131
// ]) surrounding a space separated list of Calls each formatted with the
142
132
// supplied verb and options.
143
- func (pcs Trace ) Format (s fmt.State , c rune ) {
133
+ func (pcs CallStack ) Format (s fmt.State , c rune ) {
144
134
s .Write ([]byte ("[" ))
145
135
for i , pc := range pcs {
146
136
if i > 0 {
@@ -151,52 +141,98 @@ func (pcs Trace) Format(s fmt.State, c rune) {
151
141
s .Write ([]byte ("]" ))
152
142
}
153
143
144
+ // findSigpanic intentially executes faulting code to generate a stack
145
+ // trace containing an entry for runtime.sigpanic.
146
+ func findSigpanic () * runtime.Func {
147
+ var fn * runtime.Func
148
+ func () int {
149
+ defer func () {
150
+ if p := recover (); p != nil {
151
+ pcs := pcStackPool .Get ().([]uintptr )
152
+ pcs = pcs [:cap (pcs )]
153
+ n := runtime .Callers (2 , pcs )
154
+ for _ , pc := range pcs [:n ] {
155
+ f := runtime .FuncForPC (pc )
156
+ if f .Name () == "runtime.sigpanic" {
157
+ fn = f
158
+ break
159
+ }
160
+ }
161
+ pcStackPool .Put (pcs )
162
+ }
163
+ }()
164
+ // intentional division by zero fault
165
+ a , b := 1 , 0
166
+ return a / b
167
+ }()
168
+ return fn
169
+ }
170
+
171
+ var (
172
+ sigpanic * runtime.Func
173
+ spOnce sync.Once
174
+ )
175
+
154
176
var pcStackPool = sync.Pool {
155
177
New : func () interface {} { return make ([]uintptr , 1000 ) },
156
178
}
157
179
158
- // Callers returns a Trace for the current goroutine with element 0
180
+ // Trace returns a CallStack for the current goroutine with element 0
159
181
// identifying the calling function.
160
- func Callers () Trace {
182
+ func Trace () CallStack {
183
+ spOnce .Do (func () {
184
+ sigpanic = findSigpanic ()
185
+ })
186
+
161
187
pcs := pcStackPool .Get ().([]uintptr )
162
188
pcs = pcs [:cap (pcs )]
189
+
163
190
n := runtime .Callers (2 , pcs )
164
191
cs := make ([]Call , n )
192
+
193
+ var prevFn * runtime.Func
165
194
for i , pc := range pcs [:n ] {
166
- cs [i ] = Call (pc )
195
+ pcFix := pc
196
+ if prevFn != sigpanic {
197
+ pcFix --
198
+ }
199
+ cs [i ] = Call (pcFix )
200
+ prevFn = runtime .FuncForPC (pc )
167
201
}
202
+
168
203
pcStackPool .Put (pcs )
204
+
169
205
return cs
170
206
}
171
207
172
- // TrimBelow returns a slice of the Trace with all entries below pc removed.
173
- func (pcs Trace ) TrimBelow (pc Call ) Trace {
208
+ // TrimBelow returns a slice of the CallStack with all entries below pc removed.
209
+ func (pcs CallStack ) TrimBelow (pc Call ) CallStack {
174
210
for len (pcs ) > 0 && pcs [0 ] != pc {
175
211
pcs = pcs [1 :]
176
212
}
177
213
return pcs
178
214
}
179
215
180
- // TrimAbove returns a slice of the Trace with all entries above pc removed.
181
- func (pcs Trace ) TrimAbove (pc Call ) Trace {
216
+ // TrimAbove returns a slice of the CallStack with all entries above pc removed.
217
+ func (pcs CallStack ) TrimAbove (pc Call ) CallStack {
182
218
for len (pcs ) > 0 && pcs [len (pcs )- 1 ] != pc {
183
219
pcs = pcs [:len (pcs )- 1 ]
184
220
}
185
221
return pcs
186
222
}
187
223
188
- // TrimBelowName returns a slice of the Trace with all entries below the
224
+ // TrimBelowName returns a slice of the CallStack with all entries below the
189
225
// lowest with function name name removed.
190
- func (pcs Trace ) TrimBelowName (name string ) Trace {
226
+ func (pcs CallStack ) TrimBelowName (name string ) CallStack {
191
227
for len (pcs ) > 0 && pcs [0 ].name () != name {
192
228
pcs = pcs [1 :]
193
229
}
194
230
return pcs
195
231
}
196
232
197
- // TrimAboveName returns a slice of the Trace with all entries above the
233
+ // TrimAboveName returns a slice of the CallStack with all entries above the
198
234
// highest with function name name removed.
199
- func (pcs Trace ) TrimAboveName (name string ) Trace {
235
+ func (pcs CallStack ) TrimAboveName (name string ) CallStack {
200
236
for len (pcs ) > 0 && pcs [len (pcs )- 1 ].name () != name {
201
237
pcs = pcs [:len (pcs )- 1 ]
202
238
}
@@ -219,10 +255,10 @@ func inGoroot(path string) bool {
219
255
return strings .HasPrefix (path , goroot )
220
256
}
221
257
222
- // TrimRuntime returns a slice of the Trace with the topmost entries from the
258
+ // TrimRuntime returns a slice of the CallStack with the topmost entries from the
223
259
// go runtime removed. It considers any calls originating from files under
224
260
// GOROOT as part of the runtime.
225
- func (pcs Trace ) TrimRuntime () Trace {
261
+ func (pcs CallStack ) TrimRuntime () CallStack {
226
262
for len (pcs ) > 0 && inGoroot (pcs [len (pcs )- 1 ].file ()) {
227
263
pcs = pcs [:len (pcs )- 1 ]
228
264
}
0 commit comments