aboutsummaryrefslogtreecommitdiff
path: root/std/debug.zig
diff options
context:
space:
mode:
authorVexu <15308111+Vexu@users.noreply.github.com>2019-07-17 01:20:59 +0300
committerGitHub <noreply@github.com>2019-07-17 01:20:59 +0300
commitf8e753e19c013cc605a951e5038b4a26099aa135 (patch)
tree3cbb2ce1b8815bc565344e23a39e3d39505cfd31 /std/debug.zig
parent0063953d1634ce770ce88519c66e3956832ceb7e (diff)
parent158e2312ea5f680b7c8598ef578aefb6cbdd3372 (diff)
downloadzig-f8e753e19c013cc605a951e5038b4a26099aa135.tar.gz
zig-f8e753e19c013cc605a951e5038b4a26099aa135.zip
Merge branch 'master' into comment-in-array
Diffstat (limited to 'std/debug.zig')
-rw-r--r--std/debug.zig98
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,
+ }
+}