aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/debug.zig')
-rw-r--r--lib/std/debug.zig350
1 files changed, 182 insertions, 168 deletions
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index f2b8fcc889..60288bdecc 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -12,6 +12,7 @@ const windows = std.os.windows;
const native_arch = builtin.cpu.arch;
const native_os = builtin.os.tag;
const native_endian = native_arch.endian();
+const Writer = std.io.Writer;
pub const MemoryAccessor = @import("debug/MemoryAccessor.zig");
pub const FixedBufferReader = @import("debug/FixedBufferReader.zig");
@@ -204,13 +205,26 @@ pub fn unlockStdErr() void {
std.Progress.unlockStdErr();
}
+/// Allows the caller to freely write to stderr until `unlockStdErr` is called.
+///
+/// During the lock, any `std.Progress` information is cleared from the terminal.
+///
+/// Returns a `Writer` with empty buffer, meaning that it is
+/// in fact unbuffered and does not need to be flushed.
+pub fn lockStderrWriter(buffer: []u8) *Writer {
+ return std.Progress.lockStderrWriter(buffer);
+}
+
+pub fn unlockStderrWriter() void {
+ std.Progress.unlockStderrWriter();
+}
+
/// Print to stderr, unbuffered, and silently returning on failure. Intended
-/// for use in "printf debugging." Use `std.log` functions for proper logging.
+/// for use in "printf debugging". Use `std.log` functions for proper logging.
pub fn print(comptime fmt: []const u8, args: anytype) void {
- lockStdErr();
- defer unlockStdErr();
- const stderr = fs.File.stderr().writer();
- nosuspend stderr.print(fmt, args) catch return;
+ const bw = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
+ nosuspend bw.print(fmt, args) catch return;
}
pub fn getStderrMutex() *std.Thread.Mutex {
@@ -232,50 +246,44 @@ pub fn getSelfDebugInfo() !*SelfInfo {
/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
/// Obtains the stderr mutex while dumping.
pub fn dumpHex(bytes: []const u8) void {
- lockStdErr();
- defer unlockStdErr();
- dumpHexFallible(bytes) catch {};
-}
-
-/// Prints a hexadecimal view of the bytes, unbuffered, returning any error that occurs.
-pub fn dumpHexFallible(bytes: []const u8) !void {
- const stderr: fs.File = .stderr();
- const ttyconf = std.io.tty.detectConfig(stderr);
- const writer = stderr.writer();
- try dumpHexInternal(bytes, ttyconf, writer);
+ const bw = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
+ const ttyconf = std.io.tty.detectConfig(.stderr());
+ dumpHexFallible(bw, ttyconf, bytes) catch {};
}
-fn dumpHexInternal(bytes: []const u8, ttyconf: std.io.tty.Config, writer: anytype) !void {
+/// Prints a hexadecimal view of the bytes, returning any error that occurs.
+pub fn dumpHexFallible(bw: *Writer, ttyconf: std.io.tty.Config, bytes: []const u8) !void {
var chunks = mem.window(u8, bytes, 16, 16);
while (chunks.next()) |window| {
// 1. Print the address.
const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10;
- try ttyconf.setColor(writer, .dim);
+ try ttyconf.setColor(bw, .dim);
// We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
// Also, make sure all lines are aligned by padding the address.
- try writer.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 });
- try ttyconf.setColor(writer, .reset);
+ try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 });
+ try ttyconf.setColor(bw, .reset);
// 2. Print the bytes.
for (window, 0..) |byte, index| {
- try writer.print("{X:0>2} ", .{byte});
- if (index == 7) try writer.writeByte(' ');
+ try bw.print("{X:0>2} ", .{byte});
+ if (index == 7) try bw.writeByte(' ');
}
- try writer.writeByte(' ');
+ try bw.writeByte(' ');
if (window.len < 16) {
var missing_columns = (16 - window.len) * 3;
if (window.len < 8) missing_columns += 1;
- try writer.writeByteNTimes(' ', missing_columns);
+ try bw.splatByteAll(' ', missing_columns);
}
// 3. Print the characters.
for (window) |byte| {
if (std.ascii.isPrint(byte)) {
- try writer.writeByte(byte);
+ try bw.writeByte(byte);
} else {
// Related: https://github.com/ziglang/zig/issues/7600
if (ttyconf == .windows_api) {
- try writer.writeByte('.');
+ try bw.writeByte('.');
continue;
}
@@ -283,22 +291,23 @@ fn dumpHexInternal(bytes: []const u8, ttyconf: std.io.tty.Config, writer: anytyp
// We don't want to do this for all control codes because most control codes apart from
// the ones that Zig has escape sequences for are likely not very useful to print as symbols.
switch (byte) {
- '\n' => try writer.writeAll("␊"),
- '\r' => try writer.writeAll("␍"),
- '\t' => try writer.writeAll("␉"),
- else => try writer.writeByte('.'),
+ '\n' => try bw.writeAll("␊"),
+ '\r' => try bw.writeAll("␍"),
+ '\t' => try bw.writeAll("␉"),
+ else => try bw.writeByte('.'),
}
}
}
- try writer.writeByte('\n');
+ try bw.writeByte('\n');
}
}
-test dumpHexInternal {
+test dumpHexFallible {
const bytes: []const u8 = &.{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x12, 0x13 };
- var output = std.ArrayList(u8).init(std.testing.allocator);
- defer output.deinit();
- try dumpHexInternal(bytes, .no_color, output.writer());
+ var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
+ defer aw.deinit();
+
+ try dumpHexFallible(&aw.interface, .no_color, bytes);
const expected = try std.fmt.allocPrint(std.testing.allocator,
\\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
\\{x:0>[2]} 01 12 13 ...
@@ -309,34 +318,36 @@ test dumpHexInternal {
@sizeOf(usize) * 2,
});
defer std.testing.allocator.free(expected);
- try std.testing.expectEqualStrings(expected, output.items);
+ try std.testing.expectEqualStrings(expected, aw.getWritten());
}
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
-/// TODO multithreaded awareness
pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
- nosuspend {
- if (builtin.target.cpu.arch.isWasm()) {
- if (native_os == .wasi) {
- const stderr = fs.File.stderr().writer();
- stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
- }
- return;
- }
- const stderr = fs.File.stderr().writer();
- if (builtin.strip_debug_info) {
- stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
- return;
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
+ nosuspend dumpCurrentStackTraceToWriter(start_addr, stderr) catch return;
+}
+
+/// Prints the current stack trace to the provided writer.
+pub fn dumpCurrentStackTraceToWriter(start_addr: ?usize, writer: *Writer) !void {
+ if (builtin.target.cpu.arch.isWasm()) {
+ if (native_os == .wasi) {
+ try writer.writeAll("Unable to dump stack trace: not implemented for Wasm\n");
}
- const debug_info = getSelfDebugInfo() catch |err| {
- stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
- return;
- };
- writeCurrentStackTrace(stderr, debug_info, io.tty.detectConfig(fs.File.stderr()), start_addr) catch |err| {
- stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
- return;
- };
+ return;
}
+ if (builtin.strip_debug_info) {
+ try writer.writeAll("Unable to dump stack trace: debug info stripped\n");
+ return;
+ }
+ const debug_info = getSelfDebugInfo() catch |err| {
+ try writer.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
+ return;
+ };
+ writeCurrentStackTrace(writer, debug_info, io.tty.detectConfig(.stderr()), start_addr) catch |err| {
+ try writer.print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
+ return;
+ };
}
pub const have_ucontext = posix.ucontext_t != void;
@@ -402,16 +413,14 @@ pub inline fn getContext(context: *ThreadContext) bool {
/// Tries to print the stack trace starting from the supplied base pointer to stderr,
/// unbuffered, and ignores any error returned.
/// TODO multithreaded awareness
-pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
+pub fn dumpStackTraceFromBase(context: *ThreadContext, stderr: *Writer) void {
nosuspend {
if (builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) {
- const stderr = fs.File.stderr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
}
return;
}
- const stderr = fs.File.stderr().writer();
if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
return;
@@ -420,7 +429,7 @@ pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
- const tty_config = io.tty.detectConfig(fs.File.stderr());
+ const tty_config = io.tty.detectConfig(.stderr());
if (native_os == .windows) {
// On x86_64 and aarch64, the stack will be unwound using RtlVirtualUnwind using the context
// provided by the exception handler. On x86, RtlVirtualUnwind doesn't exist. Instead, a new backtrace
@@ -510,21 +519,23 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
nosuspend {
if (builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) {
- const stderr = fs.File.stderr().writer();
- stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
+ stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return;
}
return;
}
- const stderr = fs.File.stderr().writer();
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
if (builtin.strip_debug_info) {
- stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
+ stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return;
return;
}
const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
- writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(fs.File.stderr())) catch |err| {
+ writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
@@ -573,14 +584,13 @@ pub fn panicExtra(
const size = 0x1000;
const trunc_msg = "(msg truncated)";
var buf: [size + trunc_msg.len]u8 = undefined;
+ var bw: Writer = .fixed(buf[0..size]);
// a minor annoyance with this is that it will result in the NoSpaceLeft
// error being part of the @panic stack trace (but that error should
// only happen rarely)
- const msg = std.fmt.bufPrint(buf[0..size], format, args) catch |err| switch (err) {
- error.NoSpaceLeft => blk: {
- @memcpy(buf[size..], trunc_msg);
- break :blk &buf;
- },
+ const msg = if (bw.print(format, args)) |_| bw.buffered() else |_| blk: {
+ @memcpy(buf[size..], trunc_msg);
+ break :blk &buf;
};
std.builtin.panic.call(msg, ret_addr);
}
@@ -675,10 +685,9 @@ pub fn defaultPanic(
_ = panicking.fetchAdd(1, .seq_cst);
{
- lockStdErr();
- defer unlockStdErr();
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
- const stderr = fs.File.stderr().writer();
if (builtin.single_threaded) {
stderr.print("panic: ", .{}) catch posix.abort();
} else {
@@ -688,7 +697,7 @@ pub fn defaultPanic(
stderr.print("{s}\n", .{msg}) catch posix.abort();
if (@errorReturnTrace()) |t| dumpStackTrace(t.*);
- dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
+ dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), stderr) catch {};
}
waitForOtherThreadToFinishPanicking();
@@ -723,7 +732,7 @@ fn waitForOtherThreadToFinishPanicking() void {
pub fn writeStackTrace(
stack_trace: std.builtin.StackTrace,
- out_stream: anytype,
+ writer: *Writer,
debug_info: *SelfInfo,
tty_config: io.tty.Config,
) !void {
@@ -736,15 +745,15 @@ pub fn writeStackTrace(
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) {
const return_address = stack_trace.instruction_addresses[frame_index];
- try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config);
+ try printSourceAtAddress(debug_info, writer, return_address - 1, tty_config);
}
if (stack_trace.index > stack_trace.instruction_addresses.len) {
const dropped_frames = stack_trace.index - stack_trace.instruction_addresses.len;
- tty_config.setColor(out_stream, .bold) catch {};
- try out_stream.print("({d} additional stack frames skipped...)\n", .{dropped_frames});
- tty_config.setColor(out_stream, .reset) catch {};
+ tty_config.setColor(writer, .bold) catch {};
+ try writer.print("({d} additional stack frames skipped...)\n", .{dropped_frames});
+ tty_config.setColor(writer, .reset) catch {};
}
}
@@ -954,7 +963,7 @@ pub const StackIterator = struct {
};
pub fn writeCurrentStackTrace(
- out_stream: anytype,
+ writer: *Writer,
debug_info: *SelfInfo,
tty_config: io.tty.Config,
start_addr: ?usize,
@@ -962,7 +971,7 @@ pub fn writeCurrentStackTrace(
if (native_os == .windows) {
var context: ThreadContext = undefined;
assert(getContext(&context));
- return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
+ return writeStackTraceWindows(writer, debug_info, tty_config, &context, start_addr);
}
var context: ThreadContext = undefined;
const has_context = getContext(&context);
@@ -973,7 +982,7 @@ pub fn writeCurrentStackTrace(
defer it.deinit();
while (it.next()) |return_address| {
- printLastUnwindError(&it, debug_info, out_stream, tty_config);
+ printLastUnwindError(&it, debug_info, writer, tty_config);
// On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,
// therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid
@@ -981,8 +990,8 @@ pub fn writeCurrentStackTrace(
// condition on the subsequent iteration and return `null` thus terminating the loop.
// same behaviour for x86-windows-msvc
const address = return_address -| 1;
- try printSourceAtAddress(debug_info, out_stream, address, tty_config);
- } else printLastUnwindError(&it, debug_info, out_stream, tty_config);
+ try printSourceAtAddress(debug_info, writer, address, tty_config);
+ } else printLastUnwindError(&it, debug_info, writer, tty_config);
}
pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize {
@@ -1042,7 +1051,7 @@ pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const w
}
pub fn writeStackTraceWindows(
- out_stream: anytype,
+ writer: *Writer,
debug_info: *SelfInfo,
tty_config: io.tty.Config,
context: *const windows.CONTEXT,
@@ -1058,14 +1067,14 @@ pub fn writeStackTraceWindows(
return;
} else 0;
for (addrs[start_i..]) |addr| {
- try printSourceAtAddress(debug_info, out_stream, addr - 1, tty_config);
+ try printSourceAtAddress(debug_info, writer, addr - 1, tty_config);
}
}
-fn printUnknownSource(debug_info: *SelfInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void {
+fn printUnknownSource(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: io.tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(address);
return printLineInfo(
- out_stream,
+ writer,
null,
address,
"???",
@@ -1075,38 +1084,38 @@ fn printUnknownSource(debug_info: *SelfInfo, out_stream: anytype, address: usize
);
}
-fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, out_stream: anytype, tty_config: io.tty.Config) void {
+fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writer, tty_config: io.tty.Config) void {
if (!have_ucontext) return;
if (it.getLastError()) |unwind_error| {
- printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config) catch {};
+ printUnwindError(debug_info, writer, unwind_error.address, unwind_error.err, tty_config) catch {};
}
}
-fn printUnwindError(debug_info: *SelfInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
+fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
- try tty_config.setColor(out_stream, .dim);
+ try tty_config.setColor(writer, .dim);
if (err == error.MissingDebugInfo) {
- try out_stream.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
+ try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
} else {
- try out_stream.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err });
+ try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err });
}
- try tty_config.setColor(out_stream, .reset);
+ try tty_config.setColor(writer, .reset);
}
-pub fn printSourceAtAddress(debug_info: *SelfInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void {
+pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: io.tty.Config) !void {
const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
+ error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
else => return err,
};
const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
+ error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
else => return err,
};
defer if (symbol_info.source_location) |sl| debug_info.allocator.free(sl.file_name);
return printLineInfo(
- out_stream,
+ writer,
symbol_info.source_location,
address,
symbol_info.name,
@@ -1117,7 +1126,7 @@ pub fn printSourceAtAddress(debug_info: *SelfInfo, out_stream: anytype, address:
}
fn printLineInfo(
- out_stream: anytype,
+ writer: *Writer,
source_location: ?SourceLocation,
address: usize,
symbol_name: []const u8,
@@ -1126,34 +1135,34 @@ fn printLineInfo(
comptime printLineFromFile: anytype,
) !void {
nosuspend {
- try tty_config.setColor(out_stream, .bold);
+ try tty_config.setColor(writer, .bold);
if (source_location) |*sl| {
- try out_stream.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column });
+ try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column });
} else {
- try out_stream.writeAll("???:?:?");
+ try writer.writeAll("???:?:?");
}
- try tty_config.setColor(out_stream, .reset);
- try out_stream.writeAll(": ");
- try tty_config.setColor(out_stream, .dim);
- try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
- try tty_config.setColor(out_stream, .reset);
- try out_stream.writeAll("\n");
+ try tty_config.setColor(writer, .reset);
+ try writer.writeAll(": ");
+ try tty_config.setColor(writer, .dim);
+ try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
+ try tty_config.setColor(writer, .reset);
+ try writer.writeAll("\n");
// Show the matching source code line if possible
if (source_location) |sl| {
- if (printLineFromFile(out_stream, sl)) {
+ if (printLineFromFile(writer, sl)) {
if (sl.column > 0) {
// The caret already takes one char
const space_needed = @as(usize, @intCast(sl.column - 1));
- try out_stream.writeByteNTimes(' ', space_needed);
- try tty_config.setColor(out_stream, .green);
- try out_stream.writeAll("^");
- try tty_config.setColor(out_stream, .reset);
+ try writer.splatByteAll(' ', space_needed);
+ try tty_config.setColor(writer, .green);
+ try writer.writeAll("^");
+ try tty_config.setColor(writer, .reset);
}
- try out_stream.writeAll("\n");
+ try writer.writeAll("\n");
} else |err| switch (err) {
error.EndOfFile, error.FileNotFound => {},
error.BadPathName => {},
@@ -1164,7 +1173,7 @@ fn printLineInfo(
}
}
-fn printLineFromFileAnyOs(out_stream: anytype, source_location: SourceLocation) !void {
+fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !void {
// Need this to always block even in async I/O mode, because this could potentially
// be called from e.g. the event loop code crashing.
var f = try fs.cwd().openFile(source_location.file_name, .{});
@@ -1197,31 +1206,31 @@ fn printLineFromFileAnyOs(out_stream: anytype, source_location: SourceLocation)
if (mem.indexOfScalar(u8, slice, '\n')) |pos| {
const line = slice[0 .. pos + 1];
mem.replaceScalar(u8, line, '\t', ' ');
- return out_stream.writeAll(line);
+ return writer.writeAll(line);
} else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends.
mem.replaceScalar(u8, slice, '\t', ' ');
- try out_stream.writeAll(slice);
+ try writer.writeAll(slice);
while (amt_read == buf.len) {
amt_read = try f.read(buf[0..]);
if (mem.indexOfScalar(u8, buf[0..amt_read], '\n')) |pos| {
const line = buf[0 .. pos + 1];
mem.replaceScalar(u8, line, '\t', ' ');
- return out_stream.writeAll(line);
+ return writer.writeAll(line);
} else {
const line = buf[0..amt_read];
mem.replaceScalar(u8, line, '\t', ' ');
- try out_stream.writeAll(line);
+ try writer.writeAll(line);
}
}
// Make sure printing last line of file inserts extra newline
- try out_stream.writeByte('\n');
+ try writer.writeByte('\n');
}
}
test printLineFromFileAnyOs {
- var output = std.ArrayList(u8).init(std.testing.allocator);
- defer output.deinit();
- const output_stream = output.writer();
+ var aw: Writer.Allocating = .init(std.testing.allocator);
+ defer aw.deinit();
+ const output_stream = &aw.interface;
const allocator = std.testing.allocator;
const join = std.fs.path.join;
@@ -1243,8 +1252,8 @@ test printLineFromFileAnyOs {
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
- try expectEqualStrings("no new lines in this file, but one is printed anyway\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
{
const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
@@ -1259,12 +1268,12 @@ test printLineFromFileAnyOs {
});
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
- try expectEqualStrings("1\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("1\n", aw.getWritten());
+ aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
- try expectEqualStrings("3\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("3\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
{
const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
@@ -1273,14 +1282,15 @@ test printLineFromFileAnyOs {
defer allocator.free(path);
const overlap = 10;
- var writer = file.writer();
- try writer.writeByteNTimes('a', std.heap.page_size_min - overlap);
+ var file_writer = file.writer(&.{});
+ const writer = &file_writer.interface;
+ try writer.splatByteAll('a', std.heap.page_size_min - overlap);
try writer.writeByte('\n');
- try writer.writeByteNTimes('a', overlap);
+ try writer.splatByteAll('a', overlap);
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
- try expectEqualStrings(("a" ** overlap) ++ "\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
{
const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
@@ -1288,12 +1298,13 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
defer allocator.free(path);
- var writer = file.writer();
- try writer.writeByteNTimes('a', std.heap.page_size_max);
+ var file_writer = file.writer(&.{});
+ const writer = &file_writer.interface;
+ try writer.splatByteAll('a', std.heap.page_size_max);
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
- try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
{
const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
@@ -1301,24 +1312,25 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
defer allocator.free(path);
- var writer = file.writer();
- try writer.writeByteNTimes('a', 3 * std.heap.page_size_max);
+ var file_writer = file.writer(&.{});
+ const writer = &file_writer.interface;
+ try writer.splatByteAll('a', 3 * std.heap.page_size_max);
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
- try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten());
+ aw.clearRetainingCapacity();
try writer.writeAll("a\na");
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
- try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten());
+ aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
- try expectEqualStrings("a\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("a\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
{
const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
@@ -1326,18 +1338,19 @@ test printLineFromFileAnyOs {
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
defer allocator.free(path);
- var writer = file.writer();
+ var file_writer = file.writer(&.{});
+ const writer = &file_writer.interface;
const real_file_start = 3 * std.heap.page_size_min;
- try writer.writeByteNTimes('\n', real_file_start);
+ try writer.splatByteAll('\n', real_file_start);
try writer.writeAll("abc\ndef");
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
- try expectEqualStrings("abc\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("abc\n", aw.getWritten());
+ aw.clearRetainingCapacity();
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
- try expectEqualStrings("def\n", output.items);
- output.clearRetainingCapacity();
+ try expectEqualStrings("def\n", aw.getWritten());
+ aw.clearRetainingCapacity();
}
}
@@ -1461,7 +1474,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
}
fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
- const stderr = fs.File.stderr().writer();
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
_ = switch (sig) {
posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
// x86_64 doesn't have a full 64-bit virtual address space.
@@ -1471,7 +1485,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)
// but can also happen when no addressable memory is involved;
// for example when reading/writing model-specific registers
// by executing `rdmsr` or `wrmsr` in user-space (unprivileged mode).
- stderr.print("General protection exception (no address available)\n", .{})
+ stderr.writeAll("General protection exception (no address available)\n")
else
stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
@@ -1509,7 +1523,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)
}, @ptrCast(ctx)).__mcontext_data;
}
relocateContext(&new_ctx);
- dumpStackTraceFromBase(&new_ctx);
+ dumpStackTraceFromBase(&new_ctx, stderr);
},
else => {},
}
@@ -1539,10 +1553,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
_ = panicking.fetchAdd(1, .seq_cst);
{
- lockStdErr();
- defer unlockStdErr();
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
- dumpSegfaultInfoWindows(info, msg, label);
+ dumpSegfaultInfoWindows(info, msg, label, stderr);
}
waitForOtherThreadToFinishPanicking();
@@ -1556,8 +1570,7 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
posix.abort();
}
-fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {
- const stderr = fs.File.stderr().writer();
+fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8, stderr: *Writer) void {
_ = switch (msg) {
0 => stderr.print("{s}\n", .{label.?}),
1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
@@ -1565,7 +1578,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[
else => unreachable,
} catch posix.abort();
- dumpStackTraceFromBase(info.ContextRecord);
+ dumpStackTraceFromBase(info.ContextRecord, stderr);
}
pub fn dumpStackPointerAddr(prefix: []const u8) void {
@@ -1588,10 +1601,10 @@ test "manage resources correctly" {
// self-hosted debug info is still too buggy
if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest;
- const writer = std.io.null_writer;
+ var writer: std.io.Writer = .discarding(&.{});
var di = try SelfInfo.open(testing.allocator);
defer di.deinit();
- try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(std.fs.File.stderr()));
+ try printSourceAtAddress(&di, &writer, showMyTrace(), io.tty.detectConfig(.stderr()));
}
noinline fn showMyTrace() usize {
@@ -1657,8 +1670,9 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
pub fn dump(t: @This()) void {
if (!enabled) return;
- const tty_config = io.tty.detectConfig(std.fs.File.stderr());
- const stderr = fs.File.stderr().writer();
+ const tty_config = io.tty.detectConfig(.stderr());
+ const stderr = lockStderrWriter(&.{});
+ defer unlockStderrWriter();
const end = @min(t.index, size);
const debug_info = getSelfDebugInfo() catch |err| {
stderr.print(
@@ -1688,7 +1702,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
t: @This(),
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *Writer,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, t);
_ = options;