| ... | ... | @@ -285,7 +285,7 @@ pub fn lockStderr(buffer: []u8) Io.LockedStderr { |
| 285 | 285 | const prev = io.swapCancelProtection(.blocked); |
| 286 | 286 | defer _ = io.swapCancelProtection(prev); |
| 287 | 287 | return io.lockStderr(buffer, null) catch |err| switch (err) { |
| 288 | | error.Canceled => unreachable, // Cancel protection enabled above. |
| 288 | error.Canceled => unreachable, // blocked |
| 289 | 289 | }; |
| 290 | 290 | } |
| 291 | 291 | |
| ... | ... | @@ -463,13 +463,9 @@ pub fn panicExtra( |
| 463 | 463 | std.builtin.panic.call(msg, ret_addr); |
| 464 | 464 | } |
| 465 | 465 | |
| 466 | | /// Non-zero whenever the program triggered a panic. |
| 467 | | /// The counter is incremented/decremented atomically. |
| 468 | | var panicking = std.atomic.Value(u8).init(0); |
| 469 | | |
| 470 | 466 | /// Counts how many times the panic handler is invoked by this thread. |
| 471 | 467 | /// This is used to catch and handle panics triggered by the panic handler. |
| 472 | | threadlocal var panic_stage: usize = 0; |
| 468 | threadlocal var recursive_panic_writer: ?*Io.Writer = null; |
| 473 | 469 | |
| 474 | 470 | /// For backends that cannot handle the language features depended on by the |
| 475 | 471 | /// default panic handler, we will use a simpler implementation. |
| ... | ... | @@ -533,74 +529,51 @@ pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn { |
| 533 | 529 | else => {}, |
| 534 | 530 | } |
| 535 | 531 | |
| 536 | | // Don't try to cancel during a panic. No need to re-enable cancelation, |
| 537 | | // because the panic handler doesn't return. |
| 538 | | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 539 | | |
| 540 | | if (enable_segfault_handler) { |
| 541 | | // If a segfault happens while panicking, we want it to actually segfault, not trigger |
| 542 | | // the handler. |
| 543 | | resetSegfaultHandler(); |
| 544 | | } |
| 545 | | |
| 546 | 532 | // There is very similar logic to the following in `handleSegfault`. |
| 547 | | switch (panic_stage) { |
| 548 | | 0 => { |
| 549 | | panic_stage = 1; |
| 550 | | _ = panicking.fetchAdd(1, .seq_cst); |
| 551 | | |
| 552 | | trace: { |
| 553 | | const stderr = lockStderr(&.{}).terminal(); |
| 554 | | defer unlockStderr(); |
| 555 | | const writer = stderr.writer; |
| 556 | | |
| 557 | | if (builtin.single_threaded) { |
| 558 | | writer.print("panic: ", .{}) catch break :trace; |
| 559 | | } else { |
| 560 | | const current_thread_id = std.Thread.getCurrentId(); |
| 561 | | writer.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; |
| 562 | | } |
| 563 | | writer.print("{s}\n", .{msg}) catch break :trace; |
| 564 | | |
| 565 | | if (@errorReturnTrace()) |t| if (t.index > 0) { |
| 566 | | writer.writeAll("error return context:\n") catch break :trace; |
| 567 | | writeStackTrace(t, stderr) catch break :trace; |
| 568 | | writer.writeAll("\nstack trace:\n") catch break :trace; |
| 569 | | }; |
| 570 | | writeCurrentStackTrace(.{ |
| 571 | | .first_address = first_trace_addr orelse @returnAddress(), |
| 572 | | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! |
| 573 | | }, stderr) catch break :trace; |
| 574 | | } |
| 533 | var discarding: Io.Writer.Discarding = .init(&.{}); |
| 534 | const current_recursive_panic_writer = recursive_panic_writer; |
| 535 | recursive_panic_writer = &discarding.writer; |
| 536 | if (current_recursive_panic_writer) |writer| { |
| 537 | // A panic happened while trying to print a previous panic message. |
| 538 | writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 539 | } else trace: { |
| 540 | // Don't try to cancel during a panic. No need to re-enable cancelation, |
| 541 | // because the panic handler doesn't return. |
| 542 | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 543 | |
| 544 | const stderr = lockStderr(&.{}).terminal(); |
| 545 | const writer = stderr.writer; |
| 546 | recursive_panic_writer = writer; |
| 547 | |
| 548 | if (enable_segfault_handler) { |
| 549 | // If a segfault happens while panicking, we want it to actually segfault, not trigger |
| 550 | // the handler. |
| 551 | resetSegfaultHandler(); |
| 552 | } |
| 575 | 553 | |
| 576 | | waitForOtherThreadToFinishPanicking(); |
| 577 | | }, |
| 578 | | 1 => { |
| 579 | | panic_stage = 2; |
| 580 | | // A panic happened while trying to print a previous panic message. |
| 581 | | // We're still holding the mutex but that's fine as we're going to |
| 582 | | // call abort(). |
| 583 | | const stderr = lockStderr(&.{}).terminal(); |
| 584 | | stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 585 | | }, |
| 586 | | else => {}, // Panicked while printing the recursive panic message. |
| 587 | | } |
| 554 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printCrashContext")) { |
| 555 | root.debug.printCrashContext(stderr); |
| 556 | } |
| 588 | 557 | |
| 589 | | std.process.abort(); |
| 590 | | } |
| 558 | if (builtin.single_threaded) { |
| 559 | writer.print("panic: ", .{}) catch break :trace; |
| 560 | } else { |
| 561 | const current_thread_id = std.Thread.getCurrentId(); |
| 562 | writer.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; |
| 563 | } |
| 564 | writer.print("{s}\n", .{msg}) catch break :trace; |
| 591 | 565 | |
| 592 | | /// Must be called only after adding 1 to `panicking`. There are three callsites. |
| 593 | | fn waitForOtherThreadToFinishPanicking() void { |
| 594 | | if (panicking.fetchSub(1, .seq_cst) != 1) { |
| 595 | | // Another thread is panicking, wait for the last one to finish |
| 596 | | // and call abort() |
| 597 | | if (builtin.single_threaded) unreachable; |
| 598 | | |
| 599 | | // Sleep forever without hammering the CPU |
| 600 | | var futex: u32 = 0; |
| 601 | | while (true) std.Options.debug_io.futexWaitUncancelable(u32, &futex, 0); |
| 602 | | unreachable; |
| 566 | if (@errorReturnTrace()) |t| if (t.index > 0) { |
| 567 | writer.writeAll("error return context:\n") catch break :trace; |
| 568 | writeStackTrace(t, stderr) catch break :trace; |
| 569 | writer.writeAll("\nstack trace:\n") catch break :trace; |
| 570 | }; |
| 571 | writeCurrentStackTrace(.{ |
| 572 | .first_address = first_trace_addr orelse @returnAddress(), |
| 573 | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! |
| 574 | }, stderr) catch break :trace; |
| 603 | 575 | } |
| 576 | std.process.abort(); |
| 604 | 577 | } |
| 605 | 578 | |
| 606 | 579 | pub const StackUnwindOptions = struct { |
| ... | ... | @@ -1535,44 +1508,38 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret |
| 1535 | 1508 | } |
| 1536 | 1509 | |
| 1537 | 1510 | pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn { |
| 1538 | | // Don't try to cancel during a segfault. No need to re-enable cancelation, |
| 1539 | | // because the segfault handler doesn't return. |
| 1540 | | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 1541 | | |
| 1542 | 1511 | // There is very similar logic to the following in `defaultPanic`. |
| 1543 | | switch (panic_stage) { |
| 1544 | | 0 => { |
| 1545 | | panic_stage = 1; |
| 1546 | | _ = panicking.fetchAdd(1, .seq_cst); |
| 1547 | | |
| 1548 | | trace: { |
| 1549 | | const stderr = lockStderr(&.{}).terminal(); |
| 1550 | | defer unlockStderr(); |
| 1551 | | |
| 1552 | | if (addr) |a| { |
| 1553 | | stderr.writer.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; |
| 1554 | | } else { |
| 1555 | | stderr.writer.print("{s} (no address available)\n", .{name}) catch break :trace; |
| 1556 | | } |
| 1557 | | if (opt_ctx) |context| { |
| 1558 | | writeCurrentStackTrace(.{ |
| 1559 | | .context = context, |
| 1560 | | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! |
| 1561 | | }, stderr) catch break :trace; |
| 1562 | | } |
| 1563 | | } |
| 1564 | | }, |
| 1565 | | 1 => { |
| 1566 | | panic_stage = 2; |
| 1567 | | // A segfault happened while trying to print a previous panic message. |
| 1568 | | // We're still holding the mutex but that's fine as we're going to |
| 1569 | | // call abort(). |
| 1570 | | const stderr = lockStderr(&.{}).terminal(); |
| 1571 | | stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 1572 | | }, |
| 1573 | | else => {}, // Panicked while printing the recursive panic message. |
| 1574 | | } |
| 1512 | var discarding: Io.Writer.Discarding = .init(&.{}); |
| 1513 | const current_recursive_panic_writer = recursive_panic_writer; |
| 1514 | recursive_panic_writer = &discarding.writer; |
| 1515 | if (current_recursive_panic_writer) |writer| { |
| 1516 | // A segfault happened while trying to print a previous panic message. |
| 1517 | writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 1518 | } else trace: { |
| 1519 | // Don't try to cancel during a segfault. No need to re-enable cancelation, |
| 1520 | // because the segfault handler doesn't return. |
| 1521 | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 1522 | |
| 1523 | const stderr = lockStderr(&.{}).terminal(); |
| 1524 | const writer = stderr.writer; |
| 1525 | recursive_panic_writer = writer; |
| 1526 | |
| 1527 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printCrashContext")) { |
| 1528 | root.debug.printCrashContext(stderr); |
| 1529 | } |
| 1575 | 1530 | |
| 1531 | if (addr) |a| { |
| 1532 | writer.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; |
| 1533 | } else { |
| 1534 | writer.print("{s} (no address available)\n", .{name}) catch break :trace; |
| 1535 | } |
| 1536 | if (opt_ctx) |context| { |
| 1537 | writeCurrentStackTrace(.{ |
| 1538 | .context = context, |
| 1539 | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! |
| 1540 | }, stderr) catch break :trace; |
| 1541 | } |
| 1542 | } |
| 1576 | 1543 | // We cannot allow the signal handler to return because when it runs the original instruction |
| 1577 | 1544 | // again, the memory may be mapped and undefined behavior would occur rather than repeating |
| 1578 | 1545 | // the segfault. So we simply abort here. |