Skip to content

Commit 825fea0

Browse files
David KushnerChrisHines
authored andcommitted
Stub sigpanic test and move to CallersFrames.
1 parent 44dafbc commit 825fea0

File tree

2 files changed

+64
-38
lines changed

2 files changed

+64
-38
lines changed

stack.go

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import (
2121

2222
// Call records a single function invocation from a goroutine stack.
2323
type Call struct {
24-
fn *runtime.Func
25-
pc uintptr
24+
pcs [2]uintptr
2625
}
2726

2827
// Caller returns a Call from the stack of the current goroutine. The argument
@@ -33,16 +32,11 @@ func Caller(skip int) Call {
3332
n := runtime.Callers(skip+1, pcs[:])
3433

3534
var c Call
36-
3735
if n < 2 {
3836
return c
3937
}
4038

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
4640
return c
4741
}
4842

@@ -54,9 +48,10 @@ func (c Call) String() string {
5448
// MarshalText implements encoding.TextMarshaler. It formats the Call the same
5549
// as fmt.Sprintf("%v", c).
5650
func (c Call) MarshalText() ([]byte, error) {
57-
if c.fn == nil {
51+
if c.pcs[0] == 0 {
5852
return nil, ErrNoFunc
5953
}
54+
6055
buf := bytes.Buffer{}
6156
fmt.Fprint(&buf, c)
6257
return buf.Bytes(), nil
@@ -83,19 +78,31 @@ var ErrNoFunc = errors.New("no call stack information")
8378
// %+v equivalent to %+s:%d
8479
// %#v equivalent to %#s:%d
8580
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 {
8794
fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
8895
return
8996
}
9097

9198
switch verb {
9299
case 's', 'v':
93-
file, line := c.fn.FileLine(c.pc)
100+
file, line := frame.File, frame.Line
94101
switch {
95102
case s.Flag('#'):
96103
// done
97104
case s.Flag('+'):
98-
file = file[pkgIndex(file, c.fn.Name()):]
105+
file = file[pkgIndex(file, frame.Function):]
99106
default:
100107
const sep = "/"
101108
if i := strings.LastIndex(file, sep); i != -1 {
@@ -109,12 +116,12 @@ func (c Call) Format(s fmt.State, verb rune) {
109116
}
110117

111118
case 'd':
112-
_, line := c.fn.FileLine(c.pc)
119+
line := frame.Line
113120
buf := [6]byte{}
114121
s.Write(strconv.AppendInt(buf[:0], int64(line), 10))
115122

116123
case 'k':
117-
name := c.fn.Name()
124+
name := frame.Function
118125
const pathSep = "/"
119126
start, end := 0, len(name)
120127
if i := strings.LastIndex(name, pathSep); i != -1 {
@@ -130,7 +137,7 @@ func (c Call) Format(s fmt.State, verb rune) {
130137
io.WriteString(s, name[start:end])
131138

132139
case 'n':
133-
name := c.fn.Name()
140+
name := frame.Function
134141
if !s.Flag('+') {
135142
const pathSep = "/"
136143
if i := strings.LastIndex(name, pathSep); i != -1 {
@@ -148,32 +155,47 @@ func (c Call) Format(s fmt.State, verb rune) {
148155
// PC returns the program counter for this call frame; multiple frames may
149156
// have the same PC value.
150157
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
152173
}
153174

154175
// name returns the import path qualified name of the function containing the
155176
// call.
156177
func (c Call) name() string {
157-
if c.fn == nil {
178+
frame := c.frame()
179+
if frame.Func == nil {
158180
return "???"
159181
}
160-
return c.fn.Name()
182+
return frame.Function
161183
}
162184

163185
func (c Call) file() string {
164-
if c.fn == nil {
186+
frame := c.frame()
187+
if frame.Func == nil {
165188
return "???"
166189
}
167-
file, _ := c.fn.FileLine(c.pc)
168-
return file
190+
return frame.File
169191
}
170192

171193
func (c Call) line() int {
172-
if c.fn == nil {
194+
frame := c.frame()
195+
if frame.Func == nil {
173196
return 0
174197
}
175-
_, line := c.fn.FileLine(c.pc)
176-
return line
198+
return frame.Line
177199
}
178200

179201
// CallStack records a sequence of function invocations from a goroutine
@@ -197,9 +219,6 @@ func (cs CallStack) MarshalText() ([]byte, error) {
197219
buf := bytes.Buffer{}
198220
buf.Write(openBracketBytes)
199221
for i, pc := range cs {
200-
if pc.fn == nil {
201-
return nil, ErrNoFunc
202-
}
203222
if i > 0 {
204223
buf.Write(spaceBytes)
205224
}
@@ -227,17 +246,12 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
227246
// identifying the calling function.
228247
func Trace() CallStack {
229248
var pcs [512]uintptr
230-
n := runtime.Callers(2, pcs[:])
249+
n := runtime.Callers(1, pcs[:])
231250
cs := make([]Call, n)
232251

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]},
241255
}
242256
}
243257

@@ -247,7 +261,7 @@ func Trace() CallStack {
247261
// TrimBelow returns a slice of the CallStack with all entries below c
248262
// removed.
249263
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] {
251265
cs = cs[1:]
252266
}
253267
return cs
@@ -256,7 +270,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
256270
// TrimAbove returns a slice of the CallStack with all entries above c
257271
// removed.
258272
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] {
260274
cs = cs[:len(cs)-1]
261275
}
262276
return cs

stack_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,18 @@ func TestTrimRuntime(t *testing.T) {
224224
}
225225
}
226226

227+
func TestPanic(t *testing.T) {
228+
defer func() {
229+
if recover() != nil {
230+
trace := stack.Trace().TrimRuntime()
231+
fmt.Println(trace)
232+
}
233+
}()
234+
235+
var x *uintptr
236+
_ = *x
237+
}
238+
227239
func BenchmarkCallVFmt(b *testing.B) {
228240
c := stack.Caller(0)
229241
b.ResetTimer()

0 commit comments

Comments
 (0)