Skip to content

Commit 498933c

Browse files
committed
Add TestCallerPanic and get other tests to pass.
1 parent befcdef commit 498933c

File tree

2 files changed

+82
-41
lines changed

2 files changed

+82
-41
lines changed

stack.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ var ErrNoFunc = errors.New("no call stack information")
7979
// %#v equivalent to %#s:%d
8080
func (c Call) Format(s fmt.State, verb rune) {
8181
frames := runtime.CallersFrames(c.pcs[:])
82-
head , _ := frames.Next()
82+
head, _ := frames.Next()
8383
frame, _ := frames.Next()
8484

8585
if head.Function == "runtime.signpanic" {
@@ -158,17 +158,10 @@ func (c Call) PC() uintptr {
158158
return c.pcs[1]
159159
}
160160

161-
func (c Call) frame() (*runtime.Frame) {
161+
func (c Call) frame() *runtime.Frame {
162162
frames := runtime.CallersFrames(c.pcs[:])
163-
164163
frame, _ := frames.Next()
165-
next, _ := frames.Next()
166-
if frame.Function == "runtime.sigpanic" {
167-
frame, _ = runtime.CallersFrames([]uintptr{next.PC - 1}).Next()
168-
} else {
169-
frame = next
170-
}
171-
164+
frame, _ = frames.Next()
172165
return &frame
173166
}
174167

@@ -269,8 +262,8 @@ func Trace() CallStack {
269262
cs := make([]Call, n)
270263

271264
for i := range pcs[:n] {
272-
cs[i] = Call {
273-
pcs: [2]uintptr{pcs[i], pcs[i + 1]},
265+
cs[i] = Call{
266+
pcs: [2]uintptr{pcs[i], pcs[i+1]},
274267
}
275268
}
276269

stackinternal_test.go

Lines changed: 77 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ package stack
33
import (
44
"runtime"
55
"testing"
6-
"github.com/dkushner/stack"
7-
"strings"
8-
"strconv"
96
)
107

118
func TestCaller(t *testing.T) {
@@ -27,6 +24,49 @@ func TestCaller(t *testing.T) {
2724
}
2825
}
2926

27+
func TestCallerPanic(t *testing.T) {
28+
t.Parallel()
29+
30+
var (
31+
line int
32+
ok bool
33+
)
34+
35+
defer func() {
36+
if recover() != nil {
37+
// count frames to runtime.sigpanic
38+
panicIdx := -1
39+
for i, c := range Trace() {
40+
if c.name() == "runtime.sigpanic" {
41+
panicIdx = i
42+
break
43+
}
44+
}
45+
if panicIdx == -1 {
46+
t.Fatal("no runtime.sigpanic entry on the stack")
47+
}
48+
if got, want := Caller(panicIdx).name(), "runtime.sigpanic"; got != want {
49+
t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want)
50+
}
51+
if got, want := Caller(panicIdx+1).name(), "github.com/go-stack/stack.TestCallerPanic"; got != want {
52+
t.Errorf("TestCallerPanic frame: got name == %v, want name == %v", got, want)
53+
}
54+
if got, want := Caller(panicIdx+1).line(), line; got != want {
55+
t.Errorf("TestCallerPanic frame: got line == %v, want line == %v", got, want)
56+
}
57+
}
58+
}()
59+
60+
_, _, line, ok = runtime.Caller(0)
61+
line += 7 // adjust to match line of panic below
62+
if !ok {
63+
t.Fatal("runtime.Caller(0) failed")
64+
}
65+
// Initiate a sigpanic.
66+
var x *uintptr
67+
_ = *x
68+
}
69+
3070
type fholder struct {
3171
f func() CallStack
3272
}
@@ -35,12 +75,16 @@ func (fh *fholder) labyrinth() CallStack {
3575
for {
3676
return fh.f()
3777
}
38-
panic("this line only needed for go 1.0")
3978
}
4079

4180
func TestTrace(t *testing.T) {
4281
t.Parallel()
4382

83+
_, _, line, ok := runtime.Caller(0)
84+
if !ok {
85+
t.Fatal("runtime.Caller(0) failed")
86+
}
87+
4488
fh := fholder{
4589
f: func() CallStack {
4690
cs := Trace()
@@ -50,7 +94,7 @@ func TestTrace(t *testing.T) {
5094

5195
cs := fh.labyrinth()
5296

53-
lines := []int{46, 36, 51}
97+
lines := []int{line + 7, line - 7, line + 12}
5498

5599
for i, line := range lines {
56100
if got, want := cs[i].line(), line; got != want {
@@ -63,39 +107,43 @@ func TestTrace(t *testing.T) {
63107
func TestTracePanic(t *testing.T) {
64108
t.Parallel()
65109

110+
var (
111+
line int
112+
ok bool
113+
)
114+
66115
defer func() {
67116
if recover() != nil {
68-
trace := stack.Trace().TrimRuntime()
69-
70-
if len(trace) != 6 {
71-
t.Errorf("got len(trace) == %v, want %v", len(trace), 6)
72-
}
73-
74-
// Check frames in this file, the interceding frames are somewhat
75-
// platform-dependent.
76-
lines := []int64{68, 101}
77-
78-
var local []int64
79-
for _, call := range trace {
80-
parts := strings.Split(call.String(), ":")
81-
if parts[0] == "stackinternal_test.go" {
82-
line, _ := strconv.ParseInt(parts[1], 10, 32)
83-
local = append(local, line)
117+
trace := Trace()
118+
119+
// find runtime.sigpanic
120+
panicIdx := -1
121+
for i, c := range trace {
122+
if c.name() == "runtime.sigpanic" {
123+
panicIdx = i
124+
break
84125
}
85126
}
86-
87-
if len(local) != 2 {
88-
t.Errorf("expected %v local frames but got %v", 2, len(local))
127+
if panicIdx == -1 {
128+
t.Fatal("no runtime.sigpanic entry on the stack")
89129
}
90-
91-
for i, line := range lines {
92-
if got, want := local[i], line; got != want {
93-
t.Errorf("got line[%d] == %v, want line[%d] == %v", i, got, i, want)
94-
}
130+
if got, want := trace[panicIdx].name(), "runtime.sigpanic"; got != want {
131+
t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want)
132+
}
133+
if got, want := trace[panicIdx+1].name(), "github.com/go-stack/stack.TestTracePanic"; got != want {
134+
t.Errorf("TestTracePanic frame: got name == %v, want name == %v", got, want)
135+
}
136+
if got, want := trace[panicIdx+1].line(), line; got != want {
137+
t.Errorf("TestTracePanic frame: got line == %v, want line == %v", got, want)
95138
}
96139
}
97140
}()
98141

142+
_, _, line, ok = runtime.Caller(0)
143+
line += 7 // adjust to match line of panic below
144+
if !ok {
145+
t.Fatal("runtime.Caller(0) failed")
146+
}
99147
// Initiate a sigpanic.
100148
var x *uintptr
101149
_ = *x

0 commit comments

Comments
 (0)