(breaking) rework stream abstractions

The main goal here is to make the function pointers comptime, so that we
don't have to do the crazy stuff with async function frames.

Since InStream, OutStream, and SeekableStream are already generic
across error sets, it's not really worse to make them generic across the
vtable as well.

See #764 for the open issue acknowledging that using generics for these
abstractions is a design flaw.

See #130 for the efforts to make these abstractions non-generic.

This commit also changes the OutStream API so that `write` returns
number of bytes written, and `writeAll` is the one that loops until the
whole buffer is written.
This commit is contained in:
Andrew Kelley
2020-03-10 15:27:45 -04:00
parent 1ad831a0ef
commit ba0e3be5cf
24 changed files with 748 additions and 772 deletions

View File

@@ -55,7 +55,7 @@ pub const LineInfo = struct {
var stderr_file: File = undefined;
var stderr_file_out_stream: File.OutStream = undefined;
var stderr_stream: ?*io.OutStream(File.WriteError) = null;
var stderr_stream: ?*File.OutStream = null;
var stderr_mutex = std.Mutex.init();
pub fn warn(comptime fmt: []const u8, args: var) void {
@@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {
noasync stderr.print(fmt, args) catch return;
}
pub fn getStderrStream() *io.OutStream(File.WriteError) {
pub fn getStderrStream() *File.OutStream {
if (stderr_stream) |st| {
return st;
} else {
stderr_file = io.getStdErr();
stderr_file_out_stream = stderr_file.outStream();
const st = &stderr_file_out_stream.stream;
const st = &stderr_file_out_stream;
stderr_stream = st;
return st;
}
@@ -408,15 +408,15 @@ pub const TTY = struct {
windows_api,
fn setColor(conf: Config, out_stream: var, color: Color) void {
switch (conf) {
noasync switch (conf) {
.no_color => return,
.escape_codes => switch (color) {
.Red => noasync out_stream.write(RED) catch return,
.Green => noasync out_stream.write(GREEN) catch return,
.Cyan => noasync out_stream.write(CYAN) catch return,
.White, .Bold => noasync out_stream.write(WHITE) catch return,
.Dim => noasync out_stream.write(DIM) catch return,
.Reset => noasync out_stream.write(RESET) catch return,
.Red => out_stream.writeAll(RED) catch return,
.Green => out_stream.writeAll(GREEN) catch return,
.Cyan => out_stream.writeAll(CYAN) catch return,
.White, .Bold => out_stream.writeAll(WHITE) catch return,
.Dim => out_stream.writeAll(DIM) catch return,
.Reset => out_stream.writeAll(RESET) catch return,
},
.windows_api => if (builtin.os.tag == .windows) {
const S = struct {
@@ -455,7 +455,7 @@ pub const TTY = struct {
} else {
unreachable;
},
}
};
}
};
};
@@ -565,38 +565,40 @@ fn printLineInfo(
tty_config: TTY.Config,
comptime printLineFromFile: var,
) !void {
tty_config.setColor(out_stream, .White);
noasync {
tty_config.setColor(out_stream, .White);
if (line_info) |*li| {
try noasync out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
} else {
try noasync out_stream.write("???:?:?");
}
if (line_info) |*li| {
try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
} else {
try out_stream.writeAll("???:?:?");
}
tty_config.setColor(out_stream, .Reset);
try noasync out_stream.write(": ");
tty_config.setColor(out_stream, .Dim);
try noasync out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
tty_config.setColor(out_stream, .Reset);
try noasync out_stream.write("\n");
tty_config.setColor(out_stream, .Reset);
try out_stream.writeAll(": ");
tty_config.setColor(out_stream, .Dim);
try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
tty_config.setColor(out_stream, .Reset);
try out_stream.writeAll("\n");
// Show the matching source code line if possible
if (line_info) |li| {
if (noasync printLineFromFile(out_stream, li)) {
if (li.column > 0) {
// The caret already takes one char
const space_needed = @intCast(usize, li.column - 1);
// Show the matching source code line if possible
if (line_info) |li| {
if (printLineFromFile(out_stream, li)) {
if (li.column > 0) {
// The caret already takes one char
const space_needed = @intCast(usize, li.column - 1);
try noasync out_stream.writeByteNTimes(' ', space_needed);
tty_config.setColor(out_stream, .Green);
try noasync out_stream.write("^");
tty_config.setColor(out_stream, .Reset);
try out_stream.writeByteNTimes(' ', space_needed);
tty_config.setColor(out_stream, .Green);
try out_stream.writeAll("^");
tty_config.setColor(out_stream, .Reset);
}
try out_stream.writeAll("\n");
} else |err| switch (err) {
error.EndOfFile, error.FileNotFound => {},
error.BadPathName => {},
else => return err,
}
try noasync out_stream.write("\n");
} else |err| switch (err) {
error.EndOfFile, error.FileNotFound => {},
error.BadPathName => {},
else => return err,
}
}
}
@@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{
};
/// TODO resources https://github.com/ziglang/zig/issues/4353
/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
/// make this `noasync fn` and remove the individual noasync calls.
pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
if (builtin.strip_debug_info)
return error.MissingDebugInfo;
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
return noasync root.os.debug.openSelfDebugInfo(allocator);
}
switch (builtin.os.tag) {
.linux,
.freebsd,
.macosx,
.windows,
=> return DebugInfo.init(allocator),
else => @compileError("openSelfDebugInfo unsupported for this platform"),
noasync {
if (builtin.strip_debug_info)
return error.MissingDebugInfo;
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
return root.os.debug.openSelfDebugInfo(allocator);
}
switch (builtin.os.tag) {
.linux,
.freebsd,
.macosx,
.windows,
=> return DebugInfo.init(allocator),
else => @compileError("openSelfDebugInfo unsupported for this platform"),
}
}
}
@@ -808,45 +810,64 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
/// TODO resources https://github.com/ziglang/zig/issues/4353
pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
const mapped_mem = try mapWholeFile(elf_file_path);
noasync {
const mapped_mem = try mapWholeFile(elf_file_path);
const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
var efile = try noasync elf.Elf.openStream(
allocator,
@ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
@ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
);
defer noasync efile.close();
const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
elf.ELFDATA2LSB => .Little,
elf.ELFDATA2MSB => .Big,
else => return error.InvalidElfEndian,
};
assert(endian == std.builtin.endian); // this is our own debug info
const debug_info = (try noasync efile.findSection(".debug_info")) orelse
return error.MissingDebugInfo;
const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse
return error.MissingDebugInfo;
const debug_str = (try noasync efile.findSection(".debug_str")) orelse
return error.MissingDebugInfo;
const debug_line = (try noasync efile.findSection(".debug_line")) orelse
return error.MissingDebugInfo;
const opt_debug_ranges = try noasync efile.findSection(".debug_ranges");
const shoff = hdr.e_shoff;
const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
const header_strings = mapped_mem[str_section_off..str_section_off + hdr.e_shentsize];
const shdrs = @ptrCast([*]const elf.Shdr, @alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]))[0..hdr.e_shnum];
var di = DW.DwarfInfo{
.endian = efile.endian,
.debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
.debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
.debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
.debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
.debug_ranges = if (opt_debug_ranges) |debug_ranges|
try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
else
null,
};
var opt_debug_info: ?[]const u8 = null;
var opt_debug_abbrev: ?[]const u8 = null;
var opt_debug_str: ?[]const u8 = null;
var opt_debug_line: ?[]const u8 = null;
var opt_debug_ranges: ?[]const u8 = null;
try noasync DW.openDwarfDebugInfo(&di, allocator);
for (shdrs) |*shdr| {
if (shdr.sh_type == elf.SHT_NULL) continue;
return ModuleDebugInfo{
.base_address = undefined,
.dwarf = di,
.mapped_memory = mapped_mem,
};
const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));
if (mem.eql(u8, name, ".debug_info")) {
opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
} else if (mem.eql(u8, name, ".debug_abbrev")) {
opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
} else if (mem.eql(u8, name, ".debug_str")) {
opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
} else if (mem.eql(u8, name, ".debug_line")) {
opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
} else if (mem.eql(u8, name, ".debug_ranges")) {
opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
}
}
var di = DW.DwarfInfo{
.endian = endian,
.debug_info = opt_debug_info orelse return error.MissingDebugInfo,
.debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
.debug_str = opt_debug_str orelse return error.MissingDebugInfo,
.debug_line = opt_debug_line orelse return error.MissingDebugInfo,
.debug_ranges = opt_debug_ranges,
};
try DW.openDwarfDebugInfo(&di, allocator);
return ModuleDebugInfo{
.base_address = undefined,
.dwarf = di,
.mapped_memory = mapped_mem,
};
}
}
/// TODO resources https://github.com/ziglang/zig/issues/4353
@@ -982,22 +1003,24 @@ const MachoSymbol = struct {
}
};
fn mapWholeFile(path: []const u8) ![]const u8 {
const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });
defer noasync file.close();
fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
noasync {
const file = try fs.openFileAbsolute(path, .{ .always_blocking = true });
defer file.close();
const file_len = try math.cast(usize, try file.getEndPos());
const mapped_mem = try os.mmap(
null,
file_len,
os.PROT_READ,
os.MAP_SHARED,
file.handle,
0,
);
errdefer os.munmap(mapped_mem);
const file_len = try math.cast(usize, try file.getEndPos());
const mapped_mem = try os.mmap(
null,
file_len,
os.PROT_READ,
os.MAP_SHARED,
file.handle,
0,
);
errdefer os.munmap(mapped_mem);
return mapped_mem;
return mapped_mem;
}
}
pub const DebugInfo = struct {