feature. See also
. The project being documented here (as the example) is the Zig library itself.
debug.writeCurrentStackTrace
pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Terminal) Writer.Error!void
File
Code
pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Terminal) Writer.Error!void {
const writer = t.writer;
var text_arena: std.heap.ArenaAllocator = .init(getDebugInfoAllocator());
defer text_arena.deinit();
if (!std.options.allow_stack_tracing) {
t.setColor(.dim) catch {};
try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{});
t.setColor(.reset) catch {};
return;
}
const di = getSelfDebugInfo() catch |err| switch (err) {
error.UnsupportedTarget => {
t.setColor(.dim) catch {};
try writer.print("Cannot print stack trace: debug info unavailable for target\n", .{});
t.setColor(.reset) catch {};
return;
},
};
var it: StackIterator = .init(options.context);
defer it.deinit();
if (!it.stratOk(options.allow_unsafe_unwind)) {
t.setColor(.dim) catch {};
try writer.print("Cannot print stack trace: safe unwind unavailable for target\n", .{});
t.setColor(.reset) catch {};
return;
}
var total_frames: usize = 0;
var wait_for = options.first_address;
var printed_any_frame = false;
const io = std.Options.debug_io;
while (true) switch (it.next(io)) {
.switch_to_fp => |unwind_error| {
switch (StackIterator.fp_usability) {
.useless, .unsafe => {},
.safe, .ideal => continue,
}
const module_name = di.getModuleName(io, unwind_error.address) catch "???";
const caption: []const u8 = switch (unwind_error.err) {
error.MissingDebugInfo => "unwind info unavailable",
error.InvalidDebugInfo => "unwind info invalid",
error.UnsupportedDebugInfo => "unwind info unsupported",
error.ReadFailed => "filesystem error",
error.OutOfMemory => "out of memory",
error.Canceled => "operation canceled",
error.Unexpected => "unexpected error",
};
if (it.stratOk(options.allow_unsafe_unwind)) {
t.setColor(.dim) catch {};
try writer.print(
"Unwind error at address `{s}:0x{x}` ({s}), remaining frames may be incorrect\n",
.{ module_name, unwind_error.address, caption },
);
t.setColor(.reset) catch {};
} else {
t.setColor(.dim) catch {};
try writer.print(
"Unwind error at address `{s}:0x{x}` ({s}), stopping trace early\n",
.{ module_name, unwind_error.address, caption },
);
t.setColor(.reset) catch {};
return;
}
},
.end => break,
.frame => |ret_addr| {
if (total_frames > 10_000) {
t.setColor(.dim) catch {};
try writer.print(
"Stopping trace after {d} frames (large frame count may indicate broken debug info)\n",
.{total_frames},
);
t.setColor(.reset) catch {};
return;
}
total_frames += 1;
if (wait_for) |target| {
if (ret_addr != target) continue;
wait_for = null;
}
// Subtract 1 to get an address *in* the function call for a better source location.
try printSourceAtAddress(io, &text_arena, di, t, .{
.address = ret_addr -| StackIterator.ra_call_offset,
.resolve_inline_callers = true,
});
printed_any_frame = true;
},
};
if (!printed_any_frame) return writer.writeAll("(empty stack trace)\n");
}