mirror of
https://github.com/ziglang/zig.git
synced 2026-01-11 01:45:12 +00:00
dwarf: draft poc of deferred resolution of error sets debug info
This commit is contained in:
parent
e444e69dc4
commit
b4815b3131
@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null,
|
||||
/// Table of debug symbol names.
|
||||
strtab: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
pub const DebugInfoAtom = struct {
|
||||
/// Previous/next linked list pointers.
|
||||
/// This is the linked list node for this Decl's corresponding .debug_info tag.
|
||||
@ -119,6 +121,7 @@ pub fn deinit(self: *Dwarf) void {
|
||||
self.dbg_line_fn_free_list.deinit(gpa);
|
||||
self.dbg_info_decl_free_list.deinit(gpa);
|
||||
self.strtab.deinit(gpa);
|
||||
self.deferred_error_sets_relocs.deinit(gpa);
|
||||
}
|
||||
|
||||
pub const DeclDebugBuffers = struct {
|
||||
@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo(
|
||||
var it: usize = 0;
|
||||
while (it < dbg_info_type_relocs.count()) : (it += 1) {
|
||||
const ty = dbg_info_type_relocs.keys()[it];
|
||||
const deferred: bool = blk: {
|
||||
if (ty.isAnyError()) break :blk true;
|
||||
switch (ty.tag()) {
|
||||
.error_set_inferred => {
|
||||
if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
break :blk false;
|
||||
};
|
||||
if (deferred) continue;
|
||||
|
||||
const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{
|
||||
.target = self.target,
|
||||
}).?;
|
||||
@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo(
|
||||
|
||||
{
|
||||
// Now that we have the offset assigned we can finally perform type relocations.
|
||||
for (dbg_info_type_relocs.values()) |value| {
|
||||
for (dbg_info_type_relocs.keys()) |ty| {
|
||||
const value = dbg_info_type_relocs.getContext(ty, .{
|
||||
.target = self.target,
|
||||
}).?;
|
||||
for (value.relocs.items) |off| {
|
||||
mem.writeInt(
|
||||
u32,
|
||||
dbg_info_buffer.items[off..][0..4],
|
||||
atom.off + value.off,
|
||||
target_endian,
|
||||
);
|
||||
const deferred: bool = blk: {
|
||||
if (ty.isAnyError()) break :blk true;
|
||||
switch (ty.tag()) {
|
||||
.error_set_inferred => {
|
||||
if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
break :blk false;
|
||||
};
|
||||
if (deferred) {
|
||||
// Defer until later
|
||||
try self.deferred_error_sets_relocs.append(self.allocator, atom.off + off);
|
||||
} else {
|
||||
mem.writeInt(
|
||||
u32,
|
||||
dbg_info_buffer.items[off..][0..4],
|
||||
atom.off + value.off,
|
||||
target_endian,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Offsets to positions with known a priori relative displacement values.
|
||||
@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo(
|
||||
try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);
|
||||
}
|
||||
|
||||
pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
if (self.deferred_error_sets_relocs.items.len == 0) return; // Nothing to do
|
||||
|
||||
const gpa = self.allocator;
|
||||
var arena_alloc = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_alloc.deinit();
|
||||
const arena = arena_alloc.allocator();
|
||||
|
||||
const error_set = try arena.create(Module.ErrorSet);
|
||||
const ty = try Type.Tag.error_set.create(arena, error_set);
|
||||
var names = Module.ErrorSet.NameMap{};
|
||||
try names.ensureUnusedCapacity(arena, module.global_error_set.count());
|
||||
var it = module.global_error_set.keyIterator();
|
||||
while (it.next()) |key| {
|
||||
names.putAssumeCapacityNoClobber(key.*, {});
|
||||
}
|
||||
error_set.names = names;
|
||||
|
||||
var dbg_info_buffer = std.ArrayList(u8).init(arena);
|
||||
try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer);
|
||||
|
||||
// TODO seems like we need to store DebugInfoAtoms in Dwarf object
|
||||
// In other words, I have turned Dwarf into a linker...
|
||||
// FIXME memory leak!!!
|
||||
const atom = try gpa.create(DebugInfoAtom);
|
||||
errdefer gpa.destroy(atom);
|
||||
atom.* = .{
|
||||
.prev = null,
|
||||
.next = null,
|
||||
.off = 0,
|
||||
.len = 0,
|
||||
};
|
||||
try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len));
|
||||
try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);
|
||||
|
||||
const file_pos = blk: {
|
||||
switch (self.tag) {
|
||||
.elf => {
|
||||
const elf_file = file.cast(File.Elf).?;
|
||||
const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?];
|
||||
break :blk debug_info_sect.sh_offset;
|
||||
},
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
|
||||
break :blk debug_info_sect.offset;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
};
|
||||
|
||||
const target_endian = self.target.cpu.arch.endian();
|
||||
var buf: [@sizeOf(u32)]u8 = undefined;
|
||||
while (self.deferred_error_sets_relocs.popOrNull()) |reloc| {
|
||||
mem.writeInt(u32, &buf, atom.off, target_endian);
|
||||
|
||||
switch (self.tag) {
|
||||
.elf => {
|
||||
const elf_file = file.cast(File.Elf).?;
|
||||
try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc);
|
||||
},
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
try d_sym.file.pwriteAll(&buf, file_pos + reloc);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@ -1098,51 +1204,7 @@ fn addDbgInfoType(
|
||||
}
|
||||
},
|
||||
.ErrorSet => {
|
||||
// DW.AT.enumeration_type
|
||||
try dbg_info_buffer.append(abbrev_enum_type);
|
||||
// DW.AT.byte_size, DW.FORM.sdata
|
||||
const abi_size = ty.abiSize(target);
|
||||
try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
const name = try ty.nameAllocArena(arena, target);
|
||||
try dbg_info_buffer.writer().print("{s}\x00", .{name});
|
||||
|
||||
// DW.AT.enumerator
|
||||
const no_error = "(no error)";
|
||||
try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64));
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(no_error);
|
||||
dbg_info_buffer.appendAssumeCapacity(0);
|
||||
// DW.AT.const_value, DW.FORM.data8
|
||||
mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian);
|
||||
|
||||
const error_names = blk: {
|
||||
if (ty.isAnyError())
|
||||
break :blk module.error_name_list.items;
|
||||
// TODO not quite sure about the next one, but if I don't do this, I risk
|
||||
// tripping an assert in `Type.errorSetNames` in case the inferred error set
|
||||
// was not yet fully resolved. This so far only surfaced when this code would
|
||||
// schedule analysis of an error set part of some error union.
|
||||
if (ty.tag() == .error_set_inferred)
|
||||
break :blk ty.castTag(.error_set_inferred).?.data.errors.keys();
|
||||
break :blk ty.errorSetNames();
|
||||
};
|
||||
|
||||
for (error_names) |error_name| {
|
||||
const kv = module.getErrorValue(error_name) catch unreachable;
|
||||
// DW.AT.enumerator
|
||||
try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64));
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(error_name);
|
||||
dbg_info_buffer.appendAssumeCapacity(0);
|
||||
// DW.AT.const_value, DW.FORM.data8
|
||||
mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian);
|
||||
}
|
||||
|
||||
// DW.AT.enumeration_type delimit children
|
||||
try dbg_info_buffer.append(0);
|
||||
try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer);
|
||||
},
|
||||
.ErrorUnion => {
|
||||
const error_ty = ty.errorUnionSet();
|
||||
@ -1208,6 +1270,52 @@ fn addDbgInfoType(
|
||||
}
|
||||
}
|
||||
|
||||
fn addDbgInfoErrorSet(
|
||||
self: *Dwarf,
|
||||
arena: Allocator,
|
||||
module: *Module,
|
||||
ty: Type,
|
||||
dbg_info_buffer: *std.ArrayList(u8),
|
||||
) error{OutOfMemory}!void {
|
||||
const target = self.target;
|
||||
const target_endian = self.target.cpu.arch.endian();
|
||||
|
||||
// DW.AT.enumeration_type
|
||||
try dbg_info_buffer.append(abbrev_enum_type);
|
||||
// DW.AT.byte_size, DW.FORM.sdata
|
||||
const abi_size = ty.abiSize(target);
|
||||
try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
const name = try ty.nameAllocArena(arena, target);
|
||||
try dbg_info_buffer.writer().print("{s}\x00", .{name});
|
||||
|
||||
// DW.AT.enumerator
|
||||
const no_error = "(no error)";
|
||||
try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64));
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(no_error);
|
||||
dbg_info_buffer.appendAssumeCapacity(0);
|
||||
// DW.AT.const_value, DW.FORM.data8
|
||||
mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian);
|
||||
|
||||
const error_names = ty.errorSetNames();
|
||||
for (error_names) |error_name| {
|
||||
const kv = module.getErrorValue(error_name) catch unreachable;
|
||||
// DW.AT.enumerator
|
||||
try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64));
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant);
|
||||
// DW.AT.name, DW.FORM.string
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(error_name);
|
||||
dbg_info_buffer.appendAssumeCapacity(0);
|
||||
// DW.AT.const_value, DW.FORM.data8
|
||||
mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian);
|
||||
}
|
||||
|
||||
// DW.AT.enumeration_type delimit children
|
||||
try dbg_info_buffer.append(0);
|
||||
}
|
||||
|
||||
pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
|
||||
// These are LEB encoded but since the values are all less than 127
|
||||
// we can simply append these bytes.
|
||||
|
||||
@ -958,6 +958,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||
const foreign_endian = target_endian != builtin.cpu.arch.endian();
|
||||
|
||||
if (self.dwarf) |*dwarf| {
|
||||
try dwarf.commitErrorSetDebugInfo(&self.base, module);
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.relocs.iterator();
|
||||
while (it.next()) |entry| {
|
||||
@ -2376,7 +2380,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
};
|
||||
const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
|
||||
if (debug_buffers) |dbg| {
|
||||
try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg);
|
||||
try self.dwarf.?.commitDeclDebugInfo(
|
||||
&self.base,
|
||||
module,
|
||||
decl,
|
||||
local_sym.st_value,
|
||||
local_sym.st_size,
|
||||
dbg,
|
||||
);
|
||||
}
|
||||
|
||||
// Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user