Bug 1942239 - Add option to explicitly enable incremental origin initialization in...
[gecko.git] / toolkit / xre / nsSigHandlers.cpp
blob146a586eea8edd32298372c4b03a05f81fe197c5
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/. */
6 /*
7 * This module is supposed to abstract signal handling away from the other
8 * platforms that do not support it.
9 */
11 #include "nsSigHandlers.h"
13 #ifdef XP_UNIX
15 # include <signal.h>
16 # include <stdio.h>
17 # include <string.h>
18 # include "prthread.h"
19 # include "prenv.h"
20 # include "nsDebug.h"
21 # include "nsString.h"
22 # include "nsXULAppAPI.h"
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
35 # if defined(SOLARIS)
36 # include <sys/resource.h>
37 # include <ucontext.h>
38 # endif
40 # ifdef MOZ_WIDGET_GTK
41 # include <dlfcn.h>
42 # include "WidgetUtilsGtk.h"
43 # endif
45 # ifdef MOZ_WAYLAND
46 # include "wayland-proxy.h"
47 # endif
49 // Note: some tests manipulate this value.
50 unsigned int _gdb_sleep_duration = 300;
52 # if defined(LINUX) && !defined(ANDROID) && defined(DEBUG) && \
53 (defined(__i386) || defined(__x86_64) || defined(PPC))
54 # define CRAWL_STACK_ON_SIGSEGV
55 # endif
57 # ifndef PR_SET_PTRACER
58 # define PR_SET_PTRACER 0x59616d61
59 # endif
60 # ifndef PR_SET_PTRACER_ANY
61 # define PR_SET_PTRACER_ANY ((unsigned long)-1)
62 # endif
64 # if defined(CRAWL_STACK_ON_SIGSEGV)
66 # include <unistd.h>
67 # include "nsISupportsUtils.h"
68 # include "mozilla/Attributes.h"
69 # include "mozilla/StackWalk.h"
71 static const char* gProgname = "huh?";
73 // NB: keep me up to date with the same variable in
74 // ipc/chromium/chrome/common/ipc_channel_posix.cc
75 static const int kClientChannelFd = 3;
77 extern "C" {
79 static void PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP,
80 void* aClosure) {
81 char buf[1024];
82 MozCodeAddressDetails details;
84 MozDescribeCodeAddress(aPC, &details);
85 MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
86 fprintf(stdout, "%s\n", buf);
87 fflush(stdout);
91 void common_crap_handler(int signum, const void* aFirstFramePC) {
92 printf("\nProgram %s (pid = %d) received signal %d.\n", gProgname, getpid(),
93 signum);
95 printf("Stack:\n");
96 MozStackWalk(PrintStackFrame, aFirstFramePC, /* maxFrames */ 0, nullptr);
98 printf("Sleeping for %d seconds.\n", _gdb_sleep_duration);
99 printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
100 gProgname, getpid());
102 // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled.
103 prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
105 sleep(_gdb_sleep_duration);
107 printf("Done sleeping...\n");
109 _exit(signum);
112 MOZ_NEVER_INLINE void ah_crap_handler(int signum) {
113 common_crap_handler(signum, CallerPC());
116 MOZ_NEVER_INLINE void child_ah_crap_handler(int signum) {
117 if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH"))
118 close(kClientChannelFd);
119 common_crap_handler(signum, CallerPC());
122 # endif // CRAWL_STACK_ON_SIGSEGV
124 # ifdef MOZ_WIDGET_GTK
125 // Need this include for version test below.
126 # include <glib.h>
127 # endif
129 # if defined(MOZ_WIDGET_GTK)
131 # if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 50
132 // These types are only available in glib 2.50+
133 typedef enum {
134 G_LOG_WRITER_HANDLED = 1,
135 G_LOG_WRITER_UNHANDLED = 0,
136 } GLogWriterOutput;
137 typedef struct _GLogField GLogField;
138 struct _GLogField {
139 const gchar* key;
140 gconstpointer value;
141 gssize length;
143 typedef GLogWriterOutput (*GLogWriterFunc)(GLogLevelFlags log_level,
144 const GLogField* fields,
145 gsize n_fields, gpointer user_data);
146 # endif
148 static GLogFunc orig_log_func = nullptr;
150 extern "C" {
151 static void glib_log_func(const gchar* log_domain, GLogLevelFlags log_level,
152 const gchar* message, gpointer user_data);
153 static GLogWriterOutput glib_log_writer_func(GLogLevelFlags, const GLogField*,
154 gsize, gpointer);
157 // GDK sometimes avoids calling exit handlers, but we still want to know when we
158 // crash, see https://gitlab.gnome.org/GNOME/gtk/-/issues/4514 and bug 1743144.
159 static bool IsCrashyGtkMessage(const nsACString& aMessage) {
160 if (aMessage.EqualsLiteral("Lost connection to Wayland compositor.")) {
161 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L210
162 return true;
164 if (StringBeginsWith(aMessage, "Error flushing display: "_ns)) {
165 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L68
166 return true;
168 if (StringBeginsWith(aMessage, "Error reading events from display: "_ns)) {
169 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L97
170 return true;
172 if (StringBeginsWith(aMessage, "Error "_ns) &&
173 StringEndsWith(aMessage, " dispatching to Wayland display."_ns)) {
174 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L205
175 return true;
177 return false;
180 static void HandleGLibMessage(GLogLevelFlags aLogLevel,
181 const nsDependentCString& aMessage) {
182 if (MOZ_UNLIKELY(IsCrashyGtkMessage(aMessage))) {
183 # ifdef MOZ_WAYLAND
184 MOZ_CRASH_UNSAFE_PRINTF(
185 "(%s) %s Proxy: %s",
186 mozilla::widget::GetDesktopEnvironmentIdentifier().get(),
187 strdup(aMessage.get()), WaylandProxy::GetState());
188 # else
189 MOZ_CRASH_UNSAFE_PRINTF(
190 "(%s) %s", mozilla::widget::GetDesktopEnvironmentIdentifier().get(),
191 strdup(aMessage.get()));
192 # endif
195 if (aLogLevel &
196 (G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION)) {
197 NS_DebugBreak(NS_DEBUG_ASSERTION, aMessage.get(), "glib assertion",
198 __FILE__, __LINE__);
199 } else if (aLogLevel & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) {
200 NS_DebugBreak(NS_DEBUG_WARNING, aMessage.get(), "glib warning", __FILE__,
201 __LINE__);
205 /* static */ void glib_log_func(const gchar* log_domain,
206 GLogLevelFlags log_level, const gchar* message,
207 gpointer user_data) {
208 HandleGLibMessage(log_level, nsDependentCString(message));
209 orig_log_func(log_domain, log_level, message, nullptr);
212 GLogWriterOutput glib_log_writer_func(GLogLevelFlags flags,
213 const GLogField* fields, gsize n_fields,
214 gpointer user_data) {
215 static const GLogWriterFunc sLogWriterDefault =
216 (GLogWriterFunc)dlsym(RTLD_DEFAULT, "g_log_writer_default");
217 for (gsize i = 0; i < n_fields; ++i) {
218 if (!strcmp(fields[i].key, "MESSAGE") && fields[i].length < 0) {
219 HandleGLibMessage(flags,
220 nsDependentCString((const char*)fields[i].value));
221 break;
224 return sLogWriterDefault(flags, fields, n_fields, user_data);
227 # endif
229 # ifdef SA_SIGINFO
230 static void fpehandler(int signum, siginfo_t* si, void* context) {
231 /* Integer divide by zero or integer overflow. */
232 /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */
233 if (si->si_code == FPE_INTDIV || si->si_code == FPE_INTOVF) {
234 NS_DebugBreak(NS_DEBUG_ABORT, "Divide by zero", nullptr, __FILE__,
235 __LINE__);
238 # ifdef XP_MACOSX
239 # if defined(__i386__) || defined(__amd64__)
240 ucontext_t* uc = (ucontext_t*)context;
242 _STRUCT_FP_CONTROL* ctrl = &uc->uc_mcontext->__fs.__fpu_fcw;
243 ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl =
244 ctrl->__undfl = ctrl->__precis = 1;
246 _STRUCT_FP_STATUS* status = &uc->uc_mcontext->__fs.__fpu_fsw;
247 status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl =
248 status->__undfl = status->__precis = status->__stkflt =
249 status->__errsumm = 0;
251 uint32_t* mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr;
252 *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
253 *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
254 # endif
255 # endif
256 # if defined(LINUX) && !defined(ANDROID)
258 # if defined(__i386__)
259 ucontext_t* uc = (ucontext_t*)context;
261 * It seems that we have no access to mxcsr on Linux. libc
262 * seems to be translating cw/sw to mxcsr.
264 unsigned long int* cw = &uc->uc_mcontext.fpregs->cw;
265 *cw |= FPU_EXCEPTION_MASK;
267 unsigned long int* sw = &uc->uc_mcontext.fpregs->sw;
268 *sw &= ~FPU_STATUS_FLAGS;
269 # endif
270 # if defined(__amd64__)
271 ucontext_t* uc = (ucontext_t*)context;
273 uint16_t* cw = &uc->uc_mcontext.fpregs->cwd;
274 *cw |= FPU_EXCEPTION_MASK;
276 uint16_t* sw = &uc->uc_mcontext.fpregs->swd;
277 *sw &= ~FPU_STATUS_FLAGS;
279 uint32_t* mxcsr = &uc->uc_mcontext.fpregs->mxcsr;
280 *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
281 *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
282 # endif
283 # endif
284 # ifdef SOLARIS
285 ucontext_t* uc = (ucontext_t*)context;
287 # if defined(__i386)
288 uint32_t* cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0];
289 *cw |= FPU_EXCEPTION_MASK;
291 uint32_t* sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1];
292 *sw &= ~FPU_STATUS_FLAGS;
294 /* address of the instruction that caused the exception */
295 uint32_t* ip = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[3];
296 uc->uc_mcontext.gregs[REG_PC] = *ip;
297 # endif
298 # if defined(__amd64__)
299 uint16_t* cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw;
300 *cw |= FPU_EXCEPTION_MASK;
302 uint16_t* sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw;
303 *sw &= ~FPU_STATUS_FLAGS;
305 uint32_t* mxcsr = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr;
306 *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
307 *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
308 # endif
309 # endif
311 # endif
313 void InstallSignalHandlers(const char* aProgname) {
314 # if defined(CRAWL_STACK_ON_SIGSEGV)
315 if (aProgname) {
316 const char* tmp = strdup(aProgname);
317 if (tmp) {
318 gProgname = tmp;
321 # endif // CRAWL_STACK_ON_SIGSEGV
323 const char* gdbSleep = PR_GetEnv("MOZ_GDB_SLEEP");
324 if (gdbSleep && *gdbSleep) {
325 unsigned int s;
326 if (1 == sscanf(gdbSleep, "%u", &s)) {
327 _gdb_sleep_duration = s;
331 # if defined(CRAWL_STACK_ON_SIGSEGV)
332 if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) {
333 void (*crap_handler)(int) = GeckoProcessType_Default != XRE_GetProcessType()
334 ? child_ah_crap_handler
335 : ah_crap_handler;
336 signal(SIGSEGV, crap_handler);
337 signal(SIGILL, crap_handler);
338 signal(SIGABRT, crap_handler);
340 # endif // CRAWL_STACK_ON_SIGSEGV
342 # ifdef SA_SIGINFO
343 /* Install a handler for floating point exceptions and disable them if they
344 * occur. */
345 struct sigaction sa, osa;
346 sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
347 sa.sa_sigaction = fpehandler;
348 sigemptyset(&sa.sa_mask);
349 sigaction(SIGFPE, &sa, &osa);
350 # endif
352 if (!XRE_IsParentProcess()) {
354 * If the user is debugging a Gecko parent process in gdb and hits ^C to
355 * suspend, a SIGINT signal will be sent to the child. We ignore this signal
356 * so the child isn't killed.
358 signal(SIGINT, SIG_IGN);
361 # if defined(DEBUG) && defined(LINUX)
362 const char* memLimit = PR_GetEnv("MOZ_MEM_LIMIT");
363 if (memLimit && *memLimit) {
364 long m = atoi(memLimit);
365 m *= (1024 * 1024);
366 struct rlimit r;
367 r.rlim_cur = m;
368 r.rlim_max = m;
369 setrlimit(RLIMIT_AS, &r);
371 # endif
373 # ifdef MOZ_WIDGET_GTK
374 // Override the default glib logging function to intercept some crashes that
375 // are uninterceptable otherwise. Also, when XPCOM_DEBUG_BREAK is set, we can
376 // also get stacks for them, so we get stacks for it too.
378 // If we can hook via g_log_set_writer_func, then we don't need to hook via
379 // g_log_set_default_handler, because the GTK default handler uses structured
380 // logging and thus will end up in our log writer function anyways.
381 static const auto sSetLogWriter =
382 (void (*)(GLogWriterFunc, gpointer, GDestroyNotify))dlsym(
383 RTLD_DEFAULT, "g_log_set_writer_func");
384 if (sSetLogWriter) {
385 sSetLogWriter(glib_log_writer_func, nullptr, nullptr);
386 } else {
387 orig_log_func = g_log_set_default_handler(glib_log_func, nullptr);
389 # endif
392 #elif XP_WIN
394 # include <windows.h>
396 # ifdef _M_IX86
398 * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters
399 * for ia86. We known that MxCsr is at offset 0x18 and is a DWORD.
401 # define MXCSR(ctx) (*(DWORD*)(((BYTE*)(ctx)->ExtendedRegisters) + 0x18))
402 # endif
404 # ifdef _M_X64
405 # define MXCSR(ctx) (ctx)->MxCsr
406 # endif
408 # if defined(_M_IX86) || defined(_M_X64)
410 # ifdef _M_X64
411 # define X87CW(ctx) (ctx)->FltSave.ControlWord
412 # define X87SW(ctx) (ctx)->FltSave.StatusWord
413 # else
414 # define X87CW(ctx) (ctx)->FloatSave.ControlWord
415 # define X87SW(ctx) (ctx)->FloatSave.StatusWord
416 # endif
418 static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter;
420 LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe) {
421 PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord;
422 CONTEXT* c = (CONTEXT*)pe->ContextRecord;
424 switch (e->ExceptionCode) {
425 case STATUS_FLOAT_DENORMAL_OPERAND:
426 case STATUS_FLOAT_DIVIDE_BY_ZERO:
427 case STATUS_FLOAT_INEXACT_RESULT:
428 case STATUS_FLOAT_INVALID_OPERATION:
429 case STATUS_FLOAT_OVERFLOW:
430 case STATUS_FLOAT_STACK_CHECK:
431 case STATUS_FLOAT_UNDERFLOW:
432 case STATUS_FLOAT_MULTIPLE_FAULTS:
433 case STATUS_FLOAT_MULTIPLE_TRAPS:
434 X87CW(c) |= FPU_EXCEPTION_MASK; /* disable all FPU exceptions */
435 X87SW(c) &= ~FPU_STATUS_FLAGS; /* clear all pending FPU exceptions */
436 # ifdef _M_IX86
437 if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) {
438 # endif
439 MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
440 MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
441 # ifdef _M_IX86
443 # endif
444 return EXCEPTION_CONTINUE_EXECUTION;
446 LONG action = EXCEPTION_CONTINUE_SEARCH;
447 if (gFPEPreviousFilter) action = gFPEPreviousFilter(pe);
449 return action;
452 void InstallSignalHandlers(const char* aProgname) {
453 gFPEPreviousFilter = SetUnhandledExceptionFilter(FpeHandler);
456 # else
458 void InstallSignalHandlers(const char* aProgname) {}
460 # endif
462 #else
463 # error No signal handling implementation for this platform.
464 #endif