aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-09-26 14:24:18 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-09-26 16:06:05 -0700
commitc9c080a187ae1839a5531d3d95c1080f38721229 (patch)
tree7d3ded29e2afa8244f34a31b70a5a1f79c0caf7d /lib/std/debug.zig
parentfcfbedc2f06ba5700092a2cb444261133944be01 (diff)
downloadzig-c9c080a187ae1839a5531d3d95c1080f38721229.tar.gz
zig-c9c080a187ae1839a5531d3d95c1080f38721229.zip
embrace panic helpers
Introduces `std.builtin.Panic` which is a complete interface for panicking. Provide `std.debug.FormattedPanic` and `std.debug.SimplePanic` and let the user choose, or make their own.
Diffstat (limited to 'lib/std/debug.zig')
-rw-r--r--lib/std/debug.zig181
1 files changed, 29 insertions, 152 deletions
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index 98bf6b1fd4..0cb201373a 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -21,6 +21,9 @@ pub const SelfInfo = @import("debug/SelfInfo.zig");
pub const Info = @import("debug/Info.zig");
pub const Coverage = @import("debug/Coverage.zig");
+pub const FormattedPanic = @import("debug/FormattedPanic.zig");
+pub const SimplePanic = @import("debug/SimplePanic.zig");
+
/// Unresolved source locations can be represented with a single `usize` that
/// corresponds to a virtual memory address of the program counter. Combined
/// with debug information, those values can be converted into a resolved
@@ -408,10 +411,16 @@ pub fn assertReadable(slice: []const volatile u8) void {
for (slice) |*byte| _ = byte.*;
}
+/// By including a call to this function, the caller gains an error return trace
+/// secret parameter, making `@errorReturnTrace()` more useful. This is not
+/// necessary if the function already contains a call to an errorable function
+/// elsewhere.
+pub fn errorReturnTraceHelper() anyerror!void {}
+
/// Equivalent to `@panic` but with a formatted message.
pub fn panic(comptime format: []const u8, args: anytype) noreturn {
@branchHint(.cold);
-
+ errorReturnTraceHelper() catch unreachable;
panicExtra(@errorReturnTrace(), @returnAddress(), format, args);
}
@@ -437,7 +446,7 @@ pub fn panicExtra(
break :blk &buf;
},
};
- std.builtin.panic(.{ .explicit_call = msg }, trace, ret_addr);
+ std.builtin.Panic.call(msg, trace, ret_addr);
}
/// Non-zero whenever the program triggered a panic.
@@ -448,11 +457,9 @@ var panicking = std.atomic.Value(u8).init(0);
/// This is used to catch and handle panics triggered by the panic handler.
threadlocal var panic_stage: usize = 0;
-// Dumps a stack trace to standard error, then aborts.
-//
-// This function avoids a dependency on formatted printing.
+/// Dumps a stack trace to standard error, then aborts.
pub fn defaultPanic(
- cause: std.builtin.PanicCause,
+ msg: []const u8,
error_return_trace: ?*const std.builtin.StackTrace,
first_trace_addr: ?usize,
) noreturn {
@@ -471,18 +478,6 @@ pub fn defaultPanic(
@trap();
}
- if (builtin.zig_backend == .stage2_riscv64) {
- var buffer: [1000]u8 = undefined;
- var i: usize = 0;
- i += fmtPanicCause(buffer[i..], cause);
- buffer[i] = '\n';
- i += 1;
- const msg = buffer[0..i];
- lockStdErr();
- io.getStdErr().writeAll(msg) catch {};
- @trap();
- }
-
switch (builtin.os.tag) {
.freestanding => {
@trap();
@@ -490,14 +485,10 @@ pub fn defaultPanic(
.uefi => {
const uefi = std.os.uefi;
- var buffer: [1000]u8 = undefined;
- var i: usize = 0;
- i += fmtBuf(buffer[i..], "panic: ");
- i += fmtPanicCause(buffer[i..], cause);
- i += fmtBuf(buffer[i..], "\r\n\x00");
-
var utf16_buffer: [1000]u16 = undefined;
- const len = std.unicode.utf8ToUtf16Le(&utf16_buffer, buffer[0..i]) catch 0;
+ const len_minus_3 = std.unicode.utf8ToUtf16Le(&utf16_buffer, msg) catch 0;
+ utf16_buffer[len_minus_3][0..3].* = .{ '\r', '\n', 0 };
+ const len = len_minus_3 + 3;
const exit_msg = utf16_buffer[0 .. len - 1 :0];
// Output to both std_err and con_out, as std_err is easier
@@ -521,15 +512,11 @@ pub fn defaultPanic(
},
.cuda, .amdhsa => std.posix.abort(),
.plan9 => {
- var buffer: [1000]u8 = undefined;
- comptime assert(buffer.len > std.os.plan9.ERRMAX);
- var i: usize = 0;
- i += fmtPanicCause(buffer[i..], cause);
- buffer[i] = '\n';
- i += 1;
- const len = @min(i, std.os.plan9.ERRMAX - 1);
- buffer[len] = 0;
- std.os.plan9.exits(buffer[0..len :0]);
+ var status: [std.os.plan9.ERRMAX]u8 = undefined;
+ const len = @min(msg.len, status.len - 1);
+ @memcpy(status[0..len], msg[0..len]);
+ status[len] = 0;
+ std.os.plan9.exits(status[0..len :0]);
},
else => {},
}
@@ -548,26 +535,18 @@ pub fn defaultPanic(
_ = panicking.fetchAdd(1, .seq_cst);
{
- // This code avoids a dependency on formatted printing, the writer interface,
- // and limits to only 1 syscall made to print the panic message to stderr.
- var buffer: [0x1000]u8 = undefined;
- var i: usize = 0;
+ lockStdErr();
+ defer unlockStdErr();
+
+ const stderr = io.getStdErr().writer();
if (builtin.single_threaded) {
- i += fmtBuf(buffer[i..], "panic: ");
+ stderr.print("panic: ", .{}) catch posix.abort();
} else {
- i += fmtBuf(buffer[i..], "thread ");
- i += fmtInt10(buffer[i..], std.Thread.getCurrentId());
- i += fmtBuf(buffer[i..], " panic: ");
+ const current_thread_id = std.Thread.getCurrentId();
+ stderr.print("thread {} panic: ", .{current_thread_id}) catch posix.abort();
}
- i += fmtPanicCause(buffer[i..], cause);
- buffer[i] = '\n';
- i += 1;
- const msg = buffer[0..i];
+ stderr.print("{s}\n", .{msg}) catch posix.abort();
- lockStdErr();
- defer unlockStdErr();
-
- io.getStdErr().writeAll(msg) catch posix.abort();
if (error_return_trace) |t| dumpStackTrace(t.*);
dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
}
@@ -588,108 +567,6 @@ pub fn defaultPanic(
posix.abort();
}
-pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
- var i: usize = 0;
-
- switch (cause) {
- .reached_unreachable => i += fmtBuf(buffer[i..], "reached unreachable code"),
- .unwrap_null => i += fmtBuf(buffer[i..], "attempt to use null value"),
- .cast_to_null => i += fmtBuf(buffer[i..], "cast causes pointer to be null"),
- .incorrect_alignment => i += fmtBuf(buffer[i..], "incorrect alignment"),
- .invalid_error_code => i += fmtBuf(buffer[i..], "invalid error code"),
- .cast_truncated_data => i += fmtBuf(buffer[i..], "integer cast truncated bits"),
- .negative_to_unsigned => i += fmtBuf(buffer[i..], "attempt to cast negative value to unsigned integer"),
- .integer_overflow => i += fmtBuf(buffer[i..], "integer overflow"),
- .shl_overflow => i += fmtBuf(buffer[i..], "left shift overflowed bits"),
- .shr_overflow => i += fmtBuf(buffer[i..], "right shift overflowed bits"),
- .divide_by_zero => i += fmtBuf(buffer[i..], "division by zero"),
- .exact_division_remainder => i += fmtBuf(buffer[i..], "exact division produced remainder"),
- .inactive_union_field => |info| {
- i += fmtBuf(buffer[i..], "access of union field '");
- i += fmtBuf(buffer[i..], info.accessed);
- i += fmtBuf(buffer[i..], "' while field '");
- i += fmtBuf(buffer[i..], info.active);
- i += fmtBuf(buffer[i..], "' is active");
- },
- .integer_part_out_of_bounds => i += fmtBuf(buffer[i..], "integer part of floating point value out of bounds"),
- .corrupt_switch => i += fmtBuf(buffer[i..], "switch on corrupt value"),
- .shift_rhs_too_big => i += fmtBuf(buffer[i..], "shift amount is greater than the type size"),
- .invalid_enum_value => i += fmtBuf(buffer[i..], "invalid enum value"),
- .sentinel_mismatch_usize => |mm| {
- i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
- i += fmtInt10(buffer[i..], mm.expected);
- i += fmtBuf(buffer[i..], ", found ");
- i += fmtInt10(buffer[i..], mm.found);
- },
- .sentinel_mismatch_isize => |mm| {
- i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
- i += fmtInt10s(buffer[i..], mm.expected);
- i += fmtBuf(buffer[i..], ", found ");
- i += fmtInt10s(buffer[i..], mm.found);
- },
- .sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
- .unwrap_error => |err| {
- if (builtin.zig_backend == .stage2_riscv64) {
- // https://github.com/ziglang/zig/issues/21519
- i += fmtBuf(buffer[i..], "attempt to unwrap error");
- return i;
- }
- i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
- i += fmtBuf(buffer[i..], @errorName(err));
- },
- .index_out_of_bounds => |oob| {
- i += fmtBuf(buffer[i..], "index ");
- i += fmtInt10(buffer[i..], oob.index);
- i += fmtBuf(buffer[i..], " exceeds length ");
- i += fmtInt10(buffer[i..], oob.len);
- },
- .start_index_greater_than_end => |oob| {
- i += fmtBuf(buffer[i..], "start index ");
- i += fmtInt10(buffer[i..], oob.start);
- i += fmtBuf(buffer[i..], " exceeds end index ");
- i += fmtInt10(buffer[i..], oob.end);
- },
- .for_len_mismatch => i += fmtBuf(buffer[i..], "for loop over objects with non-equal lengths"),
- .memcpy_len_mismatch => i += fmtBuf(buffer[i..], "@memcpy arguments have non-equal lengths"),
- .memcpy_alias => i += fmtBuf(buffer[i..], "@memcpy arguments alias"),
- .noreturn_returned => i += fmtBuf(buffer[i..], "'noreturn' function returned"),
- .explicit_call => |msg| i += fmtBuf(buffer[i..], msg),
- }
-
- return i;
-}
-
-fn fmtBuf(out_buf: []u8, s: []const u8) usize {
- @memcpy(out_buf[0..s.len], s);
- return s.len;
-}
-
-fn fmtInt10s(out_buf: []u8, integer_value: isize) usize {
- if (integer_value < 0) {
- out_buf[0] = '-';
- return 1 + fmtInt10(out_buf[1..], @abs(integer_value));
- } else {
- return fmtInt10(out_buf, @abs(integer_value));
- }
-}
-
-fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
- var tmp_buf: [50]u8 = undefined;
- var i: usize = tmp_buf.len;
- var a: usize = integer_value;
-
- while (true) {
- i -= 1;
- tmp_buf[i] = '0' + @as(u8, @intCast(a % 10));
- a /= 10;
- if (a == 0) break;
- }
-
- const result = tmp_buf[i..];
- @memcpy(out_buf[0..result.len], result);
- return result.len;
-}
-
/// Must be called only after adding 1 to `panicking`. There are three callsites.
fn waitForOtherThreadToFinishPanicking() void {
if (panicking.fetchSub(1, .seq_cst) != 1) {