| author | |
| committer | |
| log | b5398180d6b362346522a6067d54b90b97e23dc2 |
| tree | 227d414866a1052fd4197bb0b32e74e173300ef9 |
| parent | 0b5ea2b902b5802786cac70740e93872d2a0973d |
The implementation assumed that compilation units did not overlap, which
is not the case. The new implementation uses .debug_ranges to iterate
over the requested PCs.
This partially resolves #20990. The dump-cov tool is fixed but the same
fix needs to be applied to `std.Build.Fuzz.WebServer` (sorting the PC
list before passing it to be resolved by debug info).
I am observing LLVM emit multiple 8-bit counters for the same PC
addresses when enabling `-fsanitize-coverage=inline-8bit-counters`. This
seems like a bug in LLVM. I can't fathom why that would be desireable.5 files changed, 91 insertions(+), 76 deletions(-)
lib/std/debug/Coverage.zig+18-21| ... | @@ -151,46 +151,35 @@ pub fn resolveAddressesDwarf( | ... | @@ -151,46 +151,35 @@ pub fn resolveAddressesDwarf( |
| 151 | d: *Dwarf, | 151 | d: *Dwarf, |
| 152 | ) ResolveAddressesDwarfError!void { | 152 | ) ResolveAddressesDwarfError!void { |
| 153 | assert(sorted_pc_addrs.len == output.len); | 153 | assert(sorted_pc_addrs.len == output.len); |
| 154 | assert(d.compile_units_sorted); | 154 | assert(d.ranges.items.len != 0); // call `populateRanges` first. |
| 155 | 155 | ||
| 156 | var cu_i: usize = 0; | 156 | var range_i: usize = 0; |
| 157 | var line_table_i: usize = 0; | 157 | var range: *std.debug.Dwarf.Range = &d.ranges.items[0]; |
| 158 | var cu: *Dwarf.CompileUnit = &d.compile_unit_list.items[0]; | 158 | var line_table_i: usize = undefined; |
| 159 | var range = cu.pc_range.?; | 159 | var prev_cu: ?*std.debug.Dwarf.CompileUnit = null; |
| 160 | // Protects directories and files tables from other threads. | 160 | // Protects directories and files tables from other threads. |
| 161 | cov.mutex.lock(); | 161 | cov.mutex.lock(); |
| 162 | defer cov.mutex.unlock(); | 162 | defer cov.mutex.unlock(); |
| 163 | next_pc: for (sorted_pc_addrs, output) |pc, *out| { | 163 | next_pc: for (sorted_pc_addrs, output) |pc, *out| { |
| 164 | while (pc >= range.end) { | 164 | while (pc >= range.end) { |
| 165 | cu_i += 1; | 165 | range_i += 1; |
| 166 | if (cu_i >= d.compile_unit_list.items.len) { | 166 | if (range_i >= d.ranges.items.len) { |
| 167 | out.* = SourceLocation.invalid; | 167 | out.* = SourceLocation.invalid; |
| 168 | continue :next_pc; | 168 | continue :next_pc; |
| 169 | } | 169 | } |
| 170 | cu = &d.compile_unit_list.items[cu_i]; | 170 | range = &d.ranges.items[range_i]; |
| 171 | line_table_i = 0; | ||
| 172 | range = cu.pc_range orelse { | ||
| 173 | out.* = SourceLocation.invalid; | ||
| 174 | continue :next_pc; | ||
| 175 | }; | ||
| 176 | } | 171 | } |
| 177 | if (pc < range.start) { | 172 | if (pc < range.start) { |
| 178 | out.* = SourceLocation.invalid; | 173 | out.* = SourceLocation.invalid; |
| 179 | continue :next_pc; | 174 | continue :next_pc; |
| 180 | } | 175 | } |
| 181 | if (line_table_i == 0) { | 176 | const cu = &d.compile_unit_list.items[range.compile_unit_index]; |
| 182 | line_table_i = 1; | 177 | if (cu.src_loc_cache == null) { |
| 183 | cov.mutex.unlock(); | 178 | cov.mutex.unlock(); |
| 184 | defer cov.mutex.lock(); | 179 | defer cov.mutex.lock(); |
| 185 | d.populateSrcLocCache(gpa, cu) catch |err| switch (err) { | 180 | d.populateSrcLocCache(gpa, cu) catch |err| switch (err) { |
| 186 | error.MissingDebugInfo, error.InvalidDebugInfo => { | 181 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 187 | out.* = SourceLocation.invalid; | 182 | out.* = SourceLocation.invalid; |
| 188 | cu_i += 1; | ||
| 189 | if (cu_i < d.compile_unit_list.items.len) { | ||
| 190 | cu = &d.compile_unit_list.items[cu_i]; | ||
| 191 | line_table_i = 0; | ||
| 192 | if (cu.pc_range) |r| range = r; | ||
| 193 | } | ||
| 194 | continue :next_pc; | 183 | continue :next_pc; |
| 195 | }, | 184 | }, |
| 196 | else => |e| return e, | 185 | else => |e| return e, |
| ... | @@ -198,6 +187,14 @@ pub fn resolveAddressesDwarf( | ... | @@ -198,6 +187,14 @@ pub fn resolveAddressesDwarf( |
| 198 | } | 187 | } |
| 199 | const slc = &cu.src_loc_cache.?; | 188 | const slc = &cu.src_loc_cache.?; |
| 200 | const table_addrs = slc.line_table.keys(); | 189 | const table_addrs = slc.line_table.keys(); |
| 190 | if (cu != prev_cu) { | ||
| 191 | prev_cu = cu; | ||
| 192 | line_table_i = std.sort.upperBound(u64, table_addrs, pc, struct { | ||
| 193 | fn order(context: u64, item: u64) std.math.Order { | ||
| 194 | return std.math.order(item, context); | ||
| 195 | } | ||
| 196 | }.order); | ||
| 197 | } | ||
| 201 | while (line_table_i < table_addrs.len and table_addrs[line_table_i] < pc) line_table_i += 1; | 198 | while (line_table_i < table_addrs.len and table_addrs[line_table_i] < pc) line_table_i += 1; |
| 202 | 199 | ||
| 203 | const entry = slc.line_table.values()[line_table_i - 1]; | 200 | const entry = slc.line_table.values()[line_table_i - 1]; |
lib/std/debug/Dwarf.zig+54-42| ... | @@ -38,19 +38,30 @@ pub const call_frame = @import("Dwarf/call_frame.zig"); | ... | @@ -38,19 +38,30 @@ pub const call_frame = @import("Dwarf/call_frame.zig"); |
| 38 | endian: std.builtin.Endian, | 38 | endian: std.builtin.Endian, |
| 39 | sections: SectionArray = null_section_array, | 39 | sections: SectionArray = null_section_array, |
| 40 | is_macho: bool, | 40 | is_macho: bool, |
| 41 | compile_units_sorted: bool, | ||
| 42 | 41 | ||
| 43 | // Filled later by the initializer | 42 | /// Filled later by the initializer |
| 44 | abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{}, | 43 | abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{}, |
| 44 | /// Filled later by the initializer | ||
| 45 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, | 45 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 46 | /// Filled later by the initializer | ||
| 46 | func_list: std.ArrayListUnmanaged(Func) = .{}, | 47 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 47 | 48 | ||
| 48 | eh_frame_hdr: ?ExceptionFrameHeader = null, | 49 | eh_frame_hdr: ?ExceptionFrameHeader = null, |
| 49 | // These lookup tables are only used if `eh_frame_hdr` is null | 50 | /// These lookup tables are only used if `eh_frame_hdr` is null |
| 50 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, | 51 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, |
| 51 | // Sorted by start_pc | 52 | /// Sorted by start_pc |
| 52 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, | 53 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, |
| 53 | 54 | ||
| 55 | /// Populated by `populateRanges`. | ||
| 56 | ranges: std.ArrayListUnmanaged(Range) = .{}, | ||
| 57 | |||
| 58 | pub const Range = struct { | ||
| 59 | start: u64, | ||
| 60 | end: u64, | ||
| 61 | /// Index into `compile_unit_list`. | ||
| 62 | compile_unit_index: usize, | ||
| 63 | }; | ||
| 64 | |||
| 54 | pub const Section = struct { | 65 | pub const Section = struct { |
| 55 | data: []const u8, | 66 | data: []const u8, |
| 56 | // Module-relative virtual address. | 67 | // Module-relative virtual address. |
| ... | @@ -799,6 +810,7 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void { | ... | @@ -799,6 +810,7 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void { |
| 799 | di.func_list.deinit(gpa); | 810 | di.func_list.deinit(gpa); |
| 800 | di.cie_map.deinit(gpa); | 811 | di.cie_map.deinit(gpa); |
| 801 | di.fde_list.deinit(gpa); | 812 | di.fde_list.deinit(gpa); |
| 813 | di.ranges.deinit(gpa); | ||
| 802 | di.* = undefined; | 814 | di.* = undefined; |
| 803 | } | 815 | } |
| 804 | 816 | ||
| ... | @@ -985,8 +997,8 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void { | ... | @@ -985,8 +997,8 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 985 | try di.func_list.append(allocator, .{ | 997 | try di.func_list.append(allocator, .{ |
| 986 | .name = fn_name, | 998 | .name = fn_name, |
| 987 | .pc_range = .{ | 999 | .pc_range = .{ |
| 988 | .start = range.start_addr, | 1000 | .start = range.start, |
| 989 | .end = range.end_addr, | 1001 | .end = range.end, |
| 990 | }, | 1002 | }, |
| 991 | }); | 1003 | }); |
| 992 | } | 1004 | } |
| ... | @@ -1096,37 +1108,38 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void { | ... | @@ -1096,37 +1108,38 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 1096 | } | 1108 | } |
| 1097 | } | 1109 | } |
| 1098 | 1110 | ||
| 1099 | /// Populate missing PC ranges in compilation units, and then sort them by start address. | 1111 | pub fn populateRanges(d: *Dwarf, gpa: Allocator) ScanError!void { |
| 1100 | /// Does not guarantee pc_range to be non-null because there could be missing debug info. | 1112 | assert(d.ranges.items.len == 0); |
| 1101 | pub fn sortCompileUnits(d: *Dwarf) ScanError!void { | ||
| 1102 | assert(!d.compile_units_sorted); | ||
| 1103 | 1113 | ||
| 1104 | for (d.compile_unit_list.items) |*cu| { | 1114 | for (d.compile_unit_list.items, 0..) |*cu, cu_index| { |
| 1105 | if (cu.pc_range != null) continue; | 1115 | if (cu.pc_range) |range| { |
| 1116 | try d.ranges.append(gpa, .{ | ||
| 1117 | .start = range.start, | ||
| 1118 | .end = range.end, | ||
| 1119 | .compile_unit_index = cu_index, | ||
| 1120 | }); | ||
| 1121 | continue; | ||
| 1122 | } | ||
| 1106 | const ranges_value = cu.die.getAttr(AT.ranges) orelse continue; | 1123 | const ranges_value = cu.die.getAttr(AT.ranges) orelse continue; |
| 1107 | var iter = DebugRangeIterator.init(ranges_value, d, cu) catch continue; | 1124 | var iter = DebugRangeIterator.init(ranges_value, d, cu) catch continue; |
| 1108 | var start: u64 = maxInt(u64); | ||
| 1109 | var end: u64 = 0; | ||
| 1110 | while (try iter.next()) |range| { | 1125 | while (try iter.next()) |range| { |
| 1111 | start = @min(start, range.start_addr); | 1126 | // Not sure why LLVM thinks it's OK to emit these... |
| 1112 | end = @max(end, range.end_addr); | 1127 | if (range.start == range.end) continue; |
| 1128 | |||
| 1129 | try d.ranges.append(gpa, .{ | ||
| 1130 | .start = range.start, | ||
| 1131 | .end = range.end, | ||
| 1132 | .compile_unit_index = cu_index, | ||
| 1133 | }); | ||
| 1113 | } | 1134 | } |
| 1114 | if (end != 0) cu.pc_range = .{ | ||
| 1115 | .start = start, | ||
| 1116 | .end = end, | ||
| 1117 | }; | ||
| 1118 | } | 1135 | } |
| 1119 | 1136 | ||
| 1120 | std.mem.sortUnstable(CompileUnit, d.compile_unit_list.items, {}, struct { | 1137 | std.mem.sortUnstable(Range, d.ranges.items, {}, struct { |
| 1121 | pub fn lessThan(ctx: void, a: CompileUnit, b: CompileUnit) bool { | 1138 | pub fn lessThan(ctx: void, a: Range, b: Range) bool { |
| 1122 | _ = ctx; | 1139 | _ = ctx; |
| 1123 | const a_range = a.pc_range orelse return false; | 1140 | return a.start < b.start; |
| 1124 | const b_range = b.pc_range orelse return true; | ||
| 1125 | return a_range.start < b_range.start; | ||
| 1126 | } | 1141 | } |
| 1127 | }.lessThan); | 1142 | }.lessThan); |
| 1128 | |||
| 1129 | d.compile_units_sorted = true; | ||
| 1130 | } | 1143 | } |
| 1131 | 1144 | ||
| 1132 | const DebugRangeIterator = struct { | 1145 | const DebugRangeIterator = struct { |
| ... | @@ -1184,7 +1197,7 @@ const DebugRangeIterator = struct { | ... | @@ -1184,7 +1197,7 @@ const DebugRangeIterator = struct { |
| 1184 | } | 1197 | } |
| 1185 | 1198 | ||
| 1186 | // Returns the next range in the list, or null if the end was reached. | 1199 | // Returns the next range in the list, or null if the end was reached. |
| 1187 | pub fn next(self: *@This()) !?struct { start_addr: u64, end_addr: u64 } { | 1200 | pub fn next(self: *@This()) !?PcRange { |
| 1188 | switch (self.section_type) { | 1201 | switch (self.section_type) { |
| 1189 | .debug_rnglists => { | 1202 | .debug_rnglists => { |
| 1190 | const kind = try self.fbr.readByte(); | 1203 | const kind = try self.fbr.readByte(); |
| ... | @@ -1203,8 +1216,8 @@ const DebugRangeIterator = struct { | ... | @@ -1203,8 +1216,8 @@ const DebugRangeIterator = struct { |
| 1203 | const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index); | 1216 | const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index); |
| 1204 | 1217 | ||
| 1205 | return .{ | 1218 | return .{ |
| 1206 | .start_addr = start_addr, | 1219 | .start = start_addr, |
| 1207 | .end_addr = end_addr, | 1220 | .end = end_addr, |
| 1208 | }; | 1221 | }; |
| 1209 | }, | 1222 | }, |
| 1210 | RLE.startx_length => { | 1223 | RLE.startx_length => { |
| ... | @@ -1215,8 +1228,8 @@ const DebugRangeIterator = struct { | ... | @@ -1215,8 +1228,8 @@ const DebugRangeIterator = struct { |
| 1215 | const end_addr = start_addr + len; | 1228 | const end_addr = start_addr + len; |
| 1216 | 1229 | ||
| 1217 | return .{ | 1230 | return .{ |
| 1218 | .start_addr = start_addr, | 1231 | .start = start_addr, |
| 1219 | .end_addr = end_addr, | 1232 | .end = end_addr, |
| 1220 | }; | 1233 | }; |
| 1221 | }, | 1234 | }, |
| 1222 | RLE.offset_pair => { | 1235 | RLE.offset_pair => { |
| ... | @@ -1225,8 +1238,8 @@ const DebugRangeIterator = struct { | ... | @@ -1225,8 +1238,8 @@ const DebugRangeIterator = struct { |
| 1225 | 1238 | ||
| 1226 | // This is the only kind that uses the base address | 1239 | // This is the only kind that uses the base address |
| 1227 | return .{ | 1240 | return .{ |
| 1228 | .start_addr = self.base_address + start_addr, | 1241 | .start = self.base_address + start_addr, |
| 1229 | .end_addr = self.base_address + end_addr, | 1242 | .end = self.base_address + end_addr, |
| 1230 | }; | 1243 | }; |
| 1231 | }, | 1244 | }, |
| 1232 | RLE.base_address => { | 1245 | RLE.base_address => { |
| ... | @@ -1238,8 +1251,8 @@ const DebugRangeIterator = struct { | ... | @@ -1238,8 +1251,8 @@ const DebugRangeIterator = struct { |
| 1238 | const end_addr = try self.fbr.readInt(usize); | 1251 | const end_addr = try self.fbr.readInt(usize); |
| 1239 | 1252 | ||
| 1240 | return .{ | 1253 | return .{ |
| 1241 | .start_addr = start_addr, | 1254 | .start = start_addr, |
| 1242 | .end_addr = end_addr, | 1255 | .end = end_addr, |
| 1243 | }; | 1256 | }; |
| 1244 | }, | 1257 | }, |
| 1245 | RLE.start_length => { | 1258 | RLE.start_length => { |
| ... | @@ -1248,8 +1261,8 @@ const DebugRangeIterator = struct { | ... | @@ -1248,8 +1261,8 @@ const DebugRangeIterator = struct { |
| 1248 | const end_addr = start_addr + len; | 1261 | const end_addr = start_addr + len; |
| 1249 | 1262 | ||
| 1250 | return .{ | 1263 | return .{ |
| 1251 | .start_addr = start_addr, | 1264 | .start = start_addr, |
| 1252 | .end_addr = end_addr, | 1265 | .end = end_addr, |
| 1253 | }; | 1266 | }; |
| 1254 | }, | 1267 | }, |
| 1255 | else => return bad(), | 1268 | else => return bad(), |
| ... | @@ -1267,8 +1280,8 @@ const DebugRangeIterator = struct { | ... | @@ -1267,8 +1280,8 @@ const DebugRangeIterator = struct { |
| 1267 | } | 1280 | } |
| 1268 | 1281 | ||
| 1269 | return .{ | 1282 | return .{ |
| 1270 | .start_addr = self.base_address + start_addr, | 1283 | .start = self.base_address + start_addr, |
| 1271 | .end_addr = self.base_address + end_addr, | 1284 | .end = self.base_address + end_addr, |
| 1272 | }; | 1285 | }; |
| 1273 | }, | 1286 | }, |
| 1274 | else => unreachable, | 1287 | else => unreachable, |
| ... | @@ -1286,7 +1299,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit { | ... | @@ -1286,7 +1299,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit { |
| 1286 | const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue; | 1299 | const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue; |
| 1287 | var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue; | 1300 | var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue; |
| 1288 | while (try iter.next()) |range| { | 1301 | while (try iter.next()) |range| { |
| 1289 | if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit; | 1302 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| 1290 | } | 1303 | } |
| 1291 | } | 1304 | } |
| 1292 | 1305 | ||
| ... | @@ -2345,7 +2358,6 @@ pub const ElfModule = struct { | ... | @@ -2345,7 +2358,6 @@ pub const ElfModule = struct { |
| 2345 | .endian = endian, | 2358 | .endian = endian, |
| 2346 | .sections = sections, | 2359 | .sections = sections, |
| 2347 | .is_macho = false, | 2360 | .is_macho = false, |
| 2348 | .compile_units_sorted = false, | ||
| 2349 | }; | 2361 | }; |
| 2350 | 2362 | ||
| 2351 | try Dwarf.open(&di, gpa); | 2363 | try Dwarf.open(&di, gpa); |
lib/std/debug/Info.zig+1-1| ... | @@ -27,7 +27,7 @@ pub const LoadError = Dwarf.ElfModule.LoadError; | ... | @@ -27,7 +27,7 @@ pub const LoadError = Dwarf.ElfModule.LoadError; |
| 27 | pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info { | 27 | pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info { |
| 28 | var sections: Dwarf.SectionArray = Dwarf.null_section_array; | 28 | var sections: Dwarf.SectionArray = Dwarf.null_section_array; |
| 29 | var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null); | 29 | var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null); |
| 30 | try elf_module.dwarf.sortCompileUnits(); | 30 | try elf_module.dwarf.populateRanges(gpa); |
| 31 | var info: Info = .{ | 31 | var info: Info = .{ |
| 32 | .address_map = .{}, | 32 | .address_map = .{}, |
| 33 | .coverage = coverage, | 33 | .coverage = coverage, |
lib/std/debug/SelfInfo.zig-3| ... | @@ -606,7 +606,6 @@ pub const Module = switch (native_os) { | ... | @@ -606,7 +606,6 @@ pub const Module = switch (native_os) { |
| 606 | .endian = .little, | 606 | .endian = .little, |
| 607 | .sections = sections, | 607 | .sections = sections, |
| 608 | .is_macho = true, | 608 | .is_macho = true, |
| 609 | .compile_units_sorted = false, | ||
| 610 | }; | 609 | }; |
| 611 | 610 | ||
| 612 | try Dwarf.open(&di, allocator); | 611 | try Dwarf.open(&di, allocator); |
| ... | @@ -996,7 +995,6 @@ fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { | ... | @@ -996,7 +995,6 @@ fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { |
| 996 | .endian = native_endian, | 995 | .endian = native_endian, |
| 997 | .sections = sections, | 996 | .sections = sections, |
| 998 | .is_macho = false, | 997 | .is_macho = false, |
| 999 | .compile_units_sorted = false, | ||
| 1000 | }; | 998 | }; |
| 1001 | 999 | ||
| 1002 | try Dwarf.open(&dwarf, allocator); | 1000 | try Dwarf.open(&dwarf, allocator); |
| ... | @@ -1810,7 +1808,6 @@ fn unwindFrameMachODwarf( | ... | @@ -1810,7 +1808,6 @@ fn unwindFrameMachODwarf( |
| 1810 | var di: Dwarf = .{ | 1808 | var di: Dwarf = .{ |
| 1811 | .endian = native_endian, | 1809 | .endian = native_endian, |
| 1812 | .is_macho = true, | 1810 | .is_macho = true, |
| 1813 | .compile_units_sorted = false, | ||
| 1814 | }; | 1811 | }; |
| 1815 | defer di.deinit(context.allocator); | 1812 | defer di.deinit(context.allocator); |
| 1816 | 1813 |
tools/dump-cov.zig+18-9| ... | @@ -54,21 +54,30 @@ pub fn main() !void { | ... | @@ -54,21 +54,30 @@ pub fn main() !void { |
| 54 | const header: *SeenPcsHeader = @ptrCast(cov_bytes); | 54 | const header: *SeenPcsHeader = @ptrCast(cov_bytes); |
| 55 | try stdout.print("{any}\n", .{header.*}); | 55 | try stdout.print("{any}\n", .{header.*}); |
| 56 | const pcs = header.pcAddrs(); | 56 | const pcs = header.pcAddrs(); |
| 57 | for (0.., pcs[0 .. pcs.len - 1], pcs[1..]) |i, a, b| { | ||
| 58 | if (a > b) std.log.err("{d}: 0x{x} > 0x{x}", .{ i, a, b }); | ||
| 59 | } | ||
| 60 | assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize))); | ||
| 61 | 57 | ||
| 62 | const seen_pcs = header.seenBits(); | 58 | var indexed_pcs: std.AutoArrayHashMapUnmanaged(usize, void) = .{}; |
| 59 | try indexed_pcs.entries.resize(arena, pcs.len); | ||
| 60 | @memcpy(indexed_pcs.entries.items(.key), pcs); | ||
| 61 | try indexed_pcs.reIndex(arena); | ||
| 62 | |||
| 63 | const sorted_pcs = try arena.dupe(usize, pcs); | ||
| 64 | std.mem.sortUnstable(usize, sorted_pcs, {}, std.sort.asc(usize)); | ||
| 63 | 65 | ||
| 64 | const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len); | 66 | const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, sorted_pcs.len); |
| 65 | try debug_info.resolveAddresses(gpa, pcs, source_locations); | 67 | try debug_info.resolveAddresses(gpa, sorted_pcs, source_locations); |
| 68 | |||
| 69 | const seen_pcs = header.seenBits(); | ||
| 66 | 70 | ||
| 67 | for (pcs, source_locations, 0..) |pc, sl, i| { | 71 | for (sorted_pcs, source_locations) |pc, sl| { |
| 72 | if (sl.file == .invalid) { | ||
| 73 | try stdout.print(" {x}: invalid\n", .{pc}); | ||
| 74 | continue; | ||
| 75 | } | ||
| 68 | const file = debug_info.coverage.fileAt(sl.file); | 76 | const file = debug_info.coverage.fileAt(sl.file); |
| 69 | const dir_name = debug_info.coverage.directories.keys()[file.directory_index]; | 77 | const dir_name = debug_info.coverage.directories.keys()[file.directory_index]; |
| 70 | const dir_name_slice = debug_info.coverage.stringAt(dir_name); | 78 | const dir_name_slice = debug_info.coverage.stringAt(dir_name); |
| 71 | const hit: u1 = @truncate(seen_pcs[i / @bitSizeOf(usize)] >> @intCast(i % @bitSizeOf(usize))); | 79 | const seen_i = indexed_pcs.getIndex(pc).?; |
| 80 | const hit: u1 = @truncate(seen_pcs[seen_i / @bitSizeOf(usize)] >> @intCast(seen_i % @bitSizeOf(usize))); | ||
| 72 | try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{ | 81 | try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{ |
| 73 | "-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column, | 82 | "-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column, |
| 74 | }); | 83 | }); |