diff options
| author | Vexu <15308111+Vexu@users.noreply.github.com> | 2019-07-17 01:20:59 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-07-17 01:20:59 +0300 |
| commit | f8e753e19c013cc605a951e5038b4a26099aa135 (patch) | |
| tree | 3cbb2ce1b8815bc565344e23a39e3d39505cfd31 /std/debug.zig | |
| parent | 0063953d1634ce770ce88519c66e3956832ceb7e (diff) | |
| parent | 158e2312ea5f680b7c8598ef578aefb6cbdd3372 (diff) | |
| download | zig-f8e753e19c013cc605a951e5038b4a26099aa135.tar.gz zig-f8e753e19c013cc605a951e5038b4a26099aa135.zip | |
Merge branch 'master' into comment-in-array
Diffstat (limited to 'std/debug.zig')
| -rw-r--r-- | std/debug.zig | 98 |
1 files changed, 88 insertions, 10 deletions
diff --git a/std/debug.zig b/std/debug.zig index 223f93d1ad..d81e62901a 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { }; } +/// Tries to print the stack trace starting from the supplied base pointer to stderr, +/// unbuffered, and ignores any error returned. +/// TODO multithreaded awareness +pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { + const stderr = getStderrStream() catch return; + if (builtin.strip_debug_info) { + stderr.print("Unable to dump stack trace: debug info stripped\n") catch return; + return; + } + const debug_info = getSelfDebugInfo() catch |err| { + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + const tty_color = wantTtyColor(); + printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return; + const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*; + printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return; + var it = StackIterator{ + .first_addr = null, + .fp = bp, + }; + while (it.next()) |return_address| { + printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return; + } +} + /// Returns a slice with the same pointer as addresses, with a potentially smaller len. /// On Windows, when first_address is not null, we ask for at least 32 stack frames, /// and then try to find the first address. If addresses.len is more than 32, we @@ -173,7 +199,7 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { /// in its heuristics. /// Inside a test block, it is best to use the `std.testing` module rather /// than this function, because this function may not detect a test failure -/// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert +/// in ReleaseFast and ReleaseSmall mode. Outside of a test block, this assert /// function is the correct function to use. pub fn assert(ok: bool) void { if (!ok) unreachable; // assertion failure @@ -800,15 +826,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { defer self_file.close(); const coff_obj = try allocator.create(coff.Coff); - coff_obj.* = coff.Coff{ - .in_file = self_file, - .allocator = allocator, - .coff_header = undefined, - .pe_header = undefined, - .sections = undefined, - .guid = undefined, - .age = undefined, - }; + coff_obj.* = coff.Coff.init(allocator, self_file); var di = DebugInfo{ .coff = coff_obj, @@ -2291,3 +2309,63 @@ fn getDebugInfoAllocator() *mem.Allocator { debug_info_allocator = &debug_info_arena_allocator.allocator; return &debug_info_arena_allocator.allocator; } + +/// Whether or not the current target can print useful debug information when a segfault occurs. +pub const have_segfault_handling_support = (builtin.arch == builtin.Arch.x86_64 and builtin.os == .linux) or builtin.os == .windows; + +/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault"); +pub fn attachSegfaultHandler() void { + if (!have_segfault_handling_support) { + @compileError("segfault handler not supported for this target"); + } + switch (builtin.os) { + .linux => { + var act = os.Sigaction{ + .sigaction = handleSegfaultLinux, + .mask = os.empty_sigset, + .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND), + }; + + os.sigaction(os.SIGSEGV, &act, null); + }, + .windows => { + _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); + }, + else => unreachable, + } +} + +extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn { + // Reset to the default handler so that if a segfault happens in this handler it will crash + // the process. Also when this handler returns, the original instruction will be repeated + // and the resulting segfault will crash the process rather than continually dump stack traces. + var act = os.Sigaction{ + .sigaction = os.SIG_DFL, + .mask = os.empty_sigset, + .flags = 0, + }; + os.sigaction(os.SIGSEGV, &act, null); + + const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); + const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]); + const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]); + const addr = @ptrToInt(info.fields.sigfault.addr); + std.debug.warn("Segmentation fault at address 0x{x}\n", addr); + dumpStackTraceFromBase(bp, ip); + + // 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. + os.abort(); +} + +stdcallcc fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) c_long { + const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress); + switch (info.ExceptionRecord.ExceptionCode) { + windows.EXCEPTION_DATATYPE_MISALIGNMENT => panicExtra(null, exception_address, "Unaligned Memory Access"), + windows.EXCEPTION_ACCESS_VIOLATION => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", info.ExceptionRecord.ExceptionInformation[1]), + windows.EXCEPTION_ILLEGAL_INSTRUCTION => panicExtra(null, exception_address, "Illegal Instruction"), + windows.EXCEPTION_STACK_OVERFLOW => panicExtra(null, exception_address, "Stack Overflow"), + else => return windows.EXCEPTION_CONTINUE_SEARCH, + } +} |
