| ... | @@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ... | @@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 135 | | 135 | |
| 136 | pub const StackTraceContext = blk: { | 136 | pub const StackTraceContext = blk: { |
| 137 | if (native_os == .windows) { | 137 | if (native_os == .windows) { |
| 138 | break :blk @typeInfo(@TypeOf(os.windows.CONTEXT.getRegs)).Fn.return_type.?; | 138 | break :blk std.os.windows.CONTEXT; |
| 139 | } else if (@hasDecl(os.system, "ucontext_t")) { | 139 | } else if (@hasDecl(os.system, "ucontext_t")) { |
| 140 | break :blk os.ucontext_t; | 140 | break :blk os.ucontext_t; |
| 141 | } else { | 141 | } else { |
| ... | @@ -166,7 +166,14 @@ pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void { | ... | @@ -166,7 +166,14 @@ pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void { |
| 166 | }; | 166 | }; |
| 167 | const tty_config = io.tty.detectConfig(io.getStdErr()); | 167 | const tty_config = io.tty.detectConfig(io.getStdErr()); |
| 168 | if (native_os == .windows) { | 168 | if (native_os == .windows) { |
| 169 | writeCurrentStackTraceWindows(stderr, debug_info, tty_config, context.ip) catch return; | 169 | // On x86_64 and aarch64, the stack will be unwound using RtlVirtualUnwind using the context |
| | 170 | // provided by the exception handler. On x86, RtlVirtualUnwind doesn't exist. Instead, a new backtrace |
| | 171 | // will be captured and frames prior to the exception will be filtered. |
| | 172 | // The caveat is that RtlCaptureStackBackTrace does not include the KiUserExceptionDispatcher frame, |
| | 173 | // which is where the IP in `context` points to, so it can't be used as start_addr. |
| | 174 | // Instead, start_addr is recovered from the stack. |
| | 175 | const start_addr = if (builtin.cpu.arch == .x86) @as(*const usize, @ptrFromInt(context.getRegs().bp + 4)).* else null; |
| | 176 | writeStackTraceWindows(stderr, debug_info, tty_config, context, start_addr) catch return; |
| 170 | return; | 177 | return; |
| 171 | } | 178 | } |
| 172 | | 179 | |
| ... | @@ -196,12 +203,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT | ... | @@ -196,12 +203,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT |
| 196 | if (native_os == .windows) { | 203 | if (native_os == .windows) { |
| 197 | const addrs = stack_trace.instruction_addresses; | 204 | const addrs = stack_trace.instruction_addresses; |
| 198 | const first_addr = first_address orelse { | 205 | const first_addr = first_address orelse { |
| 199 | stack_trace.index = walkStackWindows(addrs[0..]); | 206 | stack_trace.index = walkStackWindows(addrs[0..], null); |
| 200 | return; | 207 | return; |
| 201 | }; | 208 | }; |
| 202 | var addr_buf_stack: [32]usize = undefined; | 209 | var addr_buf_stack: [32]usize = undefined; |
| 203 | const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; | 210 | const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; |
| 204 | const n = walkStackWindows(addr_buf[0..]); | 211 | const n = walkStackWindows(addr_buf[0..], null); |
| 205 | const first_index = for (addr_buf[0..n], 0..) |addr, i| { | 212 | const first_index = for (addr_buf[0..n], 0..) |addr, i| { |
| 206 | if (addr == first_addr) { | 213 | if (addr == first_addr) { |
| 207 | break i; | 214 | break i; |
| ... | @@ -218,7 +225,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT | ... | @@ -218,7 +225,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT |
| 218 | } | 225 | } |
| 219 | stack_trace.index = slice.len; | 226 | stack_trace.index = slice.len; |
| 220 | } else { | 227 | } else { |
| 221 | // TODO: This should use the dwarf unwinder if it's available | 228 | // TODO: This should use the DWARF unwinder if .eh_frame_hdr is available (so that full debug info parsing isn't required) |
| 222 | var it = StackIterator.init(first_address, null); | 229 | var it = StackIterator.init(first_address, null); |
| 223 | defer it.deinit(); | 230 | defer it.deinit(); |
| 224 | for (stack_trace.instruction_addresses, 0..) |*addr, i| { | 231 | for (stack_trace.instruction_addresses, 0..) |*addr, i| { |
| ... | @@ -415,10 +422,18 @@ pub fn writeStackTrace( | ... | @@ -415,10 +422,18 @@ pub fn writeStackTrace( |
| 415 | | 422 | |
| 416 | inline fn getContext(context: *StackTraceContext) bool { | 423 | inline fn getContext(context: *StackTraceContext) bool { |
| 417 | if (native_os == .windows) { | 424 | if (native_os == .windows) { |
| 418 | @compileError("Syscall please!"); | 425 | context.* = std.mem.zeroes(windows.CONTEXT); |
| | 426 | windows.ntdll.RtlCaptureContext(context); |
| | 427 | return true; |
| 419 | } | 428 | } |
| 420 | | 429 | |
| 421 | return @hasDecl(os.system, "getcontext") and os.system.getcontext(context) == 0; | 430 | const supports_getcontext = @hasDecl(os.system, "getcontext") and |
| | 431 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { |
| | 432 | .x86, .x86_64 => true, |
| | 433 | else => false, |
| | 434 | }); |
| | 435 | |
| | 436 | return supports_getcontext and os.system.getcontext(context) == 0; |
| 422 | } | 437 | } |
| 423 | | 438 | |
| 424 | pub const StackIterator = struct { | 439 | pub const StackIterator = struct { |
| ... | @@ -431,6 +446,7 @@ pub const StackIterator = struct { | ... | @@ -431,6 +446,7 @@ pub const StackIterator = struct { |
| 431 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer). | 446 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer). |
| 432 | debug_info: ?*DebugInfo, | 447 | debug_info: ?*DebugInfo, |
| 433 | dwarf_context: if (supports_context) DW.UnwindContext else void = undefined, | 448 | dwarf_context: if (supports_context) DW.UnwindContext else void = undefined, |
| | 449 | |
| 434 | pub const supports_context = @hasDecl(os.system, "ucontext_t") and | 450 | pub const supports_context = @hasDecl(os.system, "ucontext_t") and |
| 435 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | 451 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { |
| 436 | .mips, .mipsel, .mips64, .mips64el, .riscv64 => false, | 452 | .mips, .mipsel, .mips64, .mips64el, .riscv64 => false, |
| ... | @@ -548,19 +564,20 @@ pub const StackIterator = struct { | ... | @@ -548,19 +564,20 @@ pub const StackIterator = struct { |
| 548 | } | 564 | } |
| 549 | } | 565 | } |
| 550 | | 566 | |
| 551 | fn next_dwarf(self: *StackIterator) !void { | 567 | fn next_dwarf(self: *StackIterator) !usize { |
| 552 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); | 568 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); |
| 553 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { | 569 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 554 | self.dwarf_context.reg_ctx.eh_frame = true; | 570 | self.dwarf_context.reg_ctx.eh_frame = true; |
| 555 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; | 571 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; |
| 556 | try di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address); | 572 | return di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address); |
| 557 | } else return error.MissingDebugInfo; | 573 | } else return error.MissingDebugInfo; |
| 558 | } | 574 | } |
| 559 | | 575 | |
| 560 | fn next_internal(self: *StackIterator) ?usize { | 576 | fn next_internal(self: *StackIterator) ?usize { |
| 561 | if (supports_context and self.debug_info != null) { | 577 | if (supports_context and self.debug_info != null) { |
| 562 | if (self.next_dwarf()) |_| { | 578 | if (self.dwarf_context.pc == 0) return null; |
| 563 | return self.dwarf_context.pc; | 579 | if (self.next_dwarf()) |return_address| { |
| | 580 | return return_address; |
| 564 | } else |err| { | 581 | } else |err| { |
| 565 | if (err != error.MissingFDE) print("DWARF unwind error: {}\n", .{err}); | 582 | if (err != error.MissingFDE) print("DWARF unwind error: {}\n", .{err}); |
| 566 | | 583 | |
| ... | @@ -611,12 +628,13 @@ pub fn writeCurrentStackTrace( | ... | @@ -611,12 +628,13 @@ pub fn writeCurrentStackTrace( |
| 611 | tty_config: io.tty.Config, | 628 | tty_config: io.tty.Config, |
| 612 | start_addr: ?usize, | 629 | start_addr: ?usize, |
| 613 | ) !void { | 630 | ) !void { |
| | 631 | var context: StackTraceContext = undefined; |
| | 632 | const has_context = getContext(&context); |
| 614 | if (native_os == .windows) { | 633 | if (native_os == .windows) { |
| 615 | return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); | 634 | return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr); |
| 616 | } | 635 | } |
| 617 | | 636 | |
| 618 | var context: StackTraceContext = undefined; | 637 | var it = (if (has_context) blk: { |
| 619 | var it = (if (getContext(&context)) blk: { | | |
| 620 | break :blk StackIterator.initWithContext(start_addr, debug_info, &context) catch null; | 638 | break :blk StackIterator.initWithContext(start_addr, debug_info, &context) catch null; |
| 621 | } else null) orelse StackIterator.init(start_addr, null); | 639 | } else null) orelse StackIterator.init(start_addr, null); |
| 622 | defer it.deinit(); | 640 | defer it.deinit(); |
| ... | @@ -632,7 +650,7 @@ pub fn writeCurrentStackTrace( | ... | @@ -632,7 +650,7 @@ pub fn writeCurrentStackTrace( |
| 632 | } | 650 | } |
| 633 | } | 651 | } |
| 634 | | 652 | |
| 635 | pub noinline fn walkStackWindows(addresses: []usize) usize { | 653 | pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize { |
| 636 | if (builtin.cpu.arch == .x86) { | 654 | if (builtin.cpu.arch == .x86) { |
| 637 | // RtlVirtualUnwind doesn't exist on x86 | 655 | // RtlVirtualUnwind doesn't exist on x86 |
| 638 | return windows.ntdll.RtlCaptureStackBackTrace(0, addresses.len, @as(**anyopaque, @ptrCast(addresses.ptr)), null); | 656 | return windows.ntdll.RtlCaptureStackBackTrace(0, addresses.len, @as(**anyopaque, @ptrCast(addresses.ptr)), null); |
| ... | @@ -640,8 +658,13 @@ pub noinline fn walkStackWindows(addresses: []usize) usize { | ... | @@ -640,8 +658,13 @@ pub noinline fn walkStackWindows(addresses: []usize) usize { |
| 640 | | 658 | |
| 641 | const tib = @as(*const windows.NT_TIB, @ptrCast(&windows.teb().Reserved1)); | 659 | const tib = @as(*const windows.NT_TIB, @ptrCast(&windows.teb().Reserved1)); |
| 642 | | 660 | |
| 643 | var context: windows.CONTEXT = std.mem.zeroes(windows.CONTEXT); | 661 | var context: windows.CONTEXT = undefined; |
| 644 | windows.ntdll.RtlCaptureContext(&context); | 662 | if (existing_context) |context_ptr| { |
| | 663 | context = context_ptr.*; |
| | 664 | } else { |
| | 665 | context = std.mem.zeroes(windows.CONTEXT); |
| | 666 | windows.ntdll.RtlCaptureContext(&context); |
| | 667 | } |
| 645 | | 668 | |
| 646 | var i: usize = 0; | 669 | var i: usize = 0; |
| 647 | var image_base: usize = undefined; | 670 | var image_base: usize = undefined; |
| ... | @@ -683,14 +706,15 @@ pub noinline fn walkStackWindows(addresses: []usize) usize { | ... | @@ -683,14 +706,15 @@ pub noinline fn walkStackWindows(addresses: []usize) usize { |
| 683 | return i; | 706 | return i; |
| 684 | } | 707 | } |
| 685 | | 708 | |
| 686 | pub fn writeCurrentStackTraceWindows( | 709 | pub fn writeStackTraceWindows( |
| 687 | out_stream: anytype, | 710 | out_stream: anytype, |
| 688 | debug_info: *DebugInfo, | 711 | debug_info: *DebugInfo, |
| 689 | tty_config: io.tty.Config, | 712 | tty_config: io.tty.Config, |
| | 713 | context: *const windows.CONTEXT, |
| 690 | start_addr: ?usize, | 714 | start_addr: ?usize, |
| 691 | ) !void { | 715 | ) !void { |
| 692 | var addr_buf: [1024]usize = undefined; | 716 | var addr_buf: [1024]usize = undefined; |
| 693 | const n = walkStackWindows(addr_buf[0..]); | 717 | const n = walkStackWindows(addr_buf[0..], context); |
| 694 | const addrs = addr_buf[0..n]; | 718 | const addrs = addr_buf[0..n]; |
| 695 | var start_i: usize = if (start_addr) |saddr| blk: { | 719 | var start_i: usize = if (start_addr) |saddr| blk: { |
| 696 | for (addrs, 0..) |addr, i| { | 720 | for (addrs, 0..) |addr, i| { |
| ... | @@ -2164,16 +2188,15 @@ fn handleSegfaultWindowsExtra( | ... | @@ -2164,16 +2188,15 @@ fn handleSegfaultWindowsExtra( |
| 2164 | } | 2188 | } |
| 2165 | | 2189 | |
| 2166 | fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void { | 2190 | fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void { |
| 2167 | const regs = info.ContextRecord.getRegs(); | | |
| 2168 | const stderr = io.getStdErr().writer(); | 2191 | const stderr = io.getStdErr().writer(); |
| 2169 | _ = switch (msg) { | 2192 | _ = switch (msg) { |
| 2170 | 0 => stderr.print("{s}\n", .{label.?}), | 2193 | 0 => stderr.print("{s}\n", .{label.?}), |
| 2171 | 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), | 2194 | 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), |
| 2172 | 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}), | 2195 | 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}), |
| 2173 | else => unreachable, | 2196 | else => unreachable, |
| 2174 | } catch os.abort(); | 2197 | } catch os.abort(); |
| 2175 | | 2198 | |
| 2176 | dumpStackTraceFromBase(&regs); | 2199 | dumpStackTraceFromBase(info.ContextRecord); |
| 2177 | } | 2200 | } |
| 2178 | | 2201 | |
| 2179 | pub fn dumpStackPointerAddr(prefix: []const u8) void { | 2202 | pub fn dumpStackPointerAddr(prefix: []const u8) void { |