@@ -24,16 +24,20 @@ import (
24
24
func TestMain (m * testing.M ) {
25
25
entry := os .Getenv ("CRASHMONITOR_TEST_ENTRYPOINT" )
26
26
switch entry {
27
- case "via-stderr" :
27
+ case "via-stderr.panic" , "via-stderr.trap " :
28
28
// This mode bypasses Start and debug.SetCrashOutput;
29
29
// the crash is printed to stderr.
30
30
debug .SetTraceback ("system" )
31
31
crashmonitor .WriteSentinel (os .Stderr )
32
32
33
- child () // this line is "TestMain:+9"
33
+ if entry == "via-stderr.panic" {
34
+ childPanic () // this line is "TestMain:+10"
35
+ } else {
36
+ childTrap () // this line is "TestMain:+12"
37
+ }
34
38
panic ("unreachable" )
35
39
36
- case "start.panic" , "start.exit" :
40
+ case "start.panic" , "start.trap" , "start. exit" :
37
41
// These modes uses Start and debug.SetCrashOutput.
38
42
// We stub the actual telemetry by instead writing to a file.
39
43
crashmonitor .SetIncrementCounter (func (name string ) {
@@ -46,12 +50,18 @@ func TestMain(m *testing.M) {
46
50
ReportCrashes : true ,
47
51
TelemetryDir : os .Getenv ("CRASHMONITOR_TELEMETRY_DIR" ),
48
52
})
49
- if entry == "start.panic" {
53
+ switch entry {
54
+ case "start.panic" :
50
55
go func () {
51
- child () // this line is "TestMain.func2: 1"
56
+ childPanic () // this line is "TestMain.func3:+ 1"
52
57
}()
53
58
select {} // deadlocks when reached
54
- } else {
59
+ case "start.trap" :
60
+ go func () {
61
+ childTrap () // this line is "TestMain.func4:+1"
62
+ }()
63
+ select {} // deadlocks when reached
64
+ case "start.exit" :
55
65
os .Exit (42 )
56
66
}
57
67
@@ -60,48 +70,94 @@ func TestMain(m *testing.M) {
60
70
}
61
71
}
62
72
63
- func child () {
73
+ func childPanic () {
74
+ fmt .Println ("hello" )
75
+ grandchildPanic () // this line is "childPanic:+2"
76
+ }
77
+
78
+ func grandchildPanic () {
79
+ panic ("oops" ) // this line is "grandchildPanic:=79" (the call from child is inlined)
80
+ }
81
+
82
+ var sinkPtr * int
83
+
84
+ func childTrap () {
64
85
fmt .Println ("hello" )
65
- grandchild ( ) // this line is "child :+2"
86
+ grandchildTrap ( sinkPtr ) // this line is "childTrap :+2"
66
87
}
67
88
68
- func grandchild ( ) {
69
- panic ( "oops" ) // this line is "grandchild:=92 " (the call from child is inlined)
89
+ func grandchildTrap ( i * int ) {
90
+ * i = 42 // this line is "grandchildTrap:=90 " (the call from childTrap is inlined)
70
91
}
71
92
72
93
// TestViaStderr is an internal test that asserts that the telemetry
73
94
// stack generated by the panic in grandchild is correct. It uses
74
95
// stderr, and does not rely on [start.Start] or [debug.SetCrashOutput].
75
96
func TestViaStderr (t * testing.T ) {
76
- _ , _ , stderr := runSelf (t , "via-stderr" )
77
- got , err := crashmonitor .TelemetryCounterName (stderr )
78
- if err != nil {
79
- t .Fatal (err )
80
- }
81
- got = sanitize (counter .DecodeStack (got ))
82
- want := "crash/crash\n " +
97
+ // Standard panic.
98
+ t .Run ("panic" , func (t * testing.T ) {
99
+ _ , _ , stderr := runSelf (t , "via-stderr.panic" )
100
+ got , err := crashmonitor .TelemetryCounterName (stderr )
101
+ if err != nil {
102
+ t .Fatal (err )
103
+ }
104
+ got = sanitize (counter .DecodeStack (got ))
105
+ want := "crash/crash\n " +
83
106
"runtime.gopanic:--\n " +
84
- "golang.org/x/telemetry/internal/crashmonitor_test.grandchild:=69 \n " +
85
- "golang.org/x/telemetry/internal/crashmonitor_test.child :+2\n " +
86
- "golang.org/x/telemetry/internal/crashmonitor_test.TestMain:+9 \n " +
107
+ "golang.org/x/telemetry/internal/crashmonitor_test.grandchildPanic:=79 \n " +
108
+ "golang.org/x/telemetry/internal/crashmonitor_test.childPanic :+2\n " +
109
+ "golang.org/x/telemetry/internal/crashmonitor_test.TestMain:+10 \n " +
87
110
"main.main:--\n " +
88
111
"runtime.main:--\n " +
89
112
"runtime.goexit:--"
90
113
91
- if ! crashmonitor .Supported () { // !go1.23
92
- // Traceback excludes PCs for inlined frames. Before go1.23
93
- // (https://go.dev/cl/571798 specifically), passing the set of
94
- // PCs in the traceback to runtime.CallersFrames, would report
95
- // only the innermost inlined frame and none of the inline
96
- // "callers".
97
- //
98
- // Thus, here we must drop the caller of the inlined frame.
99
- want = strings .ReplaceAll (want , "golang.org/x/telemetry/internal/crashmonitor_test.child:+2\n " , "" )
100
- }
114
+ if ! crashmonitor .Supported () { // !go1.23
115
+ // Traceback excludes PCs for inlined frames. Before
116
+ // go1.23 (https://go.dev/cl/571798 specifically),
117
+ // passing the set of PCs in the traceback to
118
+ // runtime.CallersFrames, would report only the
119
+ // innermost inlined frame and none of the inline
120
+ // "callers".
121
+ //
122
+ // Thus, here we must drop the caller of the inlined
123
+ // frame.
124
+ want = strings .ReplaceAll (want , "golang.org/x/telemetry/internal/crashmonitor_test.childPanic:+2\n " , "" )
125
+ }
101
126
102
- if got != want {
103
- t .Errorf ("got counter name <<%s>>, want <<%s>>" , got , want )
104
- }
127
+ if got != want {
128
+ t .Errorf ("got counter name <<%s>>, want <<%s>>" , got , want )
129
+ }
130
+ })
131
+
132
+ // Panic via trap.
133
+ t .Run ("trap" , func (t * testing.T ) {
134
+ _ , _ , stderr := runSelf (t , "via-stderr.trap" )
135
+ got , err := crashmonitor .TelemetryCounterName (stderr )
136
+ if err != nil {
137
+ t .Fatal (err )
138
+ }
139
+ got = sanitize (counter .DecodeStack (got ))
140
+ want := "crash/crash\n " +
141
+ "runtime.gopanic:--\n " +
142
+ "runtime.panicmem:--\n " +
143
+ "runtime.sigpanic:--\n " +
144
+ "golang.org/x/telemetry/internal/crashmonitor_test.grandchildTrap:=90\n " +
145
+ "golang.org/x/telemetry/internal/crashmonitor_test.childTrap:+2\n " +
146
+ "golang.org/x/telemetry/internal/crashmonitor_test.TestMain:+12\n " +
147
+ "main.main:--\n " +
148
+ "runtime.main:--\n " +
149
+ "runtime.goexit:--"
150
+
151
+ if ! crashmonitor .Supported () { // !go1.23
152
+ // See above.
153
+ want = strings .ReplaceAll (want , "runtime.sigpanic:--\n " , "" )
154
+ want = strings .ReplaceAll (want , "golang.org/x/telemetry/internal/crashmonitor_test.childTrap:+2\n " , "" )
155
+ }
156
+
157
+ if got != want {
158
+ t .Errorf ("got counter name <<%s>>, want <<%s>>" , got , want )
159
+ }
160
+ })
105
161
}
106
162
107
163
func waitForExitFile (t * testing.T , exitFile string ) {
@@ -144,8 +200,8 @@ func TestStart(t *testing.T) {
144
200
}
145
201
})
146
202
147
- // Assert that the crash monitor increments a telemetry
148
- // counter of the correct name when the child process panics.
203
+ // Assert that the crash monitor increments a telemetry counter of the
204
+ // correct name when the child process panics.
149
205
t .Run ("panic" , func (t * testing.T ) {
150
206
// Gather a stack trace from executing the panic statement above.
151
207
telemetryFile , exitFile , _ := runSelf (t , "start.panic" )
@@ -157,14 +213,38 @@ func TestStart(t *testing.T) {
157
213
got := sanitize (counter .DecodeStack (string (data )))
158
214
want := "crash/crash\n " +
159
215
"runtime.gopanic:--\n " +
160
- "golang.org/x/telemetry/internal/crashmonitor_test.grandchild:=69 \n " +
161
- "golang.org/x/telemetry/internal/crashmonitor_test.child :+2\n " +
216
+ "golang.org/x/telemetry/internal/crashmonitor_test.grandchildPanic:=79 \n " +
217
+ "golang.org/x/telemetry/internal/crashmonitor_test.childPanic :+2\n " +
162
218
"golang.org/x/telemetry/internal/crashmonitor_test.TestMain.func3:+1\n " +
163
219
"runtime.goexit:--"
164
220
if got != want {
165
221
t .Errorf ("got counter name <<%s>>, want <<%s>>" , got , want )
166
222
}
167
223
})
224
+
225
+ // Assert that the crash monitor increments a telemetry counter of the
226
+ // correct name when the child process panics via trap.
227
+ t .Run ("trap" , func (t * testing.T ) {
228
+ // Gather a stack trace from executing the panic statement above.
229
+ telemetryFile , exitFile , _ := runSelf (t , "start.trap" )
230
+ waitForExitFile (t , exitFile )
231
+ data , err := os .ReadFile (telemetryFile )
232
+ if err != nil {
233
+ t .Fatalf ("failed to read file: %v" , err )
234
+ }
235
+ got := sanitize (counter .DecodeStack (string (data )))
236
+ want := "crash/crash\n " +
237
+ "runtime.gopanic:--\n " +
238
+ "runtime.panicmem:--\n " +
239
+ "runtime.sigpanic:--\n " +
240
+ "golang.org/x/telemetry/internal/crashmonitor_test.grandchildTrap:=90\n " +
241
+ "golang.org/x/telemetry/internal/crashmonitor_test.childTrap:+2\n " +
242
+ "golang.org/x/telemetry/internal/crashmonitor_test.TestMain.func4:+1\n " +
243
+ "runtime.goexit:--"
244
+ if got != want {
245
+ t .Errorf ("got counter name <<%s>>, want <<%s>>" , got , want )
246
+ }
247
+ })
168
248
}
169
249
170
250
// runSelf fork+exec's this test executable using an alternate entry point.
0 commit comments