authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-14 04:55:32-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-14 04:55:32-07:00
logb7a1ef3e19039690b182a295dff7cffe5fcc521f
treed62b8bdee5b8f332941cd846435bddf667853341
parentb470d2a7de20f8036c8976ec18ed1fb16a15c662
parent72768bddcd815f84ac6d40ffd80258ceaa511cb9
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21075 from ziglang/fuzz

fix several debug info bugs

7 files changed, 179 insertions(+), 101 deletions(-)

lib/std/Build/Fuzz/WebServer.zig+21-3
...@@ -634,10 +634,28 @@ fn prepareTables(...@@ -634,10 +634,28 @@ fn prepareTables(
634 const pcs = header.pcAddrs();634 const pcs = header.pcAddrs();
635 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);635 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
636 errdefer gpa.free(source_locations);636 errdefer gpa.free(source_locations);
637 debug_info.resolveAddresses(gpa, pcs, source_locations) catch |err| {637
638 // Unfortunately the PCs array that LLVM gives us from the 8-bit PC
639 // counters feature is not sorted.
640 var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{};
641 defer sorted_pcs.deinit(gpa);
642 try sorted_pcs.resize(gpa, pcs.len);
643 @memcpy(sorted_pcs.items(.pc), pcs);
644 for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i);
645 sorted_pcs.sortUnstable(struct {
646 addrs: []const u64,
647
648 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
649 return ctx.addrs[a_index] < ctx.addrs[b_index];
650 }
651 }{ .addrs = sorted_pcs.items(.pc) });
652
653 debug_info.resolveAddresses(gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
638 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});654 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});
639 return error.AlreadyReported;655 return error.AlreadyReported;
640 };656 };
657
658 for (sorted_pcs.items(.index), sorted_pcs.items(.sl)) |i, sl| source_locations[i] = sl;
641 gop.value_ptr.source_locations = source_locations;659 gop.value_ptr.source_locations = source_locations;
642660
643 ws.coverage_condition.broadcast();661 ws.coverage_condition.broadcast();
...@@ -664,8 +682,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo...@@ -664,8 +682,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
664 if (false) {682 if (false) {
665 const sl = coverage_map.source_locations[index];683 const sl = coverage_map.source_locations[index];
666 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);684 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
667 log.debug("server found entry point {s}:{d}:{d}", .{685 log.debug("server found entry point for 0x{x} at {s}:{d}:{d}", .{
668 file_name, sl.line, sl.column,686 addr, file_name, sl.line, sl.column,
669 });687 });
670 }688 }
671 const gpa = ws.gpa;689 const gpa = ws.gpa;
lib/std/debug/Coverage.zig+35-31
...@@ -145,60 +145,64 @@ pub const ResolveAddressesDwarfError = Dwarf.ScanError;...@@ -145,60 +145,64 @@ pub const ResolveAddressesDwarfError = Dwarf.ScanError;
145pub fn resolveAddressesDwarf(145pub fn resolveAddressesDwarf(
146 cov: *Coverage,146 cov: *Coverage,
147 gpa: Allocator,147 gpa: Allocator,
148 /// Asserts the addresses are in ascending order.
148 sorted_pc_addrs: []const u64,149 sorted_pc_addrs: []const u64,
149 /// Asserts its length equals length of `sorted_pc_addrs`.150 /// Asserts its length equals length of `sorted_pc_addrs`.
150 output: []SourceLocation,151 output: []SourceLocation,
151 d: *Dwarf,152 d: *Dwarf,
152) ResolveAddressesDwarfError!void {153) ResolveAddressesDwarfError!void {
153 assert(sorted_pc_addrs.len == output.len);154 assert(sorted_pc_addrs.len == output.len);
154 assert(d.compile_units_sorted);155 assert(d.ranges.items.len != 0); // call `populateRanges` first.
155156
156 var cu_i: usize = 0;157 var range_i: usize = 0;
157 var line_table_i: usize = 0;158 var range: *std.debug.Dwarf.Range = &d.ranges.items[0];
158 var cu: *Dwarf.CompileUnit = &d.compile_unit_list.items[0];159 var line_table_i: usize = undefined;
159 var range = cu.pc_range.?;160 var prev_pc: u64 = 0;
161 var prev_cu: ?*std.debug.Dwarf.CompileUnit = null;
160 // Protects directories and files tables from other threads.162 // Protects directories and files tables from other threads.
161 cov.mutex.lock();163 cov.mutex.lock();
162 defer cov.mutex.unlock();164 defer cov.mutex.unlock();
163 next_pc: for (sorted_pc_addrs, output) |pc, *out| {165 next_pc: for (sorted_pc_addrs, output) |pc, *out| {
166 assert(pc >= prev_pc);
167 prev_pc = pc;
168
164 while (pc >= range.end) {169 while (pc >= range.end) {
165 cu_i += 1;170 range_i += 1;
166 if (cu_i >= d.compile_unit_list.items.len) {171 if (range_i >= d.ranges.items.len) {
167 out.* = SourceLocation.invalid;172 out.* = SourceLocation.invalid;
168 continue :next_pc;173 continue :next_pc;
169 }174 }
170 cu = &d.compile_unit_list.items[cu_i];175 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 }176 }
177 if (pc < range.start) {177 if (pc < range.start) {
178 out.* = SourceLocation.invalid;178 out.* = SourceLocation.invalid;
179 continue :next_pc;179 continue :next_pc;
180 }180 }
181 if (line_table_i == 0) {181 const cu = &d.compile_unit_list.items[range.compile_unit_index];
182 line_table_i = 1;182 if (cu != prev_cu) {
183 cov.mutex.unlock();183 prev_cu = cu;
184 defer cov.mutex.lock();184 if (cu.src_loc_cache == null) {
185 d.populateSrcLocCache(gpa, cu) catch |err| switch (err) {185 cov.mutex.unlock();
186 error.MissingDebugInfo, error.InvalidDebugInfo => {186 defer cov.mutex.lock();
187 out.* = SourceLocation.invalid;187 d.populateSrcLocCache(gpa, cu) catch |err| switch (err) {
188 cu_i += 1;188 error.MissingDebugInfo, error.InvalidDebugInfo => {
189 if (cu_i < d.compile_unit_list.items.len) {189 out.* = SourceLocation.invalid;
190 cu = &d.compile_unit_list.items[cu_i];190 continue :next_pc;
191 line_table_i = 0;191 },
192 if (cu.pc_range) |r| range = r;192 else => |e| return e,
193 }193 };
194 continue :next_pc;194 }
195 },195 const slc = &cu.src_loc_cache.?;
196 else => |e| return e,196 const table_addrs = slc.line_table.keys();
197 };197 line_table_i = std.sort.upperBound(u64, table_addrs, pc, struct {
198 fn order(context: u64, item: u64) std.math.Order {
199 return std.math.order(item, context);
200 }
201 }.order);
198 }202 }
199 const slc = &cu.src_loc_cache.?;203 const slc = &cu.src_loc_cache.?;
200 const table_addrs = slc.line_table.keys();204 const table_addrs = slc.line_table.keys();
201 while (line_table_i < table_addrs.len and table_addrs[line_table_i] < pc) line_table_i += 1;205 while (line_table_i < table_addrs.len and table_addrs[line_table_i] <= pc) line_table_i += 1;
202206
203 const entry = slc.line_table.values()[line_table_i - 1];207 const entry = slc.line_table.values()[line_table_i - 1];
204 const corrected_file_index = entry.file - @intFromBool(slc.version < 5);208 const corrected_file_index = entry.file - @intFromBool(slc.version < 5);
lib/std/debug/Dwarf.zig+99-51
...@@ -26,7 +26,6 @@ const cast = std.math.cast;...@@ -26,7 +26,6 @@ const cast = std.math.cast;
26const maxInt = std.math.maxInt;26const maxInt = std.math.maxInt;
27const MemoryAccessor = std.debug.MemoryAccessor;27const MemoryAccessor = std.debug.MemoryAccessor;
28const Path = std.Build.Cache.Path;28const Path = std.Build.Cache.Path;
29
30const FixedBufferReader = std.debug.FixedBufferReader;29const FixedBufferReader = std.debug.FixedBufferReader;
3130
32const Dwarf = @This();31const Dwarf = @This();
...@@ -35,22 +34,36 @@ pub const expression = @import("Dwarf/expression.zig");...@@ -35,22 +34,36 @@ pub const expression = @import("Dwarf/expression.zig");
35pub const abi = @import("Dwarf/abi.zig");34pub const abi = @import("Dwarf/abi.zig");
36pub const call_frame = @import("Dwarf/call_frame.zig");35pub const call_frame = @import("Dwarf/call_frame.zig");
3736
37/// Useful to temporarily enable while working on this file.
38const debug_debug_mode = false;
39
38endian: std.builtin.Endian,40endian: std.builtin.Endian,
39sections: SectionArray = null_section_array,41sections: SectionArray = null_section_array,
40is_macho: bool,42is_macho: bool,
41compile_units_sorted: bool,
4243
43// Filled later by the initializer44/// Filled later by the initializer
44abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{},45abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{},
46/// Filled later by the initializer
45compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},47compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
48/// Filled later by the initializer
46func_list: std.ArrayListUnmanaged(Func) = .{},49func_list: std.ArrayListUnmanaged(Func) = .{},
4750
48eh_frame_hdr: ?ExceptionFrameHeader = null,51eh_frame_hdr: ?ExceptionFrameHeader = null,
49// These lookup tables are only used if `eh_frame_hdr` is null52/// These lookup tables are only used if `eh_frame_hdr` is null
50cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{},53cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{},
51// Sorted by start_pc54/// Sorted by start_pc
52fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},55fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},
5356
57/// Populated by `populateRanges`.
58ranges: std.ArrayListUnmanaged(Range) = .{},
59
60pub const Range = struct {
61 start: u64,
62 end: u64,
63 /// Index into `compile_unit_list`.
64 compile_unit_index: usize,
65};
66
54pub const Section = struct {67pub const Section = struct {
55 data: []const u8,68 data: []const u8,
56 // Module-relative virtual address.69 // Module-relative virtual address.
...@@ -154,6 +167,16 @@ pub const CompileUnit = struct {...@@ -154,6 +167,16 @@ pub const CompileUnit = struct {
154 column: u32,167 column: u32,
155 /// Offset by 1 depending on whether Dwarf version is >= 5.168 /// Offset by 1 depending on whether Dwarf version is >= 5.
156 file: u32,169 file: u32,
170
171 pub const invalid: LineEntry = .{
172 .line = undefined,
173 .column = undefined,
174 .file = std.math.maxInt(u32),
175 };
176
177 pub fn isInvalid(le: LineEntry) bool {
178 return le.file == invalid.file;
179 }
157 };180 };
158181
159 pub fn findSource(slc: *const SrcLocCache, address: u64) !LineEntry {182 pub fn findSource(slc: *const SrcLocCache, address: u64) !LineEntry {
...@@ -799,6 +822,7 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void {...@@ -799,6 +822,7 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void {
799 di.func_list.deinit(gpa);822 di.func_list.deinit(gpa);
800 di.cie_map.deinit(gpa);823 di.cie_map.deinit(gpa);
801 di.fde_list.deinit(gpa);824 di.fde_list.deinit(gpa);
825 di.ranges.deinit(gpa);
802 di.* = undefined;826 di.* = undefined;
803}827}
804828
...@@ -985,8 +1009,8 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -985,8 +1009,8 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
985 try di.func_list.append(allocator, .{1009 try di.func_list.append(allocator, .{
986 .name = fn_name,1010 .name = fn_name,
987 .pc_range = .{1011 .pc_range = .{
988 .start = range.start_addr,1012 .start = range.start,
989 .end = range.end_addr,1013 .end = range.end,
990 },1014 },
991 });1015 });
992 }1016 }
...@@ -1096,37 +1120,38 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -1096,37 +1120,38 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1096 }1120 }
1097}1121}
10981122
1099/// Populate missing PC ranges in compilation units, and then sort them by start address.1123pub 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.1124 assert(d.ranges.items.len == 0);
1101pub fn sortCompileUnits(d: *Dwarf) ScanError!void {
1102 assert(!d.compile_units_sorted);
11031125
1104 for (d.compile_unit_list.items) |*cu| {1126 for (d.compile_unit_list.items, 0..) |*cu, cu_index| {
1105 if (cu.pc_range != null) continue;1127 if (cu.pc_range) |range| {
1128 try d.ranges.append(gpa, .{
1129 .start = range.start,
1130 .end = range.end,
1131 .compile_unit_index = cu_index,
1132 });
1133 continue;
1134 }
1106 const ranges_value = cu.die.getAttr(AT.ranges) orelse continue;1135 const ranges_value = cu.die.getAttr(AT.ranges) orelse continue;
1107 var iter = DebugRangeIterator.init(ranges_value, d, cu) catch continue;1136 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| {1137 while (try iter.next()) |range| {
1111 start = @min(start, range.start_addr);1138 // Not sure why LLVM thinks it's OK to emit these...
1112 end = @max(end, range.end_addr);1139 if (range.start == range.end) continue;
1140
1141 try d.ranges.append(gpa, .{
1142 .start = range.start,
1143 .end = range.end,
1144 .compile_unit_index = cu_index,
1145 });
1113 }1146 }
1114 if (end != 0) cu.pc_range = .{
1115 .start = start,
1116 .end = end,
1117 };
1118 }1147 }
11191148
1120 std.mem.sortUnstable(CompileUnit, d.compile_unit_list.items, {}, struct {1149 std.mem.sortUnstable(Range, d.ranges.items, {}, struct {
1121 pub fn lessThan(ctx: void, a: CompileUnit, b: CompileUnit) bool {1150 pub fn lessThan(ctx: void, a: Range, b: Range) bool {
1122 _ = ctx;1151 _ = ctx;
1123 const a_range = a.pc_range orelse return false;1152 return a.start < b.start;
1124 const b_range = b.pc_range orelse return true;
1125 return a_range.start < b_range.start;
1126 }1153 }
1127 }.lessThan);1154 }.lessThan);
1128
1129 d.compile_units_sorted = true;
1130}1155}
11311156
1132const DebugRangeIterator = struct {1157const DebugRangeIterator = struct {
...@@ -1184,7 +1209,7 @@ const DebugRangeIterator = struct {...@@ -1184,7 +1209,7 @@ const DebugRangeIterator = struct {
1184 }1209 }
11851210
1186 // Returns the next range in the list, or null if the end was reached.1211 // 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 } {1212 pub fn next(self: *@This()) !?PcRange {
1188 switch (self.section_type) {1213 switch (self.section_type) {
1189 .debug_rnglists => {1214 .debug_rnglists => {
1190 const kind = try self.fbr.readByte();1215 const kind = try self.fbr.readByte();
...@@ -1203,8 +1228,8 @@ const DebugRangeIterator = struct {...@@ -1203,8 +1228,8 @@ const DebugRangeIterator = struct {
1203 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);1228 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);
12041229
1205 return .{1230 return .{
1206 .start_addr = start_addr,1231 .start = start_addr,
1207 .end_addr = end_addr,1232 .end = end_addr,
1208 };1233 };
1209 },1234 },
1210 RLE.startx_length => {1235 RLE.startx_length => {
...@@ -1215,8 +1240,8 @@ const DebugRangeIterator = struct {...@@ -1215,8 +1240,8 @@ const DebugRangeIterator = struct {
1215 const end_addr = start_addr + len;1240 const end_addr = start_addr + len;
12161241
1217 return .{1242 return .{
1218 .start_addr = start_addr,1243 .start = start_addr,
1219 .end_addr = end_addr,1244 .end = end_addr,
1220 };1245 };
1221 },1246 },
1222 RLE.offset_pair => {1247 RLE.offset_pair => {
...@@ -1225,8 +1250,8 @@ const DebugRangeIterator = struct {...@@ -1225,8 +1250,8 @@ const DebugRangeIterator = struct {
12251250
1226 // This is the only kind that uses the base address1251 // This is the only kind that uses the base address
1227 return .{1252 return .{
1228 .start_addr = self.base_address + start_addr,1253 .start = self.base_address + start_addr,
1229 .end_addr = self.base_address + end_addr,1254 .end = self.base_address + end_addr,
1230 };1255 };
1231 },1256 },
1232 RLE.base_address => {1257 RLE.base_address => {
...@@ -1238,8 +1263,8 @@ const DebugRangeIterator = struct {...@@ -1238,8 +1263,8 @@ const DebugRangeIterator = struct {
1238 const end_addr = try self.fbr.readInt(usize);1263 const end_addr = try self.fbr.readInt(usize);
12391264
1240 return .{1265 return .{
1241 .start_addr = start_addr,1266 .start = start_addr,
1242 .end_addr = end_addr,1267 .end = end_addr,
1243 };1268 };
1244 },1269 },
1245 RLE.start_length => {1270 RLE.start_length => {
...@@ -1248,8 +1273,8 @@ const DebugRangeIterator = struct {...@@ -1248,8 +1273,8 @@ const DebugRangeIterator = struct {
1248 const end_addr = start_addr + len;1273 const end_addr = start_addr + len;
12491274
1250 return .{1275 return .{
1251 .start_addr = start_addr,1276 .start = start_addr,
1252 .end_addr = end_addr,1277 .end = end_addr,
1253 };1278 };
1254 },1279 },
1255 else => return bad(),1280 else => return bad(),
...@@ -1267,8 +1292,8 @@ const DebugRangeIterator = struct {...@@ -1267,8 +1292,8 @@ const DebugRangeIterator = struct {
1267 }1292 }
12681293
1269 return .{1294 return .{
1270 .start_addr = self.base_address + start_addr,1295 .start = self.base_address + start_addr,
1271 .end_addr = self.base_address + end_addr,1296 .end = self.base_address + end_addr,
1272 };1297 };
1273 },1298 },
1274 else => unreachable,1299 else => unreachable,
...@@ -1286,7 +1311,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit {...@@ -1286,7 +1311,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit {
1286 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;1311 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;
1287 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;1312 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;
1288 while (try iter.next()) |range| {1313 while (try iter.next()) |range| {
1289 if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit;1314 if (target_address >= range.start and target_address < range.end) return compile_unit;
1290 }1315 }
1291 }1316 }
12921317
...@@ -1387,6 +1412,7 @@ fn parseDie(...@@ -1387,6 +1412,7 @@ fn parseDie(
1387 };1412 };
1388}1413}
13891414
1415/// Ensures that addresses in the returned LineTable are monotonically increasing.
1390fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {1416fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {
1391 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);1417 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);
1392 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);1418 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
...@@ -1562,8 +1588,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1562,8 +1588,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1562 const sub_op = try fbr.readByte();1588 const sub_op = try fbr.readByte();
1563 switch (sub_op) {1589 switch (sub_op) {
1564 DW.LNE.end_sequence => {1590 DW.LNE.end_sequence => {
1565 prog.end_sequence = true;1591 // The row being added here is an "end" address, meaning
1566 try prog.addRow(gpa, &line_table);1592 // that it does not map to the source location here -
1593 // rather it marks the previous address as the last address
1594 // that maps to this source location.
1595
1596 // In this implementation we don't mark end of addresses.
1597 // This is a performance optimization based on the fact
1598 // that we don't need to know if an address is missing
1599 // source location info; we are only interested in being
1600 // able to look up source location info for addresses that
1601 // are known to have debug info.
1602 //if (debug_debug_mode) assert(!line_table.contains(prog.address));
1603 //try line_table.put(gpa, prog.address, CompileUnit.SrcLocCache.LineEntry.invalid);
1567 prog.reset();1604 prog.reset();
1568 },1605 },
1569 DW.LNE.set_address => {1606 DW.LNE.set_address => {
...@@ -1638,6 +1675,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1638,6 +1675,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1638 }1675 }
1639 }1676 }
16401677
1678 // Dwarf standard v5, 6.2.5 says
1679 // > Within a sequence, addresses and operation pointers may only increase.
1680 // However, this is empirically not the case in reality, so we sort here.
1681 line_table.sortUnstable(struct {
1682 keys: []const u64,
1683
1684 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
1685 return ctx.keys[a_index] < ctx.keys[b_index];
1686 }
1687 }{ .keys = line_table.keys() });
1688
1641 return .{1689 return .{
1642 .line_table = line_table,1690 .line_table = line_table,
1643 .directories = try directories.toOwnedSlice(gpa),1691 .directories = try directories.toOwnedSlice(gpa),
...@@ -1882,7 +1930,6 @@ const LineNumberProgram = struct {...@@ -1882,7 +1930,6 @@ const LineNumberProgram = struct {
1882 version: u16,1930 version: u16,
1883 is_stmt: bool,1931 is_stmt: bool,
1884 basic_block: bool,1932 basic_block: bool,
1885 end_sequence: bool,
18861933
1887 default_is_stmt: bool,1934 default_is_stmt: bool,
18881935
...@@ -1894,7 +1941,6 @@ const LineNumberProgram = struct {...@@ -1894,7 +1941,6 @@ const LineNumberProgram = struct {
1894 self.column = 0;1941 self.column = 0;
1895 self.is_stmt = self.default_is_stmt;1942 self.is_stmt = self.default_is_stmt;
1896 self.basic_block = false;1943 self.basic_block = false;
1897 self.end_sequence = false;
1898 }1944 }
18991945
1900 pub fn init(is_stmt: bool, version: u16) LineNumberProgram {1946 pub fn init(is_stmt: bool, version: u16) LineNumberProgram {
...@@ -1906,13 +1952,16 @@ const LineNumberProgram = struct {...@@ -1906,13 +1952,16 @@ const LineNumberProgram = struct {
1906 .version = version,1952 .version = version,
1907 .is_stmt = is_stmt,1953 .is_stmt = is_stmt,
1908 .basic_block = false,1954 .basic_block = false,
1909 .end_sequence = false,
1910 .default_is_stmt = is_stmt,1955 .default_is_stmt = is_stmt,
1911 };1956 };
1912 }1957 }
19131958
1914 pub fn addRow(prog: *LineNumberProgram, gpa: Allocator, table: *CompileUnit.SrcLocCache.LineTable) !void {1959 pub fn addRow(prog: *LineNumberProgram, gpa: Allocator, table: *CompileUnit.SrcLocCache.LineTable) !void {
1915 if (prog.line == 0) return; // garbage data1960 if (prog.line == 0) {
1961 //if (debug_debug_mode) @panic("garbage line data");
1962 return;
1963 }
1964 if (debug_debug_mode) assert(!table.contains(prog.address));
1916 try table.put(gpa, prog.address, .{1965 try table.put(gpa, prog.address, .{
1917 .line = cast(u32, prog.line) orelse maxInt(u32),1966 .line = cast(u32, prog.line) orelse maxInt(u32),
1918 .column = cast(u32, prog.column) orelse maxInt(u32),1967 .column = cast(u32, prog.column) orelse maxInt(u32),
...@@ -1959,12 +2008,12 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {...@@ -1959,12 +2008,12 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1959/// This function is to make it handy to comment out the return and make it2008/// This function is to make it handy to comment out the return and make it
1960/// into a crash when working on this file.2009/// into a crash when working on this file.
1961pub fn bad() error{InvalidDebugInfo} {2010pub fn bad() error{InvalidDebugInfo} {
1962 //if (true) @panic("bad dwarf"); // can be handy to uncomment when working on this file2011 if (debug_debug_mode) @panic("bad dwarf");
1963 return error.InvalidDebugInfo;2012 return error.InvalidDebugInfo;
1964}2013}
19652014
1966fn missing() error{MissingDebugInfo} {2015fn missing() error{MissingDebugInfo} {
1967 //if (true) @panic("missing dwarf"); // can be handy to uncomment when working on this file2016 if (debug_debug_mode) @panic("missing dwarf");
1968 return error.MissingDebugInfo;2017 return error.MissingDebugInfo;
1969}2018}
19702019
...@@ -2345,7 +2394,6 @@ pub const ElfModule = struct {...@@ -2345,7 +2394,6 @@ pub const ElfModule = struct {
2345 .endian = endian,2394 .endian = endian,
2346 .sections = sections,2395 .sections = sections,
2347 .is_macho = false,2396 .is_macho = false,
2348 .compile_units_sorted = false,
2349 };2397 };
23502398
2351 try Dwarf.open(&di, gpa);2399 try Dwarf.open(&di, gpa);
lib/std/debug/Info.zig+2-1
...@@ -27,7 +27,7 @@ pub const LoadError = Dwarf.ElfModule.LoadError;...@@ -27,7 +27,7 @@ pub const LoadError = Dwarf.ElfModule.LoadError;
27pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {27pub 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,
...@@ -51,6 +51,7 @@ pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError;...@@ -51,6 +51,7 @@ pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError;
51pub fn resolveAddresses(51pub fn resolveAddresses(
52 info: *Info,52 info: *Info,
53 gpa: Allocator,53 gpa: Allocator,
54 /// Asserts the addresses are in ascending order.
54 sorted_pc_addrs: []const u64,55 sorted_pc_addrs: []const u64,
55 /// Asserts its length equals length of `sorted_pc_addrs`.56 /// Asserts its length equals length of `sorted_pc_addrs`.
56 output: []SourceLocation,57 output: []SourceLocation,
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 };
611610
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 };
1001999
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);
18161813
src/zig_llvm.cpp+4-3
...@@ -311,7 +311,10 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi...@@ -311,7 +311,10 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi
311 }311 }
312 });312 });
313313
314 pass_builder.registerOptimizerEarlyEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {314 //pass_builder.registerOptimizerEarlyEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
315 //});
316
317 pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) {
315 // Code coverage instrumentation.318 // Code coverage instrumentation.
316 if (options.sancov) {319 if (options.sancov) {
317 module_pm.addPass(SanitizerCoveragePass(getSanCovOptions(options.coverage)));320 module_pm.addPass(SanitizerCoveragePass(getSanCovOptions(options.coverage)));
...@@ -322,9 +325,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi...@@ -322,9 +325,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi
322 module_pm.addPass(ModuleThreadSanitizerPass());325 module_pm.addPass(ModuleThreadSanitizerPass());
323 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));326 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
324 }327 }
325 });
326328
327 pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) {
328 // Verify the output329 // Verify the output
329 if (assertions_on) {330 if (assertions_on) {
330 module_pm.addPass(VerifierPass());331 module_pm.addPass(VerifierPass());
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)));
6157
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));
6365
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();
6670
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 });