mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
std.debug.Info: basic Mach-O support
This commit is contained in:
parent
0caca625eb
commit
0a330d4f94
@ -383,7 +383,14 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
|
|||||||
errdefer gop.value_ptr.coverage.deinit(fuzz.gpa);
|
errdefer gop.value_ptr.coverage.deinit(fuzz.gpa);
|
||||||
|
|
||||||
const rebuilt_exe_path = run_step.rebuilt_executable.?;
|
const rebuilt_exe_path = run_step.rebuilt_executable.?;
|
||||||
var debug_info = std.debug.Info.load(fuzz.gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| {
|
const target = run_step.producer.?.rootModuleTarget();
|
||||||
|
var debug_info = std.debug.Info.load(
|
||||||
|
fuzz.gpa,
|
||||||
|
rebuilt_exe_path,
|
||||||
|
&gop.value_ptr.coverage,
|
||||||
|
target.ofmt,
|
||||||
|
target.cpu.arch,
|
||||||
|
) catch |err| {
|
||||||
log.err("step '{s}': failed to load debug information for '{f}': {s}", .{
|
log.err("step '{s}': failed to load debug information for '{f}': {s}", .{
|
||||||
run_step.step.name, rebuilt_exe_path, @errorName(err),
|
run_step.step.name, rebuilt_exe_path, @errorName(err),
|
||||||
});
|
});
|
||||||
@ -479,9 +486,23 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
|
|||||||
if (false) {
|
if (false) {
|
||||||
const sl = coverage_map.source_locations[index];
|
const sl = coverage_map.source_locations[index];
|
||||||
const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
|
const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
|
||||||
log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{
|
if (pcs.len == 1) {
|
||||||
addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1],
|
log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index 0 (final)", .{
|
||||||
});
|
addr, file_name, sl.line, sl.column,
|
||||||
|
});
|
||||||
|
} else if (index == 0) {
|
||||||
|
log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index 0 before {x}", .{
|
||||||
|
addr, file_name, sl.line, sl.column, pcs[index + 1],
|
||||||
|
});
|
||||||
|
} else if (index == pcs.len - 1) {
|
||||||
|
log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} (final) after {x}", .{
|
||||||
|
addr, file_name, sl.line, sl.column, index, pcs[index - 1],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{
|
||||||
|
addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));
|
try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,49 +9,67 @@
|
|||||||
const std = @import("../std.zig");
|
const std = @import("../std.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Path = std.Build.Cache.Path;
|
const Path = std.Build.Cache.Path;
|
||||||
const ElfFile = std.debug.ElfFile;
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Coverage = std.debug.Coverage;
|
const Coverage = std.debug.Coverage;
|
||||||
const SourceLocation = std.debug.Coverage.SourceLocation;
|
const SourceLocation = std.debug.Coverage.SourceLocation;
|
||||||
|
|
||||||
|
const ElfFile = std.debug.ElfFile;
|
||||||
|
const MachOFile = std.debug.MachOFile;
|
||||||
|
|
||||||
const Info = @This();
|
const Info = @This();
|
||||||
|
|
||||||
/// Sorted by key, ascending.
|
impl: union(enum) {
|
||||||
address_map: std.AutoArrayHashMapUnmanaged(u64, ElfFile),
|
elf: ElfFile,
|
||||||
|
macho: MachOFile,
|
||||||
|
},
|
||||||
/// Externally managed, outlives this `Info` instance.
|
/// Externally managed, outlives this `Info` instance.
|
||||||
coverage: *Coverage,
|
coverage: *Coverage,
|
||||||
|
|
||||||
pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || std.debug.Dwarf.ScanError || error{MissingDebugInfo};
|
pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError || error{ MissingDebugInfo, UnsupportedDebugInfo };
|
||||||
|
|
||||||
pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
|
pub fn load(gpa: Allocator, path: Path, coverage: *Coverage, format: std.Target.ObjectFormat, arch: std.Target.Cpu.Arch) LoadError!Info {
|
||||||
var file = try path.root_dir.handle.openFile(path.sub_path, .{});
|
switch (format) {
|
||||||
defer file.close();
|
.elf => {
|
||||||
|
var file = try path.root_dir.handle.openFile(path.sub_path, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
var elf_file: ElfFile = try .load(gpa, file, null, &.none);
|
var elf_file: ElfFile = try .load(gpa, file, null, &.none);
|
||||||
errdefer elf_file.deinit(gpa);
|
errdefer elf_file.deinit(gpa);
|
||||||
|
|
||||||
if (elf_file.dwarf == null) return error.MissingDebugInfo;
|
if (elf_file.dwarf == null) return error.MissingDebugInfo;
|
||||||
try elf_file.dwarf.?.open(gpa, elf_file.endian);
|
try elf_file.dwarf.?.open(gpa, elf_file.endian);
|
||||||
try elf_file.dwarf.?.populateRanges(gpa, elf_file.endian);
|
try elf_file.dwarf.?.populateRanges(gpa, elf_file.endian);
|
||||||
|
|
||||||
var info: Info = .{
|
return .{
|
||||||
.address_map = .{},
|
.impl = .{ .elf = elf_file },
|
||||||
.coverage = coverage,
|
.coverage = coverage,
|
||||||
};
|
};
|
||||||
try info.address_map.put(gpa, 0, elf_file);
|
},
|
||||||
errdefer comptime unreachable; // elf_file is owned by the map now
|
.macho => {
|
||||||
return info;
|
const path_str = try path.toString(gpa);
|
||||||
|
defer gpa.free(path_str);
|
||||||
|
|
||||||
|
var macho_file: MachOFile = try .load(gpa, path_str, arch);
|
||||||
|
errdefer macho_file.deinit(gpa);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.impl = .{ .macho = macho_file },
|
||||||
|
.coverage = coverage,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => return error.UnsupportedDebugInfo,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(info: *Info, gpa: Allocator) void {
|
pub fn deinit(info: *Info, gpa: Allocator) void {
|
||||||
for (info.address_map.values()) |*elf_file| {
|
switch (info.impl) {
|
||||||
elf_file.dwarf.?.deinit(gpa);
|
.elf => |*ef| ef.deinit(gpa),
|
||||||
|
.macho => |*mf| mf.deinit(gpa),
|
||||||
}
|
}
|
||||||
info.address_map.deinit(gpa);
|
|
||||||
info.* = undefined;
|
info.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError;
|
pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{UnsupportedDebugInfo};
|
||||||
|
|
||||||
/// Given an array of virtual memory addresses, sorted ascending, outputs a
|
/// Given an array of virtual memory addresses, sorted ascending, outputs a
|
||||||
/// corresponding array of source locations.
|
/// corresponding array of source locations.
|
||||||
@ -64,7 +82,28 @@ pub fn resolveAddresses(
|
|||||||
output: []SourceLocation,
|
output: []SourceLocation,
|
||||||
) ResolveAddressesError!void {
|
) ResolveAddressesError!void {
|
||||||
assert(sorted_pc_addrs.len == output.len);
|
assert(sorted_pc_addrs.len == output.len);
|
||||||
if (info.address_map.entries.len != 1) @panic("TODO");
|
switch (info.impl) {
|
||||||
const elf_file = &info.address_map.values()[0];
|
.elf => |*ef| return info.coverage.resolveAddressesDwarf(gpa, ef.endian, sorted_pc_addrs, output, &ef.dwarf.?),
|
||||||
return info.coverage.resolveAddressesDwarf(gpa, elf_file.endian, sorted_pc_addrs, output, &elf_file.dwarf.?);
|
.macho => |*mf| {
|
||||||
|
// Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries
|
||||||
|
// due to split debug information. For now, we'll just resolve the addreses one by one.
|
||||||
|
for (sorted_pc_addrs, output) |pc_addr, *src_loc| {
|
||||||
|
const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, pc_addr) catch |err| switch (err) {
|
||||||
|
error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
if (dwarf.ranges.items.len == 0) {
|
||||||
|
dwarf.populateRanges(gpa, .little) catch |err| switch (err) {
|
||||||
|
error.EndOfStream,
|
||||||
|
error.Overflow,
|
||||||
|
error.StreamTooLong,
|
||||||
|
error.ReadFailed,
|
||||||
|
=> return error.InvalidDebugInfo,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try info.coverage.resolveAddressesDwarf(gpa, .little, &.{dwarf_pc_addr}, src_loc[0..1], dwarf);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,31 +8,50 @@ const assert = std.debug.assert;
|
|||||||
const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader;
|
const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||||
defer _ = general_purpose_allocator.deinit();
|
defer _ = debug_allocator.deinit();
|
||||||
const gpa = general_purpose_allocator.allocator();
|
const gpa = debug_allocator.allocator();
|
||||||
|
|
||||||
var arena_instance = std.heap.ArenaAllocator.init(gpa);
|
var arena_instance: std.heap.ArenaAllocator = .init(gpa);
|
||||||
defer arena_instance.deinit();
|
defer arena_instance.deinit();
|
||||||
const arena = arena_instance.allocator();
|
const arena = arena_instance.allocator();
|
||||||
|
|
||||||
|
var threaded: std.Io.Threaded = .init(gpa);
|
||||||
|
defer threaded.deinit();
|
||||||
|
const io = threaded.io();
|
||||||
|
|
||||||
const args = try std.process.argsAlloc(arena);
|
const args = try std.process.argsAlloc(arena);
|
||||||
|
|
||||||
|
const target_query_str = switch (args.len) {
|
||||||
|
3 => "native",
|
||||||
|
4 => args[3],
|
||||||
|
else => return fatal(
|
||||||
|
\\usage: {0s} path/to/exe path/to/coverage [target]
|
||||||
|
\\ if omitted, 'target' defaults to 'native'
|
||||||
|
\\ example: {0s} zig-out/test .zig-cache/v/xxxxxxxx x86_64-linux
|
||||||
|
, .{if (args.len == 0) "dump-cov" else args[0]}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const target = std.zig.resolveTargetQueryOrFatal(io, try .parse(.{
|
||||||
|
.arch_os_abi = target_query_str,
|
||||||
|
}));
|
||||||
|
|
||||||
const exe_file_name = args[1];
|
const exe_file_name = args[1];
|
||||||
const cov_file_name = args[2];
|
const cov_file_name = args[2];
|
||||||
|
|
||||||
const exe_path: Path = .{
|
const exe_path: Path = .{
|
||||||
.root_dir = std.Build.Cache.Directory.cwd(),
|
.root_dir = .cwd(),
|
||||||
.sub_path = exe_file_name,
|
.sub_path = exe_file_name,
|
||||||
};
|
};
|
||||||
const cov_path: Path = .{
|
const cov_path: Path = .{
|
||||||
.root_dir = std.Build.Cache.Directory.cwd(),
|
.root_dir = .cwd(),
|
||||||
.sub_path = cov_file_name,
|
.sub_path = cov_file_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
var coverage = std.debug.Coverage.init;
|
var coverage: std.debug.Coverage = .init;
|
||||||
defer coverage.deinit(gpa);
|
defer coverage.deinit(gpa);
|
||||||
|
|
||||||
var debug_info = std.debug.Info.load(gpa, exe_path, &coverage) catch |err| {
|
var debug_info = std.debug.Info.load(gpa, exe_path, &coverage, target.ofmt, target.cpu.arch) catch |err| {
|
||||||
fatal("failed to load debug info for {f}: {s}", .{ exe_path, @errorName(err) });
|
fatal("failed to load debug info for {f}: {s}", .{ exe_path, @errorName(err) });
|
||||||
};
|
};
|
||||||
defer debug_info.deinit(gpa);
|
defer debug_info.deinit(gpa);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user