| author | |
| committer | |
| log | 1392a7af171c00679b0754775a8f6f54e967eafd |
| tree | b9b340770b2ecbd274316ccb77ef8e47783902c8 |
| parent | ac4d633ed691159ea61130182a1b51635a95e228 |
| signature |
...using `RtlVirtualUnwind` on x86_64 and aarch64, and
`RtaCaptureStackBackTrace` on x86.6 files changed, 162 insertions(+), 28 deletions(-)
lib/std/debug.zig+20-9| ... | @@ -378,6 +378,8 @@ pub inline fn getContext(context: *ThreadContext) bool { | ... | @@ -378,6 +378,8 @@ pub inline fn getContext(context: *ThreadContext) bool { |
| 378 | } | 378 | } |
| 379 | return true; | 379 | return true; |
| 380 | } | 380 | } |
| 381 | |||
| 382 | return false; | ||
| 381 | } | 383 | } |
| 382 | 384 | ||
| 383 | /// Invokes detectable illegal behavior when `ok` is `false`. | 385 | /// Invokes detectable illegal behavior when `ok` is `false`. |
| ... | @@ -619,7 +621,9 @@ pub const StackUnwindOptions = struct { | ... | @@ -619,7 +621,9 @@ pub const StackUnwindOptions = struct { |
| 619 | /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. | 621 | /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. |
| 620 | pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace { | 622 | pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace { |
| 621 | var context_buf: ThreadContext = undefined; | 623 | var context_buf: ThreadContext = undefined; |
| 622 | var it: StackIterator = .init(options.context, &context_buf); | 624 | var it = StackIterator.init(options.context, &context_buf) catch { |
| 625 | return .{ .index = 0, .instruction_addresses = &.{} }; | ||
| 626 | }; | ||
| 623 | defer it.deinit(); | 627 | defer it.deinit(); |
| 624 | if (!it.stratOk(options.allow_unsafe_unwind)) { | 628 | if (!it.stratOk(options.allow_unsafe_unwind)) { |
| 625 | return .{ .index = 0, .instruction_addresses = &.{} }; | 629 | return .{ .index = 0, .instruction_addresses = &.{} }; |
| ... | @@ -657,7 +661,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ | ... | @@ -657,7 +661,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 657 | }, | 661 | }, |
| 658 | }; | 662 | }; |
| 659 | var context_buf: ThreadContext = undefined; | 663 | var context_buf: ThreadContext = undefined; |
| 660 | var it: StackIterator = .init(options.context, &context_buf); | 664 | var it = StackIterator.init(options.context, &context_buf) catch |err| switch (err) { |
| 665 | error.OutOfMemory => { | ||
| 666 | tty_config.setColor(writer, .dim) catch {}; | ||
| 667 | try writer.print("Cannot print stack trace: out of memory\n", .{}); | ||
| 668 | tty_config.setColor(writer, .reset) catch {}; | ||
| 669 | return; | ||
| 670 | }, | ||
| 671 | }; | ||
| 661 | defer it.deinit(); | 672 | defer it.deinit(); |
| 662 | if (!it.stratOk(options.allow_unsafe_unwind)) { | 673 | if (!it.stratOk(options.allow_unsafe_unwind)) { |
| 663 | tty_config.setColor(writer, .dim) catch {}; | 674 | tty_config.setColor(writer, .dim) catch {}; |
| ... | @@ -751,14 +762,14 @@ pub fn dumpStackTrace(st: *const std.builtin.StackTrace) void { | ... | @@ -751,14 +762,14 @@ pub fn dumpStackTrace(st: *const std.builtin.StackTrace) void { |
| 751 | 762 | ||
| 752 | const StackIterator = union(enum) { | 763 | const StackIterator = union(enum) { |
| 753 | /// Unwinding using debug info (e.g. DWARF CFI). | 764 | /// Unwinding using debug info (e.g. DWARF CFI). |
| 754 | di: if (SelfInfo.supports_unwinding) SelfInfo.DwarfUnwindContext else noreturn, | 765 | di: if (SelfInfo.supports_unwinding) SelfInfo.UnwindContext else noreturn, |
| 755 | /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable. | 766 | /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable. |
| 756 | fp: usize, | 767 | fp: usize, |
| 757 | 768 | ||
| 758 | /// It is important that this function is marked `inline` so that it can safely use | 769 | /// It is important that this function is marked `inline` so that it can safely use |
| 759 | /// `@frameAddress` and `getContext` as the caller's stack frame and our own are one | 770 | /// `@frameAddress` and `getContext` as the caller's stack frame and our own are one |
| 760 | /// and the same. | 771 | /// and the same. |
| 761 | inline fn init(context_opt: ?*const ThreadContext, context_buf: *ThreadContext) StackIterator { | 772 | inline fn init(context_opt: ?*const ThreadContext, context_buf: *ThreadContext) error{OutOfMemory}!StackIterator { |
| 762 | if (builtin.cpu.arch.isSPARC()) { | 773 | if (builtin.cpu.arch.isSPARC()) { |
| 763 | // Flush all the register windows on stack. | 774 | // Flush all the register windows on stack. |
| 764 | if (builtin.cpu.has(.sparc, .v9)) { | 775 | if (builtin.cpu.has(.sparc, .v9)) { |
| ... | @@ -770,10 +781,10 @@ const StackIterator = union(enum) { | ... | @@ -770,10 +781,10 @@ const StackIterator = union(enum) { |
| 770 | if (context_opt) |context| { | 781 | if (context_opt) |context| { |
| 771 | context_buf.* = context.*; | 782 | context_buf.* = context.*; |
| 772 | relocateContext(context_buf); | 783 | relocateContext(context_buf); |
| 773 | return .{ .di = .init(context_buf) }; | 784 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; |
| 774 | } | 785 | } |
| 775 | if (getContext(context_buf)) { | 786 | if (getContext(context_buf)) { |
| 776 | return .{ .di = .init(context_buf) }; | 787 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; |
| 777 | } | 788 | } |
| 778 | return .{ .fp = @frameAddress() }; | 789 | return .{ .fp = @frameAddress() }; |
| 779 | } | 790 | } |
| ... | @@ -816,10 +827,10 @@ const StackIterator = union(enum) { | ... | @@ -816,10 +827,10 @@ const StackIterator = union(enum) { |
| 816 | if (ra == 0) return .end; | 827 | if (ra == 0) return .end; |
| 817 | return .{ .frame = ra }; | 828 | return .{ .frame = ra }; |
| 818 | } else |err| { | 829 | } else |err| { |
| 819 | const bad_pc = unwind_context.pc; | 830 | const pc = unwind_context.pc; |
| 820 | it.* = .{ .fp = unwind_context.getFp() catch 0 }; | 831 | it.* = .{ .fp = unwind_context.getFp() }; |
| 821 | return .{ .switch_to_fp = .{ | 832 | return .{ .switch_to_fp = .{ |
| 822 | .address = bad_pc, | 833 | .address = pc, |
| 823 | .err = err, | 834 | .err = err, |
| 824 | } }; | 835 | } }; |
| 825 | } | 836 | } |
lib/std/debug/Dwarf.zig+2-2| ... | @@ -282,13 +282,13 @@ pub const Die = struct { | ... | @@ -282,13 +282,13 @@ pub const Die = struct { |
| 282 | .@"32" => { | 282 | .@"32" => { |
| 283 | const byte_offset = compile_unit.str_offsets_base + 4 * index; | 283 | const byte_offset = compile_unit.str_offsets_base + 4 * index; |
| 284 | if (byte_offset + 4 > debug_str_offsets.len) return bad(); | 284 | if (byte_offset + 4 > debug_str_offsets.len) return bad(); |
| 285 | const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], endian); | 285 | const offset = mem.readInt(u32, debug_str_offsets[@intCast(byte_offset)..][0..4], endian); |
| 286 | return getStringGeneric(opt_str, offset); | 286 | return getStringGeneric(opt_str, offset); |
| 287 | }, | 287 | }, |
| 288 | .@"64" => { | 288 | .@"64" => { |
| 289 | const byte_offset = compile_unit.str_offsets_base + 8 * index; | 289 | const byte_offset = compile_unit.str_offsets_base + 8 * index; |
| 290 | if (byte_offset + 8 > debug_str_offsets.len) return bad(); | 290 | if (byte_offset + 8 > debug_str_offsets.len) return bad(); |
| 291 | const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], endian); | 291 | const offset = mem.readInt(u64, debug_str_offsets[@intCast(byte_offset)..][0..8], endian); |
| 292 | return getStringGeneric(opt_str, offset); | 292 | return getStringGeneric(opt_str, offset); |
| 293 | }, | 293 | }, |
| 294 | } | 294 | } |
lib/std/debug/SelfInfo.zig+21-7| ... | @@ -42,6 +42,8 @@ pub const target_supported: bool = Module != void; | ... | @@ -42,6 +42,8 @@ pub const target_supported: bool = Module != void; |
| 42 | /// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`. | 42 | /// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`. |
| 43 | pub const supports_unwinding: bool = Module.supports_unwinding; | 43 | pub const supports_unwinding: bool = Module.supports_unwinding; |
| 44 | 44 | ||
| 45 | pub const UnwindContext = if (supports_unwinding) Module.UnwindContext; | ||
| 46 | |||
| 45 | pub const init: SelfInfo = .{ | 47 | pub const init: SelfInfo = .{ |
| 46 | .modules = .empty, | 48 | .modules = .empty, |
| 47 | .lookup_cache = if (Module.LookupCache != void) .init, | 49 | .lookup_cache = if (Module.LookupCache != void) .init, |
| ... | @@ -53,7 +55,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { | ... | @@ -53,7 +55,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { |
| 53 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); | 55 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); |
| 54 | } | 56 | } |
| 55 | 57 | ||
| 56 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *DwarfUnwindContext) Error!usize { | 58 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize { |
| 57 | comptime assert(supports_unwinding); | 59 | comptime assert(supports_unwinding); |
| 58 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); | 60 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); |
| 59 | const gop = try self.modules.getOrPut(gpa, module.key()); | 61 | const gop = try self.modules.getOrPut(gpa, module.key()); |
| ... | @@ -113,14 +115,23 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -113,14 +115,23 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 113 | /// ) SelfInfo.Error!std.debug.Symbol; | 115 | /// ) SelfInfo.Error!std.debug.Symbol; |
| 114 | /// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available. | 116 | /// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available. |
| 115 | /// pub const supports_unwinding: bool; | 117 | /// pub const supports_unwinding: bool; |
| 118 | /// /// Only required if `supports_unwinding == true`. | ||
| 119 | /// pub const UnwindContext = struct { | ||
| 120 | /// /// A PC value inside the function of the last unwound frame. | ||
| 121 | /// pc: usize, | ||
| 122 | /// pub fn init(tc: *std.debug.ThreadContext, gpa: Allocator) Allocator.Error!UnwindContext; | ||
| 123 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; | ||
| 124 | /// /// Returns the frame pointer associated with the last unwound stack frame. If the frame | ||
| 125 | /// /// pointer is unknown, 0 may be returned instead. | ||
| 126 | /// pub fn getFp(uc: *UnwindContext) usize; | ||
| 127 | /// }; | ||
| 116 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns | 128 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns |
| 117 | /// /// the next return address (which may be 0 indicating end of stack). This is currently | 129 | /// /// the next return address (which may be 0 indicating end of stack). |
| 118 | /// /// specialized to DWARF unwinding. | ||
| 119 | /// pub fn unwindFrame( | 130 | /// pub fn unwindFrame( |
| 120 | /// mod: *const Module, | 131 | /// mod: *const Module, |
| 121 | /// gpa: Allocator, | 132 | /// gpa: Allocator, |
| 122 | /// di: *DebugInfo, | 133 | /// di: *DebugInfo, |
| 123 | /// ctx: *SelfInfo.DwarfUnwindContext, | 134 | /// ctx: *UnwindContext, |
| 124 | /// ) SelfInfo.Error!usize; | 135 | /// ) SelfInfo.Error!usize; |
| 125 | /// ``` | 136 | /// ``` |
| 126 | const Module: type = Module: { | 137 | const Module: type = Module: { |
| ... | @@ -136,6 +147,8 @@ const Module: type = Module: { | ... | @@ -136,6 +147,8 @@ const Module: type = Module: { |
| 136 | }; | 147 | }; |
| 137 | }; | 148 | }; |
| 138 | 149 | ||
| 150 | /// An implementation of `UnwindContext` useful for DWARF-based unwinders. The `Module.unwindFrame` | ||
| 151 | /// implementation should wrap `DwarfUnwindContext.unwindFrame`. | ||
| 139 | pub const DwarfUnwindContext = struct { | 152 | pub const DwarfUnwindContext = struct { |
| 140 | cfa: ?usize, | 153 | cfa: ?usize, |
| 141 | pc: usize, | 154 | pc: usize, |
| ... | @@ -144,8 +157,9 @@ pub const DwarfUnwindContext = struct { | ... | @@ -144,8 +157,9 @@ pub const DwarfUnwindContext = struct { |
| 144 | vm: Dwarf.Unwind.VirtualMachine, | 157 | vm: Dwarf.Unwind.VirtualMachine, |
| 145 | stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), | 158 | stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), |
| 146 | 159 | ||
| 147 | pub fn init(thread_context: *std.debug.ThreadContext) DwarfUnwindContext { | 160 | pub fn init(thread_context: *std.debug.ThreadContext, gpa: Allocator) error{}!DwarfUnwindContext { |
| 148 | comptime assert(supports_unwinding); | 161 | comptime assert(supports_unwinding); |
| 162 | _ = gpa; | ||
| 149 | 163 | ||
| 150 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; | 164 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; |
| 151 | const raw_pc_ptr = regValueNative(thread_context, ip_reg_num, null) catch { | 165 | const raw_pc_ptr = regValueNative(thread_context, ip_reg_num, null) catch { |
| ... | @@ -169,8 +183,8 @@ pub const DwarfUnwindContext = struct { | ... | @@ -169,8 +183,8 @@ pub const DwarfUnwindContext = struct { |
| 169 | self.* = undefined; | 183 | self.* = undefined; |
| 170 | } | 184 | } |
| 171 | 185 | ||
| 172 | pub fn getFp(self: *const DwarfUnwindContext) !usize { | 186 | pub fn getFp(self: *const DwarfUnwindContext) usize { |
| 173 | return (try regValueNative(self.thread_context, Dwarf.abi.fpRegNum(native_arch, self.reg_context), self.reg_context)).*; | 187 | return (regValueNative(self.thread_context, Dwarf.abi.fpRegNum(native_arch, self.reg_context), self.reg_context) catch return 0).*; |
| 174 | } | 188 | } |
| 175 | 189 | ||
| 176 | /// Resolves the register rule and places the result into `out` (see regBytes) | 190 | /// Resolves the register rule and places the result into `out` (see regBytes) |
lib/std/debug/SelfInfo/DarwinModule.zig+4-4| ... | @@ -252,10 +252,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu | ... | @@ -252,10 +252,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu |
| 252 | }; | 252 | }; |
| 253 | } | 253 | } |
| 254 | pub const supports_unwinding: bool = true; | 254 | pub const supports_unwinding: bool = true; |
| 255 | pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ||
| 255 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). | 256 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). |
| 256 | /// If the compact encoding can't encode a way to unwind a frame, it will | 257 | /// If the compact encoding can't encode a way to unwind a frame, it will |
| 257 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. | 258 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. |
| 258 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *DwarfUnwindContext) Error!usize { | 259 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { |
| 259 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { | 260 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { |
| 260 | error.InvalidDebugInfo, | 261 | error.InvalidDebugInfo, |
| 261 | error.MissingDebugInfo, | 262 | error.MissingDebugInfo, |
| ... | @@ -274,7 +275,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -274,7 +275,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 274 | => return error.InvalidDebugInfo, | 275 | => return error.InvalidDebugInfo, |
| 275 | }; | 276 | }; |
| 276 | } | 277 | } |
| 277 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *DwarfUnwindContext) !usize { | 278 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { |
| 278 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); | 279 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); |
| 279 | const unwind = &di.unwind.?; | 280 | const unwind = &di.unwind.?; |
| 280 | 281 | ||
| ... | @@ -575,7 +576,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -575,7 +576,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 575 | else => comptime unreachable, // unimplemented | 576 | else => comptime unreachable, // unimplemented |
| 576 | }; | 577 | }; |
| 577 | 578 | ||
| 578 | context.pc = DwarfUnwindContext.stripInstructionPtrAuthCode(new_ip); | 579 | context.pc = UnwindContext.stripInstructionPtrAuthCode(new_ip); |
| 579 | if (context.pc > 0) context.pc -= 1; | 580 | if (context.pc > 0) context.pc -= 1; |
| 580 | return new_ip; | 581 | return new_ip; |
| 581 | } | 582 | } |
| ... | @@ -819,7 +820,6 @@ const macho = std.macho; | ... | @@ -819,7 +820,6 @@ const macho = std.macho; |
| 819 | const mem = std.mem; | 820 | const mem = std.mem; |
| 820 | const posix = std.posix; | 821 | const posix = std.posix; |
| 821 | const testing = std.testing; | 822 | const testing = std.testing; |
| 822 | const DwarfUnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ||
| 823 | const Error = std.debug.SelfInfo.Error; | 823 | const Error = std.debug.SelfInfo.Error; |
| 824 | const regBytes = Dwarf.abi.regBytes; | 824 | const regBytes = Dwarf.abi.regBytes; |
| 825 | const regValueNative = Dwarf.abi.regValueNative; | 825 | const regValueNative = Dwarf.abi.regValueNative; |
lib/std/debug/SelfInfo/ElfModule.zig+2-2| ... | @@ -193,7 +193,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro | ... | @@ -193,7 +193,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro |
| 193 | else => unreachable, | 193 | else => unreachable, |
| 194 | } | 194 | } |
| 195 | } | 195 | } |
| 196 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *DwarfUnwindContext) Error!usize { | 196 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { |
| 197 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); | 197 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); |
| 198 | std.debug.assert(di.unwind[0] != null); | 198 | std.debug.assert(di.unwind[0] != null); |
| 199 | for (&di.unwind) |*opt_unwind| { | 199 | for (&di.unwind) |*opt_unwind| { |
| ... | @@ -205,6 +205,7 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con | ... | @@ -205,6 +205,7 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con |
| 205 | } | 205 | } |
| 206 | return error.MissingDebugInfo; | 206 | return error.MissingDebugInfo; |
| 207 | } | 207 | } |
| 208 | pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ||
| 208 | pub const supports_unwinding: bool = s: { | 209 | pub const supports_unwinding: bool = s: { |
| 209 | const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) { | 210 | const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) { |
| 210 | .linux => &.{ .x86, .x86_64, .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be }, | 211 | .linux => &.{ .x86, .x86_64, .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be }, |
| ... | @@ -233,7 +234,6 @@ const Allocator = std.mem.Allocator; | ... | @@ -233,7 +234,6 @@ const Allocator = std.mem.Allocator; |
| 233 | const Dwarf = std.debug.Dwarf; | 234 | const Dwarf = std.debug.Dwarf; |
| 234 | const elf = std.elf; | 235 | const elf = std.elf; |
| 235 | const mem = std.mem; | 236 | const mem = std.mem; |
| 236 | const DwarfUnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ||
| 237 | const Error = std.debug.SelfInfo.Error; | 237 | const Error = std.debug.SelfInfo.Error; |
| 238 | 238 | ||
| 239 | const builtin = @import("builtin"); | 239 | const builtin = @import("builtin"); |
lib/std/debug/SelfInfo/WindowsModule.zig+113-4| ... | @@ -102,7 +102,7 @@ fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) ! | ... | @@ -102,7 +102,7 @@ fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) ! |
| 102 | if (create_section_rc != .SUCCESS) return error.MissingDebugInfo; | 102 | if (create_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 103 | errdefer windows.CloseHandle(section_handle); | 103 | errdefer windows.CloseHandle(section_handle); |
| 104 | var coff_len: usize = 0; | 104 | var coff_len: usize = 0; |
| 105 | var section_view_ptr: [*]const u8 = undefined; | 105 | var section_view_ptr: ?[*]const u8 = null; |
| 106 | const map_section_rc = windows.ntdll.NtMapViewOfSection( | 106 | const map_section_rc = windows.ntdll.NtMapViewOfSection( |
| 107 | section_handle, | 107 | section_handle, |
| 108 | process_handle, | 108 | process_handle, |
| ... | @@ -116,8 +116,8 @@ fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) ! | ... | @@ -116,8 +116,8 @@ fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) ! |
| 116 | windows.PAGE_READONLY, | 116 | windows.PAGE_READONLY, |
| 117 | ); | 117 | ); |
| 118 | if (map_section_rc != .SUCCESS) return error.MissingDebugInfo; | 118 | if (map_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 119 | errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr)) == .SUCCESS); | 119 | errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr.?)) == .SUCCESS); |
| 120 | const section_view = section_view_ptr[0..coff_len]; | 120 | const section_view = section_view_ptr.?[0..coff_len]; |
| 121 | coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; | 121 | coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; |
| 122 | di.mapped_file = .{ | 122 | di.mapped_file = .{ |
| 123 | .file = coff_file, | 123 | .file = coff_file, |
| ... | @@ -246,7 +246,116 @@ pub const DebugInfo = struct { | ... | @@ -246,7 +246,116 @@ pub const DebugInfo = struct { |
| 246 | }; | 246 | }; |
| 247 | } | 247 | } |
| 248 | }; | 248 | }; |
| 249 | pub const supports_unwinding: bool = false; | 249 | |
| 250 | pub const supports_unwinding: bool = true; | ||
| 251 | pub const UnwindContext = switch (builtin.cpu.arch) { | ||
| 252 | .x86 => struct { | ||
| 253 | pc: usize, | ||
| 254 | frames: []usize, | ||
| 255 | frames_capacity: usize, | ||
| 256 | next_index: usize, | ||
| 257 | /// Marked `noinline` to ensure that `RtlCaptureStackBackTrace` includes our caller. | ||
| 258 | pub noinline fn init(ctx: *windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext { | ||
| 259 | const frames_buf = try gpa.alloc(usize, 1024); | ||
| 260 | errdefer comptime unreachable; | ||
| 261 | const frames_len = windows.ntdll.RtlCaptureStackBackTrace(0, frames_buf.len, @ptrCast(frames_buf.ptr), null); | ||
| 262 | const regs = ctx.getRegs(); | ||
| 263 | const first_index = for (frames_buf[0..frames_len], 0..) |ret_addr, idx| { | ||
| 264 | if (ret_addr == regs.ip) break idx; | ||
| 265 | } else i: { | ||
| 266 | // If we were called by an exception handler, `regs.ip` wasn't in the trace because | ||
| 267 | // RtlCaptureStackBackTrace omits the KiUserExceptionDispatcher frame, which is the | ||
| 268 | // one in `regs.ip`. In that case, we have to start one frame shallower instead, and | ||
| 269 | // we can figure out that frame's ip from the context's bp. | ||
| 270 | const start_addr_ptr: *const usize = @ptrFromInt(regs.bp + 4); | ||
| 271 | const start_addr = start_addr_ptr.*; | ||
| 272 | for (frames_buf[0..frames_len], 0..) |ret_addr, idx| { | ||
| 273 | if (ret_addr == start_addr) break :i idx; | ||
| 274 | } | ||
| 275 | // The IP in the context can't be found; return an empty trace. | ||
| 276 | gpa.free(frames_buf); | ||
| 277 | return .{ .pc = 0, .frames = &.{}, .frames_capacity = 0, .next_index = 0 }; | ||
| 278 | }; | ||
| 279 | return .{ | ||
| 280 | .pc = @returnAddress(), | ||
| 281 | .frames = frames_buf[0..frames_len], | ||
| 282 | .frames_capacity = 0, | ||
| 283 | .next_index = first_index, | ||
| 284 | }; | ||
| 285 | } | ||
| 286 | pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void { | ||
| 287 | gpa.free(ctx.frames.ptr[0..ctx.frames_capacity]); | ||
| 288 | ctx.* = undefined; | ||
| 289 | } | ||
| 290 | pub fn getFp(ctx: *UnwindContext) usize { | ||
| 291 | _ = ctx; | ||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | }, | ||
| 295 | else => struct { | ||
| 296 | pc: usize, | ||
| 297 | cur: windows.CONTEXT, | ||
| 298 | history_table: windows.UNWIND_HISTORY_TABLE, | ||
| 299 | pub fn init(ctx: *const windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext { | ||
| 300 | _ = gpa; | ||
| 301 | return .{ | ||
| 302 | .pc = @returnAddress(), | ||
| 303 | .cur = ctx.*, | ||
| 304 | .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), | ||
| 305 | }; | ||
| 306 | } | ||
| 307 | pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void { | ||
| 308 | _ = ctx; | ||
| 309 | _ = gpa; | ||
| 310 | } | ||
| 311 | pub fn getFp(ctx: *UnwindContext) usize { | ||
| 312 | return ctx.cur.getRegs().bp; | ||
| 313 | } | ||
| 314 | }, | ||
| 315 | }; | ||
| 316 | pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { | ||
| 317 | _ = module; | ||
| 318 | _ = gpa; | ||
| 319 | _ = di; | ||
| 320 | |||
| 321 | if (builtin.cpu.arch == .x86) { | ||
| 322 | const i = context.next_index; | ||
| 323 | if (i == context.frames.len) return 0; | ||
| 324 | context.next_index += 1; | ||
| 325 | const ip = context.frames[i]; | ||
| 326 | context.pc = ip -| 1; | ||
| 327 | return ip; | ||
| 328 | } | ||
| 329 | |||
| 330 | const current_regs = context.cur.getRegs(); | ||
| 331 | var image_base: windows.DWORD64 = undefined; | ||
| 332 | if (windows.ntdll.RtlLookupFunctionEntry(current_regs.ip, &image_base, &context.history_table)) |runtime_function| { | ||
| 333 | var handler_data: ?*anyopaque = null; | ||
| 334 | var establisher_frame: u64 = undefined; | ||
| 335 | _ = windows.ntdll.RtlVirtualUnwind( | ||
| 336 | windows.UNW_FLAG_NHANDLER, | ||
| 337 | image_base, | ||
| 338 | current_regs.ip, | ||
| 339 | runtime_function, | ||
| 340 | &context.cur, | ||
| 341 | &handler_data, | ||
| 342 | &establisher_frame, | ||
| 343 | null, | ||
| 344 | ); | ||
| 345 | } else { | ||
| 346 | // leaf function | ||
| 347 | context.cur.setIp(@as(*const usize, @ptrFromInt(current_regs.sp)).*); | ||
| 348 | context.cur.setSp(current_regs.sp + @sizeOf(usize)); | ||
| 349 | } | ||
| 350 | |||
| 351 | const next_regs = context.cur.getRegs(); | ||
| 352 | const tib = &windows.teb().NtTib; | ||
| 353 | if (next_regs.sp < @intFromPtr(tib.StackLimit) or next_regs.sp > @intFromPtr(tib.StackBase)) { | ||
| 354 | return 0; | ||
| 355 | } | ||
| 356 | context.pc = next_regs.ip -| 1; | ||
| 357 | return next_regs.ip; | ||
| 358 | } | ||
| 250 | 359 | ||
| 251 | const WindowsModule = @This(); | 360 | const WindowsModule = @This(); |
| 252 | 361 |