| author | |
| committer | |
| log | 1a1598c58cf506217355c6b8eec84c0e9a3a6d2e |
| tree | 2ec9f7e299e4e9d22459e305fd69eef64523a3a8 |
| parent | 0dd2e93e4cb28205823e840d5fdb0fccce9cc2e7 |
| signature |
closes #23556 files changed, 252 insertions(+), 8 deletions(-)
std/debug.zig+67| ... | @@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ... | @@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 99 | }; | 99 | }; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, | ||
| 103 | /// unbuffered, and ignores any error returned. | ||
| 104 | /// TODO multithreaded awareness | ||
| 105 | pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { | ||
| 106 | const stderr = getStderrStream() catch return; | ||
| 107 | if (builtin.strip_debug_info) { | ||
| 108 | stderr.print("Unable to dump stack trace: debug info stripped\n") catch return; | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 112 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | ||
| 113 | return; | ||
| 114 | }; | ||
| 115 | const tty_color = wantTtyColor(); | ||
| 116 | printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return; | ||
| 117 | const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*; | ||
| 118 | printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return; | ||
| 119 | var it = StackIterator{ | ||
| 120 | .first_addr = null, | ||
| 121 | .fp = bp, | ||
| 122 | }; | ||
| 123 | while (it.next()) |return_address| { | ||
| 124 | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 102 | /// Returns a slice with the same pointer as addresses, with a potentially smaller len. | 128 | /// Returns a slice with the same pointer as addresses, with a potentially smaller len. |
| 103 | /// On Windows, when first_address is not null, we ask for at least 32 stack frames, | 129 | /// On Windows, when first_address is not null, we ask for at least 32 stack frames, |
| 104 | /// and then try to find the first address. If addresses.len is more than 32, we | 130 | /// and then try to find the first address. If addresses.len is more than 32, we |
| ... | @@ -2291,3 +2317,44 @@ fn getDebugInfoAllocator() *mem.Allocator { | ... | @@ -2291,3 +2317,44 @@ fn getDebugInfoAllocator() *mem.Allocator { |
| 2291 | debug_info_allocator = &debug_info_arena_allocator.allocator; | 2317 | debug_info_allocator = &debug_info_arena_allocator.allocator; |
| 2292 | return &debug_info_arena_allocator.allocator; | 2318 | return &debug_info_arena_allocator.allocator; |
| 2293 | } | 2319 | } |
| 2320 | |||
| 2321 | /// Whether or not the current target can print useful debug information when a segfault occurs. | ||
| 2322 | pub const have_segfault_handling_support = builtin.arch == .x86_64 and builtin.os == .linux; | ||
| 2323 | |||
| 2324 | /// Attaches a global SIGSEGV handler which calls @panic("segmentation fault"); | ||
| 2325 | pub fn attachSegfaultHandler() void { | ||
| 2326 | if (!have_segfault_handling_support) { | ||
| 2327 | @compileError("segfault handler not supported for this target"); | ||
| 2328 | } | ||
| 2329 | var act = os.Sigaction{ | ||
| 2330 | .sigaction = handleSegfault, | ||
| 2331 | .mask = os.empty_sigset, | ||
| 2332 | .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND), | ||
| 2333 | }; | ||
| 2334 | |||
| 2335 | os.sigaction(os.SIGSEGV, &act, null); | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn { | ||
| 2339 | // Reset to the default handler so that if a segfault happens in this handler it will crash | ||
| 2340 | // the process. Also when this handler returns, the original instruction will be repeated | ||
| 2341 | // and the resulting segfault will crash the process rather than continually dump stack traces. | ||
| 2342 | var act = os.Sigaction{ | ||
| 2343 | .sigaction = os.SIG_DFL, | ||
| 2344 | .mask = os.empty_sigset, | ||
| 2345 | .flags = 0, | ||
| 2346 | }; | ||
| 2347 | os.sigaction(os.SIGSEGV, &act, null); | ||
| 2348 | |||
| 2349 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | ||
| 2350 | const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]); | ||
| 2351 | const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]); | ||
| 2352 | const addr = @ptrToInt(info.fields.sigfault.addr); | ||
| 2353 | std.debug.warn("Segmentation fault at address 0x{x}\n", addr); | ||
| 2354 | dumpStackTraceFromBase(bp, ip); | ||
| 2355 | |||
| 2356 | // We cannot allow the signal handler to return because when it runs the original instruction | ||
| 2357 | // again, the memory may be mapped and undefined behavior would occur rather than repeating | ||
| 2358 | // the segfault. So we simply abort here. | ||
| 2359 | os.abort(); | ||
| 2360 | } |
std/os.zig+10| ... | @@ -2529,6 +2529,16 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { | ... | @@ -2529,6 +2529,16 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { |
| 2529 | } | 2529 | } |
| 2530 | } | 2530 | } |
| 2531 | 2531 | ||
| 2532 | /// Examine and change a signal action. | ||
| 2533 | pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void { | ||
| 2534 | switch (errno(system.sigaction(sig, act, oact))) { | ||
| 2535 | 0 => return, | ||
| 2536 | EFAULT => unreachable, | ||
| 2537 | EINVAL => unreachable, | ||
| 2538 | else => unreachable, | ||
| 2539 | } | ||
| 2540 | } | ||
| 2541 | |||
| 2532 | test "" { | 2542 | test "" { |
| 2533 | _ = @import("os/darwin.zig"); | 2543 | _ = @import("os/darwin.zig"); |
| 2534 | _ = @import("os/freebsd.zig"); | 2544 | _ = @import("os/freebsd.zig"); |
std/os/bits/linux.zig+9-6| ... | @@ -12,6 +12,8 @@ pub usingnamespace switch (builtin.arch) { | ... | @@ -12,6 +12,8 @@ pub usingnamespace switch (builtin.arch) { |
| 12 | 12 | ||
| 13 | pub const pid_t = i32; | 13 | pub const pid_t = i32; |
| 14 | pub const fd_t = i32; | 14 | pub const fd_t = i32; |
| 15 | pub const uid_t = i32; | ||
| 16 | pub const clock_t = isize; | ||
| 15 | 17 | ||
| 16 | pub const PATH_MAX = 4096; | 18 | pub const PATH_MAX = 4096; |
| 17 | pub const IOV_MAX = 1024; | 19 | pub const IOV_MAX = 1024; |
| ... | @@ -712,22 +714,23 @@ pub const all_mask = [_]u32{ 0xffffffff, 0xffffffff }; | ... | @@ -712,22 +714,23 @@ pub const all_mask = [_]u32{ 0xffffffff, 0xffffffff }; |
| 712 | pub const app_mask = [_]u32{ 0xfffffffc, 0x7fffffff }; | 714 | pub const app_mask = [_]u32{ 0xfffffffc, 0x7fffffff }; |
| 713 | 715 | ||
| 714 | pub const k_sigaction = extern struct { | 716 | pub const k_sigaction = extern struct { |
| 715 | handler: extern fn (i32) void, | 717 | sigaction: ?extern fn (i32, *siginfo_t, *c_void) void, |
| 716 | flags: usize, | 718 | flags: usize, |
| 717 | restorer: extern fn () void, | 719 | restorer: extern fn () void, |
| 718 | mask: [2]u32, | 720 | mask: [2]u32, |
| 719 | }; | 721 | }; |
| 720 | 722 | ||
| 721 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | 723 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. |
| 722 | pub const Sigaction = struct { | 724 | pub const Sigaction = extern struct { |
| 723 | handler: extern fn (i32) void, | 725 | sigaction: ?extern fn (i32, *siginfo_t, *c_void) void, |
| 724 | mask: sigset_t, | 726 | mask: sigset_t, |
| 725 | flags: u32, | 727 | flags: u32, |
| 728 | restorer: ?extern fn () void = null, | ||
| 726 | }; | 729 | }; |
| 727 | 730 | ||
| 728 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | 731 | pub const SIG_ERR = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, maxInt(usize)); |
| 729 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | 732 | pub const SIG_DFL = @intToPtr(?extern fn (i32, *siginfo_t, *c_void) void, 0); |
| 730 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | 733 | pub const SIG_IGN = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, 1); |
| 731 | pub const empty_sigset = [_]usize{0} ** sigset_t.len; | 734 | pub const empty_sigset = [_]usize{0} ** sigset_t.len; |
| 732 | 735 | ||
| 733 | pub const in_port_t = u16; | 736 | pub const in_port_t = u16; |
std/os/bits/linux/x86_64.zig+155| ... | @@ -1,5 +1,10 @@ | ... | @@ -1,5 +1,10 @@ |
| 1 | // x86-64-specific declarations that are intended to be imported into the POSIX namespace. | 1 | // x86-64-specific declarations that are intended to be imported into the POSIX namespace. |
| 2 | const std = @import("../../../std.zig"); | 2 | const std = @import("../../../std.zig"); |
| 3 | const pid_t = linux.pid_t; | ||
| 4 | const uid_t = linux.uid_t; | ||
| 5 | const clock_t = linux.clock_t; | ||
| 6 | const stack_t = linux.stack_t; | ||
| 7 | const sigset_t = linux.sigset_t; | ||
| 3 | 8 | ||
| 4 | const linux = std.os.linux; | 9 | const linux = std.os.linux; |
| 5 | const sockaddr = linux.sockaddr; | 10 | const sockaddr = linux.sockaddr; |
| ... | @@ -407,6 +412,30 @@ pub const ARCH_SET_FS = 0x1002; | ... | @@ -407,6 +412,30 @@ pub const ARCH_SET_FS = 0x1002; |
| 407 | pub const ARCH_GET_FS = 0x1003; | 412 | pub const ARCH_GET_FS = 0x1003; |
| 408 | pub const ARCH_GET_GS = 0x1004; | 413 | pub const ARCH_GET_GS = 0x1004; |
| 409 | 414 | ||
| 415 | pub const REG_R8 = 0; | ||
| 416 | pub const REG_R9 = 1; | ||
| 417 | pub const REG_R10 = 2; | ||
| 418 | pub const REG_R11 = 3; | ||
| 419 | pub const REG_R12 = 4; | ||
| 420 | pub const REG_R13 = 5; | ||
| 421 | pub const REG_R14 = 6; | ||
| 422 | pub const REG_R15 = 7; | ||
| 423 | pub const REG_RDI = 8; | ||
| 424 | pub const REG_RSI = 9; | ||
| 425 | pub const REG_RBP = 10; | ||
| 426 | pub const REG_RBX = 11; | ||
| 427 | pub const REG_RDX = 12; | ||
| 428 | pub const REG_RAX = 13; | ||
| 429 | pub const REG_RCX = 14; | ||
| 430 | pub const REG_RSP = 15; | ||
| 431 | pub const REG_RIP = 16; | ||
| 432 | pub const REG_EFL = 17; | ||
| 433 | pub const REG_CSGSFS = 18; | ||
| 434 | pub const REG_ERR = 19; | ||
| 435 | pub const REG_TRAPNO = 20; | ||
| 436 | pub const REG_OLDMASK = 21; | ||
| 437 | pub const REG_CR2 = 22; | ||
| 438 | |||
| 410 | pub const msghdr = extern struct { | 439 | pub const msghdr = extern struct { |
| 411 | msg_name: ?*sockaddr, | 440 | msg_name: ?*sockaddr, |
| 412 | msg_namelen: socklen_t, | 441 | msg_namelen: socklen_t, |
| ... | @@ -468,3 +497,129 @@ pub const timezone = extern struct { | ... | @@ -468,3 +497,129 @@ pub const timezone = extern struct { |
| 468 | }; | 497 | }; |
| 469 | 498 | ||
| 470 | pub const Elf_Symndx = u32; | 499 | pub const Elf_Symndx = u32; |
| 500 | |||
| 501 | pub const sigval = extern union { | ||
| 502 | int: i32, | ||
| 503 | ptr: *c_void, | ||
| 504 | }; | ||
| 505 | |||
| 506 | pub const siginfo_t = extern struct { | ||
| 507 | signo: i32, | ||
| 508 | errno: i32, | ||
| 509 | code: i32, | ||
| 510 | fields: extern union { | ||
| 511 | pad: [128 - 2 * @sizeOf(c_int) - @sizeOf(c_long)]u8, | ||
| 512 | common: extern struct { | ||
| 513 | first: extern union { | ||
| 514 | piduid: extern struct { | ||
| 515 | pid: pid_t, | ||
| 516 | uid: uid_t, | ||
| 517 | }, | ||
| 518 | timer: extern struct { | ||
| 519 | timerid: i32, | ||
| 520 | overrun: i32, | ||
| 521 | }, | ||
| 522 | }, | ||
| 523 | second: extern union { | ||
| 524 | value: sigval, | ||
| 525 | sigchld: extern struct { | ||
| 526 | status: i32, | ||
| 527 | utime: clock_t, | ||
| 528 | stime: clock_t, | ||
| 529 | }, | ||
| 530 | }, | ||
| 531 | }, | ||
| 532 | sigfault: extern struct { | ||
| 533 | addr: *c_void, | ||
| 534 | addr_lsb: i16, | ||
| 535 | first: extern union { | ||
| 536 | addr_bnd: extern struct { | ||
| 537 | lower: *c_void, | ||
| 538 | upper: *c_void, | ||
| 539 | }, | ||
| 540 | pkey: u32, | ||
| 541 | }, | ||
| 542 | }, | ||
| 543 | sigpoll: extern struct { | ||
| 544 | band: isize, | ||
| 545 | fd: i32, | ||
| 546 | }, | ||
| 547 | sigsys: extern struct { | ||
| 548 | call_addr: *c_void, | ||
| 549 | syscall: i32, | ||
| 550 | arch: u32, | ||
| 551 | }, | ||
| 552 | }, | ||
| 553 | }; | ||
| 554 | |||
| 555 | pub const greg_t = usize; | ||
| 556 | pub const gregset_t = [23]greg_t; | ||
| 557 | pub const fpstate = extern struct { | ||
| 558 | cwd: u16, | ||
| 559 | swd: u16, | ||
| 560 | ftw: u16, | ||
| 561 | fop: u16, | ||
| 562 | rip: usize, | ||
| 563 | rdp: usize, | ||
| 564 | mxcsr: u32, | ||
| 565 | mxcr_mask: u32, | ||
| 566 | st: [8]extern struct { | ||
| 567 | significand: [4]u16, | ||
| 568 | exponent: u16, | ||
| 569 | padding: [3]u16 = undefined, | ||
| 570 | }, | ||
| 571 | xmm: [16]extern struct { | ||
| 572 | element: [4]u32, | ||
| 573 | }, | ||
| 574 | padding: [24]u32 = undefined, | ||
| 575 | }; | ||
| 576 | pub const fpregset_t = *fpstate; | ||
| 577 | pub const sigcontext = extern struct { | ||
| 578 | r8: usize, | ||
| 579 | r9: usize, | ||
| 580 | r10: usize, | ||
| 581 | r11: usize, | ||
| 582 | r12: usize, | ||
| 583 | r13: usize, | ||
| 584 | r14: usize, | ||
| 585 | r15: usize, | ||
| 586 | |||
| 587 | rdi: usize, | ||
| 588 | rsi: usize, | ||
| 589 | rbp: usize, | ||
| 590 | rbx: usize, | ||
| 591 | rdx: usize, | ||
| 592 | rax: usize, | ||
| 593 | rcx: usize, | ||
| 594 | rsp: usize, | ||
| 595 | rip: usize, | ||
| 596 | eflags: usize, | ||
| 597 | |||
| 598 | cs: u16, | ||
| 599 | gs: u16, | ||
| 600 | fs: u16, | ||
| 601 | pad0: u16 = undefined, | ||
| 602 | |||
| 603 | err: usize, | ||
| 604 | trapno: usize, | ||
| 605 | oldmask: usize, | ||
| 606 | cr2: usize, | ||
| 607 | |||
| 608 | fpstate: *fpstate, | ||
| 609 | reserved1: [8]usize = undefined, | ||
| 610 | }; | ||
| 611 | |||
| 612 | pub const mcontext_t = extern struct { | ||
| 613 | gregs: gregset_t, | ||
| 614 | fpregs: fpregset_t, | ||
| 615 | reserved1: [8]usize = undefined, | ||
| 616 | }; | ||
| 617 | |||
| 618 | pub const ucontext_t = extern struct { | ||
| 619 | flags: usize, | ||
| 620 | link: *ucontext_t, | ||
| 621 | stack: stack_t, | ||
| 622 | mcontext: mcontext_t, | ||
| 623 | sigmask: sigset_t, | ||
| 624 | fpregs_mem: [64]usize, | ||
| 625 | }; |
std/os/linux.zig+2-2| ... | @@ -542,7 +542,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti | ... | @@ -542,7 +542,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti |
| 542 | assert(sig != SIGKILL); | 542 | assert(sig != SIGKILL); |
| 543 | assert(sig != SIGSTOP); | 543 | assert(sig != SIGSTOP); |
| 544 | var ksa = k_sigaction{ | 544 | var ksa = k_sigaction{ |
| 545 | .handler = act.handler, | 545 | .sigaction = act.sigaction, |
| 546 | .flags = act.flags | SA_RESTORER, | 546 | .flags = act.flags | SA_RESTORER, |
| 547 | .mask = undefined, | 547 | .mask = undefined, |
| 548 | .restorer = @ptrCast(extern fn () void, restore_rt), | 548 | .restorer = @ptrCast(extern fn () void, restore_rt), |
| ... | @@ -555,7 +555,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti | ... | @@ -555,7 +555,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti |
| 555 | return result; | 555 | return result; |
| 556 | } | 556 | } |
| 557 | if (oact) |old| { | 557 | if (oact) |old| { |
| 558 | old.handler = ksa_old.handler; | 558 | old.sigaction = ksa_old.sigaction; |
| 559 | old.flags = @truncate(u32, ksa_old.flags); | 559 | old.flags = @truncate(u32, ksa_old.flags); |
| 560 | @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); | 560 | @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); |
| 561 | } | 561 | } |
std/special/start.zig+9| ... | @@ -99,6 +99,15 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -99,6 +99,15 @@ fn posixCallMainAndExit() noreturn { |
| 99 | inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { | 99 | inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { |
| 100 | std.os.argv = argv[0..argc]; | 100 | std.os.argv = argv[0..argc]; |
| 101 | std.os.environ = envp; | 101 | std.os.environ = envp; |
| 102 | |||
| 103 | const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler")) | ||
| 104 | root.enable_segfault_handler | ||
| 105 | else | ||
| 106 | std.debug.runtime_safety and std.debug.have_segfault_handling_support; | ||
| 107 | if (enable_segfault_handler) { | ||
| 108 | std.debug.attachSegfaultHandler(); | ||
| 109 | } | ||
| 110 | |||
| 102 | return callMain(); | 111 | return callMain(); |
| 103 | } | 112 | } |
| 104 | 113 |