master
1/// We override the panic implementation to our own one, so we can print our own information before
2/// calling the default panic handler. This declaration must be re-exposed from `@import("root")`.
3pub const panic = if (dev.env == .bootstrap)
4 std.debug.simple_panic
5else
6 std.debug.FullPanic(panicImpl);
7
8/// We let std install its segfault handler, but we override the target-agnostic handler it calls,
9/// so we can print our own information before calling the default segfault logic. This declaration
10/// must be re-exposed from `@import("root")`.
11pub const debug = struct {
12 pub const handleSegfault = handleSegfaultImpl;
13};
14
15/// Printed in panic messages when suggesting a command to run, allowing copy-pasting the command.
16/// Set by `main` as soon as arguments are known. The value here is a default in case we somehow
17/// crash earlier than that.
18pub var zig_argv0: []const u8 = "zig";
19
20fn handleSegfaultImpl(addr: ?usize, name: []const u8, opt_ctx: ?std.debug.CpuContextPtr) noreturn {
21 @branchHint(.cold);
22 dumpCrashContext() catch {};
23 std.debug.defaultHandleSegfault(addr, name, opt_ctx);
24}
25fn panicImpl(msg: []const u8, first_trace_addr: ?usize) noreturn {
26 @branchHint(.cold);
27 dumpCrashContext() catch {};
28 std.debug.defaultPanic(msg, first_trace_addr orelse @returnAddress());
29}
30
31pub const AnalyzeBody = if (build_options.enable_debug_extensions) struct {
32 parent: ?*AnalyzeBody,
33 sema: *Sema,
34 block: *Sema.Block,
35 body: []const Zir.Inst.Index,
36 body_index: usize,
37
38 threadlocal var current: ?*AnalyzeBody = null;
39
40 pub fn setBodyIndex(ab: *AnalyzeBody, index: usize) void {
41 ab.body_index = index;
42 }
43
44 pub fn push(ab: *AnalyzeBody, sema: *Sema, block: *Sema.Block, body: []const Zir.Inst.Index) void {
45 ab.* = .{
46 .parent = current,
47 .sema = sema,
48 .block = block,
49 .body = body,
50 .body_index = 0,
51 };
52 current = ab;
53 }
54 pub fn pop(ab: *AnalyzeBody) void {
55 std.debug.assert(current.? == ab); // `Sema.analyzeBodyInner` did not match push/pop calls
56 current = ab.parent;
57 }
58} else struct {
59 const current: ?noreturn = null;
60 // Dummy implementation, with functions marked `inline` to avoid interfering with tail calls.
61 pub inline fn push(_: AnalyzeBody, _: *Sema, _: *Sema.Block, _: []const Zir.Inst.Index) void {}
62 pub inline fn pop(_: AnalyzeBody) void {}
63 pub inline fn setBodyIndex(_: @This(), _: usize) void {}
64};
65
66pub const CodegenFunc = if (build_options.enable_debug_extensions) struct {
67 zcu: *const Zcu,
68 func_index: InternPool.Index,
69 threadlocal var current: ?CodegenFunc = null;
70 pub fn start(zcu: *const Zcu, func_index: InternPool.Index) void {
71 std.debug.assert(current == null);
72 current = .{ .zcu = zcu, .func_index = func_index };
73 }
74 pub fn stop(func_index: InternPool.Index) void {
75 std.debug.assert(current.?.func_index == func_index);
76 current = null;
77 }
78} else struct {
79 const current: ?noreturn = null;
80 // Dummy implementation
81 pub fn start(_: *const Zcu, _: InternPool.Index) void {}
82 pub fn stop(_: InternPool.Index) void {}
83};
84
85fn dumpCrashContext() Io.Writer.Error!void {
86 const S = struct {
87 /// In the case of recursive panics or segfaults, don't print the context for a second time.
88 threadlocal var already_dumped = false;
89 /// TODO: make this unnecessary. It exists because `print_zir` currently needs an allocator,
90 /// but that shouldn't be necessary---it's already only used in one place.
91 threadlocal var crash_heap: [64 * 1024]u8 = undefined;
92 };
93 if (S.already_dumped) return;
94 S.already_dumped = true;
95
96 // TODO: this does mean that a different thread could grab the stderr mutex between the context
97 // and the actual panic printing, which would be quite confusing.
98 const stderr, _ = std.debug.lockStderrWriter(&.{});
99 defer std.debug.unlockStderrWriter();
100
101 try stderr.writeAll("Compiler crash context:\n");
102
103 if (CodegenFunc.current) |*cg| {
104 const func_nav = cg.zcu.funcInfo(cg.func_index).owner_nav;
105 const func_fqn = cg.zcu.intern_pool.getNav(func_nav).fqn;
106 try stderr.print("Generating function '{f}'\n\n", .{func_fqn.fmt(&cg.zcu.intern_pool)});
107 } else if (AnalyzeBody.current) |anal| {
108 try dumpCrashContextSema(anal, stderr, &S.crash_heap);
109 } else {
110 try stderr.writeAll("(no context)\n\n");
111 }
112}
113fn dumpCrashContextSema(anal: *AnalyzeBody, stderr: *Io.Writer, crash_heap: []u8) Io.Writer.Error!void {
114 const block: *Sema.Block = anal.block;
115 const zcu = anal.sema.pt.zcu;
116 const comp = zcu.comp;
117
118 var fba: std.heap.FixedBufferAllocator = .init(crash_heap);
119
120 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu) orelse {
121 const file = zcu.fileByIndex(block.src_base_inst.resolveFile(&zcu.intern_pool));
122 try stderr.print("Analyzing lost instruction in file '{f}'. This should not happen!\n\n", .{file.path.fmt(comp)});
123 return;
124 };
125
126 try stderr.print("Analyzing '{f}'\n", .{file.path.fmt(comp)});
127
128 print_zir.renderInstructionContext(
129 fba.allocator(),
130 anal.body,
131 anal.body_index,
132 file,
133 src_base_node,
134 6, // indent
135 stderr,
136 ) catch |err| switch (err) {
137 error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"),
138 else => |e| return e,
139 };
140 try stderr.print(
141 \\ For full context, use the command
142 \\ {s} ast-check -t {f}
143 \\
144 \\
145 , .{ zig_argv0, file.path.fmt(comp) });
146
147 var parent = anal.parent;
148 while (parent) |curr| {
149 fba.reset();
150 const cur_block_file = zcu.fileByIndex(curr.block.src_base_inst.resolveFile(&zcu.intern_pool));
151 try stderr.print(" in {f}\n", .{cur_block_file.path.fmt(comp)});
152 _, const cur_block_src_base_node = Zcu.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, zcu) orelse {
153 try stderr.writeAll(" > [lost instruction; this should not happen]\n");
154 parent = curr.parent;
155 continue;
156 };
157 try stderr.writeAll(" > ");
158 print_zir.renderSingleInstruction(
159 fba.allocator(),
160 curr.body[curr.body_index],
161 cur_block_file,
162 cur_block_src_base_node,
163 6, // indent
164 stderr,
165 ) catch |err| switch (err) {
166 error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"),
167 else => |e| return e,
168 };
169 try stderr.writeAll("\n");
170
171 parent = curr.parent;
172 }
173
174 try stderr.writeByte('\n');
175}
176
177const std = @import("std");
178const Io = std.Io;
179const Zir = std.zig.Zir;
180
181const Sema = @import("Sema.zig");
182const Zcu = @import("Zcu.zig");
183const InternPool = @import("InternPool.zig");
184const dev = @import("dev.zig");
185const print_zir = @import("print_zir.zig");
186
187const build_options = @import("build_options");