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
This commit is contained in:
kcbanner 2023-04-18 00:01:41 -04:00 committed by Andrew Kelley
parent 34286530b7
commit e82596950f

View File

@ -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