Dumps a stack trace to standard error, then aborts.
pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn
pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn {
@branchHint(.cold);
if (use_trap_panic) @trap();
switch (builtin.os.tag) {
.freestanding,
.other,
.@"3ds",
.wiiu,
.@"switch",
.psx,
.psp,
.vita,
=> {
@trap();
},
.uefi => {
const uefi = std.os.uefi;
var utf16_buffer: [1000]u16 = undefined;
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
// to read in stuff like QEMU at times, but, unlike con_out,
// isn't visible on actual hardware if directly booted into
inline for ([_]?*uefi.protocol.SimpleTextOutput{ uefi.system_table.std_err, uefi.system_table.con_out }) |o| {
if (o) |out| {
out.setAttribute(.{ .foreground = .red }) catch {};
_ = out.outputString(exit_msg) catch {};
out.setAttribute(.{ .foreground = .white }) catch {};
}
}
if (uefi.system_table.boot_services) |bs| {
// ExitData buffer must be allocated using boot_services.allocatePool (spec: page 220)
const exit_data = uefi.raw_pool_allocator.dupeSentinel(u16, exit_msg, 0) catch @trap();
bs.exit(uefi.handle, .aborted, exit_data) catch {};
}
@trap();
},
.cuda, .amdhsa => std.process.abort(),
.plan9 => {
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 => {},
}
std.Options.debug_io.vtable.crashHandler(std.Options.debug_io.userdata);
if (enable_segfault_handler) {
// If a segfault happens while panicking, we want it to actually segfault, not trigger
// the handler.
resetSegfaultHandler();
}
// There is very similar logic to the following in `handleSegfault`.
switch (panic_stage) {
0 => {
panic_stage = 1;
_ = panicking.fetchAdd(1, .seq_cst);
trace: {
const stderr = lockStderr(&.{}).terminal();
defer unlockStderr();
const writer = stderr.writer;
if (builtin.single_threaded) {
writer.print("panic: ", .{}) catch break :trace;
} else {
const current_thread_id = std.Thread.getCurrentId();
writer.print("thread {d} panic: ", .{current_thread_id}) catch break :trace;
}
writer.print("{s}\n", .{msg}) catch break :trace;
if (@errorReturnTrace()) |t| if (t.index > 0) {
writer.writeAll("error return context:\n") catch break :trace;
writeErrorReturnTrace(t, stderr) catch break :trace;
writer.writeAll("\nstack trace:\n") catch break :trace;
};
writeCurrentStackTrace(.{
.first_address = first_trace_addr orelse @returnAddress(),
.allow_unsafe_unwind = true, // we're crashing anyway, give it our all!
}, stderr) catch break :trace;
}
waitForOtherThreadToFinishPanicking();
},
1 => {
panic_stage = 2;
// A panic happened while trying to print a previous panic message.
// We're still holding the mutex but that's fine as we're going to
// call abort().
const stderr = lockStderr(&.{}).terminal();
stderr.writer.writeAll("aborting due to recursive panic\n") catch {};
},
else => {}, // Panicked while printing the recursive panic message.
}
std.process.abort();
}