/src/mozilla-central/toolkit/xre/nsSigHandlers.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | /* |
7 | | * This module is supposed to abstract signal handling away from the other |
8 | | * platforms that do not support it. |
9 | | */ |
10 | | |
11 | | #include "nsSigHandlers.h" |
12 | | |
13 | | #ifdef XP_UNIX |
14 | | |
15 | | #include <signal.h> |
16 | | #include <stdio.h> |
17 | | #include <string.h> |
18 | | #include "prthread.h" |
19 | | #include "plstr.h" |
20 | | #include "prenv.h" |
21 | | #include "nsDebug.h" |
22 | | #include "nsXULAppAPI.h" |
23 | | |
24 | | #if defined(LINUX) |
25 | | #include <sys/time.h> |
26 | | #include <sys/resource.h> |
27 | | #include <unistd.h> |
28 | | #include <stdlib.h> // atoi |
29 | | #include <sys/prctl.h> |
30 | | #ifndef ANDROID // no Android impl |
31 | | # include <ucontext.h> |
32 | | #endif |
33 | | #endif |
34 | | |
35 | | #if defined(SOLARIS) |
36 | | #include <sys/resource.h> |
37 | | #include <ucontext.h> |
38 | | #endif |
39 | | |
40 | | // Note: some tests manipulate this value. |
41 | | unsigned int _gdb_sleep_duration = 300; |
42 | | |
43 | | #if defined(LINUX) && defined(DEBUG) && \ |
44 | | (defined(__i386) || defined(__x86_64) || defined(PPC)) |
45 | | #define CRAWL_STACK_ON_SIGSEGV |
46 | | #endif |
47 | | |
48 | | #ifndef PR_SET_PTRACER |
49 | | #define PR_SET_PTRACER 0x59616d61 |
50 | | #endif |
51 | | #ifndef PR_SET_PTRACER_ANY |
52 | | #define PR_SET_PTRACER_ANY ((unsigned long)-1) |
53 | | #endif |
54 | | |
55 | | #if defined(CRAWL_STACK_ON_SIGSEGV) |
56 | | |
57 | | #include <unistd.h> |
58 | | #include "nsISupportsUtils.h" |
59 | | #include "mozilla/StackWalk.h" |
60 | | |
61 | | static const char* gProgname = "huh?"; |
62 | | |
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; |
66 | | |
67 | | extern "C" { |
68 | | |
69 | | static void PrintStackFrame(uint32_t aFrameNumber, void *aPC, void *aSP, |
70 | | void *aClosure) |
71 | | { |
72 | | char buf[1024]; |
73 | | MozCodeAddressDetails details; |
74 | | |
75 | | MozDescribeCodeAddress(aPC, &details); |
76 | | MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details); |
77 | | fprintf(stdout, "%s\n", buf); |
78 | | fflush(stdout); |
79 | | } |
80 | | |
81 | | } |
82 | | |
83 | | void |
84 | | ah_crap_handler(int signum) |
85 | | { |
86 | | printf("\nProgram %s (pid = %d) received signal %d.\n", |
87 | | gProgname, |
88 | | getpid(), |
89 | | signum); |
90 | | |
91 | | printf("Stack:\n"); |
92 | | MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, nullptr); |
93 | | |
94 | | printf("Sleeping for %d seconds.\n",_gdb_sleep_duration); |
95 | | printf("Type 'gdb %s %d' to attach your debugger to this thread.\n", |
96 | | gProgname, |
97 | | getpid()); |
98 | | |
99 | | // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled. |
100 | | prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); |
101 | | |
102 | | sleep(_gdb_sleep_duration); |
103 | | |
104 | | printf("Done sleeping...\n"); |
105 | | |
106 | | _exit(signum); |
107 | | } |
108 | | |
109 | | void |
110 | | child_ah_crap_handler(int signum) |
111 | | { |
112 | | if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH")) |
113 | | close(kClientChannelFd); |
114 | | ah_crap_handler(signum); |
115 | | } |
116 | | |
117 | | #endif // CRAWL_STACK_ON_SIGSEGV |
118 | | |
119 | | #ifdef MOZ_WIDGET_GTK |
120 | | // Need this include for version test below. |
121 | | #include <glib.h> |
122 | | #endif |
123 | | |
124 | | #if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6)) |
125 | | |
126 | | static GLogFunc orig_log_func = nullptr; |
127 | | |
128 | | extern "C" { |
129 | | static void |
130 | | my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, |
131 | | const gchar *message, gpointer user_data); |
132 | | } |
133 | | |
134 | | /* static */ void |
135 | | my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, |
136 | | const gchar *message, gpointer user_data) |
137 | 0 | { |
138 | 0 | if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION)) { |
139 | 0 | NS_DebugBreak(NS_DEBUG_ASSERTION, message, "glib assertion", __FILE__, __LINE__); |
140 | 0 | } else if (log_level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) { |
141 | 0 | NS_DebugBreak(NS_DEBUG_WARNING, message, "glib warning", __FILE__, __LINE__); |
142 | 0 | } |
143 | 0 |
|
144 | 0 | orig_log_func(log_domain, log_level, message, nullptr); |
145 | 0 | } |
146 | | |
147 | | #endif |
148 | | |
149 | | #ifdef SA_SIGINFO |
150 | | static void fpehandler(int signum, siginfo_t *si, void *context) |
151 | 0 | { |
152 | 0 | /* Integer divide by zero or integer overflow. */ |
153 | 0 | /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */ |
154 | 0 | if (si->si_code == FPE_INTDIV || si->si_code == FPE_INTOVF) { |
155 | 0 | NS_DebugBreak(NS_DEBUG_ABORT, "Divide by zero", nullptr, __FILE__, __LINE__); |
156 | 0 | } |
157 | 0 |
|
158 | | #ifdef XP_MACOSX |
159 | | ucontext_t *uc = (ucontext_t *)context; |
160 | | |
161 | | #if defined(__i386__) || defined(__amd64__) |
162 | | _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw; |
163 | | ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1; |
164 | | |
165 | | _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw; |
166 | | status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl = |
167 | | status->__precis = status->__stkflt = status->__errsumm = 0; |
168 | | |
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 */ |
172 | | #endif |
173 | | #endif |
174 | | #if defined(LINUX) && !defined(ANDROID) |
175 | 0 | ucontext_t *uc = (ucontext_t *)context; |
176 | 0 |
|
177 | | #if defined(__i386__) |
178 | | /* |
179 | | * It seems that we have no access to mxcsr on Linux. libc |
180 | | * seems to be translating cw/sw to mxcsr. |
181 | | */ |
182 | | unsigned long int *cw = &uc->uc_mcontext.fpregs->cw; |
183 | | *cw |= FPU_EXCEPTION_MASK; |
184 | | |
185 | | unsigned long int *sw = &uc->uc_mcontext.fpregs->sw; |
186 | | *sw &= ~FPU_STATUS_FLAGS; |
187 | | #endif |
188 | | #if defined(__amd64__) |
189 | 0 | uint16_t *cw = &uc->uc_mcontext.fpregs->cwd; |
190 | 0 | *cw |= FPU_EXCEPTION_MASK; |
191 | 0 |
|
192 | 0 | uint16_t *sw = &uc->uc_mcontext.fpregs->swd; |
193 | 0 | *sw &= ~FPU_STATUS_FLAGS; |
194 | 0 |
|
195 | 0 | uint32_t *mxcsr = &uc->uc_mcontext.fpregs->mxcsr; |
196 | 0 | *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ |
197 | 0 | *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ |
198 | 0 | #endif |
199 | 0 | #endif |
200 | | #ifdef SOLARIS |
201 | | ucontext_t *uc = (ucontext_t *)context; |
202 | | |
203 | | #if defined(__i386) |
204 | | uint32_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0]; |
205 | | *cw |= FPU_EXCEPTION_MASK; |
206 | | |
207 | | uint32_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1]; |
208 | | *sw &= ~FPU_STATUS_FLAGS; |
209 | | |
210 | | /* address of the instruction that caused the exception */ |
211 | | uint32_t *ip = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[3]; |
212 | | uc->uc_mcontext.gregs[REG_PC] = *ip; |
213 | | #endif |
214 | | #if defined(__amd64__) |
215 | | uint16_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw; |
216 | | *cw |= FPU_EXCEPTION_MASK; |
217 | | |
218 | | uint16_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw; |
219 | | *sw &= ~FPU_STATUS_FLAGS; |
220 | | |
221 | | uint32_t *mxcsr = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr; |
222 | | *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ |
223 | | *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ |
224 | | #endif |
225 | | #endif |
226 | | } |
227 | | #endif |
228 | | |
229 | | void InstallSignalHandlers(const char *aProgname) |
230 | 3 | { |
231 | | #if defined(CRAWL_STACK_ON_SIGSEGV) |
232 | | const char* tmp = PL_strdup(aProgname); |
233 | | if (tmp) { |
234 | | gProgname = tmp; |
235 | | } |
236 | | #endif // CRAWL_STACK_ON_SIGSEGV |
237 | | |
238 | 3 | const char *gdbSleep = PR_GetEnv("MOZ_GDB_SLEEP"); |
239 | 3 | if (gdbSleep && *gdbSleep) |
240 | 0 | { |
241 | 0 | unsigned int s; |
242 | 0 | if (1 == sscanf(gdbSleep, "%u", &s)) { |
243 | 0 | _gdb_sleep_duration = s; |
244 | 0 | } |
245 | 0 | } |
246 | 3 | |
247 | | #if defined(CRAWL_STACK_ON_SIGSEGV) |
248 | | if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) { |
249 | | void (*crap_handler)(int) = |
250 | | GeckoProcessType_Default != XRE_GetProcessType() ? |
251 | | child_ah_crap_handler : |
252 | | ah_crap_handler; |
253 | | signal(SIGSEGV, crap_handler); |
254 | | signal(SIGILL, crap_handler); |
255 | | signal(SIGABRT, crap_handler); |
256 | | } |
257 | | #endif // CRAWL_STACK_ON_SIGSEGV |
258 | | |
259 | 3 | #ifdef SA_SIGINFO |
260 | 3 | /* Install a handler for floating point exceptions and disable them if they occur. */ |
261 | 3 | struct sigaction sa, osa; |
262 | 3 | sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; |
263 | 3 | sa.sa_sigaction = fpehandler; |
264 | 3 | sigemptyset(&sa.sa_mask); |
265 | 3 | sigaction(SIGFPE, &sa, &osa); |
266 | 3 | #endif |
267 | 3 | |
268 | 3 | if (!XRE_IsParentProcess()) { |
269 | 0 | /* |
270 | 0 | * If the user is debugging a Gecko parent process in gdb and hits ^C to |
271 | 0 | * suspend, a SIGINT signal will be sent to the child. We ignore this signal |
272 | 0 | * so the child isn't killed. |
273 | 0 | */ |
274 | 0 | signal(SIGINT, SIG_IGN); |
275 | 0 | } |
276 | 3 | |
277 | | #if defined(DEBUG) && defined(LINUX) |
278 | | const char *memLimit = PR_GetEnv("MOZ_MEM_LIMIT"); |
279 | | if (memLimit && *memLimit) |
280 | | { |
281 | | long m = atoi(memLimit); |
282 | | m *= (1024*1024); |
283 | | struct rlimit r; |
284 | | r.rlim_cur = m; |
285 | | r.rlim_max = m; |
286 | | setrlimit(RLIMIT_AS, &r); |
287 | | } |
288 | | #endif |
289 | | |
290 | 3 | #if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6)) |
291 | 3 | const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK"); |
292 | 3 | if (assertString && |
293 | 3 | (!strcmp(assertString, "suspend") || |
294 | 0 | !strcmp(assertString, "stack") || |
295 | 0 | !strcmp(assertString, "abort") || |
296 | 0 | !strcmp(assertString, "trap") || |
297 | 0 | !strcmp(assertString, "break"))) { |
298 | 0 | // Override the default glib logging function so we get stacks for it too. |
299 | 0 | orig_log_func = g_log_set_default_handler(my_glib_log_func, nullptr); |
300 | 0 | } |
301 | 3 | #endif |
302 | 3 | } |
303 | | |
304 | | #elif XP_WIN |
305 | | |
306 | | #include <windows.h> |
307 | | |
308 | | #ifdef _M_IX86 |
309 | | /* |
310 | | * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters for ia86. |
311 | | * We known that MxCsr is at offset 0x18 and is a DWORD. |
312 | | */ |
313 | | #define MXCSR(ctx) (*(DWORD *)(((BYTE *)(ctx)->ExtendedRegisters) + 0x18)) |
314 | | #endif |
315 | | |
316 | | #ifdef _M_X64 |
317 | | #define MXCSR(ctx) (ctx)->MxCsr |
318 | | #endif |
319 | | |
320 | | #if defined(_M_IX86) || defined(_M_X64) |
321 | | |
322 | | #ifdef _M_X64 |
323 | | #define X87CW(ctx) (ctx)->FltSave.ControlWord |
324 | | #define X87SW(ctx) (ctx)->FltSave.StatusWord |
325 | | #else |
326 | | #define X87CW(ctx) (ctx)->FloatSave.ControlWord |
327 | | #define X87SW(ctx) (ctx)->FloatSave.StatusWord |
328 | | #endif |
329 | | |
330 | | static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter; |
331 | | |
332 | | LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe) |
333 | | { |
334 | | PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord; |
335 | | CONTEXT *c = (CONTEXT*)pe->ContextRecord; |
336 | | |
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 */ |
349 | | #ifdef _M_IX86 |
350 | | if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { |
351 | | #endif |
352 | | MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ |
353 | | MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ |
354 | | #ifdef _M_IX86 |
355 | | } |
356 | | #endif |
357 | | return EXCEPTION_CONTINUE_EXECUTION; |
358 | | } |
359 | | LONG action = EXCEPTION_CONTINUE_SEARCH; |
360 | | if (gFPEPreviousFilter) |
361 | | action = gFPEPreviousFilter(pe); |
362 | | |
363 | | return action; |
364 | | } |
365 | | |
366 | | void InstallSignalHandlers(const char *aProgname) |
367 | | { |
368 | | gFPEPreviousFilter = SetUnhandledExceptionFilter(FpeHandler); |
369 | | } |
370 | | |
371 | | #else |
372 | | |
373 | | void InstallSignalHandlers(const char *aProgname) |
374 | | { |
375 | | } |
376 | | |
377 | | #endif |
378 | | |
379 | | #else |
380 | | #error No signal handling implementation for this platform. |
381 | | #endif |