Commit 60b2031831

Andrew Kelley <superjoe30@gmail.com>
2018-03-10 04:06:24
improvements to stack traces
* @panic generates an error return trace * printing an error return trace no longer interferes with normal stack traces. * instead of ignore_frame_count, we look at the return address when you call panic, and that's the first stack trace function makes stack traces much cleaner - the error return trace flows gracefully into the stack trace
1 parent 20011a7
Changed files (3)
src
std
debug
special
src/codegen.cpp
@@ -3299,8 +3299,8 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
 static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
         IrInstructionErrorReturnTrace *instruction)
 {
-    TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
     if (g->cur_err_ret_trace_val == nullptr) {
+        TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
         return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
     }
     return g->cur_err_ret_trace_val;
@@ -3925,7 +3925,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
 }
 
 static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
-    gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr);
+    gen_panic(g, ir_llvm_value(g, instruction->msg), g->cur_err_ret_trace_val);
     return nullptr;
 }
 
std/debug/index.zig
@@ -9,6 +9,7 @@ const macho = std.macho;
 const ArrayList = std.ArrayList;
 const builtin = @import("builtin");
 
+pub var stack_trace_start_address: ?usize = null;
 pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
 
 /// Tries to write to stderr, unbuffered, and ignores any error returned.
@@ -51,8 +52,7 @@ pub fn dumpCurrentStackTrace() void {
         stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
         return;
     };
-    defer debug_info.close();
-    writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| {
+    writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
         stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
         return;
     };
@@ -65,7 +65,6 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
         stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
         return;
     };
-    defer debug_info.close();
     writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
         stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
         return;
@@ -162,18 +161,38 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var,
 }
 
 pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
-    debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void
+    debug_info: &ElfStackTrace, tty_color: bool) !void
 {
-    var ignored_count: usize = 0;
+    const AddressState = union(enum) {
+        NotLookingForStartAddress,
+        LookingForStartAddress: usize,
+        FoundStartAddress,
+    };
+    // TODO: I want to express like this:
+    //var addr_state = if (stack_trace_start_address) |addr| AddressState { .LookingForStartAddress = addr }
+    //    else AddressState.NotLookingForStartAddress;
+    var addr_state: AddressState = undefined;
+    if (stack_trace_start_address) |addr| {
+        addr_state = AddressState { .LookingForStartAddress = addr };
+    } else {
+        addr_state = AddressState.NotLookingForStartAddress;
+    }
 
     var fp = @ptrToInt(@frameAddress());
     while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
-        if (ignored_count < ignore_frame_count) {
-            ignored_count += 1;
-            continue;
-        }
-
         const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
+
+        switch (addr_state) {
+            AddressState.NotLookingForStartAddress => continue,
+            AddressState.LookingForStartAddress => |addr| {
+                if (return_address == addr) {
+                    addr_state = AddressState.FoundStartAddress;
+                } else {
+                    continue;
+                }
+            },
+            AddressState.FoundStartAddress => {},
+        }
         try printSourceAtAddress(debug_info, out_stream, return_address);
     }
 }
std/special/panic.zig
@@ -14,10 +14,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
             while (true) {}
         },
         else => {
+            std.debug.stack_trace_start_address = @ptrToInt(@returnAddress());
             if (error_return_trace) |trace| {
-                @import("std").debug.panicWithTrace(trace, "{}", msg);
+                std.debug.panicWithTrace(trace, "{}", msg);
             }
-            @import("std").debug.panic("{}", msg);
+            std.debug.panic("{}", msg);
         },
     }
 }