/src/mozilla-central/toolkit/xre/nsX11ErrorHandler.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 40; 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 | | #include "nsX11ErrorHandler.h" |
7 | | |
8 | | #include "prenv.h" |
9 | | #include "nsXULAppAPI.h" |
10 | | #include "nsExceptionHandler.h" |
11 | | #include "nsDebug.h" |
12 | | |
13 | | #include "mozilla/X11Util.h" |
14 | | #include <X11/Xlib.h> |
15 | | |
16 | | #define BUFSIZE 2048 // What Xlib uses with XGetErrorDatabaseText |
17 | | |
18 | | extern "C" { |
19 | | int |
20 | 0 | X11Error(Display *display, XErrorEvent *event) { |
21 | 0 | // Get an indication of how long ago the request that caused the error was |
22 | 0 | // made. |
23 | 0 | unsigned long age = NextRequest(display) - event->serial; |
24 | 0 |
|
25 | 0 | // Get a string to represent the request that caused the error. |
26 | 0 | nsAutoCString message; |
27 | 0 | if (event->request_code < 128) { |
28 | 0 | // Core protocol request |
29 | 0 | message.AppendInt(event->request_code); |
30 | 0 | } else { |
31 | 0 | // Extension request |
32 | 0 |
|
33 | 0 | // man XSetErrorHandler says "the error handler should not call any |
34 | 0 | // functions (directly or indirectly) on the display that will generate |
35 | 0 | // protocol requests or that will look for input events" so we use another |
36 | 0 | // temporary Display to request extension information. This assumes on |
37 | 0 | // the DISPLAY environment variable has been set and matches what was used |
38 | 0 | // to open |display|. |
39 | 0 | Display *tmpDisplay = XOpenDisplay(nullptr); |
40 | 0 | if (tmpDisplay) { |
41 | 0 | int nExts; |
42 | 0 | char** extNames = XListExtensions(tmpDisplay, &nExts); |
43 | 0 | int first_error; |
44 | 0 | if (extNames) { |
45 | 0 | for (int i = 0; i < nExts; ++i) { |
46 | 0 | int major_opcode, first_event; |
47 | 0 | if (XQueryExtension(tmpDisplay, extNames[i], |
48 | 0 | &major_opcode, &first_event, &first_error) |
49 | 0 | && major_opcode == event->request_code) { |
50 | 0 | message.Append(extNames[i]); |
51 | 0 | message.Append('.'); |
52 | 0 | message.AppendInt(event->minor_code); |
53 | 0 | break; |
54 | 0 | } |
55 | 0 | } |
56 | 0 |
|
57 | 0 | XFreeExtensionList(extNames); |
58 | 0 | } |
59 | 0 | XCloseDisplay(tmpDisplay); |
60 | 0 | } |
61 | 0 | } |
62 | 0 |
|
63 | 0 | char buffer[BUFSIZE]; |
64 | 0 | if (message.IsEmpty()) { |
65 | 0 | buffer[0] = '\0'; |
66 | 0 | } else { |
67 | 0 | XGetErrorDatabaseText(display, "XRequest", message.get(), "", |
68 | 0 | buffer, sizeof(buffer)); |
69 | 0 | } |
70 | 0 |
|
71 | 0 | nsAutoCString notes; |
72 | 0 | if (buffer[0]) { |
73 | 0 | notes.Append(buffer); |
74 | 0 | } else { |
75 | 0 | notes.AppendLiteral("Request "); |
76 | 0 | notes.AppendInt(event->request_code); |
77 | 0 | notes.Append('.'); |
78 | 0 | notes.AppendInt(event->minor_code); |
79 | 0 | } |
80 | 0 |
|
81 | 0 | notes.AppendLiteral(": "); |
82 | 0 |
|
83 | 0 | // Get a string to describe the error. |
84 | 0 | XGetErrorText(display, event->error_code, buffer, sizeof(buffer)); |
85 | 0 | notes.Append(buffer); |
86 | 0 |
|
87 | 0 | // For requests where Xlib gets the reply synchronously, |age| will be 1 |
88 | 0 | // and the stack will include the function making the request. For |
89 | 0 | // asynchronous requests, the current stack will often be unrelated to the |
90 | 0 | // point of making the request, even if |age| is 1, but sometimes this may |
91 | 0 | // help us count back to the point of the request. With XSynchronize on, |
92 | 0 | // the stack will include the function making the request, even though |
93 | 0 | // |age| will be 2 for asynchronous requests because XSynchronize is |
94 | 0 | // implemented by an empty request from an XSync, which has not yet been |
95 | 0 | // processed. |
96 | 0 | if (age > 1) { |
97 | 0 | // XSynchronize returns the previous "after function". If a second |
98 | 0 | // XSynchronize call returns the same function after an enable call then |
99 | 0 | // synchronization must have already been enabled. |
100 | 0 | if (XSynchronize(display, True) == XSynchronize(display, False)) { |
101 | 0 | notes.AppendLiteral("; sync"); |
102 | 0 | } else { |
103 | 0 | notes.AppendLiteral("; "); |
104 | 0 | notes.AppendInt(uint32_t(age)); |
105 | 0 | notes.AppendLiteral(" requests ago"); |
106 | 0 | } |
107 | 0 | } |
108 | 0 |
|
109 | 0 | switch (XRE_GetProcessType()) { |
110 | 0 | case GeckoProcessType_Default: |
111 | 0 | case GeckoProcessType_Plugin: |
112 | 0 | case GeckoProcessType_Content: |
113 | 0 | CrashReporter::AppendAppNotesToCrashReport(notes); |
114 | 0 | break; |
115 | 0 | default: |
116 | 0 | ; // crash report notes not supported. |
117 | 0 | } |
118 | 0 |
|
119 | | #ifdef DEBUG |
120 | | // The resource id is unlikely to be useful in a crash report without |
121 | | // context of other ids, but add it to the debug console output. |
122 | | notes.AppendLiteral("; id=0x"); |
123 | | notes.AppendInt(uint32_t(event->resourceid), 16); |
124 | | #ifdef MOZ_X11 |
125 | | // Actually, for requests where Xlib gets the reply synchronously, |
126 | | // MOZ_X_SYNC=1 will not be necessary, but we don't have a table to tell us |
127 | | // which requests get a synchronous reply. |
128 | | if (!PR_GetEnv("MOZ_X_SYNC")) { |
129 | | notes.AppendLiteral("\nRe-running with MOZ_X_SYNC=1 in the environment may give a more helpful backtrace."); |
130 | | } |
131 | | #endif |
132 | | #endif |
133 | |
|
134 | 0 | MOZ_CRASH_UNSAFE_OOL(notes.get()); |
135 | 0 | } |
136 | | } |
137 | | |
138 | | void |
139 | | InstallX11ErrorHandler() |
140 | 0 | { |
141 | 0 | XSetErrorHandler(X11Error); |
142 | 0 |
|
143 | 0 | Display *display = mozilla::DefaultXDisplay(); |
144 | 0 | NS_ASSERTION(display, "No X display"); |
145 | 0 | if (PR_GetEnv("MOZ_X_SYNC")) { |
146 | 0 | XSynchronize(display, True); |
147 | 0 | } |
148 | 0 | } |