| ... | @@ -26,6 +26,10 @@ pub const Module = struct { | ... | @@ -26,6 +26,10 @@ pub const Module = struct { |
| 26 | symbols: []u8, | 26 | symbols: []u8, |
| 27 | subsect_info: []u8, | 27 | subsect_info: []u8, |
| 28 | checksum_offset: ?usize, | 28 | checksum_offset: ?usize, |
| | 29 | /// The inlinee source lines, sorted by inlinee. This saves us from repeatedly doing linear |
| | 30 | /// searches over all inlinees. We prefer binary search over a hashmap as LLVM somtimes outputs |
| | 31 | /// multiple entries for a single inlinee ID, see `getInlineeSourceLines` for more info. |
| | 32 | inlinee_source_lines: []InlineeSourceLine, |
| 29 | | 33 | |
| 30 | pub fn deinit(self: *Module, allocator: Allocator) void { | 34 | pub fn deinit(self: *Module, allocator: Allocator) void { |
| 31 | allocator.free(self.module_name); | 35 | allocator.free(self.module_name); |
| ... | @@ -33,6 +37,7 @@ pub const Module = struct { | ... | @@ -33,6 +37,7 @@ pub const Module = struct { |
| 33 | if (self.populated) { | 37 | if (self.populated) { |
| 34 | allocator.free(self.symbols); | 38 | allocator.free(self.symbols); |
| 35 | allocator.free(self.subsect_info); | 39 | allocator.free(self.subsect_info); |
| | 40 | allocator.free(self.inlinee_source_lines); |
| 36 | } | 41 | } |
| 37 | } | 42 | } |
| 38 | }; | 43 | }; |
| ... | @@ -117,6 +122,7 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -117,6 +122,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 117 | .symbols = undefined, | 122 | .symbols = undefined, |
| 118 | .subsect_info = undefined, | 123 | .subsect_info = undefined, |
| 119 | .checksum_offset = null, | 124 | .checksum_offset = null, |
| | 125 | .inlinee_source_lines = undefined, |
| 120 | }); | 126 | }); |
| 121 | | 127 | |
| 122 | mod_info_offset += this_record_len; | 128 | mod_info_offset += this_record_len; |
| ... | @@ -657,40 +663,58 @@ pub fn getSymbolName(self: *Pdb, proc_sym: *align(1) const pdb.ProcSym) []const | ... | @@ -657,40 +663,58 @@ pub fn getSymbolName(self: *Pdb, proc_sym: *align(1) const pdb.ProcSym) []const |
| 657 | pub const InlineeSourceLine = struct { | 663 | pub const InlineeSourceLine = struct { |
| 658 | signature: pdb.InlineeSourceLineSignature, | 664 | signature: pdb.InlineeSourceLineSignature, |
| 659 | info: *align(1) const pdb.InlineeSourceLine, | 665 | info: *align(1) const pdb.InlineeSourceLine, |
| | 666 | |
| | 667 | fn lessThan(_: void, lhs: InlineeSourceLine, rhs: InlineeSourceLine) bool { |
| | 668 | return lhs.info.inlinee < rhs.info.inlinee; |
| | 669 | } |
| | 670 | |
| | 671 | fn compare(inlinee: u32, self: InlineeSourceLine) std.math.Order { |
| | 672 | return std.math.order(inlinee, self.info.inlinee); |
| | 673 | } |
| 660 | }; | 674 | }; |
| 661 | | 675 | |
| 662 | pub fn getInlineeSourceLine( | 676 | /// Returns all `InlineeSourceLine`s for a given module with the given inlinee. Ideally there would |
| | 677 | /// only be one entry per inlinee, but LLVM appears to assign all functions that share a name the |
| | 678 | /// same inlinee ID. This appears to be a bug, so the best the caller can do right now is print all |
| | 679 | /// the results. |
| | 680 | pub fn getInlineeSourceLines( |
| 663 | self: *Pdb, | 681 | self: *Pdb, |
| 664 | mod: *Module, | 682 | mod: *Module, |
| 665 | inlinee: u32, | 683 | inlinee: u32, |
| 666 | ) ?InlineeSourceLine { | 684 | ) []const InlineeSourceLine { |
| 667 | _ = self; | 685 | _ = self; |
| 668 | var subsects: Io.Reader = .fixed(mod.subsect_info); | | |
| 669 | while (subsects.takeStructPointer(pdb.DebugSubsectionHeader) catch null) |subsect_hdr| { | | |
| 670 | var subsect: Io.Reader = .fixed(subsects.take(subsect_hdr.length) catch return null); | | |
| 671 | if (subsect_hdr.kind == .inlinee_lines) { | | |
| 672 | const signature = subsect.takeEnum(pdb.InlineeSourceLineSignature, .little) catch return null; | | |
| 673 | const has_extra_files = switch (signature) { | | |
| 674 | .normal => false, | | |
| 675 | .ex => true, | | |
| 676 | else => continue, | | |
| 677 | }; | | |
| 678 | | 686 | |
| 679 | while (subsect.takeStructPointer(pdb.InlineeSourceLine) catch null) |inlinee_src_line| { | 687 | // Binary search to an arbitrary match, if there are other matches they will be adjacent |
| 680 | if (has_extra_files) { | 688 | const any = std.sort.binarySearch( |
| 681 | const file_count = subsect.takeInt(u32, .little) catch return null; | 689 | InlineeSourceLine, |
| 682 | const file_bytes = std.math.mul(usize, file_count, @sizeOf(u32)) catch return null; | 690 | mod.inlinee_source_lines, |
| 683 | subsect.discardAll(file_bytes) catch return null; | 691 | inlinee, |
| 684 | } | 692 | InlineeSourceLine.compare, |
| 685 | | 693 | ) orelse return &.{}; |
| 686 | if (inlinee_src_line.inlinee == inlinee) return .{ | 694 | |
| 687 | .signature = signature, | 695 | // Linearly scan to the first match |
| 688 | .info = inlinee_src_line, | 696 | const begin = b: { |
| 689 | }; | 697 | var begin = any; |
| 690 | } | 698 | while (begin > 0) { |
| | 699 | const prev = begin - 1; |
| | 700 | if (mod.inlinee_source_lines[prev].info.inlinee != inlinee) break; |
| | 701 | begin = prev; |
| 691 | } | 702 | } |
| 692 | } | 703 | break :b begin; |
| 693 | return null; | 704 | }; |
| | 705 | |
| | 706 | // Linearly scan to the last match |
| | 707 | const end = b: { |
| | 708 | var end = any + 1; |
| | 709 | while ( |
| | 710 | end < mod.inlinee_source_lines.len and |
| | 711 | mod.inlinee_source_lines[end].info.inlinee == inlinee |
| | 712 | ) : (end += 1) {} |
| | 713 | break :b end; |
| | 714 | }; |
| | 715 | |
| | 716 | // Return a slice of all the matches |
| | 717 | return mod.inlinee_source_lines[begin..end]; |
| 694 | } | 718 | } |
| 695 | | 719 | |
| 696 | pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { | 720 | pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { |
| ... | @@ -810,7 +834,45 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { | ... | @@ -810,7 +834,45 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { |
| 810 | const gpa = self.allocator; | 834 | const gpa = self.allocator; |
| 811 | | 835 | |
| 812 | mod.symbols = try reader.readAlloc(gpa, mod.mod_info.sym_byte_size - 4); | 836 | mod.symbols = try reader.readAlloc(gpa, mod.mod_info.sym_byte_size - 4); |
| | 837 | errdefer gpa.free(mod.symbols); |
| 813 | mod.subsect_info = try reader.readAlloc(gpa, mod.mod_info.c13_byte_size); | 838 | mod.subsect_info = try reader.readAlloc(gpa, mod.mod_info.c13_byte_size); |
| | 839 | errdefer gpa.free(mod.subsect_info); |
| | 840 | mod.inlinee_source_lines = b: { |
| | 841 | var inlinee_source_lines: std.ArrayList(InlineeSourceLine) = .empty; |
| | 842 | defer inlinee_source_lines.deinit(gpa); |
| | 843 | var subsects: Io.Reader = .fixed(mod.subsect_info); |
| | 844 | while (subsects.takeStructPointer(pdb.DebugSubsectionHeader) catch null) |subsect_hdr| { |
| | 845 | var subsect: Io.Reader = .fixed(subsects.take(subsect_hdr.length) catch return null); |
| | 846 | if (subsect_hdr.kind == .inlinee_lines) { |
| | 847 | const inlinee_source_line_signature = subsect.takeEnum(pdb.InlineeSourceLineSignature, .little) |
| | 848 | catch return error.InvalidDebugInfo; |
| | 849 | const has_extra_files = switch (inlinee_source_line_signature) { |
| | 850 | .normal => false, |
| | 851 | .ex => true, |
| | 852 | else => continue, |
| | 853 | }; |
| | 854 | while (subsect.takeStructPointer(pdb.InlineeSourceLine) catch null) |info| { |
| | 855 | if (has_extra_files) { |
| | 856 | const file_count = subsect.takeInt(u32, .little) catch |
| | 857 | return error.InvalidDebugInfo; |
| | 858 | const file_bytes = std.math.mul(usize, file_count, @sizeOf(u32)) |
| | 859 | catch return error.InvalidDebugInfo; |
| | 860 | subsect.discardAll(file_bytes) catch |
| | 861 | return error.InvalidDebugInfo; |
| | 862 | } |
| | 863 | |
| | 864 | try inlinee_source_lines.append(gpa, .{ |
| | 865 | .signature = inlinee_source_line_signature, |
| | 866 | .info = info, |
| | 867 | }); |
| | 868 | } |
| | 869 | } |
| | 870 | } |
| | 871 | |
| | 872 | std.mem.sort(InlineeSourceLine, inlinee_source_lines.items, {}, InlineeSourceLine.lessThan); |
| | 873 | break :b try inlinee_source_lines.toOwnedSlice(gpa); |
| | 874 | }; |
| | 875 | errdefer gpa.free(mod.inlinee_source_lines); |
| 814 | | 876 | |
| 815 | var sect_offset: usize = 0; | 877 | var sect_offset: usize = 0; |
| 816 | var skip_len: usize = undefined; | 878 | var skip_len: usize = undefined; |