| author | |
| committer | |
| log | 0a330d4f947c1b05ac9f7d624443e2f80db2912f |
| tree | 041544172c327f401908594ed300fad56938bba1 |
| parent | 0caca625ebad92495a758e3121c91ba1f32774dd |
| signature |
3 files changed, 117 insertions(+), 38 deletions(-)
lib/std/Build/Fuzz.zig+25-4| ... | ... | @@ -383,7 +383,14 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO |
| 383 | 383 | errdefer gop.value_ptr.coverage.deinit(fuzz.gpa); |
| 384 | 384 | |
| 385 | 385 | const rebuilt_exe_path = run_step.rebuilt_executable.?; |
| 386 | var debug_info = std.debug.Info.load(fuzz.gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| { | |
| 386 | const target = run_step.producer.?.rootModuleTarget(); | |
| 387 | var debug_info = std.debug.Info.load( | |
| 388 | fuzz.gpa, | |
| 389 | rebuilt_exe_path, | |
| 390 | &gop.value_ptr.coverage, | |
| 391 | target.ofmt, | |
| 392 | target.cpu.arch, | |
| 393 | ) catch |err| { | |
| 387 | 394 | log.err("step '{s}': failed to load debug information for '{f}': {s}", .{ |
| 388 | 395 | run_step.step.name, rebuilt_exe_path, @errorName(err), |
| 389 | 396 | }); |
| ... | ... | @@ -479,9 +486,23 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte |
| 479 | 486 | if (false) { |
| 480 | 487 | const sl = coverage_map.source_locations[index]; |
| 481 | 488 | const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename); |
| 482 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{ | |
| 483 | addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1], | |
| 484 | }); | |
| 489 | if (pcs.len == 1) { | |
| 490 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index 0 (final)", .{ | |
| 491 | addr, file_name, sl.line, sl.column, | |
| 492 | }); | |
| 493 | } else if (index == 0) { | |
| 494 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index 0 before {x}", .{ | |
| 495 | addr, file_name, sl.line, sl.column, pcs[index + 1], | |
| 496 | }); | |
| 497 | } else if (index == pcs.len - 1) { | |
| 498 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} (final) after {x}", .{ | |
| 499 | addr, file_name, sl.line, sl.column, index, pcs[index - 1], | |
| 500 | }); | |
| 501 | } else { | |
| 502 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{ | |
| 503 | addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1], | |
| 504 | }); | |
| 505 | } | |
| 485 | 506 | } |
| 486 | 507 | try coverage_map.entry_points.append(fuzz.gpa, @intCast(index)); |
| 487 | 508 | } |
lib/std/debug/Info.zig+65-26| ... | ... | @@ -9,49 +9,67 @@ |
| 9 | 9 | const std = @import("../std.zig"); |
| 10 | 10 | const Allocator = std.mem.Allocator; |
| 11 | 11 | const Path = std.Build.Cache.Path; |
| 12 | const ElfFile = std.debug.ElfFile; | |
| 13 | 12 | const assert = std.debug.assert; |
| 14 | 13 | const Coverage = std.debug.Coverage; |
| 15 | 14 | const SourceLocation = std.debug.Coverage.SourceLocation; |
| 16 | 15 | |
| 16 | const ElfFile = std.debug.ElfFile; | |
| 17 | const MachOFile = std.debug.MachOFile; | |
| 18 | ||
| 17 | 19 | const Info = @This(); |
| 18 | 20 | |
| 19 | /// Sorted by key, ascending. | |
| 20 | address_map: std.AutoArrayHashMapUnmanaged(u64, ElfFile), | |
| 21 | impl: union(enum) { | |
| 22 | elf: ElfFile, | |
| 23 | macho: MachOFile, | |
| 24 | }, | |
| 21 | 25 | /// Externally managed, outlives this `Info` instance. |
| 22 | 26 | coverage: *Coverage, |
| 23 | 27 | |
| 24 | pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || std.debug.Dwarf.ScanError || error{MissingDebugInfo}; | |
| 28 | pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError || error{ MissingDebugInfo, UnsupportedDebugInfo }; | |
| 29 | ||
| 30 | pub fn load(gpa: Allocator, path: Path, coverage: *Coverage, format: std.Target.ObjectFormat, arch: std.Target.Cpu.Arch) LoadError!Info { | |
| 31 | switch (format) { | |
| 32 | .elf => { | |
| 33 | var file = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 34 | defer file.close(); | |
| 25 | 35 | |
| 26 | pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info { | |
| 27 | var file = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 28 | defer file.close(); | |
| 36 | var elf_file: ElfFile = try .load(gpa, file, null, &.none); | |
| 37 | errdefer elf_file.deinit(gpa); | |
| 29 | 38 | |
| 30 | var elf_file: ElfFile = try .load(gpa, file, null, &.none); | |
| 31 | errdefer elf_file.deinit(gpa); | |
| 39 | if (elf_file.dwarf == null) return error.MissingDebugInfo; | |
| 40 | try elf_file.dwarf.?.open(gpa, elf_file.endian); | |
| 41 | try elf_file.dwarf.?.populateRanges(gpa, elf_file.endian); | |
| 32 | 42 | |
| 33 | if (elf_file.dwarf == null) return error.MissingDebugInfo; | |
| 34 | try elf_file.dwarf.?.open(gpa, elf_file.endian); | |
| 35 | try elf_file.dwarf.?.populateRanges(gpa, elf_file.endian); | |
| 43 | return .{ | |
| 44 | .impl = .{ .elf = elf_file }, | |
| 45 | .coverage = coverage, | |
| 46 | }; | |
| 47 | }, | |
| 48 | .macho => { | |
| 49 | const path_str = try path.toString(gpa); | |
| 50 | defer gpa.free(path_str); | |
| 36 | 51 | |
| 37 | var info: Info = .{ | |
| 38 | .address_map = .{}, | |
| 39 | .coverage = coverage, | |
| 40 | }; | |
| 41 | try info.address_map.put(gpa, 0, elf_file); | |
| 42 | errdefer comptime unreachable; // elf_file is owned by the map now | |
| 43 | return info; | |
| 52 | var macho_file: MachOFile = try .load(gpa, path_str, arch); | |
| 53 | errdefer macho_file.deinit(gpa); | |
| 54 | ||
| 55 | return .{ | |
| 56 | .impl = .{ .macho = macho_file }, | |
| 57 | .coverage = coverage, | |
| 58 | }; | |
| 59 | }, | |
| 60 | else => return error.UnsupportedDebugInfo, | |
| 61 | } | |
| 44 | 62 | } |
| 45 | 63 | |
| 46 | 64 | pub fn deinit(info: *Info, gpa: Allocator) void { |
| 47 | for (info.address_map.values()) |*elf_file| { | |
| 48 | elf_file.dwarf.?.deinit(gpa); | |
| 65 | switch (info.impl) { | |
| 66 | .elf => |*ef| ef.deinit(gpa), | |
| 67 | .macho => |*mf| mf.deinit(gpa), | |
| 49 | 68 | } |
| 50 | info.address_map.deinit(gpa); | |
| 51 | 69 | info.* = undefined; |
| 52 | 70 | } |
| 53 | 71 | |
| 54 | pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError; | |
| 72 | pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{UnsupportedDebugInfo}; | |
| 55 | 73 | |
| 56 | 74 | /// Given an array of virtual memory addresses, sorted ascending, outputs a |
| 57 | 75 | /// corresponding array of source locations. |
| ... | ... | @@ -64,7 +82,28 @@ pub fn resolveAddresses( |
| 64 | 82 | output: []SourceLocation, |
| 65 | 83 | ) ResolveAddressesError!void { |
| 66 | 84 | assert(sorted_pc_addrs.len == output.len); |
| 67 | if (info.address_map.entries.len != 1) @panic("TODO"); | |
| 68 | const elf_file = &info.address_map.values()[0]; | |
| 69 | return info.coverage.resolveAddressesDwarf(gpa, elf_file.endian, sorted_pc_addrs, output, &elf_file.dwarf.?); | |
| 85 | switch (info.impl) { | |
| 86 | .elf => |*ef| return info.coverage.resolveAddressesDwarf(gpa, ef.endian, sorted_pc_addrs, output, &ef.dwarf.?), | |
| 87 | .macho => |*mf| { | |
| 88 | // Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries | |
| 89 | // due to split debug information. For now, we'll just resolve the addreses one by one. | |
| 90 | for (sorted_pc_addrs, output) |pc_addr, *src_loc| { | |
| 91 | const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, pc_addr) catch |err| switch (err) { | |
| 92 | error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo, | |
| 93 | else => |e| return e, | |
| 94 | }; | |
| 95 | if (dwarf.ranges.items.len == 0) { | |
| 96 | dwarf.populateRanges(gpa, .little) catch |err| switch (err) { | |
| 97 | error.EndOfStream, | |
| 98 | error.Overflow, | |
| 99 | error.StreamTooLong, | |
| 100 | error.ReadFailed, | |
| 101 | => return error.InvalidDebugInfo, | |
| 102 | else => |e| return e, | |
| 103 | }; | |
| 104 | } | |
| 105 | try info.coverage.resolveAddressesDwarf(gpa, .little, &.{dwarf_pc_addr}, src_loc[0..1], dwarf); | |
| 106 | } | |
| 107 | }, | |
| 108 | } | |
| 70 | 109 | } |
tools/dump-cov.zig+27-8| ... | ... | @@ -8,31 +8,50 @@ const assert = std.debug.assert; |
| 8 | 8 | const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader; |
| 9 | 9 | |
| 10 | 10 | pub fn main() !void { |
| 11 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | |
| 12 | defer _ = general_purpose_allocator.deinit(); | |
| 13 | const gpa = general_purpose_allocator.allocator(); | |
| 11 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | |
| 12 | defer _ = debug_allocator.deinit(); | |
| 13 | const gpa = debug_allocator.allocator(); | |
| 14 | 14 | |
| 15 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | |
| 15 | var arena_instance: std.heap.ArenaAllocator = .init(gpa); | |
| 16 | 16 | defer arena_instance.deinit(); |
| 17 | 17 | const arena = arena_instance.allocator(); |
| 18 | 18 | |
| 19 | var threaded: std.Io.Threaded = .init(gpa); | |
| 20 | defer threaded.deinit(); | |
| 21 | const io = threaded.io(); | |
| 22 | ||
| 19 | 23 | const args = try std.process.argsAlloc(arena); |
| 24 | ||
| 25 | const target_query_str = switch (args.len) { | |
| 26 | 3 => "native", | |
| 27 | 4 => args[3], | |
| 28 | else => return fatal( | |
| 29 | \\usage: {0s} path/to/exe path/to/coverage [target] | |
| 30 | \\ if omitted, 'target' defaults to 'native' | |
| 31 | \\ example: {0s} zig-out/test .zig-cache/v/xxxxxxxx x86_64-linux | |
| 32 | , .{if (args.len == 0) "dump-cov" else args[0]}), | |
| 33 | }; | |
| 34 | ||
| 35 | const target = std.zig.resolveTargetQueryOrFatal(io, try .parse(.{ | |
| 36 | .arch_os_abi = target_query_str, | |
| 37 | })); | |
| 38 | ||
| 20 | 39 | const exe_file_name = args[1]; |
| 21 | 40 | const cov_file_name = args[2]; |
| 22 | 41 | |
| 23 | 42 | const exe_path: Path = .{ |
| 24 | .root_dir = std.Build.Cache.Directory.cwd(), | |
| 43 | .root_dir = .cwd(), | |
| 25 | 44 | .sub_path = exe_file_name, |
| 26 | 45 | }; |
| 27 | 46 | const cov_path: Path = .{ |
| 28 | .root_dir = std.Build.Cache.Directory.cwd(), | |
| 47 | .root_dir = .cwd(), | |
| 29 | 48 | .sub_path = cov_file_name, |
| 30 | 49 | }; |
| 31 | 50 | |
| 32 | var coverage = std.debug.Coverage.init; | |
| 51 | var coverage: std.debug.Coverage = .init; | |
| 33 | 52 | defer coverage.deinit(gpa); |
| 34 | 53 | |
| 35 | var debug_info = std.debug.Info.load(gpa, exe_path, &coverage) catch |err| { | |
| 54 | var debug_info = std.debug.Info.load(gpa, exe_path, &coverage, target.ofmt, target.cpu.arch) catch |err| { | |
| 36 | 55 | fatal("failed to load debug info for {f}: {s}", .{ exe_path, @errorName(err) }); |
| 37 | 56 | }; |
| 38 | 57 | defer debug_info.deinit(gpa); |