authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-13 10:29:36+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:54+01:00
log4cb84f8e486426e66e17cc91dd880d9bdcabc680
treecf3e953e019f14491fcb2f2002fb5c025b240020
parent51d08f4b9b051d66534b77462a3bfb9bace9f1fb
signaturelock-open Commit is signed but in an unrecognized format.

test-standalone: update for std.debug changes


4 files changed, 96 insertions(+), 84 deletions(-)

test/standalone/coff_dwarf/build.zig+7-6
...@@ -1,9 +1,10 @@...@@ -1,9 +1,10 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
32
4/// This tests the path where DWARF information is embedded in a COFF binary3/// This tests the path where DWARF information is embedded in a COFF binary
5pub fn build(b: *std.Build) void {4pub fn build(b: *std.Build) void {
6 switch (builtin.cpu.arch) {5 const host = b.graph.host;
6
7 switch (host.result.cpu.arch) {
7 .aarch64,8 .aarch64,
8 .x86,9 .x86,
9 .x86_64,10 .x86_64,
...@@ -15,10 +16,10 @@ pub fn build(b: *std.Build) void {...@@ -15,10 +16,10 @@ pub fn build(b: *std.Build) void {
15 b.default_step = test_step;16 b.default_step = test_step;
1617
17 const optimize: std.builtin.OptimizeMode = .Debug;18 const optimize: std.builtin.OptimizeMode = .Debug;
18 const target = if (builtin.os.tag == .windows)19 const target = switch (host.result.os.tag) {
19 b.standardTargetOptions(.{})20 .windows => host,
20 else21 else => b.resolveTargetQuery(.{ .os_tag = .windows }),
21 b.resolveTargetQuery(.{ .os_tag = .windows });22 };
2223
23 const exe = b.addExecutable(.{24 const exe = b.addExecutable(.{
24 .name = "main",25 .name = "main",
test/standalone/coff_dwarf/main.zig+23-16
...@@ -1,27 +1,34 @@...@@ -1,27 +1,34 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;2const fatal = std.process.fatal;
3const testing = std.testing;
43
5extern fn add(a: u32, b: u32, addr: *usize) u32;4extern fn add(a: u32, b: u32, addr: *usize) u32;
65
7pub fn main() !void {6pub fn main() void {
8 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;7 var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init;
9 defer assert(gpa.deinit() == .ok);8 defer std.debug.assert(debug_alloc_inst.deinit() == .ok);
10 const allocator = gpa.allocator();9 const gpa = debug_alloc_inst.allocator();
1110
12 var debug_info = try std.debug.SelfInfo.open(allocator);11 var di: std.debug.SelfInfo = .init;
13 defer debug_info.deinit();12 defer di.deinit(gpa);
1413
15 var add_addr: usize = undefined;14 var add_addr: usize = undefined;
16 _ = add(1, 2, &add_addr);15 _ = add(1, 2, &add_addr);
1716
18 const module = try debug_info.getModuleForAddress(add_addr);17 const symbol = di.getSymbolAtAddress(gpa, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err});
19 const symbol = try module.getSymbolAtAddress(allocator, add_addr);18 defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
20 defer if (symbol.source_location) |sl| allocator.free(sl.file_name);
2119
22 try testing.expectEqualStrings("add", symbol.name);20 if (symbol.name == null) fatal("failed to resolve symbol name", .{});
23 try testing.expect(symbol.source_location != null);21 if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{});
24 try testing.expectEqualStrings("shared_lib.c", std.fs.path.basename(symbol.source_location.?.file_name));22 if (symbol.source_location == null) fatal("failed to resolve source location", .{});
25 try testing.expectEqual(@as(u64, 3), symbol.source_location.?.line);23
26 try testing.expectEqual(@as(u64, 0), symbol.source_location.?.column);24 if (!std.mem.eql(u8, symbol.name.?, "add")) {
25 fatal("incorrect symbol name '{s}'", .{symbol.name.?});
26 }
27 const sl = &symbol.source_location.?;
28 if (!std.mem.eql(u8, std.fs.path.basename(sl.file_name), "shared_lib.c")) {
29 fatal("incorrect file name '{s}'", .{sl.file_name});
30 }
31 if (sl.line != 3 or sl.column != 0) {
32 fatal("incorrect line/column :{d}:{d}", .{ sl.line, sl.column });
33 }
27}34}
test/standalone/stack_iterator/unwind.zig+31-29
...@@ -1,27 +1,19 @@...@@ -1,27 +1,19 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const debug = std.debug;3const fatal = std.process.fatal;
4const testing = std.testing;
54
6noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {5noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
7 expected[0] = @returnAddress();6 expected[0] = @returnAddress();
87 return std.debug.captureCurrentStackTrace(.{
9 var context: debug.ThreadContext = undefined;8 .first_address = @returnAddress(),
10 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");9 .allow_unsafe_unwind = true,
1110 }, addr_buf);
12 const debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
13 var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context, @frameAddress()) catch @panic("failed to initWithContext");
14 defer it.deinit();
15
16 for (unwound) |*addr| {
17 if (it.next()) |return_address| addr.* = return_address;
18 }
19}11}
2012
21noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {13noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
22 // Exercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored14 // Exercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
23 if (builtin.target.ofmt != .c) {15 if (builtin.target.ofmt != .c) {
24 switch (builtin.cpu.arch) {16 switch (builtin.target.cpu.arch) {
25 .x86 => {17 .x86 => {
26 if (builtin.omit_frame_pointer) {18 if (builtin.omit_frame_pointer) {
27 asm volatile (19 asm volatile (
...@@ -67,10 +59,10 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {...@@ -67,10 +59,10 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
67 }59 }
6860
69 expected[1] = @returnAddress();61 expected[1] = @returnAddress();
70 frame3(expected, unwound);62 return frame3(expected, addr_buf);
71}63}
7264
73noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {65noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
74 expected[2] = @returnAddress();66 expected[2] = @returnAddress();
7567
76 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding68 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
...@@ -78,22 +70,32 @@ noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {...@@ -78,22 +70,32 @@ noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
78 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;70 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
79 _ = std.mem.doNotOptimizeAway(&pad);71 _ = std.mem.doNotOptimizeAway(&pad);
8072
81 frame2(expected, unwound);73 return frame2(expected, addr_buf);
82}74}
8375
84noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {76noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
85 expected[3] = @returnAddress();77 expected[3] = @returnAddress();
86 frame1(expected, unwound);78 return frame1(expected, addr_buf);
87}79}
8880
89pub fn main() !void {81pub fn main() void {
90 // Disabled until the DWARF unwinder bugs on .aarch64 are solved82 if (std.posix.ucontext_t == void and builtin.omit_frame_pointer) {
91 if (builtin.omit_frame_pointer and comptime builtin.target.os.tag.isDarwin() and builtin.cpu.arch == .aarch64) return;83 // Stack unwinding is impossible.
9284 return;
93 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;85 }
9486
95 var expected: [4]usize = undefined;87 var expected: [4]usize = undefined;
96 var unwound: [4]usize = undefined;88 var addr_buf: [4]usize = undefined;
97 frame0(&expected, &unwound);89 const trace = frame0(&expected, &addr_buf);
98 try testing.expectEqual(expected, unwound);90 // There may be *more* than 4 frames (due to the caller of `main`); that's okay.
91 if (trace.index < 4) {
92 fatal("expected at least 4 frames, got '{d}':\n{f}", .{ trace.index, &trace });
93 }
94 if (!std.mem.eql(usize, trace.instruction_addresses, &expected)) {
95 const expected_trace: std.builtin.StackTrace = .{
96 .index = 4,
97 .instruction_addresses = &expected,
98 };
99 fatal("expected trace:\n{f}\nactual trace:\n{f}", .{ &expected_trace, &trace });
100 }
99}101}
test/standalone/stack_iterator/unwind_freestanding.zig+35-33
...@@ -1,26 +1,21 @@...@@ -1,26 +1,21 @@
1/// Test StackIterator on 'freestanding' target. Based on unwind.zig.1//! Test StackIterator on 'freestanding' target. Based on unwind.zig.
2
2const std = @import("std");3const std = @import("std");
3const builtin = @import("builtin");
4const debug = std.debug;
54
6noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {5noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
7 expected[0] = @returnAddress();6 expected[0] = @returnAddress();
87 return std.debug.captureCurrentStackTrace(.{
9 var it = debug.StackIterator.init(@returnAddress(), @frameAddress());8 .first_address = @returnAddress(),
10 defer it.deinit();9 .allow_unsafe_unwind = true,
1110 }, addr_buf);
12 // Save StackIterator's frame addresses into `unwound`:
13 for (unwound) |*addr| {
14 if (it.next()) |return_address| addr.* = return_address;
15 }
16}11}
1712
18noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {13noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
19 expected[1] = @returnAddress();14 expected[1] = @returnAddress();
20 frame3(expected, unwound);15 return frame3(expected, addr_buf);
21}16}
2217
23noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {18noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
24 expected[2] = @returnAddress();19 expected[2] = @returnAddress();
2520
26 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding21 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
...@@ -28,37 +23,44 @@ noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {...@@ -28,37 +23,44 @@ noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
28 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;23 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
29 _ = std.mem.doNotOptimizeAway(&pad);24 _ = std.mem.doNotOptimizeAway(&pad);
3025
31 frame2(expected, unwound);26 return frame2(expected, addr_buf);
32}27}
3328
34noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {29noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
35 expected[3] = @returnAddress();30 expected[3] = @returnAddress();
36 frame1(expected, unwound);31 return frame1(expected, addr_buf);
37}32}
3833
39// No-OS entrypoint34/// No-OS entrypoint
40export fn _start() callconv(.withStackAlign(.c, 1)) noreturn {35export fn _start() callconv(.withStackAlign(.c, 1)) noreturn {
41 var expected: [4]usize = undefined;36 var expected: [4]usize = undefined;
42 var unwound: [4]usize = undefined;37 var addr_buf: [4]usize = undefined;
43 frame0(&expected, &unwound);38 const trace = frame0(&expected, &addr_buf);
4439
45 // Verify result (no std.testing in freestanding)40 // Since we can't print from this freestanding test, we'll just use the exit
46 var missed: c_int = 0;41 // code to communicate error conditions.
47 for (expected, unwound) |expectFA, actualFA| {42
48 if (expectFA != actualFA) {43 // Unlike `unwind.zig`, we can expect *exactly* 4 frames, as we are the
49 missed += 1;44 // actual entry point and the frame pointer will be 0 on entry.
50 }45 if (trace.index != 4) exit(1);
51 }46 if (trace.instruction_addresses[0] != expected[0]) exit(2);
47 if (trace.instruction_addresses[1] != expected[1]) exit(3);
48 if (trace.instruction_addresses[2] != expected[2]) exit(4);
49 if (trace.instruction_addresses[3] != expected[3]) exit(5);
50
51 exit(0);
52}
5253
53 // Need to compile with the target OS as "freestanding" or "other" to54fn exit(code: u8) noreturn {
54 // exercise the StackIterator code, but when run as a regression test55 // We are intentionally compiling with the target OS being "freestanding" to
55 // need to actually exit. So assume we're running on x86_64-linux ...56 // exercise std.debug, but we still need to exit the process somehow; so do
57 // the appropriate x86_64-linux syscall.
56 asm volatile (58 asm volatile (
57 \\movl $60, %%eax59 \\movl $60, %%eax
58 \\syscall60 \\syscall
59 :61 :
60 : [missed] "{edi}" (missed),62 : [code] "{edi}" (code),
61 : .{ .edi = true, .eax = true });63 : .{ .edi = true, .eax = true });
6264
63 while (true) {} // unreached65 unreachable;
64}66}