michael@0: /* -*- Mode: C++; tab-width: 40; 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: #include "nsX11ErrorHandler.h" michael@0: michael@0: #include "prenv.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsExceptionHandler.h" michael@0: #include "nsDebug.h" michael@0: michael@0: #include "mozilla/X11Util.h" michael@0: #include michael@0: michael@0: #define BUFSIZE 2048 // What Xlib uses with XGetErrorDatabaseText michael@0: michael@0: extern "C" { michael@0: static int michael@0: X11Error(Display *display, XErrorEvent *event) { michael@0: // Get an indication of how long ago the request that caused the error was michael@0: // made. michael@0: unsigned long age = NextRequest(display) - event->serial; michael@0: michael@0: // Get a string to represent the request that caused the error. michael@0: nsAutoCString message; michael@0: if (event->request_code < 128) { michael@0: // Core protocol request michael@0: message.AppendInt(event->request_code); michael@0: } else { michael@0: // Extension request michael@0: michael@0: // man XSetErrorHandler says "the error handler should not call any michael@0: // functions (directly or indirectly) on the display that will generate michael@0: // protocol requests or that will look for input events" so we use another michael@0: // temporary Display to request extension information. This assumes on michael@0: // the DISPLAY environment variable has been set and matches what was used michael@0: // to open |display|. michael@0: Display *tmpDisplay = XOpenDisplay(nullptr); michael@0: if (tmpDisplay) { michael@0: int nExts; michael@0: char** extNames = XListExtensions(tmpDisplay, &nExts); michael@0: int first_error; michael@0: if (extNames) { michael@0: for (int i = 0; i < nExts; ++i) { michael@0: int major_opcode, first_event; michael@0: if (XQueryExtension(tmpDisplay, extNames[i], michael@0: &major_opcode, &first_event, &first_error) michael@0: && major_opcode == event->request_code) { michael@0: message.Append(extNames[i]); michael@0: message.Append('.'); michael@0: message.AppendInt(event->minor_code); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: XFreeExtensionList(extNames); michael@0: } michael@0: XCloseDisplay(tmpDisplay); michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: // GDK2 calls XCloseDevice the devices that it opened on startup, but michael@0: // the XI protocol no longer ensures that the devices will still exist. michael@0: // If they have been removed, then a BadDevice error results. Ignore michael@0: // this error. michael@0: if (message.EqualsLiteral("XInputExtension.4") && michael@0: event->error_code == first_error + 0) { michael@0: return 0; michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: char buffer[BUFSIZE]; michael@0: if (message.IsEmpty()) { michael@0: buffer[0] = '\0'; michael@0: } else { michael@0: XGetErrorDatabaseText(display, "XRequest", message.get(), "", michael@0: buffer, sizeof(buffer)); michael@0: } michael@0: michael@0: nsAutoCString notes; michael@0: if (buffer[0]) { michael@0: notes.Append(buffer); michael@0: } else { michael@0: notes.Append("Request "); michael@0: notes.AppendInt(event->request_code); michael@0: notes.Append('.'); michael@0: notes.AppendInt(event->minor_code); michael@0: } michael@0: michael@0: notes.Append(": "); michael@0: michael@0: // Get a string to describe the error. michael@0: XGetErrorText(display, event->error_code, buffer, sizeof(buffer)); michael@0: notes.Append(buffer); michael@0: michael@0: // For requests where Xlib gets the reply synchronously, |age| will be 1 michael@0: // and the stack will include the function making the request. For michael@0: // asynchronous requests, the current stack will often be unrelated to the michael@0: // point of making the request, even if |age| is 1, but sometimes this may michael@0: // help us count back to the point of the request. With XSynchronize on, michael@0: // the stack will include the function making the request, even though michael@0: // |age| will be 2 for asynchronous requests because XSynchronize is michael@0: // implemented by an empty request from an XSync, which has not yet been michael@0: // processed. michael@0: if (age > 1) { michael@0: // XSynchronize returns the previous "after function". If a second michael@0: // XSynchronize call returns the same function after an enable call then michael@0: // synchronization must have already been enabled. michael@0: if (XSynchronize(display, True) == XSynchronize(display, False)) { michael@0: notes.Append("; sync"); michael@0: } else { michael@0: notes.Append("; "); michael@0: notes.AppendInt(uint32_t(age)); michael@0: notes.Append(" requests ago"); michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: switch (XRE_GetProcessType()) { michael@0: case GeckoProcessType_Default: michael@0: case GeckoProcessType_Plugin: michael@0: case GeckoProcessType_Content: michael@0: CrashReporter::AppendAppNotesToCrashReport(notes); michael@0: break; michael@0: default: michael@0: ; // crash report notes not supported. michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: // The resource id is unlikely to be useful in a crash report without michael@0: // context of other ids, but add it to the debug console output. michael@0: notes.Append("; id=0x"); michael@0: notes.AppendInt(uint32_t(event->resourceid), 16); michael@0: #ifdef MOZ_X11 michael@0: // Actually, for requests where Xlib gets the reply synchronously, michael@0: // MOZ_X_SYNC=1 will not be necessary, but we don't have a table to tell us michael@0: // which requests get a synchronous reply. michael@0: if (!PR_GetEnv("MOZ_X_SYNC")) { michael@0: notes.Append("\nRe-running with MOZ_X_SYNC=1 in the environment may give a more helpful backtrace."); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: // We should not abort here if MOZ_X_SYNC is not set michael@0: // until http://bugreports.qt.nokia.com/browse/QTBUG-4042 michael@0: // not fixed, just print error value michael@0: if (!PR_GetEnv("MOZ_X_SYNC")) { michael@0: fprintf(stderr, "XError: %s\n", notes.get()); michael@0: return 0; // temporary workaround for bug 161472 michael@0: } michael@0: #endif michael@0: michael@0: NS_RUNTIMEABORT(notes.get()); michael@0: return 0; // not reached michael@0: } michael@0: } michael@0: michael@0: void michael@0: InstallX11ErrorHandler() michael@0: { michael@0: XSetErrorHandler(X11Error); michael@0: michael@0: Display *display = mozilla::DefaultXDisplay(); michael@0: NS_ASSERTION(display, "No X display"); michael@0: if (PR_GetEnv("MOZ_X_SYNC")) { michael@0: XSynchronize(display, True); michael@0: } michael@0: }