Commit e82596950f

kcbanner <kcbanner@gmail.com>
2023-04-18 06:01:41
debug: fix missing stack traces during crashes on windows
- walk the stack via the method that is aware of unwind info (fixes x86_64 / aarch64 traces) - enhance the output for frames where the debug info isn't available by printing the module name
1 parent 3428653
Changed files (1)
lib
lib/std/debug.zig
@@ -183,6 +183,11 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
             return;
         };
         const tty_config = detectTTYConfig(io.getStdErr());
+        if (native_os == .windows) {
+            writeCurrentStackTraceWindows(stderr, debug_info, tty_config, ip) catch return;
+            return;
+        }
+
         printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;
         var it = StackIterator.init(null, bp);
         while (it.next()) |return_address| {
@@ -595,7 +600,16 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {
         if (windows.ntdll.RtlLookupFunctionEntry(current_regs.ip, &image_base, &history_table)) |runtime_function| {
             var handler_data: ?*anyopaque = null;
             var establisher_frame: u64 = undefined;
-            _ = windows.ntdll.RtlVirtualUnwind(windows.UNW_FLAG_NHANDLER, image_base, current_regs.ip, runtime_function, &context, &handler_data, &establisher_frame, null);
+            _ = windows.ntdll.RtlVirtualUnwind(
+                windows.UNW_FLAG_NHANDLER,
+                image_base,
+                current_regs.ip,
+                runtime_function,
+                &context,
+                &handler_data,
+                &establisher_frame,
+                null,
+            );
         } else {
             // leaf function
             context.setIp(@intToPtr(*u64, current_regs.sp).*);
@@ -769,23 +783,29 @@ test "machoSearchSymbols" {
     try testing.expectEqual(&symbols[2], machoSearchSymbols(&symbols, 5000).?);
 }
 
+fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
+    const module_name = debug_info.getModuleNameForAddress(address);
+    return printLineInfo(
+        out_stream,
+        null,
+        address,
+        "???",
+        module_name orelse "???",
+        tty_config,
+        printLineFromFileAnyOs,
+    );
+}
+
 pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
     const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
-        error.MissingDebugInfo, error.InvalidDebugInfo => {
-            return printLineInfo(
-                out_stream,
-                null,
-                address,
-                "???",
-                "???",
-                tty_config,
-                printLineFromFileAnyOs,
-            );
-        },
+        error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
         else => return err,
     };
 
-    const symbol_info = try module.getSymbolAtAddress(debug_info.allocator, address);
+    const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) {
+        error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
+        else => return err,
+    };
     defer symbol_info.deinit(debug_info.allocator);
 
     return printLineInfo(
@@ -1275,15 +1295,16 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
     }
 }
 
-pub const ModuleInfo = struct {
+pub const WindowsModuleInfo = struct {
     base_address: usize,
     size: u32,
+    name: []const u8,
 };
 
 pub const DebugInfo = struct {
     allocator: mem.Allocator,
     address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
-    modules: if (native_os == .windows) std.ArrayListUnmanaged(ModuleInfo) else void,
+    modules: if (native_os == .windows) std.ArrayListUnmanaged(WindowsModuleInfo) else void,
 
     pub fn init(allocator: mem.Allocator) !DebugInfo {
         var debug_info = DebugInfo{
@@ -1299,7 +1320,6 @@ pub const DebugInfo = struct {
                     else => |err| return windows.unexpectedError(err),
                 }
             }
-
             defer windows.CloseHandle(handle);
 
             var module_entry: windows.MODULEENTRY32 = undefined;
@@ -1313,6 +1333,7 @@ pub const DebugInfo = struct {
                 const module_info = try debug_info.modules.addOne(allocator);
                 module_info.base_address = @ptrToInt(module_entry.modBaseAddr);
                 module_info.size = module_entry.modBaseSize;
+                module_info.name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{};
                 module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1;
             }
         }
@@ -1328,7 +1349,12 @@ pub const DebugInfo = struct {
             self.allocator.destroy(mdi);
         }
         self.address_map.deinit();
-        if (native_os == .windows) self.modules.deinit(self.allocator);
+        if (native_os == .windows) {
+            for (self.modules.items) |module| {
+                self.allocator.free(module.name);
+            }
+            self.modules.deinit(self.allocator);
+        }
     }
 
     pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
@@ -1351,6 +1377,22 @@ pub const DebugInfo = struct {
         }
     }
 
+    pub fn getModuleNameForAddress(self: *DebugInfo, address: usize) ?[]const u8 {
+        if (builtin.zig_backend == .stage2_c) {
+            return null;
+        } else if (comptime builtin.target.isDarwin()) {
+            return null;
+        } else if (native_os == .windows) {
+            return self.lookupModuleNameWin32(address);
+        } else if (native_os == .haiku) {
+            return null;
+        } else if (comptime builtin.target.isWasm()) {
+            return null;
+        } else {
+            return null;
+        }
+    }
+
     fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
         const image_count = std.c._dyld_image_count();
 
@@ -1428,6 +1470,15 @@ pub const DebugInfo = struct {
         return error.MissingDebugInfo;
     }
 
+    fn lookupModuleNameWin32(self: *DebugInfo, address: usize) ?[]const u8 {
+        for (self.modules.items) |module| {
+            if (address >= module.base_address and address < module.base_address + module.size) {
+                return module.name;
+            }
+        }
+        return null;
+    }
+
     fn lookupModuleDl(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
         var ctx: struct {
             // Input