mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
std.debug.Info.resolveSourceLocations: O(N) implementation
This commit is contained in:
parent
c2ab4614b6
commit
53aa9d75a9
@ -152,6 +152,7 @@ pub const CompileUnit = struct {
|
|||||||
pub const LineEntry = struct {
|
pub const LineEntry = struct {
|
||||||
line: u32,
|
line: u32,
|
||||||
column: u32,
|
column: u32,
|
||||||
|
/// Offset by 1 depending on whether Dwarf version is >= 5.
|
||||||
file: u32,
|
file: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -809,7 +810,7 @@ pub fn getSymbolName(di: *Dwarf, address: u64) ?[]const u8 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScanError = error{
|
pub const ScanError = error{
|
||||||
InvalidDebugInfo,
|
InvalidDebugInfo,
|
||||||
MissingDebugInfo,
|
MissingDebugInfo,
|
||||||
} || Allocator.Error || std.debug.FixedBufferReader.Error;
|
} || Allocator.Error || std.debug.FixedBufferReader.Error;
|
||||||
@ -1113,7 +1114,7 @@ pub fn sortCompileUnits(d: *Dwarf) ScanError!void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std.mem.sortUnstable(CompileUnit, d.compile_unit_list.items, {}, struct {
|
std.mem.sortUnstable(CompileUnit, d.compile_unit_list.items, {}, struct {
|
||||||
fn lessThan(ctx: void, a: CompileUnit, b: CompileUnit) bool {
|
pub fn lessThan(ctx: void, a: CompileUnit, b: CompileUnit) bool {
|
||||||
_ = ctx;
|
_ = ctx;
|
||||||
const a_range = a.pc_range orelse return false;
|
const a_range = a.pc_range orelse return false;
|
||||||
const b_range = b.pc_range orelse return true;
|
const b_range = b.pc_range orelse return true;
|
||||||
@ -1641,14 +1642,18 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn populateSrcLocCache(d: *Dwarf, gpa: Allocator, cu: *CompileUnit) ScanError!void {
|
||||||
|
if (cu.src_loc_cache != null) return;
|
||||||
|
cu.src_loc_cache = try runLineNumberProgram(d, gpa, cu);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getLineNumberInfo(
|
pub fn getLineNumberInfo(
|
||||||
d: *Dwarf,
|
d: *Dwarf,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
compile_unit: *CompileUnit,
|
compile_unit: *CompileUnit,
|
||||||
target_address: u64,
|
target_address: u64,
|
||||||
) !std.debug.SourceLocation {
|
) !std.debug.SourceLocation {
|
||||||
if (compile_unit.src_loc_cache == null)
|
try populateSrcLocCache(d, gpa, compile_unit);
|
||||||
compile_unit.src_loc_cache = try runLineNumberProgram(d, gpa, compile_unit);
|
|
||||||
const slc = &compile_unit.src_loc_cache.?;
|
const slc = &compile_unit.src_loc_cache.?;
|
||||||
const entry = try slc.findSource(target_address);
|
const entry = try slc.findSource(target_address);
|
||||||
const file_index = entry.file - @intFromBool(slc.version < 5);
|
const file_index = entry.file - @intFromBool(slc.version < 5);
|
||||||
@ -2343,52 +2348,6 @@ pub const ElfModule = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ResolveSourceLocationsError = Allocator.Error || FixedBufferReader.Error;
|
|
||||||
|
|
||||||
/// Given an array of virtual memory addresses, sorted ascending, outputs a
|
|
||||||
/// corresponding array of source locations, by appending to the provided
|
|
||||||
/// array list.
|
|
||||||
pub fn resolveSourceLocations(
|
|
||||||
d: *Dwarf,
|
|
||||||
gpa: Allocator,
|
|
||||||
sorted_pc_addrs: []const u64,
|
|
||||||
/// Asserts its length equals length of `sorted_pc_addrs`.
|
|
||||||
output: []std.debug.SourceLocation,
|
|
||||||
) ResolveSourceLocationsError!void {
|
|
||||||
assert(sorted_pc_addrs.len == output.len);
|
|
||||||
assert(d.compile_units_sorted);
|
|
||||||
|
|
||||||
var cu_i: usize = 0;
|
|
||||||
var cu: *CompileUnit = &d.compile_unit_list.items[0];
|
|
||||||
var range = cu.pc_range.?;
|
|
||||||
next_pc: for (sorted_pc_addrs, output) |pc, *out| {
|
|
||||||
while (pc >= range.end) {
|
|
||||||
cu_i += 1;
|
|
||||||
if (cu_i >= d.compile_unit_list.items.len) {
|
|
||||||
out.* = std.debug.SourceLocation.invalid;
|
|
||||||
continue :next_pc;
|
|
||||||
}
|
|
||||||
cu = &d.compile_unit_list.items[cu_i];
|
|
||||||
range = cu.pc_range orelse {
|
|
||||||
out.* = std.debug.SourceLocation.invalid;
|
|
||||||
continue :next_pc;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (pc < range.start) {
|
|
||||||
out.* = std.debug.SourceLocation.invalid;
|
|
||||||
continue :next_pc;
|
|
||||||
}
|
|
||||||
// TODO: instead of calling this function, break the function up into one that parses the
|
|
||||||
// information once and prepares a context that can be reused for the entire batch.
|
|
||||||
if (getLineNumberInfo(d, gpa, cu, pc)) |src_loc| {
|
|
||||||
out.* = src_loc;
|
|
||||||
} else |err| switch (err) {
|
|
||||||
error.MissingDebugInfo, error.InvalidDebugInfo => out.* = std.debug.SourceLocation.invalid,
|
|
||||||
else => |e| return e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getSymbol(di: *Dwarf, allocator: Allocator, address: u64) !std.debug.Symbol {
|
fn getSymbol(di: *Dwarf, allocator: Allocator, address: u64) !std.debug.Symbol {
|
||||||
if (di.findCompileUnit(address)) |compile_unit| {
|
if (di.findCompileUnit(address)) |compile_unit| {
|
||||||
return .{
|
return .{
|
||||||
|
|||||||
@ -12,12 +12,66 @@ const Path = std.Build.Cache.Path;
|
|||||||
const Dwarf = std.debug.Dwarf;
|
const Dwarf = std.debug.Dwarf;
|
||||||
const page_size = std.mem.page_size;
|
const page_size = std.mem.page_size;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const Hash = std.hash.Wyhash;
|
||||||
|
|
||||||
const Info = @This();
|
const Info = @This();
|
||||||
|
|
||||||
/// Sorted by key, ascending.
|
/// Sorted by key, ascending.
|
||||||
address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule),
|
address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule),
|
||||||
|
|
||||||
|
/// Provides a globally-scoped integer index for directories.
|
||||||
|
///
|
||||||
|
/// As opposed to, for example, a directory index that is compilation-unit
|
||||||
|
/// scoped inside a single ELF module.
|
||||||
|
///
|
||||||
|
/// String memory references the memory-mapped debug information.
|
||||||
|
///
|
||||||
|
/// Protected by `mutex`.
|
||||||
|
directories: std.StringArrayHashMapUnmanaged(void),
|
||||||
|
/// Provides a globally-scoped integer index for files.
|
||||||
|
///
|
||||||
|
/// String memory references the memory-mapped debug information.
|
||||||
|
///
|
||||||
|
/// Protected by `mutex`.
|
||||||
|
files: std.ArrayHashMapUnmanaged(File, void, File.MapContext, false),
|
||||||
|
/// Protects `directories` and `files`.
|
||||||
|
mutex: std.Thread.Mutex,
|
||||||
|
|
||||||
|
pub const SourceLocation = struct {
|
||||||
|
file: File.Index,
|
||||||
|
line: u32,
|
||||||
|
column: u32,
|
||||||
|
|
||||||
|
pub const invalid: SourceLocation = .{
|
||||||
|
.file = .invalid,
|
||||||
|
.line = 0,
|
||||||
|
.column = 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const File = struct {
|
||||||
|
directory_index: u32,
|
||||||
|
basename: []const u8,
|
||||||
|
|
||||||
|
pub const Index = enum(u32) {
|
||||||
|
invalid = std.math.maxInt(u32),
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MapContext = struct {
|
||||||
|
pub fn hash(ctx: MapContext, a: File) u32 {
|
||||||
|
_ = ctx;
|
||||||
|
return @truncate(Hash.hash(a.directory_index, a.basename));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eql(ctx: MapContext, a: File, b: File, b_index: usize) bool {
|
||||||
|
_ = ctx;
|
||||||
|
_ = b_index;
|
||||||
|
return a.directory_index == b.directory_index and std.mem.eql(u8, a.basename, b.basename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
pub const LoadError = Dwarf.ElfModule.LoadError;
|
pub const LoadError = Dwarf.ElfModule.LoadError;
|
||||||
|
|
||||||
pub fn load(gpa: Allocator, path: Path) LoadError!Info {
|
pub fn load(gpa: Allocator, path: Path) LoadError!Info {
|
||||||
@ -26,12 +80,17 @@ pub fn load(gpa: Allocator, path: Path) LoadError!Info {
|
|||||||
try elf_module.dwarf.sortCompileUnits();
|
try elf_module.dwarf.sortCompileUnits();
|
||||||
var info: Info = .{
|
var info: Info = .{
|
||||||
.address_map = .{},
|
.address_map = .{},
|
||||||
|
.directories = .{},
|
||||||
|
.files = .{},
|
||||||
|
.mutex = .{},
|
||||||
};
|
};
|
||||||
try info.address_map.put(gpa, elf_module.base_address, elf_module);
|
try info.address_map.put(gpa, elf_module.base_address, elf_module);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(info: *Info, gpa: Allocator) void {
|
pub fn deinit(info: *Info, gpa: Allocator) void {
|
||||||
|
info.directories.deinit(gpa);
|
||||||
|
info.files.deinit(gpa);
|
||||||
for (info.address_map.values()) |*elf_module| {
|
for (info.address_map.values()) |*elf_module| {
|
||||||
elf_module.dwarf.deinit(gpa);
|
elf_module.dwarf.deinit(gpa);
|
||||||
}
|
}
|
||||||
@ -39,17 +98,98 @@ pub fn deinit(info: *Info, gpa: Allocator) void {
|
|||||||
info.* = undefined;
|
info.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ResolveSourceLocationsError = Dwarf.ResolveSourceLocationsError;
|
pub fn fileAt(info: *Info, index: File.Index) *File {
|
||||||
|
return &info.files.keys()[@intFromEnum(index)];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ResolveSourceLocationsError = Dwarf.ScanError;
|
||||||
|
|
||||||
|
/// Given an array of virtual memory addresses, sorted ascending, outputs a
|
||||||
|
/// corresponding array of source locations.
|
||||||
pub fn resolveSourceLocations(
|
pub fn resolveSourceLocations(
|
||||||
info: *Info,
|
info: *Info,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
sorted_pc_addrs: []const u64,
|
sorted_pc_addrs: []const u64,
|
||||||
/// Asserts its length equals length of `sorted_pc_addrs`.
|
/// Asserts its length equals length of `sorted_pc_addrs`.
|
||||||
output: []std.debug.SourceLocation,
|
output: []SourceLocation,
|
||||||
) ResolveSourceLocationsError!void {
|
) ResolveSourceLocationsError!void {
|
||||||
assert(sorted_pc_addrs.len == output.len);
|
assert(sorted_pc_addrs.len == output.len);
|
||||||
if (info.address_map.entries.len != 1) @panic("TODO");
|
if (info.address_map.entries.len != 1) @panic("TODO");
|
||||||
const elf_module = &info.address_map.values()[0];
|
const elf_module = &info.address_map.values()[0];
|
||||||
return elf_module.dwarf.resolveSourceLocations(gpa, sorted_pc_addrs, output);
|
return resolveSourceLocationsDwarf(info, gpa, sorted_pc_addrs, output, &elf_module.dwarf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolveSourceLocationsDwarf(
|
||||||
|
info: *Info,
|
||||||
|
gpa: Allocator,
|
||||||
|
sorted_pc_addrs: []const u64,
|
||||||
|
/// Asserts its length equals length of `sorted_pc_addrs`.
|
||||||
|
output: []SourceLocation,
|
||||||
|
d: *Dwarf,
|
||||||
|
) ResolveSourceLocationsError!void {
|
||||||
|
assert(sorted_pc_addrs.len == output.len);
|
||||||
|
assert(d.compile_units_sorted);
|
||||||
|
|
||||||
|
var cu_i: usize = 0;
|
||||||
|
var line_table_i: usize = 0;
|
||||||
|
var cu: *Dwarf.CompileUnit = &d.compile_unit_list.items[0];
|
||||||
|
var range = cu.pc_range.?;
|
||||||
|
// Protects directories and files tables from other threads.
|
||||||
|
info.mutex.lock();
|
||||||
|
defer info.mutex.unlock();
|
||||||
|
next_pc: for (sorted_pc_addrs, output) |pc, *out| {
|
||||||
|
while (pc >= range.end) {
|
||||||
|
cu_i += 1;
|
||||||
|
if (cu_i >= d.compile_unit_list.items.len) {
|
||||||
|
out.* = SourceLocation.invalid;
|
||||||
|
continue :next_pc;
|
||||||
|
}
|
||||||
|
cu = &d.compile_unit_list.items[cu_i];
|
||||||
|
line_table_i = 0;
|
||||||
|
range = cu.pc_range orelse {
|
||||||
|
out.* = SourceLocation.invalid;
|
||||||
|
continue :next_pc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (pc < range.start) {
|
||||||
|
out.* = SourceLocation.invalid;
|
||||||
|
continue :next_pc;
|
||||||
|
}
|
||||||
|
if (line_table_i == 0) {
|
||||||
|
line_table_i = 1;
|
||||||
|
info.mutex.unlock();
|
||||||
|
defer info.mutex.lock();
|
||||||
|
d.populateSrcLocCache(gpa, cu) catch |err| switch (err) {
|
||||||
|
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||||
|
out.* = SourceLocation.invalid;
|
||||||
|
cu_i += 1;
|
||||||
|
if (cu_i < d.compile_unit_list.items.len) {
|
||||||
|
cu = &d.compile_unit_list.items[cu_i];
|
||||||
|
line_table_i = 0;
|
||||||
|
if (cu.pc_range) |r| range = r;
|
||||||
|
}
|
||||||
|
continue :next_pc;
|
||||||
|
},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const slc = &cu.src_loc_cache.?;
|
||||||
|
const table_addrs = slc.line_table.keys();
|
||||||
|
while (line_table_i < table_addrs.len and table_addrs[line_table_i] < pc) line_table_i += 1;
|
||||||
|
|
||||||
|
const entry = slc.line_table.values()[line_table_i - 1];
|
||||||
|
const corrected_file_index = entry.file - @intFromBool(slc.version < 5);
|
||||||
|
const file_entry = slc.files[corrected_file_index];
|
||||||
|
const dir_path = slc.directories[file_entry.dir_index].path;
|
||||||
|
const dir_gop = try info.directories.getOrPut(gpa, dir_path);
|
||||||
|
const file_gop = try info.files.getOrPut(gpa, .{
|
||||||
|
.directory_index = @intCast(dir_gop.index),
|
||||||
|
.basename = file_entry.path,
|
||||||
|
});
|
||||||
|
out.* = .{
|
||||||
|
.file = @enumFromInt(file_gop.index),
|
||||||
|
.line = entry.line,
|
||||||
|
.column = entry.column,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,15 +50,14 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
|
assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
|
||||||
|
|
||||||
const source_locations = try arena.alloc(std.debug.SourceLocation, pcs.len);
|
const source_locations = try arena.alloc(std.debug.Info.SourceLocation, pcs.len);
|
||||||
try debug_info.resolveSourceLocations(gpa, pcs, source_locations);
|
try debug_info.resolveSourceLocations(gpa, pcs, source_locations);
|
||||||
defer for (source_locations) |sl| {
|
|
||||||
gpa.free(sl.file_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (pcs, source_locations) |pc, sl| {
|
for (pcs, source_locations) |pc, sl| {
|
||||||
try stdout.print("{x}: {s}:{d}:{d}\n", .{
|
const file = debug_info.fileAt(sl.file);
|
||||||
pc, sl.file_name, sl.line, sl.column,
|
const dir_name = debug_info.directories.keys()[file.directory_index];
|
||||||
|
try stdout.print("{x}: {s}/{s}:{d}:{d}\n", .{
|
||||||
|
pc, dir_name, file.basename, sl.line, sl.column,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user