diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-09-26 14:24:18 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-09-26 16:06:05 -0700 |
| commit | c9c080a187ae1839a5531d3d95c1080f38721229 (patch) | |
| tree | 7d3ded29e2afa8244f34a31b70a5a1f79c0caf7d /lib/std/debug.zig | |
| parent | fcfbedc2f06ba5700092a2cb444261133944be01 (diff) | |
| download | zig-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.zig | 181 |
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) { |
