| ... | ... | @@ -26,10 +26,11 @@ 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 | /// The inlinee source lines, sorted by inlinee, then file, then line number. |
| 30 | /// This saves us from repeatedly doing linear searches over all inlinees. |
| 31 | /// We prefer binary search over a hashmap as LLVM somtimes outputs multiple entries |
| 32 | /// for a single inlinee ID, see `getInlineeSourceLines` for more info. |
| 33 | inlinee_source_lines: []*align(1) const pdb.InlineeSourceLine, |
| 33 | 34 | |
| 34 | 35 | pub fn deinit(self: *Module, allocator: Allocator) void { |
| 35 | 36 | allocator.free(self.module_name); |
| ... | ... | @@ -669,44 +670,68 @@ pub fn getSymbolName(self: *Pdb, proc_sym: *align(1) const pdb.ProcSym) []const |
| 669 | 670 | return std.mem.sliceTo(@as([*:0]const u8, @ptrCast(&proc_sym.name[0])), 0); |
| 670 | 671 | } |
| 671 | 672 | |
| 672 | | pub const InlineeSourceLine = struct { |
| 673 | | signature: pdb.InlineeSourceLineSignature, |
| 674 | | info: *align(1) const pdb.InlineeSourceLine, |
| 673 | fn inlineeSourceLineLessThan( |
| 674 | _: void, |
| 675 | lhs: *align(1) const pdb.InlineeSourceLine, |
| 676 | rhs: *align(1) const pdb.InlineeSourceLine, |
| 677 | ) bool { |
| 678 | if (lhs.inlinee < rhs.inlinee) return true; |
| 679 | if (lhs.inlinee > rhs.inlinee) return false; |
| 680 | if (lhs.file_id < rhs.file_id) return true; |
| 681 | if (lhs.file_id > rhs.file_id) return false; |
| 682 | return lhs.source_line_num < rhs.source_line_num; |
| 683 | } |
| 675 | 684 | |
| 676 | | fn lessThan(_: void, lhs: InlineeSourceLine, rhs: InlineeSourceLine) bool { |
| 677 | | return lhs.info.inlinee < rhs.info.inlinee; |
| 678 | | } |
| 685 | fn compareInlineeSourceLineInlinee( |
| 686 | inlinee: u32, |
| 687 | inlinee_src_line: *align(1) const pdb.InlineeSourceLine, |
| 688 | ) std.math.Order { |
| 689 | return std.math.order(inlinee, inlinee_src_line.inlinee); |
| 690 | } |
| 679 | 691 | |
| 680 | | fn compare(inlinee: u32, self: InlineeSourceLine) std.math.Order { |
| 681 | | return std.math.order(inlinee, self.info.inlinee); |
| 692 | pub const InlineeSourceLocationIterator = struct { |
| 693 | /// The iterator assumes that all source lines in the slice are associated |
| 694 | /// with the same inlinee, and that it is sorted by file, then line number. |
| 695 | lines: []*align(1) const pdb.InlineeSourceLine, |
| 696 | |
| 697 | pub const empty: InlineeSourceLocationIterator = .{ .lines = &.{} }; |
| 698 | |
| 699 | pub fn next(iter: *InlineeSourceLocationIterator) ?*align(1) const pdb.InlineeSourceLine { |
| 700 | if (iter.lines.len == 0) return null; |
| 701 | const line = iter.lines[0]; |
| 702 | iter.lines = iter.lines[1..]; |
| 703 | // Filter out duplicate entries |
| 704 | while (iter.lines.len != 0 and |
| 705 | iter.lines[0].file_id == line.file_id and |
| 706 | iter.lines[0].source_line_num == line.source_line_num) |
| 707 | { |
| 708 | iter.lines = iter.lines[1..]; |
| 709 | } |
| 710 | return line; |
| 682 | 711 | } |
| 683 | 712 | }; |
| 684 | 713 | |
| 685 | | /// Returns all `InlineeSourceLine`s for a given module with the given inlinee. Ideally there would |
| 686 | | /// only be one entry per inlinee, but LLVM appears to assign all functions that share a name the |
| 687 | | /// same inlinee ID. This appears to be a bug, so the best the caller can do right now is print all |
| 688 | | /// the results. |
| 689 | | pub fn getInlineeSourceLines( |
| 690 | | self: *Pdb, |
| 691 | | mod: *Module, |
| 692 | | inlinee: u32, |
| 693 | | ) []const InlineeSourceLine { |
| 714 | /// Returns all `pdb.InlineeSourceLine`s for a given module with the given inlinee. Ideally |
| 715 | /// there would only be one entry per inlinee, but LLVM appears to assign all functions that share |
| 716 | /// a name the same inlinee ID. This is a bug: https://github.com/llvm/llvm-project/issues/191787 |
| 717 | /// The best the caller can do right now is print all the results. |
| 718 | pub fn getInlineeSourceLines(self: *Pdb, mod: *Module, inlinee: u32) InlineeSourceLocationIterator { |
| 694 | 719 | _ = self; |
| 695 | 720 | |
| 696 | 721 | // Binary search to an arbitrary match, if there are other matches they will be adjacent |
| 697 | 722 | const any = std.sort.binarySearch( |
| 698 | | InlineeSourceLine, |
| 723 | *align(1) const pdb.InlineeSourceLine, |
| 699 | 724 | mod.inlinee_source_lines, |
| 700 | 725 | inlinee, |
| 701 | | InlineeSourceLine.compare, |
| 702 | | ) orelse return &.{}; |
| 726 | compareInlineeSourceLineInlinee, |
| 727 | ) orelse return .empty; |
| 703 | 728 | |
| 704 | 729 | // Linearly scan to the first match |
| 705 | 730 | const begin = b: { |
| 706 | 731 | var begin = any; |
| 707 | 732 | while (begin > 0) { |
| 708 | 733 | const prev = begin - 1; |
| 709 | | if (mod.inlinee_source_lines[prev].info.inlinee != inlinee) break; |
| 734 | if (mod.inlinee_source_lines[prev].inlinee != inlinee) break; |
| 710 | 735 | begin = prev; |
| 711 | 736 | } |
| 712 | 737 | break :b begin; |
| ... | ... | @@ -716,13 +741,13 @@ pub fn getInlineeSourceLines( |
| 716 | 741 | const end = b: { |
| 717 | 742 | var end = any + 1; |
| 718 | 743 | while (end < mod.inlinee_source_lines.len and |
| 719 | | mod.inlinee_source_lines[end].info.inlinee == inlinee) : (end += 1) |
| 744 | mod.inlinee_source_lines[end].inlinee == inlinee) : (end += 1) |
| 720 | 745 | {} |
| 721 | 746 | break :b end; |
| 722 | 747 | }; |
| 723 | 748 | |
| 724 | | // Return a slice of all the matches |
| 725 | | return mod.inlinee_source_lines[begin..end]; |
| 749 | // Return an iterator over all matches (the iterator filters out duplicate entries) |
| 750 | return .{ .lines = mod.inlinee_source_lines[begin..end] }; |
| 726 | 751 | } |
| 727 | 752 | |
| 728 | 753 | pub fn getLineNumberInfo(self: *Pdb, gpa: Allocator, module: *Module, address: u64) !std.debug.SourceLocation { |
| ... | ... | @@ -845,7 +870,7 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { |
| 845 | 870 | mod.subsect_info = try reader.readAlloc(gpa, mod.mod_info.c13_byte_size); |
| 846 | 871 | errdefer gpa.free(mod.subsect_info); |
| 847 | 872 | mod.inlinee_source_lines = b: { |
| 848 | | var inlinee_source_lines: std.ArrayList(InlineeSourceLine) = .empty; |
| 873 | var inlinee_source_lines: std.ArrayList(*align(1) const pdb.InlineeSourceLine) = .empty; |
| 849 | 874 | defer inlinee_source_lines.deinit(gpa); |
| 850 | 875 | var subsects: Io.Reader = .fixed(mod.subsect_info); |
| 851 | 876 | while (subsects.takeStructPointer(pdb.DebugSubsectionHeader) catch null) |subsect_hdr| { |
| ... | ... | @@ -866,15 +891,17 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { |
| 866 | 891 | return error.InvalidDebugInfo; |
| 867 | 892 | } |
| 868 | 893 | |
| 869 | | try inlinee_source_lines.append(gpa, .{ |
| 870 | | .signature = inlinee_source_line_signature, |
| 871 | | .info = info, |
| 872 | | }); |
| 894 | try inlinee_source_lines.append(gpa, info); |
| 873 | 895 | } |
| 874 | 896 | } |
| 875 | 897 | } |
| 876 | 898 | |
| 877 | | std.mem.sortUnstable(InlineeSourceLine, inlinee_source_lines.items, {}, InlineeSourceLine.lessThan); |
| 899 | std.mem.sortUnstable( |
| 900 | *align(1) const pdb.InlineeSourceLine, |
| 901 | inlinee_source_lines.items, |
| 902 | {}, |
| 903 | inlineeSourceLineLessThan, |
| 904 | ); |
| 878 | 905 | break :b try inlinee_source_lines.toOwnedSlice(gpa); |
| 879 | 906 | }; |
| 880 | 907 | errdefer gpa.free(mod.inlinee_source_lines); |