authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-02-05 23:40:36+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-06 11:25:40-05:00
logcfcaf09cce38821f8b82b840db537d7082a7a8dc
treee51369596b0bc0fc4efa32d8307e9ca6f5c1a5f5
parent5cf30b6791243f1df55c7730399d1a772c85305f

debug: Improve the frame-walking strategy

Clean up the code a bit and introduce a few checks meant to avoid overshooting the end of the frame chain. The code is now stable enough not to cause panics during the call frame walking.

2 files changed, 49 insertions(+), 28 deletions(-)

lib/std/debug.zig+40-28
...@@ -130,13 +130,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {...@@ -130,13 +130,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
130 };130 };
131 const tty_config = detectTTYConfig();131 const tty_config = detectTTYConfig();
132 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;132 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;
133 const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*;133 var it = StackIterator.init(null, bp);
134 if (first_return_address == 0) return; // The whole call stack may be optimized out
135 printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_config) catch return;
136 var it = StackIterator{
137 .first_addr = null,
138 .fp = bp,
139 };
140 while (it.next()) |return_address| {134 while (it.next()) |return_address| {
141 printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return;135 printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return;
142 }136 }
...@@ -179,7 +173,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace...@@ -179,7 +173,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace
179 }173 }
180 stack_trace.index = slice.len;174 stack_trace.index = slice.len;
181 } else {175 } else {
182 var it = StackIterator.init(first_address);176 var it = StackIterator.init(first_address, null);
183 for (stack_trace.instruction_addresses) |*addr, i| {177 for (stack_trace.instruction_addresses) |*addr, i| {
184 addr.* = it.next() orelse {178 addr.* = it.next() orelse {
185 stack_trace.index = i;179 stack_trace.index = i;
...@@ -291,13 +285,15 @@ pub fn writeStackTrace(...@@ -291,13 +285,15 @@ pub fn writeStackTrace(
291}285}
292286
293pub const StackIterator = struct {287pub const StackIterator = struct {
294 first_addr: ?usize,288 // Skip every frame before this address is found
289 first_address: ?usize,
290 // Last known value of the frame pointer register
295 fp: usize,291 fp: usize,
296292
297 pub fn init(first_addr: ?usize) StackIterator {293 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
298 return StackIterator{294 return StackIterator{
299 .first_addr = first_addr,295 .first_address = first_address,
300 .fp = @frameAddress(),296 .fp = fp orelse @frameAddress(),
301 };297 };
302 }298 }
303299
...@@ -305,29 +301,45 @@ pub const StackIterator = struct {...@@ -305,29 +301,45 @@ pub const StackIterator = struct {
305 // the previous fp is stored, while on some other architectures such as301 // the previous fp is stored, while on some other architectures such as
306 // RISC-V it points to the "top" of the frame, just above where the previous302 // RISC-V it points to the "top" of the frame, just above where the previous
307 // fp and the return address are stored.303 // fp and the return address are stored.
308 const fp_adjust_factor = if (builtin.arch == .riscv32 or builtin.arch == .riscv64)304 const fp_offset = if (builtin.arch.isRISCV())
309 2 * @sizeOf(usize)305 2 * @sizeOf(usize)
310 else306 else
311 0;307 0;
312308
313 fn next(self: *StackIterator) ?usize {309 fn next(self: *StackIterator) ?usize {
314 if (self.fp <= fp_adjust_factor) return null;310 var address = self.next_internal() orelse return null;
315 self.fp = @intToPtr(*const usize, self.fp - fp_adjust_factor).*;311
316 if (self.fp <= fp_adjust_factor) return null;312 if (self.first_address) |first_address| {
317313 while (address != first_address) {
318 if (self.first_addr) |addr| {314 address = self.next_internal() orelse return null;
319 while (self.fp > fp_adjust_factor) : (self.fp = @intToPtr(*const usize, self.fp - fp_adjust_factor).*) {
320 const return_address = @intToPtr(*const usize, self.fp - fp_adjust_factor + @sizeOf(usize)).*;
321 if (addr == return_address) {
322 self.first_addr = null;
323 return return_address;
324 }
325 }315 }
316 self.first_address = null;
326 }317 }
327318
328 const return_address = @intToPtr(*const usize, self.fp - fp_adjust_factor + @sizeOf(usize)).*;319 return address;
329 if (return_address == 0) return null;320 }
330 return return_address;321
322 fn next_internal(self: *StackIterator) ?usize {
323 const fp = math.sub(usize, self.fp, fp_offset) catch return null;
324
325 // Sanity check
326 if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)))
327 return null;
328
329 const new_fp = @intToPtr(*const usize, fp).*;
330
331 // Sanity check: the stack grows down thus all the parent frames must be
332 // be at addresses that are greater (or equal) than the previous one.
333 // A zero frame pointer often signals this is the last frame, that case
334 // is gracefully handled by the next call to next_internal
335 if (new_fp != 0 and new_fp < self.fp)
336 return null;
337
338 const new_pc = @intToPtr(*const usize, fp + @sizeOf(usize)).*;
339
340 self.fp = new_fp;
341
342 return new_pc;
331 }343 }
332};344};
333345
...@@ -340,7 +352,7 @@ pub fn writeCurrentStackTrace(...@@ -340,7 +352,7 @@ pub fn writeCurrentStackTrace(
340 if (builtin.os == .windows) {352 if (builtin.os == .windows) {
341 return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);353 return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);
342 }354 }
343 var it = StackIterator.init(start_addr);355 var it = StackIterator.init(start_addr, null);
344 while (it.next()) |return_address| {356 while (it.next()) |return_address| {
345 try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config);357 try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config);
346 }358 }
lib/std/target.zig+9
...@@ -238,6 +238,13 @@ pub const Target = union(enum) {...@@ -238,6 +238,13 @@ pub const Target = union(enum) {
238 };238 };
239 }239 }
240240
241 pub fn isRISCV(arch: Arch) bool {
242 return switch (arch) {
243 .riscv32, .riscv64 => true,
244 else => false,
245 };
246 }
247
241 pub fn isMIPS(arch: Arch) bool {248 pub fn isMIPS(arch: Arch) bool {
242 return switch (arch) {249 return switch (arch) {
243 .mips, .mipsel, .mips64, .mips64el => true,250 .mips, .mipsel, .mips64, .mips64el => true,
...@@ -594,6 +601,8 @@ pub const Target = union(enum) {...@@ -594,6 +601,8 @@ pub const Target = union(enum) {
594 }601 }
595602
596 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {603 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
604 @setEvalBranchQuota(1000000);
605
597 var old = set.ints;606 var old = set.ints;
598 while (true) {607 while (true) {
599 for (all_features_list) |feature, index_usize| {608 for (all_features_list) |feature, index_usize| {