1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * This module is supposed to abstract signal handling away from the other
8 * platforms that do not support it.
11 #include "nsSigHandlers.h"
18 # include "prthread.h"
21 # include "nsXULAppAPI.h"
24 # include <sys/time.h>
25 # include <sys/resource.h>
27 # include <stdlib.h> // atoi
28 # include <sys/prctl.h>
29 # ifndef ANDROID // no Android impl
30 # include <ucontext.h>
35 # include <sys/resource.h>
36 # include <ucontext.h>
39 // Note: some tests manipulate this value.
40 unsigned int _gdb_sleep_duration
= 300;
42 # if defined(LINUX) && !defined(ANDROID) && defined(DEBUG) && \
43 (defined(__i386) || defined(__x86_64) || defined(PPC))
44 # define CRAWL_STACK_ON_SIGSEGV
47 # ifndef PR_SET_PTRACER
48 # define PR_SET_PTRACER 0x59616d61
50 # ifndef PR_SET_PTRACER_ANY
51 # define PR_SET_PTRACER_ANY ((unsigned long)-1)
54 # if defined(CRAWL_STACK_ON_SIGSEGV)
57 # include "nsISupportsUtils.h"
58 # include "mozilla/Attributes.h"
59 # include "mozilla/StackWalk.h"
61 static const char* gProgname
= "huh?";
63 // NB: keep me up to date with the same variable in
64 // ipc/chromium/chrome/common/ipc_channel_posix.cc
65 static const int kClientChannelFd
= 3;
69 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
72 MozCodeAddressDetails details
;
74 MozDescribeCodeAddress(aPC
, &details
);
75 MozFormatCodeAddressDetails(buf
, sizeof(buf
), aFrameNumber
, aPC
, &details
);
76 fprintf(stdout
, "%s\n", buf
);
81 void common_crap_handler(int signum
, const void* aFirstFramePC
) {
82 printf("\nProgram %s (pid = %d) received signal %d.\n", gProgname
, getpid(),
86 MozStackWalk(PrintStackFrame
, aFirstFramePC
, /* maxFrames */ 0, nullptr);
88 printf("Sleeping for %d seconds.\n", _gdb_sleep_duration
);
89 printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
92 // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled.
93 prctl(PR_SET_PTRACER
, PR_SET_PTRACER_ANY
);
95 sleep(_gdb_sleep_duration
);
97 printf("Done sleeping...\n");
102 MOZ_NEVER_INLINE
void ah_crap_handler(int signum
) {
103 common_crap_handler(signum
, CallerPC());
106 MOZ_NEVER_INLINE
void child_ah_crap_handler(int signum
) {
107 if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH"))
108 close(kClientChannelFd
);
109 common_crap_handler(signum
, CallerPC());
112 # endif // CRAWL_STACK_ON_SIGSEGV
114 # ifdef MOZ_WIDGET_GTK
115 // Need this include for version test below.
119 # if defined(MOZ_WIDGET_GTK) && \
120 (GLIB_MAJOR_VERSION > 2 || \
121 (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6))
123 static GLogFunc orig_log_func
= nullptr;
126 static void my_glib_log_func(const gchar
* log_domain
, GLogLevelFlags log_level
,
127 const gchar
* message
, gpointer user_data
);
130 /* static */ void my_glib_log_func(const gchar
* log_domain
,
131 GLogLevelFlags log_level
,
132 const gchar
* message
, gpointer user_data
) {
134 (G_LOG_LEVEL_ERROR
| G_LOG_FLAG_FATAL
| G_LOG_FLAG_RECURSION
)) {
135 NS_DebugBreak(NS_DEBUG_ASSERTION
, message
, "glib assertion", __FILE__
,
137 } else if (log_level
& (G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
)) {
138 NS_DebugBreak(NS_DEBUG_WARNING
, message
, "glib warning", __FILE__
,
142 orig_log_func(log_domain
, log_level
, message
, nullptr);
148 static void fpehandler(int signum
, siginfo_t
* si
, void* context
) {
149 /* Integer divide by zero or integer overflow. */
150 /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */
151 if (si
->si_code
== FPE_INTDIV
|| si
->si_code
== FPE_INTOVF
) {
152 NS_DebugBreak(NS_DEBUG_ABORT
, "Divide by zero", nullptr, __FILE__
,
157 # if defined(__i386__) || defined(__amd64__)
158 ucontext_t
* uc
= (ucontext_t
*)context
;
160 _STRUCT_FP_CONTROL
* ctrl
= &uc
->uc_mcontext
->__fs
.__fpu_fcw
;
161 ctrl
->__invalid
= ctrl
->__denorm
= ctrl
->__zdiv
= ctrl
->__ovrfl
=
162 ctrl
->__undfl
= ctrl
->__precis
= 1;
164 _STRUCT_FP_STATUS
* status
= &uc
->uc_mcontext
->__fs
.__fpu_fsw
;
165 status
->__invalid
= status
->__denorm
= status
->__zdiv
= status
->__ovrfl
=
166 status
->__undfl
= status
->__precis
= status
->__stkflt
=
167 status
->__errsumm
= 0;
169 uint32_t* mxcsr
= &uc
->uc_mcontext
->__fs
.__fpu_mxcsr
;
170 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
171 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
174 # if defined(LINUX) && !defined(ANDROID)
176 # if defined(__i386__)
177 ucontext_t
* uc
= (ucontext_t
*)context
;
179 * It seems that we have no access to mxcsr on Linux. libc
180 * seems to be translating cw/sw to mxcsr.
182 unsigned long int* cw
= &uc
->uc_mcontext
.fpregs
->cw
;
183 *cw
|= FPU_EXCEPTION_MASK
;
185 unsigned long int* sw
= &uc
->uc_mcontext
.fpregs
->sw
;
186 *sw
&= ~FPU_STATUS_FLAGS
;
188 # if defined(__amd64__)
189 ucontext_t
* uc
= (ucontext_t
*)context
;
191 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
->cwd
;
192 *cw
|= FPU_EXCEPTION_MASK
;
194 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
->swd
;
195 *sw
&= ~FPU_STATUS_FLAGS
;
197 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
->mxcsr
;
198 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
199 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
203 ucontext_t
* uc
= (ucontext_t
*)context
;
206 uint32_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[0];
207 *cw
|= FPU_EXCEPTION_MASK
;
209 uint32_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[1];
210 *sw
&= ~FPU_STATUS_FLAGS
;
212 /* address of the instruction that caused the exception */
213 uint32_t* ip
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[3];
214 uc
->uc_mcontext
.gregs
[REG_PC
] = *ip
;
216 # if defined(__amd64__)
217 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.cw
;
218 *cw
|= FPU_EXCEPTION_MASK
;
220 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.sw
;
221 *sw
&= ~FPU_STATUS_FLAGS
;
223 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
;
224 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
225 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
231 void InstallSignalHandlers(const char* aProgname
) {
232 # if defined(CRAWL_STACK_ON_SIGSEGV)
234 const char* tmp
= strdup(aProgname
);
239 # endif // CRAWL_STACK_ON_SIGSEGV
241 const char* gdbSleep
= PR_GetEnv("MOZ_GDB_SLEEP");
242 if (gdbSleep
&& *gdbSleep
) {
244 if (1 == sscanf(gdbSleep
, "%u", &s
)) {
245 _gdb_sleep_duration
= s
;
249 # if defined(CRAWL_STACK_ON_SIGSEGV)
250 if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) {
251 void (*crap_handler
)(int) = GeckoProcessType_Default
!= XRE_GetProcessType()
252 ? child_ah_crap_handler
254 signal(SIGSEGV
, crap_handler
);
255 signal(SIGILL
, crap_handler
);
256 signal(SIGABRT
, crap_handler
);
258 # endif // CRAWL_STACK_ON_SIGSEGV
261 /* Install a handler for floating point exceptions and disable them if they
263 struct sigaction sa
, osa
;
264 sa
.sa_flags
= SA_ONSTACK
| SA_RESTART
| SA_SIGINFO
;
265 sa
.sa_sigaction
= fpehandler
;
266 sigemptyset(&sa
.sa_mask
);
267 sigaction(SIGFPE
, &sa
, &osa
);
270 if (!XRE_IsParentProcess()) {
272 * If the user is debugging a Gecko parent process in gdb and hits ^C to
273 * suspend, a SIGINT signal will be sent to the child. We ignore this signal
274 * so the child isn't killed.
276 signal(SIGINT
, SIG_IGN
);
279 # if defined(DEBUG) && defined(LINUX)
280 const char* memLimit
= PR_GetEnv("MOZ_MEM_LIMIT");
281 if (memLimit
&& *memLimit
) {
282 long m
= atoi(memLimit
);
287 setrlimit(RLIMIT_AS
, &r
);
291 # if defined(MOZ_WIDGET_GTK) && \
292 (GLIB_MAJOR_VERSION > 2 || \
293 (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6))
294 const char* assertString
= PR_GetEnv("XPCOM_DEBUG_BREAK");
296 (!strcmp(assertString
, "suspend") || !strcmp(assertString
, "stack") ||
297 !strcmp(assertString
, "abort") || !strcmp(assertString
, "trap") ||
298 !strcmp(assertString
, "break"))) {
299 // Override the default glib logging function so we get stacks for it too.
300 orig_log_func
= g_log_set_default_handler(my_glib_log_func
, nullptr);
307 # include <windows.h>
311 * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters
312 * for ia86. We known that MxCsr is at offset 0x18 and is a DWORD.
314 # define MXCSR(ctx) (*(DWORD*)(((BYTE*)(ctx)->ExtendedRegisters) + 0x18))
318 # define MXCSR(ctx) (ctx)->MxCsr
321 # if defined(_M_IX86) || defined(_M_X64)
324 # define X87CW(ctx) (ctx)->FltSave.ControlWord
325 # define X87SW(ctx) (ctx)->FltSave.StatusWord
327 # define X87CW(ctx) (ctx)->FloatSave.ControlWord
328 # define X87SW(ctx) (ctx)->FloatSave.StatusWord
331 static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter
;
333 LONG __stdcall
FpeHandler(PEXCEPTION_POINTERS pe
) {
334 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)pe
->ExceptionRecord
;
335 CONTEXT
* c
= (CONTEXT
*)pe
->ContextRecord
;
337 switch (e
->ExceptionCode
) {
338 case STATUS_FLOAT_DENORMAL_OPERAND
:
339 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
340 case STATUS_FLOAT_INEXACT_RESULT
:
341 case STATUS_FLOAT_INVALID_OPERATION
:
342 case STATUS_FLOAT_OVERFLOW
:
343 case STATUS_FLOAT_STACK_CHECK
:
344 case STATUS_FLOAT_UNDERFLOW
:
345 case STATUS_FLOAT_MULTIPLE_FAULTS
:
346 case STATUS_FLOAT_MULTIPLE_TRAPS
:
347 X87CW(c
) |= FPU_EXCEPTION_MASK
; /* disable all FPU exceptions */
348 X87SW(c
) &= ~FPU_STATUS_FLAGS
; /* clear all pending FPU exceptions */
350 if (c
->ContextFlags
& CONTEXT_EXTENDED_REGISTERS
) {
352 MXCSR(c
) |= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
353 MXCSR(c
) &= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
357 return EXCEPTION_CONTINUE_EXECUTION
;
359 LONG action
= EXCEPTION_CONTINUE_SEARCH
;
360 if (gFPEPreviousFilter
) action
= gFPEPreviousFilter(pe
);
365 void InstallSignalHandlers(const char* aProgname
) {
366 gFPEPreviousFilter
= SetUnhandledExceptionFilter(FpeHandler
);
371 void InstallSignalHandlers(const char* aProgname
) {}
376 # error No signal handling implementation for this platform.