| ... | ... | @@ -26,6 +26,10 @@ pub const Module = struct { |
| 26 | 26 | symbols: []u8, |
| 27 | 27 | subsect_info: []u8, |
| 28 | 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 | 34 | pub fn deinit(self: *Module, allocator: Allocator) void { |
| 31 | 35 | allocator.free(self.module_name); |
| ... | ... | @@ -33,6 +37,7 @@ pub const Module = struct { |
| 33 | 37 | if (self.populated) { |
| 34 | 38 | allocator.free(self.symbols); |
| 35 | 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 | 122 | .symbols = undefined, |
| 118 | 123 | .subsect_info = undefined, |
| 119 | 124 | .checksum_offset = null, |
| 125 | .inlinee_source_lines = undefined, |
| 120 | 126 | }); |
| 121 | 127 | |
| 122 | 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 | 663 | pub const InlineeSourceLine = struct { |
| 658 | 664 | signature: pdb.InlineeSourceLineSignature, |
| 659 | 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 | 681 | self: *Pdb, |
| 664 | 682 | mod: *Module, |
| 665 | 683 | inlinee: u32, |
| 666 | | ) ?InlineeSourceLine { |
| 684 | ) []const InlineeSourceLine { |
| 667 | 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| { |
| 680 | | if (has_extra_files) { |
| 681 | | const file_count = subsect.takeInt(u32, .little) catch return null; |
| 682 | | const file_bytes = std.math.mul(usize, file_count, @sizeOf(u32)) catch return null; |
| 683 | | subsect.discardAll(file_bytes) catch return null; |
| 684 | | } |
| 685 | | |
| 686 | | if (inlinee_src_line.inlinee == inlinee) return .{ |
| 687 | | .signature = signature, |
| 688 | | .info = inlinee_src_line, |
| 689 | | }; |
| 690 | | } |
| 687 | // Binary search to an arbitrary match, if there are other matches they will be adjacent |
| 688 | const any = std.sort.binarySearch( |
| 689 | InlineeSourceLine, |
| 690 | mod.inlinee_source_lines, |
| 691 | inlinee, |
| 692 | InlineeSourceLine.compare, |
| 693 | ) orelse return &.{}; |
| 694 | |
| 695 | // Linearly scan to the first match |
| 696 | const begin = b: { |
| 697 | var begin = any; |
| 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 | | } |
| 693 | | return null; |
| 703 | break :b begin; |
| 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 | 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 | 834 | const gpa = self.allocator; |
| 811 | 835 | |
| 812 | 836 | mod.symbols = try reader.readAlloc(gpa, mod.mod_info.sym_byte_size - 4); |
| 837 | errdefer gpa.free(mod.symbols); |
| 813 | 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 | 877 | var sect_offset: usize = 0; |
| 816 | 878 | var skip_len: usize = undefined; |