1const std = @import("std");
2const Context = @import("tests.zig").StackTracesContext;
3
4pub fn addCases(cases: *Context, params: *const Context.CaseParameters, target: *const std.Target) void {
5 cases.addCase(.{
6 .params = params,
7 .target = target,
8 .name = "simple panic",
9 .source =
10 \\pub fn main() void {
11 \\ foo();
12 \\}
13 \\fn foo() void {
14 \\ @panic("oh no");
15 \\}
16 \\
17 ,
18 .unwind = .any,
19 .expect_panic = true,
20 .expect =
21 \\panic: oh no
22 \\source.zig:5:5: [address] in foo
23 \\ @panic("oh no");
24 \\ ^
25 \\source.zig:2:8: [address] in main
26 \\ foo();
27 \\ ^
28 \\
29 ,
30 .expect_strip =
31 \\panic: oh no
32 \\???:?:?: [address] in source.foo
33 \\???:?:?: [address] in source.main
34 \\
35 ,
36 });
37
38 cases.addCase(.{
39 .params = params,
40 .target = target,
41 .name = "simple panic with no unwind strategy",
42 .source =
43 \\pub fn main() void {
44 \\ foo();
45 \\}
46 \\fn foo() void {
47 \\ @panic("oh no");
48 \\}
49 \\
50 ,
51 .unwind = .none,
52 .expect_panic = true,
53 .expect = "panic: oh no",
54 .expect_strip = "panic: oh no",
55 });
56
57 cases.addCase(.{
58 .params = params,
59 .target = target,
60 .name = "dump current trace",
61 .source =
62 \\pub fn main() void {
63 \\ foo(bar());
64 \\}
65 \\fn bar() void {
66 \\ qux(123);
67 \\}
68 \\fn foo(_: void) void {}
69 \\fn qux(x: u32) void {
70 \\ std.debug.dumpCurrentStackTrace(.{});
71 \\ _ = x;
72 \\}
73 \\const std = @import("std");
74 \\
75 ,
76 .unwind = .safe,
77 .expect_panic = false,
78 .expect =
79 \\source.zig:9:36: [address] in qux
80 \\ std.debug.dumpCurrentStackTrace(.{});
81 \\ ^
82 \\source.zig:5:8: [address] in bar
83 \\ qux(123);
84 \\ ^
85 \\source.zig:2:12: [address] in main
86 \\ foo(bar());
87 \\ ^
88 \\
89 ,
90 .expect_strip =
91 \\???:?:?: [address] in source.qux
92 \\???:?:?: [address] in source.bar
93 \\???:?:?: [address] in source.main
94 \\
95 ,
96 });
97
98 cases.addCase(.{
99 .params = params,
100 .target = target,
101 .name = "dump current trace with no unwind strategy",
102 .source =
103 \\pub fn main() void {
104 \\ foo(bar());
105 \\}
106 \\fn bar() void {
107 \\ qux(123);
108 \\}
109 \\fn foo(_: void) void {}
110 \\fn qux(x: u32) void {
111 \\ std.debug.print("pre\n", .{});
112 \\ std.debug.dumpCurrentStackTrace(.{});
113 \\ std.debug.print("post\n", .{});
114 \\ _ = x;
115 \\}
116 \\const std = @import("std");
117 \\
118 ,
119 .unwind = .no_safe,
120 .expect_panic = false,
121 .expect = "pre\npost\n",
122 .expect_strip = "pre\npost\n",
123 });
124
125 cases.addCase(.{
126 .params = params,
127 .target = target,
128 .name = "dump captured trace",
129 .source =
130 \\pub fn main() void {
131 \\ var stack_trace_buf: [8]usize = undefined;
132 \\ dumpIt(&captureIt(&stack_trace_buf));
133 \\}
134 \\fn captureIt(buf: []usize) std.debug.StackTrace {
135 \\ return captureItInner(buf);
136 \\}
137 \\fn dumpIt(st: *const std.debug.StackTrace) void {
138 \\ std.debug.dumpStackTrace(st);
139 \\}
140 \\fn captureItInner(buf: []usize) std.debug.StackTrace {
141 \\ return std.debug.captureCurrentStackTrace(.{}, buf);
142 \\}
143 \\const std = @import("std");
144 \\
145 ,
146 .unwind = .safe,
147 .expect_panic = false,
148 .expect =
149 \\source.zig:12:46: [address] in captureItInner
150 \\ return std.debug.captureCurrentStackTrace(.{}, buf);
151 \\ ^
152 \\source.zig:6:26: [address] in captureIt
153 \\ return captureItInner(buf);
154 \\ ^
155 \\source.zig:3:22: [address] in main
156 \\ dumpIt(&captureIt(&stack_trace_buf));
157 \\ ^
158 \\
159 ,
160 .expect_strip =
161 \\???:?:?: [address] in source.captureItInner
162 \\???:?:?: [address] in source.captureIt
163 \\???:?:?: [address] in source.main
164 \\
165 ,
166 });
167
168 cases.addCase(.{
169 .params = params,
170 .target = target,
171 .name = "dump captured trace with no unwind strategy",
172 .source =
173 \\pub fn main() void {
174 \\ var stack_trace_buf: [8]usize = undefined;
175 \\ dumpIt(&captureIt(&stack_trace_buf));
176 \\}
177 \\fn captureIt(buf: []usize) std.debug.StackTrace {
178 \\ return captureItInner(buf);
179 \\}
180 \\fn dumpIt(st: *const std.debug.StackTrace) void {
181 \\ std.debug.dumpStackTrace(st);
182 \\}
183 \\fn captureItInner(buf: []usize) std.debug.StackTrace {
184 \\ return std.debug.captureCurrentStackTrace(.{}, buf);
185 \\}
186 \\const std = @import("std");
187 \\
188 ,
189 .unwind = .no_safe,
190 .expect_panic = false,
191 .expect = "(empty stack trace)\n",
192 .expect_strip = "(empty stack trace)\n",
193 });
194
195 cases.addCase(.{
196 .params = params,
197 .target = target,
198 .name = "dump captured trace on thread",
199 .source =
200 \\pub fn main() !void {
201 \\ var stack_trace_buf: [8]usize = undefined;
202 \\ const t = try std.Thread.spawn(.{}, threadMain, .{&stack_trace_buf});
203 \\ t.join();
204 \\}
205 \\fn threadMain(stack_trace_buf: []usize) void {
206 \\ dumpIt(&captureIt(stack_trace_buf));
207 \\}
208 \\fn captureIt(buf: []usize) std.debug.StackTrace {
209 \\ return captureItInner(buf);
210 \\}
211 \\fn dumpIt(st: *const std.debug.StackTrace) void {
212 \\ std.debug.dumpStackTrace(st);
213 \\}
214 \\fn captureItInner(buf: []usize) std.debug.StackTrace {
215 \\ return std.debug.captureCurrentStackTrace(.{}, buf);
216 \\}
217 \\const std = @import("std");
218 \\
219 ,
220 .unwind = .safe,
221 .expect_panic = false,
222 .expect =
223 \\source.zig:16:46: [address] in captureItInner
224 \\ return std.debug.captureCurrentStackTrace(.{}, buf);
225 \\ ^
226 \\source.zig:10:26: [address] in captureIt
227 \\ return captureItInner(buf);
228 \\ ^
229 \\source.zig:7:22: [address] in threadMain
230 \\ dumpIt(&captureIt(stack_trace_buf));
231 \\ ^
232 \\
233 ,
234 .expect_strip =
235 \\???:?:?: [address] in source.captureItInner
236 \\???:?:?: [address] in source.captureIt
237 \\???:?:?: [address] in source.threadMain
238 \\
239 ,
240 });
241
242 cases.addCase(.{
243 .params = params,
244 .target = target,
245 .name = "simple inline panic",
246 // The main function has two inline calls to ensure
247 // that inlinees in PDBs are properly deduplicated.
248 .source =
249 \\pub fn main() void {
250 \\ foo(false);
251 \\ foo(true);
252 \\}
253 \\inline fn foo(b: bool) void {
254 \\ if (b) @panic("oh no");
255 \\}
256 \\
257 ,
258 .unwind = .any,
259 .expect_panic = true,
260 .expect = switch (target.os.tag) {
261 // LLVM doesn't emit column info in the binary annotations for inlinee callees in PDBs,
262 // so the first location has only a row.
263 .windows =>
264 \\panic: oh no
265 \\source.zig:6: [address] in foo
266 \\ if (b) @panic("oh no");
267 \\
268 \\source.zig:3:8: [address] in main
269 \\ foo(true);
270 \\ ^
271 \\
272 ,
273 // On all other platforms, we resolve the innermost inline callee but we don't yet
274 // resolve the inline callers.
275 else =>
276 \\panic: oh no
277 \\source.zig:6:12: [address] in foo
278 \\ if (b) @panic("oh no");
279 \\ ^
280 ,
281 },
282 .expect_strip = switch (target.os.tag) {
283 .windows =>
284 \\panic: oh no
285 \\???:?:?: [address] in source.foo
286 \\???:?:?: [address] in source.main
287 \\
288 ,
289 else =>
290 \\panic: oh no
291 \\???:?:?: [address] in source.foo
292 \\
293 ,
294 },
295 });
296
297 // Make sure all inline calls are resolved and in the right order!
298 cases.addCase(.{
299 .params = params,
300 .target = target,
301 .name = "nested inline panic",
302 .source =
303 \\pub fn main() void {
304 \\ foo();
305 \\}
306 \\inline fn foo() void {
307 \\ bar();
308 \\}
309 \\inline fn bar() void {
310 \\ baz();
311 \\}
312 \\inline fn baz() void {
313 \\ @panic("oh no");
314 \\}
315 \\
316 ,
317 .unwind = .any,
318 .expect_panic = true,
319 // This switch serves a similar purpose as in "inline panic".
320 .expect = switch (target.os.tag) {
321 .windows =>
322 \\panic: oh no
323 \\source.zig:11: [address] in baz
324 \\ @panic("oh no");
325 \\
326 \\source.zig:8: [address] in bar
327 \\ baz();
328 \\
329 \\source.zig:5: [address] in foo
330 \\ bar();
331 \\
332 \\source.zig:2:8: [address] in main
333 \\ foo();
334 \\ ^
335 \\
336 ,
337 else =>
338 \\panic: oh no
339 \\source.zig:11:5: [address] in baz
340 \\ @panic("oh no");
341 \\ ^
342 ,
343 },
344 .expect_strip = switch (target.os.tag) {
345 .windows =>
346 \\panic: oh no
347 \\???:?:?: [address] in baz
348 \\???:?:?: [address] in bar
349 \\???:?:?: [address] in foo
350 \\???:?:?: [address] in main
351 \\
352 ,
353 else =>
354 \\panic: oh no
355 \\???:?:?: [address] in baz
356 \\
357 ,
358 },
359 });
360}