| author | |
| committer | |
| log | 2ab650b4817cbb22244c17de828e82cbb0ccf15e |
| tree | deebb1090f939f52a363de30179f2136f8819588 |
| parent | 9434bab3134edadae7ae7e575f6b025cafc6a59a |
| signature |
...and just deal with signal handlers by adding 1 to create a fake
"return address". The system I tried out where the addresses returned by
`StackIterator` were pre-subtracted didn't play nicely with error
traces, which in hindsight, makes perfect sense. This definition also
removes some ugly off-by-one issues in matching `first_address`, so I do
think this is a better approach.7 files changed, 65 insertions(+), 48 deletions(-)
lib/std/debug.zig+21-18| ... | @@ -577,14 +577,12 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) | ... | @@ -577,14 +577,12 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) |
| 577 | while (true) switch (it.next()) { | 577 | while (true) switch (it.next()) { |
| 578 | .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break, | 578 | .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break, |
| 579 | .end => break, | 579 | .end => break, |
| 580 | .frame => |pc_addr| { | 580 | .frame => |ret_addr| { |
| 581 | if (wait_for) |target| { | 581 | if (wait_for) |target| { |
| 582 | // Possible off-by-one error: `pc_addr` might be one less than the return address (so | 582 | if (ret_addr != target) continue; |
| 583 | // that it falls *inside* the function call), while `target` *is* a return address. | ||
| 584 | if (pc_addr != target and pc_addr + 1 != target) continue; | ||
| 585 | wait_for = null; | 583 | wait_for = null; |
| 586 | } | 584 | } |
| 587 | if (frame_idx < addr_buf.len) addr_buf[frame_idx] = pc_addr; | 585 | if (frame_idx < addr_buf.len) addr_buf[frame_idx] = ret_addr; |
| 588 | frame_idx += 1; | 586 | frame_idx += 1; |
| 589 | }, | 587 | }, |
| 590 | }; | 588 | }; |
| ... | @@ -659,14 +657,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ | ... | @@ -659,14 +657,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 659 | } | 657 | } |
| 660 | }, | 658 | }, |
| 661 | .end => break, | 659 | .end => break, |
| 662 | .frame => |pc_addr| { | 660 | .frame => |ret_addr| { |
| 663 | if (wait_for) |target| { | 661 | if (wait_for) |target| { |
| 664 | // Possible off-by-one error: `pc_addr` might be one less than the return address (so | 662 | if (ret_addr != target) continue; |
| 665 | // that it falls *inside* the function call), while `target` *is* a return address. | ||
| 666 | if (pc_addr != target and pc_addr + 1 != target) continue; | ||
| 667 | wait_for = null; | 663 | wait_for = null; |
| 668 | } | 664 | } |
| 669 | try printSourceAtAddress(di_gpa, di, writer, pc_addr, tty_config); | 665 | // `ret_addr` is the return address, which is *after* the function call. |
| 666 | // Subtract 1 to get an address *in* the function call for a better source location. | ||
| 667 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config); | ||
| 670 | printed_any_frame = true; | 668 | printed_any_frame = true; |
| 671 | }, | 669 | }, |
| 672 | }; | 670 | }; |
| ... | @@ -712,8 +710,10 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c | ... | @@ -712,8 +710,10 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c |
| 712 | }, | 710 | }, |
| 713 | }; | 711 | }; |
| 714 | const captured_frames = @min(n_frames, st.instruction_addresses.len); | 712 | const captured_frames = @min(n_frames, st.instruction_addresses.len); |
| 715 | for (st.instruction_addresses[0..captured_frames]) |pc_addr| { | 713 | for (st.instruction_addresses[0..captured_frames]) |ret_addr| { |
| 716 | try printSourceAtAddress(di_gpa, di, writer, pc_addr, tty_config); | 714 | // `ret_addr` is the return address, which is *after* the function call. |
| 715 | // Subtract 1 to get an address *in* the function call for a better source location. | ||
| 716 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config); | ||
| 717 | } | 717 | } |
| 718 | if (n_frames > captured_frames) { | 718 | if (n_frames > captured_frames) { |
| 719 | tty_config.setColor(writer, .bold) catch {}; | 719 | tty_config.setColor(writer, .bold) catch {}; |
| ... | @@ -787,7 +787,7 @@ const StackIterator = union(enum) { | ... | @@ -787,7 +787,7 @@ const StackIterator = union(enum) { |
| 787 | } | 787 | } |
| 788 | 788 | ||
| 789 | const Result = union(enum) { | 789 | const Result = union(enum) { |
| 790 | /// A stack frame has been found; this is the corresponding program counter address. | 790 | /// A stack frame has been found; this is the corresponding return address. |
| 791 | frame: usize, | 791 | frame: usize, |
| 792 | /// The end of the stack has been reached. | 792 | /// The end of the stack has been reached. |
| 793 | end, | 793 | end, |
| ... | @@ -797,18 +797,21 @@ const StackIterator = union(enum) { | ... | @@ -797,18 +797,21 @@ const StackIterator = union(enum) { |
| 797 | err: SelfInfo.Error, | 797 | err: SelfInfo.Error, |
| 798 | }, | 798 | }, |
| 799 | }; | 799 | }; |
| 800 | |||
| 800 | fn next(it: *StackIterator) Result { | 801 | fn next(it: *StackIterator) Result { |
| 801 | switch (it.*) { | 802 | switch (it.*) { |
| 802 | .di_first => |unwind_context| { | 803 | .di_first => |unwind_context| { |
| 803 | const first_pc = unwind_context.pc; | 804 | const first_pc = unwind_context.pc; |
| 804 | if (first_pc == 0) return .end; | 805 | if (first_pc == 0) return .end; |
| 805 | it.* = .{ .di = unwind_context }; | 806 | it.* = .{ .di = unwind_context }; |
| 806 | return .{ .frame = first_pc }; | 807 | // The caller expects *return* addresses, where they will subtract 1 to find the address of the call. |
| 808 | // However, we have the actual current PC, which should not be adjusted. Compensate by adding 1. | ||
| 809 | return .{ .frame = first_pc +| 1 }; | ||
| 807 | }, | 810 | }, |
| 808 | .di => |*unwind_context| { | 811 | .di => |*unwind_context| { |
| 809 | const di = getSelfDebugInfo() catch unreachable; | 812 | const di = getSelfDebugInfo() catch unreachable; |
| 810 | const di_gpa = getDebugInfoAllocator(); | 813 | const di_gpa = getDebugInfoAllocator(); |
| 811 | di.unwindFrame(di_gpa, unwind_context) catch |err| { | 814 | const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| { |
| 812 | const pc = unwind_context.pc; | 815 | const pc = unwind_context.pc; |
| 813 | it.* = .{ .fp = unwind_context.getFp() }; | 816 | it.* = .{ .fp = unwind_context.getFp() }; |
| 814 | return .{ .switch_to_fp = .{ | 817 | return .{ .switch_to_fp = .{ |
| ... | @@ -816,8 +819,8 @@ const StackIterator = union(enum) { | ... | @@ -816,8 +819,8 @@ const StackIterator = union(enum) { |
| 816 | .err = err, | 819 | .err = err, |
| 817 | } }; | 820 | } }; |
| 818 | }; | 821 | }; |
| 819 | const pc = unwind_context.pc; | 822 | if (ret_addr <= 1) return .end; |
| 820 | return if (pc == 0) .end else .{ .frame = pc }; | 823 | return .{ .frame = ret_addr }; |
| 821 | }, | 824 | }, |
| 822 | .fp => |fp| { | 825 | .fp => |fp| { |
| 823 | if (fp == 0) return .end; // we reached the "sentinel" base pointer | 826 | if (fp == 0) return .end; // we reached the "sentinel" base pointer |
| ... | @@ -845,7 +848,7 @@ const StackIterator = union(enum) { | ... | @@ -845,7 +848,7 @@ const StackIterator = union(enum) { |
| 845 | it.fp = bp; | 848 | it.fp = bp; |
| 846 | const ra = stripInstructionPtrAuthCode(ra_ptr.*); | 849 | const ra = stripInstructionPtrAuthCode(ra_ptr.*); |
| 847 | if (ra <= 1) return .end; | 850 | if (ra <= 1) return .end; |
| 848 | return .{ .frame = ra - 1 }; | 851 | return .{ .frame = ra }; |
| 849 | }, | 852 | }, |
| 850 | } | 853 | } |
| 851 | } | 854 | } |
lib/std/debug/SelfInfo.zig+20-15| ... | @@ -53,7 +53,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { | ... | @@ -53,7 +53,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { |
| 53 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); | 53 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!void { | 56 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize { |
| 57 | comptime assert(supports_unwinding); | 57 | comptime assert(supports_unwinding); |
| 58 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); | 58 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); |
| 59 | const gop = try self.modules.getOrPut(gpa, module.key()); | 59 | const gop = try self.modules.getOrPut(gpa, module.key()); |
| ... | @@ -124,15 +124,14 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -124,15 +124,14 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 124 | /// /// pointer is unknown, 0 may be returned instead. | 124 | /// /// pointer is unknown, 0 may be returned instead. |
| 125 | /// pub fn getFp(uc: *UnwindContext) usize; | 125 | /// pub fn getFp(uc: *UnwindContext) usize; |
| 126 | /// }; | 126 | /// }; |
| 127 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame. | 127 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame, and returns |
| 128 | /// /// The caller will read the new instruction poiter from the `pc` field. | 128 | /// /// the frame's return address. |
| 129 | /// /// `pc = 0` indicates end of stack / no more frames. | ||
| 130 | /// pub fn unwindFrame( | 129 | /// pub fn unwindFrame( |
| 131 | /// mod: *const Module, | 130 | /// mod: *const Module, |
| 132 | /// gpa: Allocator, | 131 | /// gpa: Allocator, |
| 133 | /// di: *DebugInfo, | 132 | /// di: *DebugInfo, |
| 134 | /// ctx: *UnwindContext, | 133 | /// ctx: *UnwindContext, |
| 135 | /// ) SelfInfo.Error!void; | 134 | /// ) SelfInfo.Error!usize; |
| 136 | /// ``` | 135 | /// ``` |
| 137 | const Module: type = Module: { | 136 | const Module: type = Module: { |
| 138 | // Allow overriding the target-specific `SelfInfo` implementation by exposing `root.debug.Module`. | 137 | // Allow overriding the target-specific `SelfInfo` implementation by exposing `root.debug.Module`. |
| ... | @@ -312,7 +311,7 @@ pub const DwarfUnwindContext = struct { | ... | @@ -312,7 +311,7 @@ pub const DwarfUnwindContext = struct { |
| 312 | unwind: *const Dwarf.Unwind, | 311 | unwind: *const Dwarf.Unwind, |
| 313 | load_offset: usize, | 312 | load_offset: usize, |
| 314 | explicit_fde_offset: ?usize, | 313 | explicit_fde_offset: ?usize, |
| 315 | ) Error!void { | 314 | ) Error!usize { |
| 316 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { | 315 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { |
| 317 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, | 316 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, |
| 318 | 317 | ||
| ... | @@ -360,10 +359,10 @@ pub const DwarfUnwindContext = struct { | ... | @@ -360,10 +359,10 @@ pub const DwarfUnwindContext = struct { |
| 360 | unwind: *const Dwarf.Unwind, | 359 | unwind: *const Dwarf.Unwind, |
| 361 | load_offset: usize, | 360 | load_offset: usize, |
| 362 | explicit_fde_offset: ?usize, | 361 | explicit_fde_offset: ?usize, |
| 363 | ) !void { | 362 | ) !usize { |
| 364 | comptime assert(supports_unwinding); | 363 | comptime assert(supports_unwinding); |
| 365 | 364 | ||
| 366 | if (context.pc == 0) return; | 365 | if (context.pc == 0) return 0; |
| 367 | 366 | ||
| 368 | const pc_vaddr = context.pc - load_offset; | 367 | const pc_vaddr = context.pc - load_offset; |
| 369 | 368 | ||
| ... | @@ -443,13 +442,19 @@ pub const DwarfUnwindContext = struct { | ... | @@ -443,13 +442,19 @@ pub const DwarfUnwindContext = struct { |
| 443 | // The new CPU context is complete; flush changes. | 442 | // The new CPU context is complete; flush changes. |
| 444 | context.cpu_context = new_cpu_context; | 443 | context.cpu_context = new_cpu_context; |
| 445 | 444 | ||
| 446 | // Also update the stored pc. However, because `return_address` points to the instruction | 445 | // The caller will subtract 1 from the return address to get an address corresponding to the |
| 447 | // *after* the call, it could (in the case of noreturn functions) actually point outside of | 446 | // function call. However, if this is a signal frame, that's actually incorrect, because the |
| 448 | // the caller's address range, meaning an FDE lookup would fail. We can handle this by | 447 | // "return address" we have is the instruction which triggered the signal (if the signal |
| 449 | // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside | 448 | // handler returned, the instruction would be re-run). Compensate for this by incrementing |
| 450 | // the `call` instruction. The exception to this rule is signal frames, where the return | 449 | // the address in that case. |
| 451 | // address is the same instruction that triggered the handler. | 450 | const adjusted_ret_addr = if (cie.is_signal_frame) return_address +| 1 else return_address; |
| 452 | context.pc = if (cie.is_signal_frame) return_address else return_address -| 1; | 451 | |
| 452 | // We also want to do that same subtraction here to get the PC for the next frame's FDE. | ||
| 453 | // This is because if the callee was noreturn, then the function call might be the caller's | ||
| 454 | // last instruction, so `return_address` might actually point outside of it! | ||
| 455 | context.pc = adjusted_ret_addr -| 1; | ||
| 456 | |||
| 457 | return adjusted_ret_addr; | ||
| 453 | } | 458 | } |
| 454 | /// Since register rules are applied (usually) during a panic, | 459 | /// Since register rules are applied (usually) during a panic, |
| 455 | /// checked addition / subtraction is used so that we can return | 460 | /// checked addition / subtraction is used so that we can return |
lib/std/debug/SelfInfo/DarwinModule.zig+9-3| ... | @@ -324,7 +324,7 @@ pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ... | @@ -324,7 +324,7 @@ pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; |
| 324 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). | 324 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). |
| 325 | /// If the compact encoding can't encode a way to unwind a frame, it will | 325 | /// If the compact encoding can't encode a way to unwind a frame, it will |
| 326 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. | 326 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. |
| 327 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!void { | 327 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { |
| 328 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { | 328 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { |
| 329 | error.InvalidDebugInfo, | 329 | error.InvalidDebugInfo, |
| 330 | error.MissingDebugInfo, | 330 | error.MissingDebugInfo, |
| ... | @@ -340,7 +340,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -340,7 +340,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 340 | => return error.InvalidDebugInfo, | 340 | => return error.InvalidDebugInfo, |
| 341 | }; | 341 | }; |
| 342 | } | 342 | } |
| 343 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !void { | 343 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { |
| 344 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); | 344 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); |
| 345 | const unwind = &di.unwind.?; | 345 | const unwind = &di.unwind.?; |
| 346 | 346 | ||
| ... | @@ -640,7 +640,13 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -640,7 +640,13 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 640 | else => comptime unreachable, // unimplemented | 640 | else => comptime unreachable, // unimplemented |
| 641 | }; | 641 | }; |
| 642 | 642 | ||
| 643 | context.pc = std.debug.stripInstructionPtrAuthCode(new_ip) -| 1; | 643 | const ret_addr = std.debug.stripInstructionPtrAuthCode(new_ip); |
| 644 | |||
| 645 | // Like `DwarfUnwindContext.unwindFrame`, adjust our next lookup pc in case the `call` was this | ||
| 646 | // function's last instruction making `ret_addr` one byte past its end. | ||
| 647 | context.pc = ret_addr -| 1; | ||
| 648 | |||
| 649 | return ret_addr; | ||
| 644 | } | 650 | } |
| 645 | pub const DebugInfo = struct { | 651 | pub const DebugInfo = struct { |
| 646 | unwind: ?Unwind, | 652 | unwind: ?Unwind, |
lib/std/debug/SelfInfo/ElfModule.zig+1-1| ... | @@ -230,7 +230,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro | ... | @@ -230,7 +230,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro |
| 230 | else => unreachable, | 230 | else => unreachable, |
| 231 | } | 231 | } |
| 232 | } | 232 | } |
| 233 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!void { | 233 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { |
| 234 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); | 234 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); |
| 235 | std.debug.assert(di.unwind[0] != null); | 235 | std.debug.assert(di.unwind[0] != null); |
| 236 | for (&di.unwind) |*opt_unwind| { | 236 | for (&di.unwind) |*opt_unwind| { |
lib/std/debug/SelfInfo/WindowsModule.zig+6-3| ... | @@ -373,7 +373,7 @@ pub const UnwindContext = struct { | ... | @@ -373,7 +373,7 @@ pub const UnwindContext = struct { |
| 373 | return ctx.cur.getRegs().bp; | 373 | return ctx.cur.getRegs().bp; |
| 374 | } | 374 | } |
| 375 | }; | 375 | }; |
| 376 | pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !void { | 376 | pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { |
| 377 | _ = module; | 377 | _ = module; |
| 378 | _ = gpa; | 378 | _ = gpa; |
| 379 | _ = di; | 379 | _ = di; |
| ... | @@ -403,9 +403,12 @@ pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -403,9 +403,12 @@ pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, |
| 403 | const tib = &windows.teb().NtTib; | 403 | const tib = &windows.teb().NtTib; |
| 404 | if (next_regs.sp < @intFromPtr(tib.StackLimit) or next_regs.sp > @intFromPtr(tib.StackBase)) { | 404 | if (next_regs.sp < @intFromPtr(tib.StackLimit) or next_regs.sp > @intFromPtr(tib.StackBase)) { |
| 405 | context.pc = 0; | 405 | context.pc = 0; |
| 406 | } else { | 406 | return 0; |
| 407 | context.pc = next_regs.ip -| 1; | ||
| 408 | } | 407 | } |
| 408 | // Like `DwarfUnwindContext.unwindFrame`, adjust our next lookup pc in case the `call` was this | ||
| 409 | // function's last instruction making `next_regs.ip` one byte past its end. | ||
| 410 | context.pc = next_regs.ip -| 1; | ||
| 411 | return next_regs.ip; | ||
| 409 | } | 412 | } |
| 410 | 413 | ||
| 411 | const WindowsModule = @This(); | 414 | const WindowsModule = @This(); |
test/standalone/stack_iterator/unwind.zig+4-4| ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); |
| 3 | const fatal = std.process.fatal; | 3 | const fatal = std.process.fatal; |
| 4 | 4 | ||
| 5 | noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 5 | noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 6 | expected[0] = @returnAddress() - 1; | 6 | expected[0] = @returnAddress(); |
| 7 | return std.debug.captureCurrentStackTrace(.{ | 7 | return std.debug.captureCurrentStackTrace(.{ |
| 8 | .first_address = @returnAddress(), | 8 | .first_address = @returnAddress(), |
| 9 | .allow_unsafe_unwind = true, | 9 | .allow_unsafe_unwind = true, |
| ... | @@ -58,12 +58,12 @@ noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr | ... | @@ -58,12 +58,12 @@ noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | expected[1] = @returnAddress() - 1; | 61 | expected[1] = @returnAddress(); |
| 62 | return frame3(expected, addr_buf); | 62 | return frame3(expected, addr_buf); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 65 | noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 66 | expected[2] = @returnAddress() - 1; | 66 | expected[2] = @returnAddress(); |
| 67 | 67 | ||
| 68 | // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding | 68 | // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding |
| 69 | // to exercise the stack-indirect encoding path | 69 | // to exercise the stack-indirect encoding path |
| ... | @@ -74,7 +74,7 @@ noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr | ... | @@ -74,7 +74,7 @@ noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 76 | noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 77 | expected[3] = @returnAddress() - 1; | 77 | expected[3] = @returnAddress(); |
| 78 | return frame1(expected, addr_buf); | 78 | return frame1(expected, addr_buf); |
| 79 | } | 79 | } |
| 80 | 80 |
test/standalone/stack_iterator/unwind_freestanding.zig+4-4| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | 4 | ||
| 5 | noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 5 | noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 6 | expected[0] = @returnAddress() - 1; | 6 | expected[0] = @returnAddress(); |
| 7 | return std.debug.captureCurrentStackTrace(.{ | 7 | return std.debug.captureCurrentStackTrace(.{ |
| 8 | .first_address = @returnAddress(), | 8 | .first_address = @returnAddress(), |
| 9 | .allow_unsafe_unwind = true, | 9 | .allow_unsafe_unwind = true, |
| ... | @@ -11,12 +11,12 @@ noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr | ... | @@ -11,12 +11,12 @@ noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 13 | noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 14 | expected[1] = @returnAddress() - 1; | 14 | expected[1] = @returnAddress(); |
| 15 | return frame3(expected, addr_buf); | 15 | return frame3(expected, addr_buf); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 18 | noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 19 | expected[2] = @returnAddress() - 1; | 19 | expected[2] = @returnAddress(); |
| 20 | 20 | ||
| 21 | // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding | 21 | // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding |
| 22 | // to exercise the stack-indirect encoding path | 22 | // to exercise the stack-indirect encoding path |
| ... | @@ -27,7 +27,7 @@ noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr | ... | @@ -27,7 +27,7 @@ noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTr |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { | 29 | noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { |
| 30 | expected[3] = @returnAddress() - 1; | 30 | expected[3] = @returnAddress(); |
| 31 | return frame1(expected, addr_buf); | 31 | return frame1(expected, addr_buf); |
| 32 | } | 32 | } |
| 33 | 33 |