| author | |
| committer | |
| log | f6a3a0ca723325b2b84e67b1f7ef78a8f69df650 |
| tree | 48d52b5a735047e850e548cbfa032b7b2939ed6f |
| parent | 5a4b5c8b94236429263ef25d9287b6c5cc829bde |
The intention behind the iterator was to avoid needing to allocate the
symbols, but in practice we need to allocate them anyway since we need
to reverse their order and don't have random access. The alternative
would be an N^2 algorithm.
In practice this isn't that bad, because even if the allocation fails,
we'll still end up printing the address, so the user still ends up with
the necessary information to reconstruct the crash. I don't think it's
worth it to try to set up some kind of ring buffer or return partial
results on failure, but may revisit this.5 files changed, 193 insertions(+), 248 deletions(-)
lib/std/debug.zig+29-25| ... | @@ -38,8 +38,12 @@ pub const cpu_context = @import("debug/cpu_context.zig"); | ... | @@ -38,8 +38,12 @@ pub const cpu_context = @import("debug/cpu_context.zig"); |
| 38 | /// pub const init: SelfInfo; | 38 | /// pub const init: SelfInfo; |
| 39 | /// pub fn deinit(si: *SelfInfo, io: Io) void; | 39 | /// pub fn deinit(si: *SelfInfo, io: Io) void; |
| 40 | /// | 40 | /// |
| 41 | /// /// Returns an iterator over the symbols and source locations of the instruction at `address`. | 41 | /// /// Returns the the symbols and source locations of the instruction at `address`. Often this |
| 42 | /// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator; | 42 | /// /// will return a single result, but in the case of inlines it may return multiple. When |
| 43 | /// /// multiple results are returned, they are sorted from innermost to outermost. | ||
| 44 | /// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const Symbol; | ||
| 45 | /// /// Frees symbols returned from `getSymbols`. | ||
| 46 | /// pub fn freeSymbols(si: *SelfInfo, symbols: []const Symbol) void; | ||
| 43 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. | 47 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. |
| 44 | /// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; | 48 | /// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; |
| 45 | /// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize; | 49 | /// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize; |
| ... | @@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig"); | ... | @@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig"); |
| 60 | /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's | 64 | /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's |
| 61 | /// /// return address, or 0 if the end of the stack has been reached. | 65 | /// /// return address, or 0 if the end of the stack has been reached. |
| 62 | /// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize; | 66 | /// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize; |
| 63 | /// /// Iterates symbols found at an address. | ||
| 64 | /// pub const SymbolIterator = struct { | ||
| 65 | /// pub fn deinit(Self: *SymbolIterator, io: Io) void; | ||
| 66 | /// pub fn next(self: *SymbolIterator) ?SelfInfoError!Symbol; | ||
| 67 | /// }; | ||
| 68 | /// ``` | 67 | /// ``` |
| 69 | pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) | 68 | pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) |
| 70 | root.debug.SelfInfo | 69 | root.debug.SelfInfo |
| ... | @@ -1193,35 +1192,35 @@ fn printSourceAtAddress( | ... | @@ -1193,35 +1192,35 @@ fn printSourceAtAddress( |
| 1193 | t: Io.Terminal, | 1192 | t: Io.Terminal, |
| 1194 | options: PrintSourceAddressOptions, | 1193 | options: PrintSourceAddressOptions, |
| 1195 | ) Writer.Error!void { | 1194 | ) Writer.Error!void { |
| 1196 | var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address); | 1195 | const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| { |
| 1197 | defer symbols.deinit(io); | 1196 | t.setColor(.dim) catch {}; |
| 1198 | while (symbols.next()) |curr| { | 1197 | defer t.setColor(.reset) catch {}; |
| 1199 | const symbol: Symbol = curr catch |err| switch (err) { | 1198 | switch (err) { |
| 1200 | error.MissingDebugInfo, | 1199 | error.MissingDebugInfo, |
| 1201 | error.UnsupportedDebugInfo, | 1200 | error.UnsupportedDebugInfo, |
| 1202 | error.InvalidDebugInfo, | 1201 | error.InvalidDebugInfo, |
| 1203 | => .unknown, | 1202 | => {}, |
| 1204 | error.ReadFailed, error.Unexpected, error.Canceled => s: { | 1203 | error.ReadFailed, error.Unexpected, error.Canceled => { |
| 1205 | t.setColor(.dim) catch {}; | ||
| 1206 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); | 1204 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); |
| 1207 | t.setColor(.reset) catch {}; | ||
| 1208 | break :s .unknown; | ||
| 1209 | }, | 1205 | }, |
| 1210 | error.OutOfMemory => s: { | 1206 | error.OutOfMemory => { |
| 1211 | t.setColor(.dim) catch {}; | 1207 | t.setColor(.dim) catch {}; |
| 1212 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); | 1208 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); |
| 1213 | t.setColor(.reset) catch {}; | 1209 | t.setColor(.reset) catch {}; |
| 1214 | break :s .unknown; | ||
| 1215 | }, | 1210 | }, |
| 1216 | }; | 1211 | } |
| 1217 | defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); | 1212 | return printLineInfo(io, t, debug_info, null, options.address, null, null); |
| 1213 | }; | ||
| 1214 | defer debug_info.freeSymbols(symbols); | ||
| 1215 | for (symbols) |symbol| { | ||
| 1218 | try printLineInfo( | 1216 | try printLineInfo( |
| 1219 | io, | 1217 | io, |
| 1220 | t, | 1218 | t, |
| 1219 | debug_info, | ||
| 1221 | symbol.source_location, | 1220 | symbol.source_location, |
| 1222 | options.address, | 1221 | options.address, |
| 1223 | symbol.name orelse "???", | 1222 | symbol.name, |
| 1224 | symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???", | 1223 | symbol.compile_unit_name, |
| 1225 | ); | 1224 | ); |
| 1226 | if (!options.resolve_inline_callers) break; | 1225 | if (!options.resolve_inline_callers) break; |
| 1227 | } | 1226 | } |
| ... | @@ -1229,10 +1228,11 @@ fn printSourceAtAddress( | ... | @@ -1229,10 +1228,11 @@ fn printSourceAtAddress( |
| 1229 | fn printLineInfo( | 1228 | fn printLineInfo( |
| 1230 | io: Io, | 1229 | io: Io, |
| 1231 | t: Io.Terminal, | 1230 | t: Io.Terminal, |
| 1231 | debug_info: *SelfInfo, | ||
| 1232 | source_location: ?SourceLocation, | 1232 | source_location: ?SourceLocation, |
| 1233 | address: usize, | 1233 | address: usize, |
| 1234 | symbol_name: []const u8, | 1234 | symbol_name: ?[]const u8, |
| 1235 | compile_unit_name: []const u8, | 1235 | compile_unit_name: ?[]const u8, |
| 1236 | ) Writer.Error!void { | 1236 | ) Writer.Error!void { |
| 1237 | const writer = t.writer; | 1237 | const writer = t.writer; |
| 1238 | t.setColor(.bold) catch {}; | 1238 | t.setColor(.bold) catch {}; |
| ... | @@ -1250,7 +1250,11 @@ fn printLineInfo( | ... | @@ -1250,7 +1250,11 @@ fn printLineInfo( |
| 1250 | t.setColor(.reset) catch {}; | 1250 | t.setColor(.reset) catch {}; |
| 1251 | try writer.writeAll(": "); | 1251 | try writer.writeAll(": "); |
| 1252 | t.setColor(.dim) catch {}; | 1252 | t.setColor(.dim) catch {}; |
| 1253 | try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); | 1253 | try writer.print("0x{x} in {s} ({s})", .{ |
| 1254 | address, | ||
| 1255 | symbol_name orelse "???", | ||
| 1256 | compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", | ||
| 1257 | }); | ||
| 1254 | t.setColor(.reset) catch {}; | 1258 | t.setColor(.reset) catch {}; |
| 1255 | try writer.writeAll("\n"); | 1259 | try writer.writeAll("\n"); |
| 1256 | 1260 |
lib/std/debug/Dwarf.zig+13-21| ... | @@ -1545,26 +1545,17 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 { | ... | @@ -1545,26 +1545,17 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 { |
| 1545 | return str[casted_offset..last :0]; | 1545 | return str[casted_offset..last :0]; |
| 1546 | } | 1546 | } |
| 1547 | 1547 | ||
| 1548 | pub const SymbolIterator = struct { | 1548 | pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.debug.SelfInfoError![]const std.debug.Symbol { |
| 1549 | curr: ?std.debug.SelfInfoError!std.debug.Symbol, | 1549 | const symbol = try gpa.create(std.debug.Symbol); |
| 1550 | 1550 | errdefer gpa.destroy(symbol); | |
| 1551 | pub fn deinit(self: *SymbolIterator, _: Io) void { | ||
| 1552 | self.* = undefined; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol { | ||
| 1556 | const result = self.curr; | ||
| 1557 | self.curr = null; | ||
| 1558 | return result; | ||
| 1559 | } | ||
| 1560 | }; | ||
| 1561 | |||
| 1562 | pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) SymbolIterator { | ||
| 1563 | const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) { | 1551 | const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) { |
| 1564 | error.EndOfStream, error.Overflow => return .{ .curr = error.InvalidDebugInfo }, | 1552 | error.EndOfStream, error.Overflow => { |
| 1565 | else => |e| return .{ .curr = e }, | 1553 | symbol.* = .unknown; |
| 1554 | return symbol[0..1]; | ||
| 1555 | }, | ||
| 1556 | else => |e| return e, | ||
| 1566 | }; | 1557 | }; |
| 1567 | return .{ .curr = .{ | 1558 | symbol.* = .{ |
| 1568 | .name = di.getSymbolName(address), | 1559 | .name = di.getSymbolName(address), |
| 1569 | .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) { | 1560 | .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) { |
| 1570 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | 1561 | error.MissingDebugInfo, error.InvalidDebugInfo => null, |
| ... | @@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb | ... | @@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb |
| 1575 | error.EndOfStream, | 1566 | error.EndOfStream, |
| 1576 | error.Overflow, | 1567 | error.Overflow, |
| 1577 | error.StreamTooLong, | 1568 | error.StreamTooLong, |
| 1578 | => return .{ .curr = error.InvalidDebugInfo }, | 1569 | => return error.InvalidDebugInfo, |
| 1579 | else => |e| return .{ .curr = e }, | 1570 | else => |e| return e, |
| 1580 | }, | 1571 | }, |
| 1581 | } }; | 1572 | }; |
| 1573 | return symbol[0..1]; | ||
| 1582 | } | 1574 | } |
| 1583 | 1575 | ||
| 1584 | /// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and | 1576 | /// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and |
lib/std/debug/SelfInfo/Elf.zig+22-13| ... | @@ -30,41 +30,50 @@ pub fn deinit(si: *SelfInfo, io: Io) void { | ... | @@ -30,41 +30,50 @@ pub fn deinit(si: *SelfInfo, io: Io) void { |
| 30 | if (si.unwind_cache) |cache| gpa.free(cache); | 30 | if (si.unwind_cache) |cache| gpa.free(cache); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | pub const SymbolIterator = std.debug.Dwarf.SymbolIterator; | 33 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol { |
| 34 | |||
| 35 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ||
| 36 | const gpa = std.debug.getDebugInfoAllocator(); | 34 | const gpa = std.debug.getDebugInfoAllocator(); |
| 37 | const module = si.findModule(gpa, io, address, .exclusive) catch |err| return .{ .curr = err }; | 35 | const module = try si.findModule(gpa, io, address, .exclusive); |
| 38 | defer si.rwlock.unlock(io); | 36 | defer si.rwlock.unlock(io); |
| 39 | 37 | ||
| 40 | const vaddr = address - module.load_offset; | 38 | const vaddr = address - module.load_offset; |
| 41 | 39 | ||
| 42 | const loaded_elf = module.getLoadedElf(gpa, io) catch |err| return .{ .curr = err }; | 40 | const loaded_elf = try module.getLoadedElf(gpa, io); |
| 43 | if (loaded_elf.file.dwarf) |*dwarf| { | 41 | if (loaded_elf.file.dwarf) |*dwarf| { |
| 44 | if (!loaded_elf.scanned_dwarf) { | 42 | if (!loaded_elf.scanned_dwarf) { |
| 45 | dwarf.open(gpa, native_endian) catch |err| switch (err) { | 43 | dwarf.open(gpa, native_endian) catch |err| switch (err) { |
| 46 | error.InvalidDebugInfo, | 44 | error.InvalidDebugInfo, |
| 47 | error.MissingDebugInfo, | 45 | error.MissingDebugInfo, |
| 48 | error.OutOfMemory, | 46 | error.OutOfMemory, |
| 49 | => |e| return .{ .curr = e }, | 47 | => |e| return e, |
| 50 | error.EndOfStream, | 48 | error.EndOfStream, |
| 51 | error.Overflow, | 49 | error.Overflow, |
| 52 | error.ReadFailed, | 50 | error.ReadFailed, |
| 53 | error.StreamTooLong, | 51 | error.StreamTooLong, |
| 54 | => return .{ .curr = error.InvalidDebugInfo }, | 52 | => return error.InvalidDebugInfo, |
| 55 | }; | 53 | }; |
| 56 | loaded_elf.scanned_dwarf = true; | 54 | loaded_elf.scanned_dwarf = true; |
| 57 | } | 55 | } |
| 58 | return dwarf.getSymbols(gpa, native_endian, vaddr); | 56 | return dwarf.getSymbols(gpa, native_endian, vaddr); |
| 59 | } | 57 | } |
| 60 | // When DWARF is unavailable, fall back to searching the symtab. | 58 | // When DWARF is unavailable, fall back to searching the symtab. |
| 61 | const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { | 59 | const symbol = try gpa.create(std.debug.Symbol); |
| 62 | error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo }, | 60 | errdefer gpa.destroy(symbol); |
| 63 | error.BadSymtab => return .{ .curr = error.InvalidDebugInfo }, | 61 | symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { |
| 64 | error.OutOfMemory => |e| return .{ .curr = e }, | 62 | error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo, |
| 63 | error.BadSymtab => return error.InvalidDebugInfo, | ||
| 64 | error.OutOfMemory => |e| return e, | ||
| 65 | }; | 65 | }; |
| 66 | 66 | return symbol[0..1]; | |
| 67 | return .{ .curr = symbol }; | 67 | } |
| 68 | pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { | ||
| 69 | _ = si; | ||
| 70 | const gpa = std.debug.getDebugInfoAllocator(); | ||
| 71 | for (symbols) |symbol| { | ||
| 72 | if (symbol.source_location) |source_location| { | ||
| 73 | gpa.free(source_location.file_name); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | gpa.free(symbols); | ||
| 68 | } | 77 | } |
| 69 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | 78 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 70 | const gpa = std.debug.getDebugInfoAllocator(); | 79 | const gpa = std.debug.getDebugInfoAllocator(); |
lib/std/debug/SelfInfo/MachO.zig+28-13| ... | @@ -36,12 +36,15 @@ pub const SymbolIterator = struct { | ... | @@ -36,12 +36,15 @@ pub const SymbolIterator = struct { |
| 36 | } | 36 | } |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | 39 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol { |
| 40 | const gpa = std.debug.getDebugInfoAllocator(); | 40 | const gpa = std.debug.getDebugInfoAllocator(); |
| 41 | const module = si.findModule(gpa, io, address) catch |err| return .{ .curr = err }; | 41 | const module = try si.findModule(gpa, io, address); |
| 42 | defer si.mutex.unlock(io); | 42 | defer si.mutex.unlock(io); |
| 43 | 43 | ||
| 44 | const file = module.getFile(gpa, io) catch |err| return .{ .curr = err }; | 44 | const file = try module.getFile(gpa, io); |
| 45 | |||
| 46 | const symbol = try gpa.create(std.debug.Symbol); | ||
| 47 | errdefer gpa.destroy(symbol); | ||
| 45 | 48 | ||
| 46 | // This is not necessarily the same as the vmaddr_slide that dyld would report. This is | 49 | // This is not necessarily the same as the vmaddr_slide that dyld would report. This is |
| 47 | // because the segments in the file on disk might differ from the ones in memory. Normally | 50 | // because the segments in the file on disk might differ from the ones in memory. Normally |
| ... | @@ -57,26 +60,27 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ... | @@ -57,26 +60,27 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { |
| 57 | 60 | ||
| 58 | const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch { | 61 | const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch { |
| 59 | // Return at least the symbol name if available. | 62 | // Return at least the symbol name if available. |
| 60 | return .{ .curr = .{ | 63 | symbol.* = .{ |
| 61 | .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err }, | 64 | .name = try file.lookupSymbolName(vaddr), |
| 62 | .compile_unit_name = null, | 65 | .compile_unit_name = null, |
| 63 | .source_location = null, | 66 | .source_location = null, |
| 64 | } }; | 67 | }; |
| 68 | return symbol[0..1]; | ||
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| 67 | const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch { | 71 | const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch { |
| 68 | // Return at least the symbol name if available. | 72 | // Return at least the symbol name if available. |
| 69 | return .{ .curr = .{ | 73 | symbol.* = .{ |
| 70 | .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err }, | 74 | .name = try file.lookupSymbolName(vaddr), |
| 71 | .compile_unit_name = null, | 75 | .compile_unit_name = null, |
| 72 | .source_location = null, | 76 | .source_location = null, |
| 73 | } }; | 77 | }; |
| 78 | return symbol[0..1]; | ||
| 74 | }; | 79 | }; |
| 75 | 80 | ||
| 76 | return .{ .curr = .{ | 81 | symbol.* = .{ |
| 77 | .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse | 82 | .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse |
| 78 | file.lookupSymbolName(vaddr) catch |err| | 83 | try file.lookupSymbolName(vaddr), |
| 79 | return .{ .curr = err }, | ||
| 80 | .compile_unit_name = compile_unit.die.getAttrString( | 84 | .compile_unit_name = compile_unit.die.getAttrString( |
| 81 | ofile_dwarf, | 85 | ofile_dwarf, |
| 82 | native_endian, | 86 | native_endian, |
| ... | @@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ... | @@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { |
| 92 | compile_unit, | 96 | compile_unit, |
| 93 | ofile_vaddr, | 97 | ofile_vaddr, |
| 94 | ) catch null, | 98 | ) catch null, |
| 95 | } }; | 99 | }; |
| 100 | return symbol[0..1]; | ||
| 101 | } | ||
| 102 | pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { | ||
| 103 | _ = si; | ||
| 104 | const gpa = std.debug.getDebugInfoAllocator(); | ||
| 105 | for (symbols) |symbol| { | ||
| 106 | if (symbol.source_location) |source_location| { | ||
| 107 | gpa.free(source_location.file_name); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | gpa.free(symbols); | ||
| 96 | } | 111 | } |
| 97 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | 112 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 98 | _ = si; | 113 | _ = si; |
lib/std/debug/SelfInfo/Windows.zig+101-176| ... | @@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void { | ... | @@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void { |
| 25 | si.modules.deinit(gpa); | 25 | si.modules.deinit(gpa); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | pub const SymbolIterator = struct { | 28 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol { |
| 29 | err: Error!void = {}, | 29 | const gpa = std.debug.getDebugInfoAllocator(); |
| 30 | lock: ?*Io.RwLock, | 30 | try si.lock.lockShared(io); |
| 31 | module: *Module, | 31 | defer si.lock.unlockShared(io); |
| 32 | symbols: Module.DebugInfo.Symbols, | 32 | const module = try si.findModule(gpa, address); |
| 33 | 33 | const di = try module.getDebugInfo(gpa, io); | |
| 34 | pub fn deinit(self: *SymbolIterator, io: Io) void { | 34 | return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase)); |
| 35 | if (self.lock) |lock| lock.unlockShared(io); | 35 | } |
| 36 | self.symbols.deinit(io); | ||
| 37 | self.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | fn failing(err: Error) SymbolIterator { | ||
| 41 | return .{ | ||
| 42 | .err = err, | ||
| 43 | .lock = null, | ||
| 44 | .module = undefined, | ||
| 45 | .symbols = .none, | ||
| 46 | }; | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol { | ||
| 50 | // Check for errors | ||
| 51 | self.err catch |err| { | ||
| 52 | self.err = {}; | ||
| 53 | self.symbols = .none; | ||
| 54 | return err; | ||
| 55 | }; | ||
| 56 | |||
| 57 | // Return the next symbol for the debug info type | ||
| 58 | switch (self.symbols) { | ||
| 59 | .pdb => |*info| { | ||
| 60 | // The failure cases are unreachable because we only set the pdb field if these are | ||
| 61 | // set | ||
| 62 | const di = if (self.module.di.?) |*di| di else |_| unreachable; | ||
| 63 | const pdb = if (di.pdb) |*pdb| pdb else unreachable; | ||
| 64 | |||
| 65 | // Get the next inlinee if it exists | ||
| 66 | if (info.proc) |proc| { | ||
| 67 | const offset_in_func = info.addr - proc.code_offset; | ||
| 68 | while (info.inline_sites.pop()) |site| { | ||
| 69 | // If our address points into this site, get the source location it points | ||
| 70 | // at | ||
| 71 | const inlinee_src_line = pdb.getInlineeSourceLine( | ||
| 72 | info.module, | ||
| 73 | site.inlinee, | ||
| 74 | ) orelse continue; | ||
| 75 | const maybe_loc = pdb.getInlineSiteSourceLocation( | ||
| 76 | info.module, | ||
| 77 | site, | ||
| 78 | inlinee_src_line.info, | ||
| 79 | offset_in_func, | ||
| 80 | ) catch continue; | ||
| 81 | const loc = maybe_loc orelse continue; | ||
| 82 | |||
| 83 | // If we've found a match, filter out any duplicates that might follow. | ||
| 84 | // Tools like llvm-addr2line output duplicate sites in the same cases as us, | ||
| 85 | // implying that they exist in the underlying data and are not indicative of | ||
| 86 | // a parser bug. | ||
| 87 | while (info.inline_sites.getLastOrNull()) |top| { | ||
| 88 | if (top.inlinee != site.inlinee) break; | ||
| 89 | _ = info.inline_sites.pop(); | ||
| 90 | } | ||
| 91 | |||
| 92 | return .{ | ||
| 93 | .name = pdb.findInlineeName(site.inlinee), | ||
| 94 | .compile_unit_name = fs.path.basename(info.module.obj_file_name), | ||
| 95 | .source_location = loc, | ||
| 96 | }; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | 36 | ||
| 100 | // Return the main symbol and end the iterator | 37 | pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { |
| 101 | defer self.symbols = .none; | 38 | _ = si; |
| 102 | return .{ | 39 | const gpa = std.debug.getDebugInfoAllocator(); |
| 103 | .name = if (info.proc) |proc| pdb.getSymbolName(proc) else null, | 40 | for (symbols) |symbol| { |
| 104 | .compile_unit_name = fs.path.basename(info.module.obj_file_name), | 41 | if (symbol.source_location) |source_location| { |
| 105 | .source_location = pdb.getLineNumberInfo(info.module, info.addr) catch null, | 42 | gpa.free(source_location.file_name); |
| 106 | }; | ||
| 107 | }, | ||
| 108 | .dwarf => |*info| return info.next(), | ||
| 109 | .none => return null, | ||
| 110 | } | 43 | } |
| 111 | } | 44 | } |
| 112 | }; | 45 | gpa.free(symbols); |
| 113 | |||
| 114 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ||
| 115 | const gpa = std.debug.getDebugInfoAllocator(); | ||
| 116 | si.lock.lockShared(io) catch |err| return .failing(err); | ||
| 117 | errdefer si.lock.unlockShared(io); | ||
| 118 | const module = si.findModule(gpa, address) catch |err| return .failing(err); | ||
| 119 | const di = module.getDebugInfo(gpa, io) catch |err| return .failing(err); | ||
| 120 | const symbols = Module.DebugInfo.Symbols.init(di, address - @intFromPtr(module.entry.DllBase)) | ||
| 121 | catch |err| return .failing(err); | ||
| 122 | errdefer comptime unreachable; | ||
| 123 | return .{ | ||
| 124 | .lock = &si.lock, | ||
| 125 | .module = module, | ||
| 126 | .symbols = symbols, | ||
| 127 | }; | ||
| 128 | } | 46 | } |
| 47 | |||
| 129 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | 48 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 130 | const gpa = std.debug.getDebugInfoAllocator(); | 49 | const gpa = std.debug.getDebugInfoAllocator(); |
| 131 | try si.lock.lockShared(io); | 50 | try si.lock.lockShared(io); |
| ... | @@ -333,94 +252,100 @@ const Module = struct { | ... | @@ -333,94 +252,100 @@ const Module = struct { |
| 333 | arena.deinit(); | 252 | arena.deinit(); |
| 334 | } | 253 | } |
| 335 | 254 | ||
| 336 | pub const Symbols = union(enum) { | 255 | fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol { |
| 337 | pdb: struct { | 256 | pdb: { |
| 338 | module: *Pdb.Module, | 257 | const pdb = &(di.pdb orelse break :pdb); |
| 339 | proc: ?*align(1) const std.pdb.ProcSym, | 258 | var coff_section: *align(1) const coff.SectionHeader = undefined; |
| 340 | addr: usize, | 259 | const mod_index = for (pdb.sect_contribs) |sect_contrib| { |
| 341 | /// Inline sites are stored in the pdb in reverse order, so we build up a list of up | 260 | if (sect_contrib.section > di.coff_section_headers.len) continue; |
| 342 | /// front so that our iterator can return them in the correct order without doing an | 261 | // Remember that SectionContribEntry.Section is 1-based. |
| 343 | /// n^2 search. We don't try to filter inline sites based on address until the user | 262 | coff_section = &di.coff_section_headers[sect_contrib.section - 1]; |
| 344 | /// calls `next` as this requires parsing binary annotations, and this is work we | 263 | |
| 345 | /// may be able to elide if the caller chooses to early out before finishing | 264 | const vaddr_start = coff_section.virtual_address + sect_contrib.offset; |
| 346 | /// iteration, e.g. because they only wanted the topmost call. | 265 | const vaddr_end = vaddr_start + sect_contrib.size; |
| 347 | inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym), | 266 | if (vaddr >= vaddr_start and vaddr < vaddr_end) { |
| 348 | }, | 267 | break sect_contrib.module_index; |
| 349 | dwarf: std.debug.Dwarf.SymbolIterator, | ||
| 350 | none: void, | ||
| 351 | |||
| 352 | fn init(di: *DebugInfo, vaddr: usize) Error!Symbols { | ||
| 353 | const gpa = std.debug.getDebugInfoAllocator(); | ||
| 354 | |||
| 355 | pdb: { | ||
| 356 | const pdb = &(di.pdb orelse break :pdb); | ||
| 357 | var coff_section: *align(1) const coff.SectionHeader = undefined; | ||
| 358 | const mod_index = for (pdb.sect_contribs) |sect_contrib| { | ||
| 359 | if (sect_contrib.section > di.coff_section_headers.len) continue; | ||
| 360 | // Remember that SectionContribEntry.Section is 1-based. | ||
| 361 | coff_section = &di.coff_section_headers[sect_contrib.section - 1]; | ||
| 362 | |||
| 363 | const vaddr_start = coff_section.virtual_address + sect_contrib.offset; | ||
| 364 | const vaddr_end = vaddr_start + sect_contrib.size; | ||
| 365 | if (vaddr >= vaddr_start and vaddr < vaddr_end) { | ||
| 366 | break sect_contrib.module_index; | ||
| 367 | } | ||
| 368 | } else { | ||
| 369 | // we have no information to add to the address | ||
| 370 | break :pdb; | ||
| 371 | }; | ||
| 372 | const module = pdb.getModule(mod_index) catch |err| switch (err) { | ||
| 373 | error.InvalidDebugInfo, | ||
| 374 | error.MissingDebugInfo, | ||
| 375 | error.OutOfMemory, | ||
| 376 | => |e| return e, | ||
| 377 | |||
| 378 | error.ReadFailed, | ||
| 379 | error.EndOfStream, | ||
| 380 | => return error.InvalidDebugInfo, | ||
| 381 | } orelse { | ||
| 382 | return error.InvalidDebugInfo; // bad module index | ||
| 383 | }; | ||
| 384 | |||
| 385 | const addr = vaddr - coff_section.virtual_address; | ||
| 386 | const maybe_proc = pdb.getProcSym(module, addr); | ||
| 387 | |||
| 388 | var inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym) = .empty; | ||
| 389 | if (maybe_proc) |proc| { | ||
| 390 | var iter = pdb.getInlinees(module, proc); | ||
| 391 | while (iter.next(module)) |inline_site| { | ||
| 392 | try inline_sites.append(gpa, inline_site); | ||
| 393 | } | ||
| 394 | } | 268 | } |
| 269 | } else { | ||
| 270 | // we have no information to add to the address | ||
| 271 | break :pdb; | ||
| 272 | }; | ||
| 273 | const module = pdb.getModule(mod_index) catch |err| switch (err) { | ||
| 274 | error.InvalidDebugInfo, | ||
| 275 | error.MissingDebugInfo, | ||
| 276 | error.OutOfMemory, | ||
| 277 | => |e| return e, | ||
| 278 | |||
| 279 | error.ReadFailed, | ||
| 280 | error.EndOfStream, | ||
| 281 | => return error.InvalidDebugInfo, | ||
| 282 | } orelse { | ||
| 283 | return error.InvalidDebugInfo; // bad module index | ||
| 284 | }; | ||
| 395 | 285 | ||
| 396 | return .{ .pdb = .{ | 286 | const addr = vaddr - coff_section.virtual_address; |
| 397 | .module = module, | 287 | const maybe_proc = pdb.getProcSym(module, addr); |
| 398 | .proc = maybe_proc, | 288 | var symbols: std.ArrayList(std.debug.Symbol) = .empty; |
| 399 | .addr = addr, | 289 | errdefer symbols.deinit(gpa); |
| 400 | .inline_sites = inline_sites, | 290 | |
| 401 | } }; | 291 | if (maybe_proc) |proc| { |
| 402 | } | 292 | const offset_in_func = addr - proc.code_offset; |
| 293 | var last_inlinee: ?u32 = null; | ||
| 294 | var iter = pdb.getInlinees(module, proc); | ||
| 295 | while (iter.next(module)) |inline_site| { | ||
| 296 | // If our address points into this site, get the source location it | ||
| 297 | // points at | ||
| 298 | const inlinee_src_line = pdb.getInlineeSourceLine( | ||
| 299 | module, | ||
| 300 | inline_site.inlinee, | ||
| 301 | ) orelse continue; | ||
| 302 | const maybe_loc = pdb.getInlineSiteSourceLocation( | ||
| 303 | module, | ||
| 304 | inline_site, | ||
| 305 | inlinee_src_line.info, | ||
| 306 | offset_in_func, | ||
| 307 | ) catch continue; | ||
| 308 | const loc = maybe_loc orelse continue; | ||
| 309 | |||
| 310 | // Filter out duplicate inline sites. Tools like llvm-addr2line output | ||
| 311 | // duplicate sites in the same cases as us if we elide this check, | ||
| 312 | // implying that they exist in the underlying data and are not | ||
| 313 | // indicative of a parser bug. No useful information is lost here since an | ||
| 314 | // inline site can't actually reference itself. | ||
| 315 | if (inline_site.inlinee == last_inlinee) continue; | ||
| 316 | last_inlinee = inline_site.inlinee; | ||
| 317 | |||
| 318 | try symbols.append(gpa, .{ | ||
| 319 | .name = pdb.findInlineeName(inline_site.inlinee), | ||
| 320 | .compile_unit_name = fs.path.basename(module.obj_file_name), | ||
| 321 | .source_location = loc, | ||
| 322 | }); | ||
| 323 | } | ||
| 403 | 324 | ||
| 404 | // Dwarf | 325 | // Inline sites are stored in the pdb in reverse order, so we reverse the |
| 405 | dwarf: { | 326 | // matching sites here. We could alternatively use the parent fields to |
| 406 | const dwarf = &(di.dwarf orelse break :dwarf); | 327 | // determine the order, but this would introduce seemingly unecessary |
| 407 | const addr = vaddr + di.coff_image_base; | 328 | // complexity. |
| 408 | return .{ .dwarf = dwarf.getSymbols(gpa, native_endian, addr) }; | 329 | std.mem.reverse(std.debug.Symbol, symbols.items); |
| 409 | } | 330 | } |
| 410 | 331 | ||
| 411 | return error.MissingDebugInfo; | 332 | try symbols.append(gpa, .{ |
| 333 | .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null, | ||
| 334 | .compile_unit_name = fs.path.basename(module.obj_file_name), | ||
| 335 | .source_location = pdb.getLineNumberInfo(module, addr) catch null, | ||
| 336 | }); | ||
| 337 | |||
| 338 | return symbols.toOwnedSlice(gpa); | ||
| 412 | } | 339 | } |
| 413 | 340 | ||
| 414 | fn deinit(self: *Symbols, io: Io) void { | 341 | dwarf: { |
| 415 | const gpa = std.debug.getDebugInfoAllocator(); | 342 | const dwarf = &(di.dwarf orelse break :dwarf); |
| 416 | switch (self.*) { | 343 | const addr = vaddr + di.coff_image_base; |
| 417 | .pdb => |*info| info.inline_sites.deinit(gpa), | 344 | return dwarf.getSymbols(gpa, native_endian, addr); |
| 418 | .dwarf => |*info| info.deinit(io), | ||
| 419 | .none => {}, | ||
| 420 | } | ||
| 421 | } | 345 | } |
| 422 | }; | ||
| 423 | 346 | ||
| 347 | return error.MissingDebugInfo; | ||
| 348 | } | ||
| 424 | }; | 349 | }; |
| 425 | 350 | ||
| 426 | fn deinit(module: *Module, gpa: Allocator, io: Io) void { | 351 | fn deinit(module: *Module, gpa: Allocator, io: Io) void { |