| ... | @@ -334,6 +334,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize | ... | @@ -334,6 +334,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize |
| 334 | resetSegfaultHandler(); | 334 | resetSegfaultHandler(); |
| 335 | } | 335 | } |
| 336 | | 336 | |
| | 337 | // Note there is similar logic in handleSegfaultPosix and handleSegfaultWindowsExtra. |
| 337 | nosuspend switch (panic_stage) { | 338 | nosuspend switch (panic_stage) { |
| 338 | 0 => { | 339 | 0 => { |
| 339 | panic_stage = 1; | 340 | panic_stage = 1; |
| ... | @@ -359,16 +360,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize | ... | @@ -359,16 +360,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize |
| 359 | dumpCurrentStackTrace(first_trace_addr); | 360 | dumpCurrentStackTrace(first_trace_addr); |
| 360 | } | 361 | } |
| 361 | | 362 | |
| 362 | if (panicking.fetchSub(1, .SeqCst) != 1) { | 363 | waitForOtherThreadToFinishPanicking(); |
| 363 | // Another thread is panicking, wait for the last one to finish | | |
| 364 | // and call abort() | | |
| 365 | if (builtin.single_threaded) unreachable; | | |
| 366 | | | |
| 367 | // Sleep forever without hammering the CPU | | |
| 368 | var futex = std.atomic.Atomic(u32).init(0); | | |
| 369 | while (true) std.Thread.Futex.wait(&futex, 0); | | |
| 370 | unreachable; | | |
| 371 | } | | |
| 372 | }, | 364 | }, |
| 373 | 1 => { | 365 | 1 => { |
| 374 | panic_stage = 2; | 366 | panic_stage = 2; |
| ... | @@ -387,6 +379,20 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize | ... | @@ -387,6 +379,20 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize |
| 387 | os.abort(); | 379 | os.abort(); |
| 388 | } | 380 | } |
| 389 | | 381 | |
| | 382 | /// Must be called only after adding 1 to `panicking`. There are three callsites. |
| | 383 | fn waitForOtherThreadToFinishPanicking() void { |
| | 384 | if (panicking.fetchSub(1, .SeqCst) != 1) { |
| | 385 | // Another thread is panicking, wait for the last one to finish |
| | 386 | // and call abort() |
| | 387 | if (builtin.single_threaded) unreachable; |
| | 388 | |
| | 389 | // Sleep forever without hammering the CPU |
| | 390 | var futex = std.atomic.Atomic(u32).init(0); |
| | 391 | while (true) std.Thread.Futex.wait(&futex, 0); |
| | 392 | unreachable; |
| | 393 | } |
| | 394 | } |
| | 395 | |
| 390 | pub fn writeStackTrace( | 396 | pub fn writeStackTrace( |
| 391 | stack_trace: std.builtin.StackTrace, | 397 | stack_trace: std.builtin.StackTrace, |
| 392 | out_stream: anytype, | 398 | out_stream: anytype, |
| ... | @@ -1971,17 +1977,41 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any | ... | @@ -1971,17 +1977,41 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any |
| 1971 | else => unreachable, | 1977 | else => unreachable, |
| 1972 | }; | 1978 | }; |
| 1973 | | 1979 | |
| 1974 | // Don't use std.debug.print() as stderr_mutex may still be locked. | 1980 | nosuspend switch (panic_stage) { |
| 1975 | nosuspend { | 1981 | 0 => { |
| 1976 | const stderr = io.getStdErr().writer(); | 1982 | panic_stage = 1; |
| 1977 | _ = switch (sig) { | 1983 | _ = panicking.fetchAdd(1, .SeqCst); |
| 1978 | os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}), | 1984 | |
| 1979 | os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}), | 1985 | { |
| 1980 | os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}), | 1986 | panic_mutex.lock(); |
| 1981 | os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}), | 1987 | defer panic_mutex.unlock(); |
| 1982 | else => unreachable, | 1988 | |
| 1983 | } catch os.abort(); | 1989 | dumpSegfaultInfoPosix(sig, addr, ctx_ptr); |
| 1984 | } | 1990 | } |
| | 1991 | |
| | 1992 | waitForOtherThreadToFinishPanicking(); |
| | 1993 | }, |
| | 1994 | else => { |
| | 1995 | // panic mutex already locked |
| | 1996 | dumpSegfaultInfoPosix(sig, addr, ctx_ptr); |
| | 1997 | }, |
| | 1998 | }; |
| | 1999 | |
| | 2000 | // We cannot allow the signal handler to return because when it runs the original instruction |
| | 2001 | // again, the memory may be mapped and undefined behavior would occur rather than repeating |
| | 2002 | // the segfault. So we simply abort here. |
| | 2003 | os.abort(); |
| | 2004 | } |
| | 2005 | |
| | 2006 | fn dumpSegfaultInfoPosix(sig: i32, addr: usize, ctx_ptr: ?*const anyopaque) void { |
| | 2007 | const stderr = io.getStdErr().writer(); |
| | 2008 | _ = switch (sig) { |
| | 2009 | os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}), |
| | 2010 | os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}), |
| | 2011 | os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}), |
| | 2012 | os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}), |
| | 2013 | else => unreachable, |
| | 2014 | } catch os.abort(); |
| 1985 | | 2015 | |
| 1986 | switch (native_arch) { | 2016 | switch (native_arch) { |
| 1987 | .x86 => { | 2017 | .x86 => { |
| ... | @@ -2033,11 +2063,6 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any | ... | @@ -2033,11 +2063,6 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any |
| 2033 | }, | 2063 | }, |
| 2034 | else => {}, | 2064 | else => {}, |
| 2035 | } | 2065 | } |
| 2036 | | | |
| 2037 | // We cannot allow the signal handler to return because when it runs the original instruction | | |
| 2038 | // again, the memory may be mapped and undefined behavior would occur rather than repeating | | |
| 2039 | // the segfault. So we simply abort here. | | |
| 2040 | os.abort(); | | |
| 2041 | } | 2066 | } |
| 2042 | | 2067 | |
| 2043 | fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long { | 2068 | fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long { |
| ... | @@ -2050,27 +2075,36 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WIN | ... | @@ -2050,27 +2075,36 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WIN |
| 2050 | } | 2075 | } |
| 2051 | } | 2076 | } |
| 2052 | | 2077 | |
| 2053 | // zig won't let me use an anon enum here https://github.com/ziglang/zig/issues/3707 | 2078 | fn handleSegfaultWindowsExtra( |
| 2054 | fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u8, comptime format: ?[]const u8) noreturn { | 2079 | info: *windows.EXCEPTION_POINTERS, |
| | 2080 | msg: u8, |
| | 2081 | label: ?[]const u8, |
| | 2082 | ) noreturn { |
| 2055 | const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress); | 2083 | const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress); |
| 2056 | if (@hasDecl(windows, "CONTEXT")) { | 2084 | if (@hasDecl(windows, "CONTEXT")) { |
| 2057 | const regs = info.ContextRecord.getRegs(); | 2085 | nosuspend switch (panic_stage) { |
| 2058 | // Don't use std.debug.print() as stderr_mutex may still be locked. | 2086 | 0 => { |
| 2059 | nosuspend { | 2087 | panic_stage = 1; |
| 2060 | const stderr = io.getStdErr().writer(); | 2088 | _ = panicking.fetchAdd(1, .SeqCst); |
| 2061 | _ = switch (msg) { | | |
| 2062 | 0 => stderr.print("{s}\n", .{format.?}), | | |
| 2063 | 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), | | |
| 2064 | 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}), | | |
| 2065 | else => unreachable, | | |
| 2066 | } catch os.abort(); | | |
| 2067 | } | | |
| 2068 | | 2089 | |
| 2069 | dumpStackTraceFromBase(regs.bp, regs.ip); | 2090 | { |
| | 2091 | panic_mutex.lock(); |
| | 2092 | defer panic_mutex.unlock(); |
| | 2093 | |
| | 2094 | dumpSegfaultInfoWindows(info, msg, label); |
| | 2095 | } |
| | 2096 | |
| | 2097 | waitForOtherThreadToFinishPanicking(); |
| | 2098 | }, |
| | 2099 | else => { |
| | 2100 | // panic mutex already locked |
| | 2101 | dumpSegfaultInfoWindows(info, msg, label); |
| | 2102 | }, |
| | 2103 | }; |
| 2070 | os.abort(); | 2104 | os.abort(); |
| 2071 | } else { | 2105 | } else { |
| 2072 | switch (msg) { | 2106 | switch (msg) { |
| 2073 | 0 => panicImpl(null, exception_address, format.?), | 2107 | 0 => panicImpl(null, exception_address, "{s}", label.?), |
| 2074 | 1 => { | 2108 | 1 => { |
| 2075 | const format_item = "Segmentation fault at address 0x{x}"; | 2109 | const format_item = "Segmentation fault at address 0x{x}"; |
| 2076 | var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large | 2110 | var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large |
| ... | @@ -2083,6 +2117,19 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u | ... | @@ -2083,6 +2117,19 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u |
| 2083 | } | 2117 | } |
| 2084 | } | 2118 | } |
| 2085 | | 2119 | |
| | 2120 | fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void { |
| | 2121 | const regs = info.ContextRecord.getRegs(); |
| | 2122 | const stderr = io.getStdErr().writer(); |
| | 2123 | _ = switch (msg) { |
| | 2124 | 0 => stderr.print("{s}\n", .{label.?}), |
| | 2125 | 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), |
| | 2126 | 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}), |
| | 2127 | else => unreachable, |
| | 2128 | } catch os.abort(); |
| | 2129 | |
| | 2130 | dumpStackTraceFromBase(regs.bp, regs.ip); |
| | 2131 | } |
| | 2132 | |
| 2086 | pub fn dumpStackPointerAddr(prefix: []const u8) void { | 2133 | pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 2087 | const sp = asm ("" | 2134 | const sp = asm ("" |
| 2088 | : [argc] "={rsp}" (-> usize), | 2135 | : [argc] "={rsp}" (-> usize), |