| ... | @@ -285,7 +285,7 @@ pub fn lockStderr(buffer: []u8) Io.LockedStderr { | ... | @@ -285,7 +285,7 @@ pub fn lockStderr(buffer: []u8) Io.LockedStderr { |
| 285 | const prev = io.swapCancelProtection(.blocked); | 285 | const prev = io.swapCancelProtection(.blocked); |
| 286 | defer _ = io.swapCancelProtection(prev); | 286 | defer _ = io.swapCancelProtection(prev); |
| 287 | return io.lockStderr(buffer, null) catch |err| switch (err) { | 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,13 +463,9 @@ pub fn panicExtra( |
| 463 | std.builtin.panic.call(msg, ret_addr); | 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 | /// Counts how many times the panic handler is invoked by this thread. | 466 | /// Counts how many times the panic handler is invoked by this thread. |
| 471 | /// This is used to catch and handle panics triggered by the panic handler. | 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 | /// For backends that cannot handle the language features depended on by the | 470 | /// For backends that cannot handle the language features depended on by the |
| 475 | /// default panic handler, we will use a simpler implementation. | 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,74 +529,51 @@ pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn { |
| 533 | else => {}, | 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 | // There is very similar logic to the following in `handleSegfault`. | 532 | // There is very similar logic to the following in `handleSegfault`. |
| 547 | switch (panic_stage) { | 533 | var discarding: Io.Writer.Discarding = .init(&.{}); |
| 548 | 0 => { | 534 | const current_recursive_panic_writer = recursive_panic_writer; |
| 549 | panic_stage = 1; | 535 | recursive_panic_writer = &discarding.writer; |
| 550 | _ = panicking.fetchAdd(1, .seq_cst); | 536 | if (current_recursive_panic_writer) |writer| { |
| 551 | | 537 | // A panic happened while trying to print a previous panic message. |
| 552 | trace: { | 538 | writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 553 | const stderr = lockStderr(&.{}).terminal(); | 539 | } else trace: { |
| 554 | defer unlockStderr(); | 540 | // Don't try to cancel during a panic. No need to re-enable cancelation, |
| 555 | const writer = stderr.writer; | 541 | // because the panic handler doesn't return. |
| 556 | | 542 | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 557 | if (builtin.single_threaded) { | 543 | |
| 558 | writer.print("panic: ", .{}) catch break :trace; | 544 | const stderr = lockStderr(&.{}).terminal(); |
| 559 | } else { | 545 | const writer = stderr.writer; |
| 560 | const current_thread_id = std.Thread.getCurrentId(); | 546 | recursive_panic_writer = writer; |
| 561 | writer.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; | 547 | |
| 562 | } | 548 | if (enable_segfault_handler) { |
| 563 | writer.print("{s}\n", .{msg}) catch break :trace; | 549 | // If a segfault happens while panicking, we want it to actually segfault, not trigger |
| 564 | | 550 | // the handler. |
| 565 | if (@errorReturnTrace()) |t| if (t.index > 0) { | 551 | resetSegfaultHandler(); |
| 566 | writer.writeAll("error return context:\n") catch break :trace; | 552 | } |
| 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 | } | | |
| 575 | | 553 | |
| 576 | waitForOtherThreadToFinishPanicking(); | 554 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printCrashContext")) { |
| 577 | }, | 555 | root.debug.printCrashContext(stderr); |
| 578 | 1 => { | 556 | } |
| 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 | } | | |
| 588 | | 557 | |
| 589 | std.process.abort(); | 558 | if (builtin.single_threaded) { |
| 590 | } | 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. | 566 | if (@errorReturnTrace()) |t| if (t.index > 0) { |
| 593 | fn waitForOtherThreadToFinishPanicking() void { | 567 | writer.writeAll("error return context:\n") catch break :trace; |
| 594 | if (panicking.fetchSub(1, .seq_cst) != 1) { | 568 | writeStackTrace(t, stderr) catch break :trace; |
| 595 | // Another thread is panicking, wait for the last one to finish | 569 | writer.writeAll("\nstack trace:\n") catch break :trace; |
| 596 | // and call abort() | 570 | }; |
| 597 | if (builtin.single_threaded) unreachable; | 571 | writeCurrentStackTrace(.{ |
| 598 | | 572 | .first_address = first_trace_addr orelse @returnAddress(), |
| 599 | // Sleep forever without hammering the CPU | 573 | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! |
| 600 | var futex: u32 = 0; | 574 | }, stderr) catch break :trace; |
| 601 | while (true) std.Options.debug_io.futexWaitUncancelable(u32, &futex, 0); | | |
| 602 | unreachable; | | |
| 603 | } | 575 | } |
| | 576 | std.process.abort(); |
| 604 | } | 577 | } |
| 605 | | 578 | |
| 606 | pub const StackUnwindOptions = struct { | 579 | pub const StackUnwindOptions = struct { |
| ... | @@ -1535,44 +1508,38 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret | ... | @@ -1535,44 +1508,38 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret |
| 1535 | } | 1508 | } |
| 1536 | | 1509 | |
| 1537 | pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn { | 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 | // There is very similar logic to the following in `defaultPanic`. | 1511 | // There is very similar logic to the following in `defaultPanic`. |
| 1543 | switch (panic_stage) { | 1512 | var discarding: Io.Writer.Discarding = .init(&.{}); |
| 1544 | 0 => { | 1513 | const current_recursive_panic_writer = recursive_panic_writer; |
| 1545 | panic_stage = 1; | 1514 | recursive_panic_writer = &discarding.writer; |
| 1546 | _ = panicking.fetchAdd(1, .seq_cst); | 1515 | if (current_recursive_panic_writer) |writer| { |
| 1547 | | 1516 | // A segfault happened while trying to print a previous panic message. |
| 1548 | trace: { | 1517 | writer.writeAll("aborting due to recursive panic\n") catch {}; |
| 1549 | const stderr = lockStderr(&.{}).terminal(); | 1518 | } else trace: { |
| 1550 | defer unlockStderr(); | 1519 | // Don't try to cancel during a segfault. No need to re-enable cancelation, |
| 1551 | | 1520 | // because the segfault handler doesn't return. |
| 1552 | if (addr) |a| { | 1521 | _ = std.Options.debug_io.swapCancelProtection(.blocked); |
| 1553 | stderr.writer.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; | 1522 | |
| 1554 | } else { | 1523 | const stderr = lockStderr(&.{}).terminal(); |
| 1555 | stderr.writer.print("{s} (no address available)\n", .{name}) catch break :trace; | 1524 | const writer = stderr.writer; |
| 1556 | } | 1525 | recursive_panic_writer = writer; |
| 1557 | if (opt_ctx) |context| { | 1526 | |
| 1558 | writeCurrentStackTrace(.{ | 1527 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printCrashContext")) { |
| 1559 | .context = context, | 1528 | root.debug.printCrashContext(stderr); |
| 1560 | .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! | 1529 | } |
| 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 | } | | |
| 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 | // We cannot allow the signal handler to return because when it runs the original instruction | 1543 | // We cannot allow the signal handler to return because when it runs the original instruction |
| 1577 | // again, the memory may be mapped and undefined behavior would occur rather than repeating | 1544 | // again, the memory may be mapped and undefined behavior would occur rather than repeating |
| 1578 | // the segfault. So we simply abort here. | 1545 | // the segfault. So we simply abort here. |