michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This module is supposed to abstract signal handling away from the other michael@0: * platforms that do not support it. michael@0: */ michael@0: michael@0: #include "nsSigHandlers.h" michael@0: michael@0: #ifdef XP_UNIX michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "prthread.h" michael@0: #include "plstr.h" michael@0: #include "prenv.h" michael@0: #include "nsDebug.h" michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #if defined(LINUX) michael@0: #include michael@0: #include michael@0: #include michael@0: #include // atoi michael@0: #ifndef ANDROID // no Android impl michael@0: # include michael@0: #endif michael@0: #endif michael@0: michael@0: #if defined(SOLARIS) michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: static char _progname[1024] = "huh?"; michael@0: static unsigned int _gdb_sleep_duration = 300; michael@0: michael@0: // NB: keep me up to date with the same variable in michael@0: // ipc/chromium/chrome/common/ipc_channel_posix.cc michael@0: static const int kClientChannelFd = 3; michael@0: michael@0: #if defined(LINUX) && defined(DEBUG) && \ michael@0: (defined(__i386) || defined(__x86_64) || defined(PPC)) michael@0: #define CRAWL_STACK_ON_SIGSEGV michael@0: #endif michael@0: michael@0: #if defined(CRAWL_STACK_ON_SIGSEGV) michael@0: michael@0: #include michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsStackWalk.h" michael@0: michael@0: extern "C" { michael@0: michael@0: static void PrintStackFrame(void *aPC, void *aSP, void *aClosure) michael@0: { michael@0: char buf[1024]; michael@0: nsCodeAddressDetails details; michael@0: michael@0: NS_DescribeCodeAddress(aPC, &details); michael@0: NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf)); michael@0: fputs(buf, stdout); michael@0: } michael@0: michael@0: } michael@0: michael@0: void michael@0: ah_crap_handler(int signum) michael@0: { michael@0: printf("\nProgram %s (pid = %d) received signal %d.\n", michael@0: _progname, michael@0: getpid(), michael@0: signum); michael@0: michael@0: printf("Stack:\n"); michael@0: NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, michael@0: nullptr, 0, nullptr); michael@0: michael@0: printf("Sleeping for %d seconds.\n",_gdb_sleep_duration); michael@0: printf("Type 'gdb %s %d' to attach your debugger to this thread.\n", michael@0: _progname, michael@0: getpid()); michael@0: michael@0: sleep(_gdb_sleep_duration); michael@0: michael@0: printf("Done sleeping...\n"); michael@0: michael@0: _exit(signum); michael@0: } michael@0: michael@0: void michael@0: child_ah_crap_handler(int signum) michael@0: { michael@0: if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH")) michael@0: close(kClientChannelFd); michael@0: ah_crap_handler(signum); michael@0: } michael@0: michael@0: #endif // CRAWL_STACK_ON_SIGSEGV michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: // Need this include for version test below. michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6)) michael@0: michael@0: static GLogFunc orig_log_func = nullptr; michael@0: michael@0: extern "C" { michael@0: static void michael@0: my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, michael@0: const gchar *message, gpointer user_data); michael@0: } michael@0: michael@0: /* static */ void michael@0: my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, michael@0: const gchar *message, gpointer user_data) michael@0: { michael@0: if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION)) { michael@0: NS_DebugBreak(NS_DEBUG_ASSERTION, message, "glib assertion", __FILE__, __LINE__); michael@0: } else if (log_level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) { michael@0: NS_DebugBreak(NS_DEBUG_WARNING, message, "glib warning", __FILE__, __LINE__); michael@0: } michael@0: michael@0: orig_log_func(log_domain, log_level, message, nullptr); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifdef SA_SIGINFO michael@0: static void fpehandler(int signum, siginfo_t *si, void *context) michael@0: { michael@0: /* Integer divide by zero or integer overflow. */ michael@0: /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */ michael@0: if (si->si_code == FPE_INTDIV || si->si_code == FPE_INTOVF) { michael@0: NS_DebugBreak(NS_DEBUG_ABORT, "Divide by zero", nullptr, __FILE__, __LINE__); michael@0: } michael@0: michael@0: #ifdef XP_MACOSX michael@0: ucontext_t *uc = (ucontext_t *)context; michael@0: michael@0: #if defined(__i386__) || defined(__amd64__) michael@0: _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw; michael@0: ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1; michael@0: michael@0: _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw; michael@0: status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl = michael@0: status->__precis = status->__stkflt = status->__errsumm = 0; michael@0: michael@0: __uint32_t *mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr; michael@0: *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ michael@0: *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ michael@0: #endif michael@0: #endif michael@0: #if defined(LINUX) && !defined(ANDROID) michael@0: ucontext_t *uc = (ucontext_t *)context; michael@0: michael@0: #if defined(__i386__) michael@0: /* michael@0: * It seems that we have no access to mxcsr on Linux. libc michael@0: * seems to be translating cw/sw to mxcsr. michael@0: */ michael@0: unsigned long int *cw = &uc->uc_mcontext.fpregs->cw; michael@0: *cw |= FPU_EXCEPTION_MASK; michael@0: michael@0: unsigned long int *sw = &uc->uc_mcontext.fpregs->sw; michael@0: *sw &= ~FPU_STATUS_FLAGS; michael@0: #endif michael@0: #if defined(__amd64__) michael@0: __uint16_t *cw = &uc->uc_mcontext.fpregs->cwd; michael@0: *cw |= FPU_EXCEPTION_MASK; michael@0: michael@0: __uint16_t *sw = &uc->uc_mcontext.fpregs->swd; michael@0: *sw &= ~FPU_STATUS_FLAGS; michael@0: michael@0: __uint32_t *mxcsr = &uc->uc_mcontext.fpregs->mxcsr; michael@0: *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ michael@0: *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ michael@0: #endif michael@0: #endif michael@0: #ifdef SOLARIS michael@0: ucontext_t *uc = (ucontext_t *)context; michael@0: michael@0: #if defined(__i386) michael@0: uint32_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0]; michael@0: *cw |= FPU_EXCEPTION_MASK; michael@0: michael@0: uint32_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1]; michael@0: *sw &= ~FPU_STATUS_FLAGS; michael@0: michael@0: /* address of the instruction that caused the exception */ michael@0: uint32_t *ip = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[3]; michael@0: uc->uc_mcontext.gregs[REG_PC] = *ip; michael@0: #endif michael@0: #if defined(__amd64__) michael@0: uint16_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw; michael@0: *cw |= FPU_EXCEPTION_MASK; michael@0: michael@0: uint16_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw; michael@0: *sw &= ~FPU_STATUS_FLAGS; michael@0: michael@0: uint32_t *mxcsr = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr; michael@0: *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ michael@0: *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ michael@0: #endif michael@0: #endif michael@0: } michael@0: #endif michael@0: michael@0: void InstallSignalHandlers(const char *ProgramName) michael@0: { michael@0: PL_strncpy(_progname,ProgramName, (sizeof(_progname)-1) ); michael@0: michael@0: const char *gdbSleep = PR_GetEnv("MOZ_GDB_SLEEP"); michael@0: if (gdbSleep && *gdbSleep) michael@0: { michael@0: unsigned int s; michael@0: if (1 == sscanf(gdbSleep, "%u", &s)) { michael@0: _gdb_sleep_duration = s; michael@0: } michael@0: } michael@0: michael@0: #if defined(CRAWL_STACK_ON_SIGSEGV) michael@0: if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) { michael@0: void (*crap_handler)(int) = michael@0: GeckoProcessType_Default != XRE_GetProcessType() ? michael@0: child_ah_crap_handler : michael@0: ah_crap_handler; michael@0: signal(SIGSEGV, crap_handler); michael@0: signal(SIGILL, crap_handler); michael@0: signal(SIGABRT, crap_handler); michael@0: } michael@0: #endif // CRAWL_STACK_ON_SIGSEGV michael@0: michael@0: #ifdef SA_SIGINFO michael@0: /* Install a handler for floating point exceptions and disable them if they occur. */ michael@0: struct sigaction sa, osa; michael@0: sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; michael@0: sa.sa_sigaction = fpehandler; michael@0: sigemptyset(&sa.sa_mask); michael@0: sigaction(SIGFPE, &sa, &osa); michael@0: #endif michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: /* michael@0: * If the user is debugging a Gecko parent process in gdb and hits ^C to michael@0: * suspend, a SIGINT signal will be sent to the child. We ignore this signal michael@0: * so the child isn't killed. michael@0: */ michael@0: signal(SIGINT, SIG_IGN); michael@0: } michael@0: michael@0: #if defined(DEBUG) && defined(LINUX) michael@0: const char *memLimit = PR_GetEnv("MOZ_MEM_LIMIT"); michael@0: if (memLimit && *memLimit) michael@0: { michael@0: long m = atoi(memLimit); michael@0: m *= (1024*1024); michael@0: struct rlimit r; michael@0: r.rlim_cur = m; michael@0: r.rlim_max = m; michael@0: setrlimit(RLIMIT_AS, &r); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(SOLARIS) michael@0: #define NOFILES 512 michael@0: michael@0: // Boost Solaris file descriptors michael@0: { michael@0: struct rlimit rl; michael@0: michael@0: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) michael@0: michael@0: if (rl.rlim_cur < NOFILES) { michael@0: rl.rlim_cur = NOFILES; michael@0: michael@0: if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { michael@0: perror("setrlimit(RLIMIT_NOFILE)"); michael@0: fprintf(stderr, "Cannot exceed hard limit for open files"); michael@0: } michael@0: #if defined(DEBUG) michael@0: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) michael@0: printf("File descriptors set to %d\n", rl.rlim_cur); michael@0: #endif //DEBUG michael@0: } michael@0: } michael@0: #endif //SOLARIS michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6)) michael@0: const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK"); michael@0: if (assertString && michael@0: (!strcmp(assertString, "suspend") || michael@0: !strcmp(assertString, "stack") || michael@0: !strcmp(assertString, "abort") || michael@0: !strcmp(assertString, "trap") || michael@0: !strcmp(assertString, "break"))) { michael@0: // Override the default glib logging function so we get stacks for it too. michael@0: orig_log_func = g_log_set_default_handler(my_glib_log_func, nullptr); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #elif XP_WIN michael@0: michael@0: #include michael@0: michael@0: #ifdef _M_IX86 michael@0: /* michael@0: * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters for ia86. michael@0: * We known that MxCsr is at offset 0x18 and is a DWORD. michael@0: */ michael@0: #define MXCSR(ctx) (*(DWORD *)(((BYTE *)(ctx)->ExtendedRegisters) + 0x18)) michael@0: #endif michael@0: michael@0: #ifdef _M_X64 michael@0: #define MXCSR(ctx) (ctx)->MxCsr michael@0: #endif michael@0: michael@0: #if defined(_M_IX86) || defined(_M_X64) michael@0: michael@0: #ifdef _M_X64 michael@0: #define X87CW(ctx) (ctx)->FltSave.ControlWord michael@0: #define X87SW(ctx) (ctx)->FltSave.StatusWord michael@0: #else michael@0: #define X87CW(ctx) (ctx)->FloatSave.ControlWord michael@0: #define X87SW(ctx) (ctx)->FloatSave.StatusWord michael@0: #endif michael@0: michael@0: /* michael@0: * SSE traps raise these exception codes, which are defined in internal NT headers michael@0: * but not winbase.h michael@0: */ michael@0: #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 michael@0: #define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5 michael@0: michael@0: static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter; michael@0: michael@0: LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe) michael@0: { michael@0: PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord; michael@0: CONTEXT *c = (CONTEXT*)pe->ContextRecord; michael@0: michael@0: switch (e->ExceptionCode) { michael@0: case STATUS_FLOAT_DENORMAL_OPERAND: michael@0: case STATUS_FLOAT_DIVIDE_BY_ZERO: michael@0: case STATUS_FLOAT_INEXACT_RESULT: michael@0: case STATUS_FLOAT_INVALID_OPERATION: michael@0: case STATUS_FLOAT_OVERFLOW: michael@0: case STATUS_FLOAT_STACK_CHECK: michael@0: case STATUS_FLOAT_UNDERFLOW: michael@0: case STATUS_FLOAT_MULTIPLE_FAULTS: michael@0: case STATUS_FLOAT_MULTIPLE_TRAPS: michael@0: X87CW(c) |= FPU_EXCEPTION_MASK; /* disable all FPU exceptions */ michael@0: X87SW(c) &= ~FPU_STATUS_FLAGS; /* clear all pending FPU exceptions */ michael@0: #ifdef _M_IX86 michael@0: if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { michael@0: #endif michael@0: MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ michael@0: MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ michael@0: #ifdef _M_IX86 michael@0: } michael@0: #endif michael@0: return EXCEPTION_CONTINUE_EXECUTION; michael@0: } michael@0: LONG action = EXCEPTION_CONTINUE_SEARCH; michael@0: if (gFPEPreviousFilter) michael@0: action = gFPEPreviousFilter(pe); michael@0: michael@0: return action; michael@0: } michael@0: michael@0: void InstallSignalHandlers(const char *ProgramName) michael@0: { michael@0: gFPEPreviousFilter = SetUnhandledExceptionFilter(FpeHandler); michael@0: } michael@0: michael@0: #else michael@0: michael@0: void InstallSignalHandlers(const char *ProgramName) michael@0: { michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #else michael@0: #error No signal handling implementation for this platform. michael@0: #endif