182 lines
6.3 KiB
Zig
182 lines
6.3 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const os = std.os;
|
|
const windows = std.os.windows;
|
|
const core = @import("core.zig");
|
|
const lua = @import("lua");
|
|
|
|
fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
|
|
switch (info.ExceptionRecord.ExceptionCode) {
|
|
windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, 0, "Unaligned Memory Access"),
|
|
windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, 1, null),
|
|
windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, 2, null),
|
|
windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, 0, "Stack Overflow"),
|
|
else => return windows.EXCEPTION_CONTINUE_SEARCH,
|
|
}
|
|
}
|
|
|
|
var panic_mutex = std.Thread.Mutex{};
|
|
var panicking = std.atomic.Value(u8).init(0);
|
|
threadlocal var panic_stage: usize = 0;
|
|
|
|
fn waitForOtherThreadToFinishPanicking() void {
|
|
if (panicking.fetchSub(1, .seq_cst) != 1) {
|
|
// Another thread is panicking, wait for the last one to finish
|
|
// and call abort()
|
|
if (builtin.single_threaded) unreachable;
|
|
|
|
// Sleep forever without hammering the CPU
|
|
var futex = std.atomic.Value(u32).init(0);
|
|
while (true) std.Thread.Futex.wait(&futex, 0);
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
fn handleSegfaultWindowsExtra(
|
|
info: *windows.EXCEPTION_POINTERS,
|
|
msg: u8,
|
|
label: ?[]const u8,
|
|
) noreturn {
|
|
const exception_address = @intFromPtr(info.ExceptionRecord.ExceptionAddress);
|
|
core.engine_logs("PANIC!!");
|
|
core.forceFlush();
|
|
|
|
if (!@hasDecl(windows, "CONTEXT")) {
|
|
switch (msg) {
|
|
0 => {
|
|
dumpSegfaultInfoWindows(info, msg, label);
|
|
std.posix.abort();
|
|
},
|
|
1 => {
|
|
const format_item = "Segmentation fault at address 0x{x}";
|
|
var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large
|
|
const to_print = std.fmt.bufPrint(buf[0..buf.len], format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable;
|
|
std.debug.panicImpl(null, exception_address, to_print);
|
|
},
|
|
2 => std.debug.panicImpl(null, exception_address, "Illegal Instruction"),
|
|
else => unreachable,
|
|
}
|
|
} else {
|
|
dumpSegfaultInfoWindows(info, msg, label);
|
|
std.posix.abort();
|
|
}
|
|
}
|
|
|
|
fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {
|
|
const stderr = std.io.getStdErr().writer();
|
|
_ = switch (msg) {
|
|
0 => stderr.print("{s}\n", .{label.?}),
|
|
1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
|
|
2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}),
|
|
else => unreachable,
|
|
} catch std.posix.abort();
|
|
|
|
std.debug.dumpStackTraceFromBase(info.ContextRecord);
|
|
}
|
|
|
|
pub fn dumpStackPointerAddr(prefix: []const u8) void {
|
|
const sp = asm (""
|
|
: [argc] "={rsp}" (-> usize),
|
|
);
|
|
std.debug.print("{} sp = 0x{x}\n", .{ prefix, sp });
|
|
}
|
|
|
|
fn dumpSegfaultInfoPosix(sig: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
|
|
const stderr = std.io.getStdErr().writer();
|
|
_ = switch (sig) {
|
|
std.posix.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
|
|
std.posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
|
|
std.posix.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
|
|
std.posix.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
|
|
else => unreachable,
|
|
} catch std.posix.abort();
|
|
|
|
switch (builtin.cpu.arch) {
|
|
.x86,
|
|
.x86_64,
|
|
.arm,
|
|
.aarch64,
|
|
=> {
|
|
const ctx: *std.c.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
|
|
std.debug.dumpStackTraceFromBase(ctx);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
fn handleSegfaultPosix(sig: i32, info: *const std.posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.C) noreturn {
|
|
core.engine_logs("PANIC!!");
|
|
core.forceFlush();
|
|
resetSegfaultHandler();
|
|
|
|
const addr = switch (builtin.os.tag) {
|
|
.linux => @intFromPtr(info.fields.sigfault.addr),
|
|
.freebsd, .macos => @intFromPtr(info.addr),
|
|
.netbsd => @intFromPtr(info.info.reason.fault.addr),
|
|
.openbsd => @intFromPtr(info.data.fault.addr),
|
|
.solaris => @intFromPtr(info.reason.fault.addr),
|
|
else => unreachable,
|
|
};
|
|
|
|
nosuspend switch (panic_stage) {
|
|
0 => {
|
|
panic_stage = 1;
|
|
_ = panicking.fetchAdd(1, .seq_cst);
|
|
|
|
{
|
|
panic_mutex.lock();
|
|
defer panic_mutex.unlock();
|
|
|
|
dumpSegfaultInfoPosix(sig, addr, ctx_ptr);
|
|
}
|
|
|
|
waitForOtherThreadToFinishPanicking();
|
|
},
|
|
else => {
|
|
// panic mutex already locked
|
|
dumpSegfaultInfoPosix(sig, addr, ctx_ptr);
|
|
},
|
|
};
|
|
|
|
// We cannot allow the signal handler to return because when it runs the original instruction
|
|
// again, the memory may be mapped and undefined behavior would occur rather than repeating
|
|
// the segfault. So we simply abort here.
|
|
std.posix.abort();
|
|
}
|
|
//
|
|
//
|
|
fn resetSegfaultHandler() void {
|
|
if (builtin.os.tag == .windows) {
|
|
if (windows_segfault_handle) |handle| {
|
|
_ = windows.kernel32.RemoveVectoredExceptionHandler(handle);
|
|
windows_segfault_handle = null;
|
|
}
|
|
return;
|
|
}
|
|
|
|
var act = std.c.Sigaction{
|
|
.handler = .{ .handler = std.posix.SIG.DFL },
|
|
.mask = std.posix.empty_sigset,
|
|
.flags = 0,
|
|
};
|
|
// To avoid a double-panic, do nothing if an error happens here.
|
|
std.debug.updateSegfaultHandler(&act);
|
|
}
|
|
|
|
// my custom version of segfault handler.
|
|
var windows_segfault_handle: ?windows.HANDLE = null;
|
|
|
|
pub fn attachSegfaultHandler() void {
|
|
if (builtin.os.tag == .windows) {
|
|
windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
|
|
return;
|
|
}
|
|
var act = std.c.Sigaction{
|
|
.handler = .{ .sigaction = handleSegfaultPosix },
|
|
.mask = std.posix.empty_sigset,
|
|
.flags = (std.posix.SA.SIGINFO | std.posix.SA.RESTART | std.posix.SA.RESETHAND),
|
|
};
|
|
|
|
std.debug.updateSegfaultHandler(&act);
|
|
}
|