mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
Replaces the inline symbol iterator with an array of symbols
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.
This commit is contained in:
+29
-25
@@ -38,8 +38,12 @@ pub const cpu_context = @import("debug/cpu_context.zig");
|
||||
/// pub const init: SelfInfo;
|
||||
/// pub fn deinit(si: *SelfInfo, io: Io) void;
|
||||
///
|
||||
/// /// Returns an iterator over the symbols and source locations of the instruction at `address`.
|
||||
/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator;
|
||||
/// /// Returns the the symbols and source locations of the instruction at `address`. Often this
|
||||
/// /// will return a single result, but in the case of inlines it may return multiple. When
|
||||
/// /// multiple results are returned, they are sorted from innermost to outermost.
|
||||
/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const Symbol;
|
||||
/// /// Frees symbols returned from `getSymbols`.
|
||||
/// pub fn freeSymbols(si: *SelfInfo, symbols: []const Symbol) void;
|
||||
/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
|
||||
/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
|
||||
/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
|
||||
@@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig");
|
||||
/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's
|
||||
/// /// return address, or 0 if the end of the stack has been reached.
|
||||
/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize;
|
||||
/// /// Iterates symbols found at an address.
|
||||
/// pub const SymbolIterator = struct {
|
||||
/// pub fn deinit(Self: *SymbolIterator, io: Io) void;
|
||||
/// pub fn next(self: *SymbolIterator) ?SelfInfoError!Symbol;
|
||||
/// };
|
||||
/// ```
|
||||
pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
|
||||
root.debug.SelfInfo
|
||||
@@ -1193,35 +1192,35 @@ fn printSourceAtAddress(
|
||||
t: Io.Terminal,
|
||||
options: PrintSourceAddressOptions,
|
||||
) Writer.Error!void {
|
||||
var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address);
|
||||
defer symbols.deinit(io);
|
||||
while (symbols.next()) |curr| {
|
||||
const symbol: Symbol = curr catch |err| switch (err) {
|
||||
const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| {
|
||||
t.setColor(.dim) catch {};
|
||||
defer t.setColor(.reset) catch {};
|
||||
switch (err) {
|
||||
error.MissingDebugInfo,
|
||||
error.UnsupportedDebugInfo,
|
||||
error.InvalidDebugInfo,
|
||||
=> .unknown,
|
||||
error.ReadFailed, error.Unexpected, error.Canceled => s: {
|
||||
t.setColor(.dim) catch {};
|
||||
=> {},
|
||||
error.ReadFailed, error.Unexpected, error.Canceled => {
|
||||
try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});
|
||||
t.setColor(.reset) catch {};
|
||||
break :s .unknown;
|
||||
},
|
||||
error.OutOfMemory => s: {
|
||||
error.OutOfMemory => {
|
||||
t.setColor(.dim) catch {};
|
||||
try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{});
|
||||
t.setColor(.reset) catch {};
|
||||
break :s .unknown;
|
||||
},
|
||||
};
|
||||
defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name);
|
||||
}
|
||||
return printLineInfo(io, t, debug_info, null, options.address, null, null);
|
||||
};
|
||||
defer debug_info.freeSymbols(symbols);
|
||||
for (symbols) |symbol| {
|
||||
try printLineInfo(
|
||||
io,
|
||||
t,
|
||||
debug_info,
|
||||
symbol.source_location,
|
||||
options.address,
|
||||
symbol.name orelse "???",
|
||||
symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???",
|
||||
symbol.name,
|
||||
symbol.compile_unit_name,
|
||||
);
|
||||
if (!options.resolve_inline_callers) break;
|
||||
}
|
||||
@@ -1229,10 +1228,11 @@ fn printSourceAtAddress(
|
||||
fn printLineInfo(
|
||||
io: Io,
|
||||
t: Io.Terminal,
|
||||
debug_info: *SelfInfo,
|
||||
source_location: ?SourceLocation,
|
||||
address: usize,
|
||||
symbol_name: []const u8,
|
||||
compile_unit_name: []const u8,
|
||||
symbol_name: ?[]const u8,
|
||||
compile_unit_name: ?[]const u8,
|
||||
) Writer.Error!void {
|
||||
const writer = t.writer;
|
||||
t.setColor(.bold) catch {};
|
||||
@@ -1250,7 +1250,11 @@ fn printLineInfo(
|
||||
t.setColor(.reset) catch {};
|
||||
try writer.writeAll(": ");
|
||||
t.setColor(.dim) catch {};
|
||||
try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
|
||||
try writer.print("0x{x} in {s} ({s})", .{
|
||||
address,
|
||||
symbol_name orelse "???",
|
||||
compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
|
||||
});
|
||||
t.setColor(.reset) catch {};
|
||||
try writer.writeAll("\n");
|
||||
|
||||
|
||||
+13
-21
@@ -1545,26 +1545,17 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
|
||||
return str[casted_offset..last :0];
|
||||
}
|
||||
|
||||
pub const SymbolIterator = struct {
|
||||
curr: ?std.debug.SelfInfoError!std.debug.Symbol,
|
||||
|
||||
pub fn deinit(self: *SymbolIterator, _: Io) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol {
|
||||
const result = self.curr;
|
||||
self.curr = null;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) SymbolIterator {
|
||||
pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.debug.SelfInfoError![]const std.debug.Symbol {
|
||||
const symbol = try gpa.create(std.debug.Symbol);
|
||||
errdefer gpa.destroy(symbol);
|
||||
const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
|
||||
error.EndOfStream, error.Overflow => return .{ .curr = error.InvalidDebugInfo },
|
||||
else => |e| return .{ .curr = e },
|
||||
error.EndOfStream, error.Overflow => {
|
||||
symbol.* = .unknown;
|
||||
return symbol[0..1];
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
return .{ .curr = .{
|
||||
symbol.* = .{
|
||||
.name = di.getSymbolName(address),
|
||||
.compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => null,
|
||||
@@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb
|
||||
error.EndOfStream,
|
||||
error.Overflow,
|
||||
error.StreamTooLong,
|
||||
=> return .{ .curr = error.InvalidDebugInfo },
|
||||
else => |e| return .{ .curr = e },
|
||||
=> return error.InvalidDebugInfo,
|
||||
else => |e| return e,
|
||||
},
|
||||
} };
|
||||
};
|
||||
return symbol[0..1];
|
||||
}
|
||||
|
||||
/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and
|
||||
|
||||
@@ -30,41 +30,50 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
|
||||
if (si.unwind_cache) |cache| gpa.free(cache);
|
||||
}
|
||||
|
||||
pub const SymbolIterator = std.debug.Dwarf.SymbolIterator;
|
||||
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = si.findModule(gpa, io, address, .exclusive) catch |err| return .{ .curr = err };
|
||||
const module = try si.findModule(gpa, io, address, .exclusive);
|
||||
defer si.rwlock.unlock(io);
|
||||
|
||||
const vaddr = address - module.load_offset;
|
||||
|
||||
const loaded_elf = module.getLoadedElf(gpa, io) catch |err| return .{ .curr = err };
|
||||
const loaded_elf = try module.getLoadedElf(gpa, io);
|
||||
if (loaded_elf.file.dwarf) |*dwarf| {
|
||||
if (!loaded_elf.scanned_dwarf) {
|
||||
dwarf.open(gpa, native_endian) catch |err| switch (err) {
|
||||
error.InvalidDebugInfo,
|
||||
error.MissingDebugInfo,
|
||||
error.OutOfMemory,
|
||||
=> |e| return .{ .curr = e },
|
||||
=> |e| return e,
|
||||
error.EndOfStream,
|
||||
error.Overflow,
|
||||
error.ReadFailed,
|
||||
error.StreamTooLong,
|
||||
=> return .{ .curr = error.InvalidDebugInfo },
|
||||
=> return error.InvalidDebugInfo,
|
||||
};
|
||||
loaded_elf.scanned_dwarf = true;
|
||||
}
|
||||
return dwarf.getSymbols(gpa, native_endian, vaddr);
|
||||
}
|
||||
// When DWARF is unavailable, fall back to searching the symtab.
|
||||
const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
|
||||
error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo },
|
||||
error.BadSymtab => return .{ .curr = error.InvalidDebugInfo },
|
||||
error.OutOfMemory => |e| return .{ .curr = e },
|
||||
const symbol = try gpa.create(std.debug.Symbol);
|
||||
errdefer gpa.destroy(symbol);
|
||||
symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
|
||||
error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
|
||||
error.BadSymtab => return error.InvalidDebugInfo,
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
|
||||
return .{ .curr = symbol };
|
||||
return symbol[0..1];
|
||||
}
|
||||
pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
|
||||
_ = si;
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
for (symbols) |symbol| {
|
||||
if (symbol.source_location) |source_location| {
|
||||
gpa.free(source_location.file_name);
|
||||
}
|
||||
}
|
||||
gpa.free(symbols);
|
||||
}
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
|
||||
@@ -36,12 +36,15 @@ pub const SymbolIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = si.findModule(gpa, io, address) catch |err| return .{ .curr = err };
|
||||
const module = try si.findModule(gpa, io, address);
|
||||
defer si.mutex.unlock(io);
|
||||
|
||||
const file = module.getFile(gpa, io) catch |err| return .{ .curr = err };
|
||||
const file = try module.getFile(gpa, io);
|
||||
|
||||
const symbol = try gpa.create(std.debug.Symbol);
|
||||
errdefer gpa.destroy(symbol);
|
||||
|
||||
// This is not necessarily the same as the vmaddr_slide that dyld would report. This is
|
||||
// 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 {
|
||||
|
||||
const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
|
||||
// Return at least the symbol name if available.
|
||||
return .{ .curr = .{
|
||||
.name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },
|
||||
symbol.* = .{
|
||||
.name = try file.lookupSymbolName(vaddr),
|
||||
.compile_unit_name = null,
|
||||
.source_location = null,
|
||||
} };
|
||||
};
|
||||
return symbol[0..1];
|
||||
};
|
||||
|
||||
const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
|
||||
// Return at least the symbol name if available.
|
||||
return .{ .curr = .{
|
||||
.name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },
|
||||
symbol.* = .{
|
||||
.name = try file.lookupSymbolName(vaddr),
|
||||
.compile_unit_name = null,
|
||||
.source_location = null,
|
||||
} };
|
||||
};
|
||||
return symbol[0..1];
|
||||
};
|
||||
|
||||
return .{ .curr = .{
|
||||
symbol.* = .{
|
||||
.name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
|
||||
file.lookupSymbolName(vaddr) catch |err|
|
||||
return .{ .curr = err },
|
||||
try file.lookupSymbolName(vaddr),
|
||||
.compile_unit_name = compile_unit.die.getAttrString(
|
||||
ofile_dwarf,
|
||||
native_endian,
|
||||
@@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
|
||||
compile_unit,
|
||||
ofile_vaddr,
|
||||
) catch null,
|
||||
} };
|
||||
};
|
||||
return symbol[0..1];
|
||||
}
|
||||
pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
|
||||
_ = si;
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
for (symbols) |symbol| {
|
||||
if (symbol.source_location) |source_location| {
|
||||
gpa.free(source_location.file_name);
|
||||
}
|
||||
}
|
||||
gpa.free(symbols);
|
||||
}
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
_ = si;
|
||||
|
||||
@@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
|
||||
si.modules.deinit(gpa);
|
||||
}
|
||||
|
||||
pub const SymbolIterator = struct {
|
||||
err: Error!void = {},
|
||||
lock: ?*Io.RwLock,
|
||||
module: *Module,
|
||||
symbols: Module.DebugInfo.Symbols,
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
try si.lock.lockShared(io);
|
||||
defer si.lock.unlockShared(io);
|
||||
const module = try si.findModule(gpa, address);
|
||||
const di = try module.getDebugInfo(gpa, io);
|
||||
return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase));
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SymbolIterator, io: Io) void {
|
||||
if (self.lock) |lock| lock.unlockShared(io);
|
||||
self.symbols.deinit(io);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn failing(err: Error) SymbolIterator {
|
||||
return .{
|
||||
.err = err,
|
||||
.lock = null,
|
||||
.module = undefined,
|
||||
.symbols = .none,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol {
|
||||
// Check for errors
|
||||
self.err catch |err| {
|
||||
self.err = {};
|
||||
self.symbols = .none;
|
||||
return err;
|
||||
};
|
||||
|
||||
// Return the next symbol for the debug info type
|
||||
switch (self.symbols) {
|
||||
.pdb => |*info| {
|
||||
// The failure cases are unreachable because we only set the pdb field if these are
|
||||
// set
|
||||
const di = if (self.module.di.?) |*di| di else |_| unreachable;
|
||||
const pdb = if (di.pdb) |*pdb| pdb else unreachable;
|
||||
|
||||
// Get the next inlinee if it exists
|
||||
if (info.proc) |proc| {
|
||||
const offset_in_func = info.addr - proc.code_offset;
|
||||
while (info.inline_sites.pop()) |site| {
|
||||
// If our address points into this site, get the source location it points
|
||||
// at
|
||||
const inlinee_src_line = pdb.getInlineeSourceLine(
|
||||
info.module,
|
||||
site.inlinee,
|
||||
) orelse continue;
|
||||
const maybe_loc = pdb.getInlineSiteSourceLocation(
|
||||
info.module,
|
||||
site,
|
||||
inlinee_src_line.info,
|
||||
offset_in_func,
|
||||
) catch continue;
|
||||
const loc = maybe_loc orelse continue;
|
||||
|
||||
// If we've found a match, filter out any duplicates that might follow.
|
||||
// Tools like llvm-addr2line output duplicate sites in the same cases as us,
|
||||
// implying that they exist in the underlying data and are not indicative of
|
||||
// a parser bug.
|
||||
while (info.inline_sites.getLastOrNull()) |top| {
|
||||
if (top.inlinee != site.inlinee) break;
|
||||
_ = info.inline_sites.pop();
|
||||
}
|
||||
|
||||
return .{
|
||||
.name = pdb.findInlineeName(site.inlinee),
|
||||
.compile_unit_name = fs.path.basename(info.module.obj_file_name),
|
||||
.source_location = loc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Return the main symbol and end the iterator
|
||||
defer self.symbols = .none;
|
||||
return .{
|
||||
.name = if (info.proc) |proc| pdb.getSymbolName(proc) else null,
|
||||
.compile_unit_name = fs.path.basename(info.module.obj_file_name),
|
||||
.source_location = pdb.getLineNumberInfo(info.module, info.addr) catch null,
|
||||
};
|
||||
},
|
||||
.dwarf => |*info| return info.next(),
|
||||
.none => return null,
|
||||
pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
|
||||
_ = si;
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
for (symbols) |symbol| {
|
||||
if (symbol.source_location) |source_location| {
|
||||
gpa.free(source_location.file_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
si.lock.lockShared(io) catch |err| return .failing(err);
|
||||
errdefer si.lock.unlockShared(io);
|
||||
const module = si.findModule(gpa, address) catch |err| return .failing(err);
|
||||
const di = module.getDebugInfo(gpa, io) catch |err| return .failing(err);
|
||||
const symbols = Module.DebugInfo.Symbols.init(di, address - @intFromPtr(module.entry.DllBase))
|
||||
catch |err| return .failing(err);
|
||||
errdefer comptime unreachable;
|
||||
return .{
|
||||
.lock = &si.lock,
|
||||
.module = module,
|
||||
.symbols = symbols,
|
||||
};
|
||||
gpa.free(symbols);
|
||||
}
|
||||
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
try si.lock.lockShared(io);
|
||||
@@ -333,94 +252,100 @@ const Module = struct {
|
||||
arena.deinit();
|
||||
}
|
||||
|
||||
pub const Symbols = union(enum) {
|
||||
pdb: struct {
|
||||
module: *Pdb.Module,
|
||||
proc: ?*align(1) const std.pdb.ProcSym,
|
||||
addr: usize,
|
||||
/// Inline sites are stored in the pdb in reverse order, so we build up a list of up
|
||||
/// front so that our iterator can return them in the correct order without doing an
|
||||
/// n^2 search. We don't try to filter inline sites based on address until the user
|
||||
/// calls `next` as this requires parsing binary annotations, and this is work we
|
||||
/// may be able to elide if the caller chooses to early out before finishing
|
||||
/// iteration, e.g. because they only wanted the topmost call.
|
||||
inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym),
|
||||
},
|
||||
dwarf: std.debug.Dwarf.SymbolIterator,
|
||||
none: void,
|
||||
fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol {
|
||||
pdb: {
|
||||
const pdb = &(di.pdb orelse break :pdb);
|
||||
var coff_section: *align(1) const coff.SectionHeader = undefined;
|
||||
const mod_index = for (pdb.sect_contribs) |sect_contrib| {
|
||||
if (sect_contrib.section > di.coff_section_headers.len) continue;
|
||||
// Remember that SectionContribEntry.Section is 1-based.
|
||||
coff_section = &di.coff_section_headers[sect_contrib.section - 1];
|
||||
|
||||
fn init(di: *DebugInfo, vaddr: usize) Error!Symbols {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const vaddr_start = coff_section.virtual_address + sect_contrib.offset;
|
||||
const vaddr_end = vaddr_start + sect_contrib.size;
|
||||
if (vaddr >= vaddr_start and vaddr < vaddr_end) {
|
||||
break sect_contrib.module_index;
|
||||
}
|
||||
} else {
|
||||
// we have no information to add to the address
|
||||
break :pdb;
|
||||
};
|
||||
const module = pdb.getModule(mod_index) catch |err| switch (err) {
|
||||
error.InvalidDebugInfo,
|
||||
error.MissingDebugInfo,
|
||||
error.OutOfMemory,
|
||||
=> |e| return e,
|
||||
|
||||
pdb: {
|
||||
const pdb = &(di.pdb orelse break :pdb);
|
||||
var coff_section: *align(1) const coff.SectionHeader = undefined;
|
||||
const mod_index = for (pdb.sect_contribs) |sect_contrib| {
|
||||
if (sect_contrib.section > di.coff_section_headers.len) continue;
|
||||
// Remember that SectionContribEntry.Section is 1-based.
|
||||
coff_section = &di.coff_section_headers[sect_contrib.section - 1];
|
||||
error.ReadFailed,
|
||||
error.EndOfStream,
|
||||
=> return error.InvalidDebugInfo,
|
||||
} orelse {
|
||||
return error.InvalidDebugInfo; // bad module index
|
||||
};
|
||||
|
||||
const vaddr_start = coff_section.virtual_address + sect_contrib.offset;
|
||||
const vaddr_end = vaddr_start + sect_contrib.size;
|
||||
if (vaddr >= vaddr_start and vaddr < vaddr_end) {
|
||||
break sect_contrib.module_index;
|
||||
}
|
||||
} else {
|
||||
// we have no information to add to the address
|
||||
break :pdb;
|
||||
};
|
||||
const module = pdb.getModule(mod_index) catch |err| switch (err) {
|
||||
error.InvalidDebugInfo,
|
||||
error.MissingDebugInfo,
|
||||
error.OutOfMemory,
|
||||
=> |e| return e,
|
||||
const addr = vaddr - coff_section.virtual_address;
|
||||
const maybe_proc = pdb.getProcSym(module, addr);
|
||||
var symbols: std.ArrayList(std.debug.Symbol) = .empty;
|
||||
errdefer symbols.deinit(gpa);
|
||||
|
||||
error.ReadFailed,
|
||||
error.EndOfStream,
|
||||
=> return error.InvalidDebugInfo,
|
||||
} orelse {
|
||||
return error.InvalidDebugInfo; // bad module index
|
||||
};
|
||||
if (maybe_proc) |proc| {
|
||||
const offset_in_func = addr - proc.code_offset;
|
||||
var last_inlinee: ?u32 = null;
|
||||
var iter = pdb.getInlinees(module, proc);
|
||||
while (iter.next(module)) |inline_site| {
|
||||
// If our address points into this site, get the source location it
|
||||
// points at
|
||||
const inlinee_src_line = pdb.getInlineeSourceLine(
|
||||
module,
|
||||
inline_site.inlinee,
|
||||
) orelse continue;
|
||||
const maybe_loc = pdb.getInlineSiteSourceLocation(
|
||||
module,
|
||||
inline_site,
|
||||
inlinee_src_line.info,
|
||||
offset_in_func,
|
||||
) catch continue;
|
||||
const loc = maybe_loc orelse continue;
|
||||
|
||||
const addr = vaddr - coff_section.virtual_address;
|
||||
const maybe_proc = pdb.getProcSym(module, addr);
|
||||
// Filter out duplicate inline sites. Tools like llvm-addr2line output
|
||||
// duplicate sites in the same cases as us if we elide this check,
|
||||
// implying that they exist in the underlying data and are not
|
||||
// indicative of a parser bug. No useful information is lost here since an
|
||||
// inline site can't actually reference itself.
|
||||
if (inline_site.inlinee == last_inlinee) continue;
|
||||
last_inlinee = inline_site.inlinee;
|
||||
|
||||
var inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym) = .empty;
|
||||
if (maybe_proc) |proc| {
|
||||
var iter = pdb.getInlinees(module, proc);
|
||||
while (iter.next(module)) |inline_site| {
|
||||
try inline_sites.append(gpa, inline_site);
|
||||
}
|
||||
try symbols.append(gpa, .{
|
||||
.name = pdb.findInlineeName(inline_site.inlinee),
|
||||
.compile_unit_name = fs.path.basename(module.obj_file_name),
|
||||
.source_location = loc,
|
||||
});
|
||||
}
|
||||
|
||||
return .{ .pdb = .{
|
||||
.module = module,
|
||||
.proc = maybe_proc,
|
||||
.addr = addr,
|
||||
.inline_sites = inline_sites,
|
||||
} };
|
||||
// Inline sites are stored in the pdb in reverse order, so we reverse the
|
||||
// matching sites here. We could alternatively use the parent fields to
|
||||
// determine the order, but this would introduce seemingly unecessary
|
||||
// complexity.
|
||||
std.mem.reverse(std.debug.Symbol, symbols.items);
|
||||
}
|
||||
|
||||
// Dwarf
|
||||
dwarf: {
|
||||
const dwarf = &(di.dwarf orelse break :dwarf);
|
||||
const addr = vaddr + di.coff_image_base;
|
||||
return .{ .dwarf = dwarf.getSymbols(gpa, native_endian, addr) };
|
||||
}
|
||||
try symbols.append(gpa, .{
|
||||
.name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,
|
||||
.compile_unit_name = fs.path.basename(module.obj_file_name),
|
||||
.source_location = pdb.getLineNumberInfo(module, addr) catch null,
|
||||
});
|
||||
|
||||
return error.MissingDebugInfo;
|
||||
return symbols.toOwnedSlice(gpa);
|
||||
}
|
||||
|
||||
fn deinit(self: *Symbols, io: Io) void {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
switch (self.*) {
|
||||
.pdb => |*info| info.inline_sites.deinit(gpa),
|
||||
.dwarf => |*info| info.deinit(io),
|
||||
.none => {},
|
||||
}
|
||||
dwarf: {
|
||||
const dwarf = &(di.dwarf orelse break :dwarf);
|
||||
const addr = vaddr + di.coff_image_base;
|
||||
return dwarf.getSymbols(gpa, native_endian, addr);
|
||||
}
|
||||
};
|
||||
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(module: *Module, gpa: Allocator, io: Io) void {
|
||||
|
||||
Reference in New Issue
Block a user