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