mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
test: remove stack_iterator standalone test
Our new stack trace tests cover all the important parts of this.
This commit is contained in:
parent
5a71e15f1f
commit
d97954a8ea
6 changed files with 0 additions and 407 deletions
|
|
@ -163,9 +163,6 @@
|
|||
.zerolength_check = .{
|
||||
.path = "zerolength_check",
|
||||
},
|
||||
.stack_iterator = .{
|
||||
.path = "stack_iterator",
|
||||
},
|
||||
.coff_dwarf = .{
|
||||
.path = "coff_dwarf",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,167 +0,0 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const test_step = b.step("test", "Test it");
|
||||
b.default_step = test_step;
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
if (target.result.cpu.arch.isRISCV() and target.result.os.tag == .linux) {
|
||||
// https://github.com/ziglang/zig/issues/24310
|
||||
return;
|
||||
}
|
||||
|
||||
// Unwinding with a frame pointer
|
||||
//
|
||||
// getcontext version: zig std
|
||||
//
|
||||
// Unwind info type:
|
||||
// - ELF: DWARF .debug_frame
|
||||
// - MachO: __unwind_info encodings:
|
||||
// - x86_64: RBP_FRAME
|
||||
// - aarch64: FRAME, DWARF
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "unwind_fp",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("unwind.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
|
||||
.omit_frame_pointer = false,
|
||||
}),
|
||||
});
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// Unwinding without a frame pointer
|
||||
//
|
||||
// getcontext version: zig std
|
||||
//
|
||||
// Unwind info type:
|
||||
// - ELF: DWARF .eh_frame_hdr + .eh_frame
|
||||
// - MachO: __unwind_info encodings:
|
||||
// - x86_64: STACK_IMMD, STACK_IND
|
||||
// - aarch64: FRAMELESS, DWARF
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "unwind_nofp",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("unwind.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.unwind_tables = .async,
|
||||
.omit_frame_pointer = true,
|
||||
}),
|
||||
// self-hosted lacks omit_frame_pointer support
|
||||
.use_llvm = true,
|
||||
});
|
||||
|
||||
if (builtin.os.tag != .freebsd) {
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
} else {
|
||||
test_step.dependOn(&exe.step);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ziglang/zig/issues/24522
|
||||
//// Unwinding through a C shared library without a frame pointer (libc)
|
||||
////
|
||||
//// getcontext version: libc
|
||||
////
|
||||
//// Unwind info type:
|
||||
//// - ELF: DWARF .eh_frame + .debug_frame
|
||||
//// - MachO: __unwind_info encodings:
|
||||
//// - x86_64: STACK_IMMD, STACK_IND
|
||||
//// - aarch64: FRAMELESS, DWARF
|
||||
//{
|
||||
// const c_shared_lib = b.addLibrary(.{
|
||||
// .linkage = .dynamic,
|
||||
// .name = "c_shared_lib",
|
||||
// .root_module = b.createModule(.{
|
||||
// .root_source_file = null,
|
||||
// .target = target,
|
||||
// .optimize = optimize,
|
||||
// .link_libc = true,
|
||||
// .strip = false,
|
||||
// }),
|
||||
// });
|
||||
|
||||
// if (target.result.os.tag == .windows)
|
||||
// c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)");
|
||||
|
||||
// c_shared_lib.root_module.addCSourceFile(.{
|
||||
// .file = b.path("shared_lib.c"),
|
||||
// .flags = &.{"-fomit-frame-pointer"},
|
||||
// });
|
||||
|
||||
// const exe = b.addExecutable(.{
|
||||
// .name = "shared_lib_unwind",
|
||||
// .root_module = b.createModule(.{
|
||||
// .root_source_file = b.path("shared_lib_unwind.zig"),
|
||||
// .target = target,
|
||||
// .optimize = optimize,
|
||||
// .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
|
||||
// .omit_frame_pointer = true,
|
||||
// }),
|
||||
// // zig objcopy doesn't support incremental binaries
|
||||
// .use_llvm = true,
|
||||
// });
|
||||
|
||||
// exe.root_module.linkLibrary(c_shared_lib);
|
||||
|
||||
// const run_cmd = b.addRunArtifact(exe);
|
||||
// test_step.dependOn(&run_cmd.step);
|
||||
|
||||
// // Separate debug info ELF file
|
||||
// if (target.result.ofmt == .elf) {
|
||||
// const filename = b.fmt("{s}_stripped", .{exe.out_filename});
|
||||
// const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{
|
||||
// .basename = filename, // set the name for the debuglink
|
||||
// .compress_debug = true,
|
||||
// .strip = .debug,
|
||||
// .extract_to_separate_file = true,
|
||||
// });
|
||||
|
||||
// const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename}));
|
||||
// run_stripped.addFileArg(stripped_exe.getOutput());
|
||||
// test_step.dependOn(&run_stripped.step);
|
||||
// }
|
||||
//}
|
||||
|
||||
// Unwinding without libc/posix
|
||||
//
|
||||
// No "getcontext" or "ucontext_t"
|
||||
const no_os_targets = [_]std.Target.Os.Tag{ .freestanding, .other };
|
||||
inline for (no_os_targets) |os_tag| {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "unwind_freestanding",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("unwind_freestanding.zig"),
|
||||
.target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = os_tag,
|
||||
}),
|
||||
.optimize = optimize,
|
||||
.unwind_tables = null,
|
||||
.omit_frame_pointer = false,
|
||||
}),
|
||||
// self-hosted lacks omit_frame_pointer support
|
||||
.use_llvm = true,
|
||||
});
|
||||
|
||||
// This "freestanding" binary is runnable because it invokes the
|
||||
// Linux exit syscall directly.
|
||||
if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
} else {
|
||||
test_step.dependOn(&exe.step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#ifndef LIB_API
|
||||
#define LIB_API
|
||||
#endif
|
||||
|
||||
__attribute__((noinline)) void frame1(
|
||||
void** expected,
|
||||
void** unwound,
|
||||
void (*frame2)(void** expected, void** unwound)) {
|
||||
expected[3] = __builtin_extract_return_addr(__builtin_return_address(0));
|
||||
frame2(expected, unwound);
|
||||
}
|
||||
|
||||
LIB_API void frame0(
|
||||
void** expected,
|
||||
void** unwound,
|
||||
void (*frame2)(void** expected, void** unwound)) {
|
||||
expected[4] = __builtin_extract_return_addr(__builtin_return_address(0));
|
||||
frame1(expected, unwound, frame2);
|
||||
}
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const debug = std.debug;
|
||||
const testing = std.testing;
|
||||
|
||||
noinline fn frame4(expected: *[5]usize, unwound: *[5]usize) void {
|
||||
expected[0] = @returnAddress();
|
||||
|
||||
var context: debug.ThreadContext = undefined;
|
||||
testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
|
||||
|
||||
const debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
|
||||
var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
|
||||
defer it.deinit();
|
||||
|
||||
for (unwound) |*addr| {
|
||||
if (it.next()) |return_address| addr.* = return_address;
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn frame3(expected: *[5]usize, unwound: *[5]usize) void {
|
||||
expected[1] = @returnAddress();
|
||||
frame4(expected, unwound);
|
||||
}
|
||||
|
||||
fn frame2(expected: *[5]usize, unwound: *[5]usize) callconv(.c) void {
|
||||
expected[2] = @returnAddress();
|
||||
frame3(expected, unwound);
|
||||
}
|
||||
|
||||
extern fn frame0(
|
||||
expected: *[5]usize,
|
||||
unwound: *[5]usize,
|
||||
frame_2: *const fn (expected: *[5]usize, unwound: *[5]usize) callconv(.c) void,
|
||||
) void;
|
||||
|
||||
pub fn main() !void {
|
||||
// Disabled until the DWARF unwinder bugs on .aarch64 are solved
|
||||
if (builtin.omit_frame_pointer and comptime builtin.target.os.tag.isDarwin() and builtin.cpu.arch == .aarch64) return;
|
||||
if (builtin.target.os.tag.isDarwin() and builtin.cpu.arch == .x86_64) return; // https://github.com/ziglang/zig/issues/21337
|
||||
|
||||
if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
|
||||
|
||||
var expected: [5]usize = undefined;
|
||||
var unwound: [5]usize = undefined;
|
||||
frame0(&expected, &unwound, &frame2);
|
||||
try testing.expectEqual(expected, unwound);
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const fatal = std.process.fatal;
|
||||
|
||||
noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[0] = @returnAddress();
|
||||
return std.debug.captureCurrentStackTrace(.{
|
||||
.first_address = @returnAddress(),
|
||||
.allow_unsafe_unwind = true,
|
||||
}, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
// Exercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
|
||||
if (builtin.target.ofmt != .c) {
|
||||
switch (builtin.target.cpu.arch) {
|
||||
.x86 => {
|
||||
if (builtin.omit_frame_pointer) {
|
||||
asm volatile (
|
||||
\\movl $3, %%ebx
|
||||
\\movl $1, %%ecx
|
||||
\\movl $2, %%edx
|
||||
\\movl $7, %%edi
|
||||
\\movl $6, %%esi
|
||||
\\movl $5, %%ebp
|
||||
::: .{ .ebx = true, .ecx = true, .edx = true, .edi = true, .esi = true, .ebp = true });
|
||||
} else {
|
||||
asm volatile (
|
||||
\\movl $3, %%ebx
|
||||
\\movl $1, %%ecx
|
||||
\\movl $2, %%edx
|
||||
\\movl $7, %%edi
|
||||
\\movl $6, %%esi
|
||||
::: .{ .ebx = true, .ecx = true, .edx = true, .edi = true, .esi = true });
|
||||
}
|
||||
},
|
||||
.x86_64 => {
|
||||
if (builtin.omit_frame_pointer) {
|
||||
asm volatile (
|
||||
\\movq $3, %%rbx
|
||||
\\movq $12, %%r12
|
||||
\\movq $13, %%r13
|
||||
\\movq $14, %%r14
|
||||
\\movq $15, %%r15
|
||||
\\movq $6, %%rbp
|
||||
::: .{ .rbx = true, .r12 = true, .r13 = true, .r14 = true, .r15 = true, .rbp = true });
|
||||
} else {
|
||||
asm volatile (
|
||||
\\movq $3, %%rbx
|
||||
\\movq $12, %%r12
|
||||
\\movq $13, %%r13
|
||||
\\movq $14, %%r14
|
||||
\\movq $15, %%r15
|
||||
::: .{ .rbx = true, .r12 = true, .r13 = true, .r14 = true, .r15 = true });
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
expected[1] = @returnAddress();
|
||||
return frame3(expected, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[2] = @returnAddress();
|
||||
|
||||
// Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
|
||||
// to exercise the stack-indirect encoding path
|
||||
var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
|
||||
_ = std.mem.doNotOptimizeAway(&pad);
|
||||
|
||||
return frame2(expected, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[3] = @returnAddress();
|
||||
return frame1(expected, addr_buf);
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
if (std.debug.cpu_context.Native == noreturn and builtin.omit_frame_pointer) {
|
||||
// Stack unwinding is impossible.
|
||||
return;
|
||||
}
|
||||
|
||||
var expected: [4]usize = undefined;
|
||||
var addr_buf: [4]usize = undefined;
|
||||
const trace = frame0(&expected, &addr_buf);
|
||||
// There may be *more* than 4 frames (due to the caller of `main`); that's okay.
|
||||
if (trace.index < 4) {
|
||||
fatal("expected at least 4 frames, got '{d}':\n{f}", .{ trace.index, &trace });
|
||||
}
|
||||
if (!std.mem.eql(usize, trace.instruction_addresses, &expected)) {
|
||||
const expected_trace: std.builtin.StackTrace = .{
|
||||
.index = 4,
|
||||
.instruction_addresses = &expected,
|
||||
};
|
||||
fatal("expected trace:\n{f}\nactual trace:\n{f}", .{ &expected_trace, &trace });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
//! Test StackIterator on 'freestanding' target. Based on unwind.zig.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[0] = @returnAddress();
|
||||
return std.debug.captureCurrentStackTrace(.{
|
||||
.first_address = @returnAddress(),
|
||||
.allow_unsafe_unwind = true,
|
||||
}, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[1] = @returnAddress();
|
||||
return frame3(expected, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[2] = @returnAddress();
|
||||
|
||||
// Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
|
||||
// to exercise the stack-indirect encoding path
|
||||
var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
|
||||
_ = std.mem.doNotOptimizeAway(&pad);
|
||||
|
||||
return frame2(expected, addr_buf);
|
||||
}
|
||||
|
||||
noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace {
|
||||
expected[3] = @returnAddress();
|
||||
return frame1(expected, addr_buf);
|
||||
}
|
||||
|
||||
/// No-OS entrypoint
|
||||
export fn _start() callconv(.withStackAlign(.c, 1)) noreturn {
|
||||
var expected: [4]usize = undefined;
|
||||
var addr_buf: [4]usize = undefined;
|
||||
const trace = frame0(&expected, &addr_buf);
|
||||
|
||||
// Since we can't print from this freestanding test, we'll just use the exit
|
||||
// code to communicate error conditions.
|
||||
|
||||
// Unlike `unwind.zig`, we can expect *exactly* 4 frames, as we are the
|
||||
// actual entry point and the frame pointer will be 0 on entry.
|
||||
if (trace.index != 4) exit(1);
|
||||
if (trace.instruction_addresses[0] != expected[0]) exit(2);
|
||||
if (trace.instruction_addresses[1] != expected[1]) exit(3);
|
||||
if (trace.instruction_addresses[2] != expected[2]) exit(4);
|
||||
if (trace.instruction_addresses[3] != expected[3]) exit(5);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fn exit(code: u8) noreturn {
|
||||
// We are intentionally compiling with the target OS being "freestanding" to
|
||||
// exercise std.debug, but we still need to exit the process somehow; so do
|
||||
// the appropriate x86_64-linux syscall.
|
||||
asm volatile (
|
||||
\\movl $60, %%eax
|
||||
\\syscall
|
||||
:
|
||||
: [code] "{edi}" (code),
|
||||
: .{ .edi = true, .eax = true });
|
||||
|
||||
unreachable;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue