authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-07 15:57:58-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:29-07:00
log7ec2f2b27d72c46b6d2e0b68f8f91aaf61c2263e
tree579f049b96c82bed04ec47ab9dfafda853c68331
parenta1a8dd1b40d2f42e9df6fee053c2cb737e0753f7

Cleans up, implements handling for the change file binary annotation


2 files changed, 90 insertions(+), 76 deletions(-)

lib/std/debug/Pdb.zig+62-58
......@@ -312,7 +312,7 @@ pub const BinaryAnnotation = union(enum) {
312312
313313 const PartialRange = struct {
314314 line_offset: u32,
315 file_offset: u32,
315 file_id: ?u32,
316316 code_offset: u32,
317317 code_length: ?u32,
318318 };
......@@ -322,7 +322,7 @@ pub const BinaryAnnotation = union(enum) {
322322 .annotations = annotations,
323323 .curr = .{
324324 .line_offset = 0,
325 .file_offset = 0,
325 .file_id = null,
326326 .code_offset = 0,
327327 .code_length = null,
328328 },
......@@ -332,7 +332,7 @@ pub const BinaryAnnotation = union(enum) {
332332
333333 pub const Range = struct {
334334 line_offset: u32,
335 file_offset: u32,
335 file_id: ?u32,
336336 code_offset: u32,
337337 code_length: u32,
338338
......@@ -352,7 +352,11 @@ pub const BinaryAnnotation = union(enum) {
352352 if (self.prev) |*prev| prev.code_length = prev.code_length orelse length;
353353 self.curr.code_offset += length;
354354 },
355 .change_file => @panic("unimplemented"),
355 // LLVM has code to emit these, but I wasn't able to figure out how trigger it
356 // so this logic is untested.
357 .change_file => |file_id| {
358 self.curr.file_id = file_id;
359 },
356360 // LLVM never emits this opcode, but it's clear enough how to interpret it so we may as
357361 // well in case they use it in the future
358362 .change_code_length_and_code_offset => |info| {
......@@ -402,7 +406,7 @@ pub const BinaryAnnotation = union(enum) {
402406 .code_offset = self.curr.code_offset,
403407 .code_length = self.curr.code_length,
404408 .line_offset = self.curr.line_offset,
405 .file_offset = self.curr.file_offset,
409 .file_id = self.curr.file_id,
406410 };
407411 const prev = self.prev orelse continue;
408412 const prev_code_length = prev.code_length orelse continue;
......@@ -410,7 +414,7 @@ pub const BinaryAnnotation = union(enum) {
410414 .code_offset = prev.code_offset,
411415 .code_length = prev_code_length,
412416 .line_offset = prev.line_offset,
413 .file_offset = prev.file_offset,
417 .file_id = prev.file_id,
414418 };
415419 }
416420
......@@ -421,7 +425,7 @@ pub const BinaryAnnotation = union(enum) {
421425 .code_offset = prev.code_offset,
422426 .code_length = prev_code_length,
423427 .line_offset = prev.line_offset,
424 .file_offset = prev.file_offset,
428 .file_id = prev.file_id,
425429 };
426430 }
427431 };
......@@ -588,30 +592,62 @@ pub fn getBinaryAnnotations(self: *Pdb, site: *align(1) const pdb.InlineSiteSym)
588592 return .{ .reader = Io.Reader.fixed(slice) };
589593}
590594
591pub fn calculateOffset(
595pub fn getInlineSiteSourceLocation(
592596 self: *Pdb,
597 mod: *Module,
593598 site: *align(1) const pdb.InlineSiteSym,
594 loc: std.debug.SourceLocation,
599 inlinee_src_line: *align(1) const pdb.InlineeSourceLine,
595600 offset_in_func: usize,
596) error{InvalidDebugInfo, MissingDebugInfo, ReadFailed}!?std.debug.SourceLocation {
601) !?std.debug.SourceLocation {
597602 var ranges: BinaryAnnotation.RangeIterator = .init(self.getBinaryAnnotations(site));
598603 while (try ranges.next()) |range| {
599 if (range.contains(offset_in_func)) {
600 var result: std.debug.SourceLocation = loc;
601 result.line += range.line_offset;
602 return result;
603 }
604 if (!range.contains(offset_in_func)) continue;
605
606 const file_id = range.file_id orelse inlinee_src_line.file_id;
607 const file_name = try self.getFileName(mod, file_id);
608 errdefer self.allocator.free(file_name);
609
610 return .{
611 .line = inlinee_src_line.source_line_num + range.line_offset,
612 // LLVM doesn't currently emit column information for inlined calls in PDBs.
613 .column = 0,
614 .file_name = file_name,
615 };
604616 }
605617 return null;
606618}
607619
620pub fn getFileName(self: *Pdb, mod: *Module, file_id: u32) ![]const u8 {
621 const checksum_offset = mod.checksum_offset orelse return error.MissingDebugInfo;
622 const subsect_index = checksum_offset + file_id;
623 const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&mod.subsect_info[subsect_index]);
624 const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset;
625 self.string_table.?.seekTo(strtab_offset) catch return error.InvalidDebugInfo;
626 const string_reader = &self.string_table.?.interface;
627 var source_file_name: Io.Writer.Allocating = .init(self.allocator);
628 defer source_file_name.deinit();
629 _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024));
630 assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API
631 string_reader.toss(1);
632 return try source_file_name.toOwnedSlice();
633}
634
608635pub fn getSymbolName(self: *Pdb, proc_sym: *align(1) const pdb.ProcSym) []const u8 {
609636 _ = self;
610637 return std.mem.sliceTo(@as([*:0]const u8, @ptrCast(&proc_sym.name[0])), 0);
611638}
612639
613pub fn getInlineeInfo(self: *Pdb, mod: *Module, inlinee: u32) !std.debug.SourceLocation {
614 const gpa = self.allocator;
640pub const InlineeSourceLine = struct {
641 signature: pdb.InlineeSourceLineSignature,
642 info: *align(1) const pdb.InlineeSourceLine,
643};
644
645pub fn getInlineeSourceLine(
646 self: *Pdb,
647 mod: *Module,
648 inlinee: u32,
649) ?InlineeSourceLine {
650 _ = self;
615651 var sect_offset: usize = 0;
616652 var skip_len: usize = undefined;
617653 while (sect_offset < mod.subsect_info.len) : (sect_offset += skip_len) {
......@@ -631,7 +667,7 @@ pub fn getInlineeInfo(self: *Pdb, mod: *Module, inlinee: u32) !std.debug.SourceL
631667 };
632668
633669 while (offset < sect_offset + subsect_hdr.length) {
634 const entry: *const align(1) pdb.InlineeSourceLine = @ptrCast(&mod.subsect_info[offset]);
670 const inlinee_src_line: *const align(1) pdb.InlineeSourceLine = @ptrCast(&mod.subsect_info[offset]);
635671 offset += @sizeOf(pdb.InlineeSourceLine);
636672
637673 if (has_extra_files) {
......@@ -640,33 +676,14 @@ pub fn getInlineeInfo(self: *Pdb, mod: *Module, inlinee: u32) !std.debug.SourceL
640676 offset += file_count.* * @sizeOf(u32);
641677 }
642678
643 if (entry.inlinee == inlinee) {
644 const source_file_name = s: {
645 const checksum_offset = mod.checksum_offset orelse return error.MissingDebugInfo;
646 const subsect_index = checksum_offset + entry.file_id;
647 const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&mod.subsect_info[subsect_index]);
648 const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset;
649 try self.string_table.?.seekTo(strtab_offset);
650 const string_reader = &self.string_table.?.interface;
651 var source_file_name: Io.Writer.Allocating = .init(gpa);
652 defer source_file_name.deinit();
653 _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024));
654 assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API
655 string_reader.toss(1);
656 break :s try source_file_name.toOwnedSlice();
657 };
658 errdefer gpa.free(source_file_name);
659
660 return .{
661 .line = entry.source_line_num,
662 .column = 0,
663 .file_name = source_file_name,
664 };
665 }
679 if (inlinee_src_line.inlinee == inlinee) return .{
680 .signature = signature.*,
681 .info = inlinee_src_line,
682 };
666683 }
667684 }
668685 }
669 return error.MissingDebugInfo;
686 return null;
670687}
671688
672689pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation {
......@@ -676,7 +693,6 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S
676693
677694 var sect_offset: usize = 0;
678695 var skip_len: usize = undefined;
679 const checksum_offset = module.checksum_offset orelse return error.MissingDebugInfo;
680696 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
681697 const subsect_hdr: *align(1) pdb.DebugSubsectionHeader = @ptrCast(&subsect_info[sect_offset]);
682698 skip_len = subsect_hdr.length;
......@@ -723,20 +739,8 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S
723739
724740 // line_i == 0 would mean that no matching pdb.LineNumberEntry was found.
725741 if (line_i > 0) {
726 const subsect_index = checksum_offset + block_hdr.name_index;
727 const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&module.subsect_info[subsect_index]);
728 const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset;
729 try self.string_table.?.seekTo(strtab_offset);
730 const source_file_name = s: {
731 const string_reader = &self.string_table.?.interface;
732 var source_file_name: Io.Writer.Allocating = .init(gpa);
733 defer source_file_name.deinit();
734 _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024));
735 assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API
736 string_reader.toss(1);
737 break :s try source_file_name.toOwnedSlice();
738 };
739 errdefer gpa.free(source_file_name);
742 const file_name = try self.getFileName(module, block_hdr.name_index);
743 errdefer gpa.free(file_name);
740744
741745 const line_entry_idx = line_i - 1;
742746
......@@ -751,7 +755,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S
751755 const line_num_entry: *align(1) pdb.LineNumberEntry = @ptrCast(&subsect_info[found_line_index]);
752756
753757 return .{
754 .file_name = source_file_name,
758 .file_name = file_name,
755759 .line = line_num_entry.flags.start,
756760 .column = column,
757761 };
lib/std/debug/SelfInfo/Windows.zig+28-18
......@@ -64,26 +64,36 @@ pub const SymbolIterator = struct {
6464
6565 // Get the next inlinee if it exists
6666 if (info.proc) |proc| {
67 const offset_in_func = info.addr - proc.code_offset;
6768 while (info.inline_sites.pop()) |site| {
68 if (pdb.getInlineeInfo(info.module, site.inlinee) catch null) |loc| {
69 const offset_in_func = info.addr - proc.code_offset;
70 if (try pdb.calculateOffset(site, loc, offset_in_func)) |offset| {
71 // If we've found a match, filter out any duplicate sites that
72 // follow. Tools like llvm-addr2line output duplicate sites in the
73 // same cases as us, implying that they exist in the underlying
74 // data and are not indicative of a parser bug.
75 while (info.inline_sites.getLastOrNull()) |top| {
76 if (top.inlinee != site.inlinee) break;
77 _ = info.inline_sites.pop();
78 }
79
80 return .{
81 .name = pdb.findInlineeName(site.inlinee),
82 .compile_unit_name = fs.path.basename(info.module.obj_file_name),
83 .source_location = offset,
84 };
85 }
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();
8690 }
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 };
8797 }
8898 }
8999