mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
Adds tests for inline traces
This commit is contained in:
+53
-50
@@ -1,4 +1,6 @@
|
||||
pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext) void {
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target.Os.Tag) void {
|
||||
cases.addCase(.{
|
||||
.name = "return",
|
||||
.source =
|
||||
@@ -450,53 +452,54 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext) void {
|
||||
},
|
||||
});
|
||||
|
||||
cases.addCase(.{
|
||||
.name = "trace through inline call",
|
||||
.source =
|
||||
\\pub fn main() !void {
|
||||
\\ try foo();
|
||||
\\}
|
||||
\\inline fn foo() !void {
|
||||
\\ try bar();
|
||||
\\}
|
||||
\\fn bar() !void {
|
||||
\\ return error.ThisIsSoSad;
|
||||
\\}
|
||||
,
|
||||
.expect_error = "ThisIsSoSad",
|
||||
.expect_trace =
|
||||
\\source.zig:8:5: [address] in bar
|
||||
\\ return error.ThisIsSoSad;
|
||||
\\ ^
|
||||
\\source.zig:5:5: [address] in foo
|
||||
\\ try bar();
|
||||
\\ ^
|
||||
\\source.zig:2:5: [address] in main
|
||||
\\ try foo();
|
||||
\\ ^
|
||||
,
|
||||
.disable_trace_optimized = &.{
|
||||
.{ .x86_64, .freebsd },
|
||||
.{ .x86_64, .netbsd },
|
||||
.{ .x86_64, .linux },
|
||||
.{ .x86, .linux },
|
||||
.{ .aarch64, .freebsd },
|
||||
.{ .aarch64, .netbsd },
|
||||
.{ .aarch64, .linux },
|
||||
.{ .loongarch64, .linux },
|
||||
.{ .powerpc64le, .linux },
|
||||
.{ .riscv64, .linux },
|
||||
.{ .s390x, .linux },
|
||||
.{ .x86_64, .openbsd },
|
||||
.{ .x86_64, .windows },
|
||||
.{ .x86, .windows },
|
||||
.{ .x86_64, .macos },
|
||||
.{ .aarch64, .macos },
|
||||
},
|
||||
// TODO: the standard library has a bug in PDB parsing where given an address corresponding
|
||||
// to an inline call, the frame we see will be for the *caller*, not the *callee*. As a
|
||||
// result this test gives bogus results on Windows right now.
|
||||
// This is a part of https://codeberg.org/ziglang/zig/issues/30847.
|
||||
.disable_trace_pdb = true,
|
||||
});
|
||||
// TODO: the standard library has a bug in PDB parsing where given an address corresponding
|
||||
// to an inline call, the frame we see will be for the *caller*, not the *callee*. As a
|
||||
// result this test gives bogus results on Windows right now.
|
||||
// This is a part of https://codeberg.org/ziglang/zig/issues/30847.
|
||||
if (os != .windows) {
|
||||
cases.addCase(.{
|
||||
.name = "trace through inline call",
|
||||
.source =
|
||||
\\pub fn main() !void {
|
||||
\\ try foo();
|
||||
\\}
|
||||
\\inline fn foo() !void {
|
||||
\\ try bar();
|
||||
\\}
|
||||
\\fn bar() !void {
|
||||
\\ return error.ThisIsSoSad;
|
||||
\\}
|
||||
,
|
||||
.expect_error = "ThisIsSoSad",
|
||||
.expect_trace =
|
||||
\\source.zig:8:5: [address] in bar
|
||||
\\ return error.ThisIsSoSad;
|
||||
\\ ^
|
||||
\\source.zig:5:5: [address] in foo
|
||||
\\ try bar();
|
||||
\\ ^
|
||||
\\source.zig:2:5: [address] in main
|
||||
\\ try foo();
|
||||
\\ ^
|
||||
,
|
||||
.disable_trace_optimized = &.{
|
||||
.{ .x86_64, .freebsd },
|
||||
.{ .x86_64, .netbsd },
|
||||
.{ .x86_64, .linux },
|
||||
.{ .x86, .linux },
|
||||
.{ .aarch64, .freebsd },
|
||||
.{ .aarch64, .netbsd },
|
||||
.{ .aarch64, .linux },
|
||||
.{ .loongarch64, .linux },
|
||||
.{ .powerpc64le, .linux },
|
||||
.{ .riscv64, .linux },
|
||||
.{ .s390x, .linux },
|
||||
.{ .x86_64, .openbsd },
|
||||
.{ .x86_64, .windows },
|
||||
.{ .x86, .windows },
|
||||
.{ .x86_64, .macos },
|
||||
.{ .aarch64, .macos },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ pub const Case = struct {
|
||||
/// LLVM ReleaseSmall builds always have the trace disabled regardless of this field, because it
|
||||
/// seems that LLVM is particularly good at optimizing traces away in those.
|
||||
disable_trace_optimized: []const DisableConfig = &.{},
|
||||
/// If `true` then we will not test the error trace on Windows due to bugs in PDB handling.
|
||||
disable_trace_pdb: bool = false,
|
||||
|
||||
pub const DisableConfig = struct { std.Target.Cpu.Arch, std.Target.Os.Tag };
|
||||
pub const Backend = enum { llvm, selfhosted };
|
||||
@@ -62,7 +60,6 @@ fn addCaseConfig(
|
||||
const b = self.b;
|
||||
|
||||
const error_tracing: bool = tracing: {
|
||||
if (target.result.os.tag == .windows and case.disable_trace_pdb) break :tracing false;
|
||||
if (optimize == .Debug) break :tracing true;
|
||||
if (backend != .llvm) break :tracing true;
|
||||
if (optimize == .ReleaseSmall) break :tracing false;
|
||||
|
||||
@@ -52,20 +52,19 @@ pub fn main(init: std.process.Init) !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
const src_col_end = std.mem.indexOf(u8, in_line, ": 0x") orelse {
|
||||
// If both the row and column are present, this it he column end. Otherwise it's the line end.
|
||||
const src_pos_end = std.mem.indexOf(u8, in_line, ": 0x") orelse {
|
||||
try w.writeAll(in_line);
|
||||
continue;
|
||||
};
|
||||
const src_row_end = std.mem.lastIndexOfScalar(u8, in_line[0..src_col_end], ':') orelse {
|
||||
try w.writeAll(in_line);
|
||||
continue;
|
||||
};
|
||||
const src_path_end = std.mem.lastIndexOfScalar(u8, in_line[0..src_row_end], ':') orelse {
|
||||
const src_row_or_path_end = std.mem.lastIndexOfScalar(u8, in_line[0..src_pos_end], ':') orelse {
|
||||
try w.writeAll(in_line);
|
||||
continue;
|
||||
};
|
||||
const src_path_end = std.mem.lastIndexOfScalar(u8, in_line[0..src_row_or_path_end], ':')
|
||||
orelse src_row_or_path_end;
|
||||
|
||||
const addr_end = std.mem.indexOfPos(u8, in_line, src_col_end, " in ") orelse {
|
||||
const addr_end = std.mem.indexOfPos(u8, in_line, src_pos_end, " in ") orelse {
|
||||
try w.writeAll(in_line);
|
||||
continue;
|
||||
};
|
||||
@@ -91,7 +90,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
const src_path = in_line[0..src_path_end];
|
||||
const basename_start = if (std.mem.lastIndexOfAny(u8, src_path, "/\\")) |i| i + 1 else 0;
|
||||
const symbol_start = addr_end + " in ".len;
|
||||
try w.writeAll(in_line[basename_start..src_col_end]);
|
||||
try w.writeAll(in_line[basename_start..src_pos_end]);
|
||||
try w.writeAll(": [address] in ");
|
||||
try w.writeAll(in_line[symbol_start..symbol_end]);
|
||||
try w.writeByte('\n');
|
||||
|
||||
+117
-1
@@ -1,4 +1,6 @@
|
||||
pub fn addCases(cases: *@import("tests.zig").StackTracesContext) void {
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target.Os.Tag) void {
|
||||
cases.addCase(.{
|
||||
.name = "simple panic",
|
||||
.source =
|
||||
@@ -221,4 +223,118 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext) void {
|
||||
\\
|
||||
,
|
||||
});
|
||||
|
||||
cases.addCase(.{
|
||||
.name = "simple inline panic",
|
||||
.source =
|
||||
\\pub fn main() void {
|
||||
\\ foo();
|
||||
\\}
|
||||
\\inline fn foo() void {
|
||||
\\ @panic("oh no");
|
||||
\\}
|
||||
\\
|
||||
,
|
||||
.unwind = .any,
|
||||
.expect_panic = true,
|
||||
.expect = switch (os) {
|
||||
// We use the information present in PDBs to resolve inlines when dumping stack traces
|
||||
// on Windows. Column numbers are missing as LLVM doesn't emit column info in the PDBs
|
||||
// for inline functions.
|
||||
.windows =>
|
||||
\\panic: oh no
|
||||
\\source.zig:5: [address] in foo
|
||||
\\ @panic("oh no");
|
||||
\\
|
||||
\\source.zig:2:8: [address] in main
|
||||
\\ foo();
|
||||
\\ ^
|
||||
\\
|
||||
,
|
||||
// We don't yet resolve inlines on other platforms.
|
||||
else =>
|
||||
\\panic: oh no
|
||||
\\source.zig:5:5: [address] in foo
|
||||
\\ @panic("oh no");
|
||||
\\ ^
|
||||
,
|
||||
},
|
||||
.expect_strip = switch (os) {
|
||||
.windows =>
|
||||
\\panic: oh no
|
||||
\\???:?:?: [address] in source.foo
|
||||
\\???:?:?: [address] in source.main
|
||||
\\
|
||||
,
|
||||
else =>
|
||||
\\panic: oh no
|
||||
\\???:?:?: [address] in source.foo
|
||||
\\
|
||||
,
|
||||
},
|
||||
});
|
||||
|
||||
// Make sure all inline calls are resolved and in the right order!
|
||||
cases.addCase(.{
|
||||
.name = "nested inline panic",
|
||||
.source =
|
||||
\\pub fn main() void {
|
||||
\\ foo();
|
||||
\\}
|
||||
\\inline fn foo() void {
|
||||
\\ bar();
|
||||
\\}
|
||||
\\inline fn bar() void {
|
||||
\\ baz();
|
||||
\\}
|
||||
\\inline fn baz() void {
|
||||
\\ @panic("oh no");
|
||||
\\}
|
||||
\\
|
||||
,
|
||||
.unwind = .any,
|
||||
.expect_panic = true,
|
||||
.expect = switch (os) {
|
||||
// Similarly to "inline panic", we can resolve inlines from PDBs but LLVM doesn't emit
|
||||
// column info for them.
|
||||
.windows =>
|
||||
\\panic: oh no
|
||||
\\source.zig:11: [address] in baz
|
||||
\\ @panic("oh no");
|
||||
\\
|
||||
\\source.zig:8: [address] in bar
|
||||
\\ baz();
|
||||
\\
|
||||
\\source.zig:5: [address] in foo
|
||||
\\ bar();
|
||||
\\
|
||||
\\source.zig:2:8: [address] in main
|
||||
\\ foo();
|
||||
\\ ^
|
||||
\\
|
||||
,
|
||||
// Similarly to "inline panic", we don't yet resolve inlines on other platforms.
|
||||
else =>
|
||||
\\panic: oh no
|
||||
\\source.zig:11:5: [address] in baz
|
||||
\\ @panic("oh no");
|
||||
\\ ^
|
||||
,
|
||||
},
|
||||
.expect_strip = switch (os) {
|
||||
.windows =>
|
||||
\\panic: oh no
|
||||
\\???:?:?: [address] in baz
|
||||
\\???:?:?: [address] in bar
|
||||
\\???:?:?: [address] in foo
|
||||
\\???:?:?: [address] in main
|
||||
\\
|
||||
,
|
||||
else =>
|
||||
\\panic: oh no
|
||||
\\???:?:?: [address] in baz
|
||||
\\
|
||||
,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+115
-44
@@ -1989,56 +1989,75 @@ const c_abi_targets = blk: {
|
||||
};
|
||||
};
|
||||
|
||||
/// For stack trace tests, we only test native, because external executors are pretty unreliable at
|
||||
/// stack tracing. However, if there's a 32-bit equivalent target which the host can trivially run,
|
||||
/// we may as well at least test that!
|
||||
fn nativeAndCompatible32bit(b: *std.Build, skip_non_native: bool) []const std.Build.ResolvedTarget {
|
||||
fn compatible32bitArch(b: *std.Build) ?std.Target.Cpu.Arch {
|
||||
const host = b.graph.host.result;
|
||||
const only_native = (&b.graph.host)[0..1];
|
||||
if (skip_non_native) return only_native;
|
||||
const arch32: std.Target.Cpu.Arch = switch (host.os.tag) {
|
||||
return switch (host.os.tag) {
|
||||
.windows => switch (host.cpu.arch) {
|
||||
.x86_64 => .x86,
|
||||
.aarch64 => .thumb,
|
||||
.aarch64_be => .thumbeb,
|
||||
else => return only_native,
|
||||
else => null,
|
||||
},
|
||||
.freebsd => switch (host.cpu.arch) {
|
||||
.aarch64 => .arm,
|
||||
.aarch64_be => .armeb,
|
||||
else => return only_native,
|
||||
else => null,
|
||||
},
|
||||
.linux, .netbsd => switch (host.cpu.arch) {
|
||||
.x86_64 => .x86,
|
||||
.aarch64 => .arm,
|
||||
.aarch64_be => .armeb,
|
||||
else => return only_native,
|
||||
else => null,
|
||||
},
|
||||
else => return only_native,
|
||||
else => null,
|
||||
};
|
||||
var targets = std.ArrayList(std.Build.ResolvedTarget).initCapacity(b.graph.arena, 2)
|
||||
catch @panic("OOM");
|
||||
targets.appendAssumeCapacity(b.graph.host);
|
||||
targets.appendAssumeCapacity(b.resolveTargetQuery(.{
|
||||
.cpu_arch = arch32,
|
||||
.os_tag = host.os.tag,
|
||||
}));
|
||||
if (b.enable_wine and b.graph.host.result.os.tag != .windows) {
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = host.cpu.arch,
|
||||
.os_tag = .windows,
|
||||
})) catch @panic("OOM");
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = arch32,
|
||||
.os_tag = .windows,
|
||||
})) catch @panic("OOM");
|
||||
}
|
||||
if (b.enable_darling and b.graph.host.result.os.tag != .macos) {
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = host.cpu.arch,
|
||||
.os_tag = .macos,
|
||||
})) catch @panic("OOM");
|
||||
}
|
||||
|
||||
/// For stack trace tests, we only test native by default, because external executors are pretty
|
||||
/// unreliable at stack tracing. However, if there's a 32-bit equivalent target which the host can
|
||||
/// trivially run, we may as well at least test that!
|
||||
fn nativeAndCompatible32bit(b: *std.Build, skip_non_native: bool) []const std.Build.ResolvedTarget {
|
||||
const host = b.graph.host.result;
|
||||
const only_native = (&b.graph.host)[0..1];
|
||||
if (skip_non_native) return only_native;
|
||||
const arch32 = compatible32bitArch(b) orelse return only_native;
|
||||
return b.graph.arena.dupe(std.Build.ResolvedTarget, &.{
|
||||
b.graph.host,
|
||||
b.resolveTargetQuery(.{ .cpu_arch = arch32, .os_tag = host.os.tag }),
|
||||
}) catch @panic("OOM");
|
||||
}
|
||||
|
||||
fn wineAndCompatible32bit(b: *std.Build, skip_non_native: bool) []const std.Build.ResolvedTarget {
|
||||
var targets: std.ArrayList(std.Build.ResolvedTarget) = .empty;
|
||||
|
||||
const host = b.graph.host.result;
|
||||
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = host.cpu.arch,
|
||||
.os_tag = .windows,
|
||||
})) catch @panic("OOM");
|
||||
if (!skip_non_native) {
|
||||
if (compatible32bitArch(b)) |arch| {
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = arch,
|
||||
.os_tag = .windows,
|
||||
})) catch @panic("OOM");
|
||||
}
|
||||
}
|
||||
|
||||
return targets.toOwnedSlice(b.graph.arena) catch @panic("OOM");
|
||||
}
|
||||
|
||||
fn darlingTargets(b: *std.Build) []const std.Build.ResolvedTarget {
|
||||
var targets: std.ArrayList(std.Build.ResolvedTarget) = .empty;
|
||||
|
||||
const host = b.graph.host.result;
|
||||
|
||||
targets.append(b.graph.arena, b.resolveTargetQuery(.{
|
||||
.cpu_arch = host.cpu.arch,
|
||||
.os_tag = .macos,
|
||||
})) catch @panic("OOM");
|
||||
|
||||
return targets.toOwnedSlice(b.graph.arena) catch @panic("OOM");
|
||||
}
|
||||
|
||||
@@ -2047,6 +2066,8 @@ pub fn addStackTraceTests(
|
||||
test_filters: []const []const u8,
|
||||
skip_non_native: bool,
|
||||
) *Step {
|
||||
const step = b.step("test-stack-traces", "Run the stack trace tests");
|
||||
|
||||
const convert_exe = b.addExecutable(.{
|
||||
.name = "convert-stack-trace",
|
||||
.root_module = b.createModule(.{
|
||||
@@ -2056,19 +2077,41 @@ pub fn addStackTraceTests(
|
||||
}),
|
||||
});
|
||||
|
||||
const cases = b.allocator.create(StackTracesContext) catch @panic("OOM");
|
||||
|
||||
cases.* = .{
|
||||
const host_cases = b.allocator.create(StackTracesContext) catch @panic("OOM");
|
||||
host_cases.* = .{
|
||||
.b = b,
|
||||
.step = b.step("test-stack-traces", "Run the stack trace tests"),
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = nativeAndCompatible32bit(b, skip_non_native),
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
stack_traces.addCases(host_cases, b.graph.host.result.os.tag);
|
||||
|
||||
stack_traces.addCases(cases);
|
||||
if (b.enable_wine) {
|
||||
const wine_cases = b.allocator.create(StackTracesContext) catch @panic("OOM");
|
||||
wine_cases.* = .{
|
||||
.b = b,
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = wineAndCompatible32bit(b, skip_non_native),
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
stack_traces.addCases(wine_cases, .windows);
|
||||
}
|
||||
|
||||
return cases.step;
|
||||
if (b.enable_darling) {
|
||||
const darling_cases = b.allocator.create(StackTracesContext) catch @panic("OOM");
|
||||
darling_cases.* = .{
|
||||
.b = b,
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = darlingTargets(b),
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
stack_traces.addCases(darling_cases, .macos);
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
pub fn addErrorTraceTests(
|
||||
@@ -2077,6 +2120,8 @@ pub fn addErrorTraceTests(
|
||||
optimize_modes: []const OptimizeMode,
|
||||
skip_non_native: bool,
|
||||
) *Step {
|
||||
const step = b.step("test-error-traces", "Run the error trace tests");
|
||||
|
||||
const convert_exe = b.addExecutable(.{
|
||||
.name = "convert-stack-trace",
|
||||
.root_module = b.createModule(.{
|
||||
@@ -2086,19 +2131,45 @@ pub fn addErrorTraceTests(
|
||||
}),
|
||||
});
|
||||
|
||||
const cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM");
|
||||
cases.* = .{
|
||||
const host_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM");
|
||||
host_cases.* = .{
|
||||
.b = b,
|
||||
.step = b.step("test-error-traces", "Run the error trace tests"),
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = nativeAndCompatible32bit(b, skip_non_native),
|
||||
.optimize_modes = optimize_modes,
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
error_traces.addCases(host_cases, b.graph.host.result.os.tag);
|
||||
|
||||
error_traces.addCases(cases);
|
||||
if (b.enable_wine) {
|
||||
const wine_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM");
|
||||
wine_cases.* = .{
|
||||
.b = b,
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = wineAndCompatible32bit(b, skip_non_native),
|
||||
.optimize_modes = optimize_modes,
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
error_traces.addCases(wine_cases, .windows);
|
||||
}
|
||||
|
||||
return cases.step;
|
||||
if (b.enable_darling) {
|
||||
const darling_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM");
|
||||
darling_cases.* = .{
|
||||
.b = b,
|
||||
.step = step,
|
||||
.test_filters = test_filters,
|
||||
.targets = darlingTargets(b),
|
||||
.optimize_modes = optimize_modes,
|
||||
.convert_exe = convert_exe,
|
||||
};
|
||||
error_traces.addCases(darling_cases, .macos);
|
||||
}
|
||||
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
fn compilerHasPackageManager(b: *std.Build) bool {
|
||||
|
||||
Reference in New Issue
Block a user