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