Skip to content

Commit 96d4680

Browse files
committed
use runtime.Callers
referencing PR: * stretchr/testify#1614 * stretchr/testify#1768
1 parent 3d0582b commit 96d4680

File tree

1 file changed

+57
-36
lines changed

1 file changed

+57
-36
lines changed

witness/trace/trace.go

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,70 @@ func Info(traceFilterFunc ...func(filepath string) bool) []string {
1313
var pc uintptr
1414
var file string
1515
var line int
16-
var ok bool
17-
1816
var funcName string
1917
var lastCaller string
2018

21-
callers := []string{}
22-
for i := 0; ; i++ {
23-
pc, file, line, ok = runtime.Caller(i)
24-
if !ok {
25-
break
26-
}
27-
28-
// Avoid panic. https://github.com/stretchr/testify/issues/180
29-
if file == "<autogenerated>" {
30-
break
31-
}
19+
const stackFrameBufferSize = 10
20+
pcs := make([]uintptr, stackFrameBufferSize)
3221

33-
f := runtime.FuncForPC(pc)
34-
if f == nil {
35-
break
36-
}
22+
callers := []string{}
23+
offset := 1
3724

38-
funcName = f.Name()
25+
for {
26+
n := runtime.Callers(offset, pcs)
3927

40-
// testing.tRunner is the standard library function that calls
41-
// tests. Subtests are called directly by tRunner, without going through
42-
// the Test/Benchmark/Example function that contains the t.Run calls.
43-
if funcName == "testing.tRunner" {
28+
if n == 0 {
4429
break
4530
}
4631

47-
lastCaller = fmt.Sprintf("%s:%d", file, line)
48-
49-
if len(strings.Split(file, "/")) > 1 && // https://github.com/stretchr/testify/pull/402
50-
!skipLibs(file, traceFilterFunc...) {
51-
callers = append(callers, lastCaller)
32+
frames := runtime.CallersFrames(pcs[:n])
33+
34+
for {
35+
frame, more := frames.Next()
36+
pc = frame.PC
37+
file = frame.File
38+
line = frame.Line
39+
40+
// This is a huge edge case, but it will panic if this is the case, see #180
41+
if file == "<autogenerated>" {
42+
break
43+
}
44+
45+
f := runtime.FuncForPC(pc)
46+
if f == nil {
47+
break
48+
}
49+
funcName = f.Name()
50+
51+
// testing.tRunner is the standard library function that calls
52+
// tests. Subtests are called directly by tRunner, without going through
53+
// the Test/Benchmark/Example function that contains the t.Run calls, so
54+
// with subtests we should break when we hit tRunner, without adding it
55+
// to the list of callers.
56+
if funcName == "testing.tRunner" {
57+
break
58+
}
59+
60+
lastCaller = fmt.Sprintf("%s:%d", file, line)
61+
62+
if len(strings.Split(file, "/")) > 1 && // https://github.com/stretchr/testify/pull/402
63+
!skipLibs(file, traceFilterFunc...) {
64+
callers = append(callers, lastCaller)
65+
}
66+
67+
funcName = funcName[strings.LastIndexByte(funcName, '.')+1:]
68+
if isGolangTestFunc(funcName) {
69+
break
70+
}
71+
72+
73+
if !more {
74+
break
75+
}
5276
}
5377

54-
funcName = funcName[strings.LastIndexByte(funcName, '.')+1:]
55-
56-
if isGolangTestFunc(funcName) {
57-
break
58-
}
78+
// Next batch
79+
offset += cap(pcs)
5980
}
6081

6182
if len(callers) == 0 {
@@ -75,15 +96,15 @@ func skipLibs(filepath string, traceFilterFunc ...func(filepath string) bool) bo
7596
return false
7697
}
7798

78-
func isGolangTestFunc(name string) bool {
99+
func isGolangTestFunc(funcName string) bool {
79100
for _, prefix := range [3]string{"Test", "Benchmark", "Example"} {
80-
if !strings.HasPrefix(name, prefix) {
101+
if !strings.HasPrefix(funcName, prefix) {
81102
continue
82103
}
83-
if len(name) == len(prefix) {
104+
if len(funcName) == len(prefix) {
84105
return true
85106
}
86-
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
107+
r, _ := utf8.DecodeRuneInString(funcName[len(prefix):])
87108
if !unicode.IsLower(r) {
88109
return true
89110
}

0 commit comments

Comments
 (0)