|
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 static int |
|
20 X11Error(Display *display, XErrorEvent *event) { |
|
21 // Get an indication of how long ago the request that caused the error was |
|
22 // made. |
|
23 unsigned long age = NextRequest(display) - event->serial; |
|
24 |
|
25 // Get a string to represent the request that caused the error. |
|
26 nsAutoCString message; |
|
27 if (event->request_code < 128) { |
|
28 // Core protocol request |
|
29 message.AppendInt(event->request_code); |
|
30 } else { |
|
31 // Extension request |
|
32 |
|
33 // man XSetErrorHandler says "the error handler should not call any |
|
34 // functions (directly or indirectly) on the display that will generate |
|
35 // protocol requests or that will look for input events" so we use another |
|
36 // temporary Display to request extension information. This assumes on |
|
37 // the DISPLAY environment variable has been set and matches what was used |
|
38 // to open |display|. |
|
39 Display *tmpDisplay = XOpenDisplay(nullptr); |
|
40 if (tmpDisplay) { |
|
41 int nExts; |
|
42 char** extNames = XListExtensions(tmpDisplay, &nExts); |
|
43 int first_error; |
|
44 if (extNames) { |
|
45 for (int i = 0; i < nExts; ++i) { |
|
46 int major_opcode, first_event; |
|
47 if (XQueryExtension(tmpDisplay, extNames[i], |
|
48 &major_opcode, &first_event, &first_error) |
|
49 && major_opcode == event->request_code) { |
|
50 message.Append(extNames[i]); |
|
51 message.Append('.'); |
|
52 message.AppendInt(event->minor_code); |
|
53 break; |
|
54 } |
|
55 } |
|
56 |
|
57 XFreeExtensionList(extNames); |
|
58 } |
|
59 XCloseDisplay(tmpDisplay); |
|
60 |
|
61 #if (MOZ_WIDGET_GTK == 2) |
|
62 // GDK2 calls XCloseDevice the devices that it opened on startup, but |
|
63 // the XI protocol no longer ensures that the devices will still exist. |
|
64 // If they have been removed, then a BadDevice error results. Ignore |
|
65 // this error. |
|
66 if (message.EqualsLiteral("XInputExtension.4") && |
|
67 event->error_code == first_error + 0) { |
|
68 return 0; |
|
69 } |
|
70 #endif |
|
71 } |
|
72 } |
|
73 |
|
74 char buffer[BUFSIZE]; |
|
75 if (message.IsEmpty()) { |
|
76 buffer[0] = '\0'; |
|
77 } else { |
|
78 XGetErrorDatabaseText(display, "XRequest", message.get(), "", |
|
79 buffer, sizeof(buffer)); |
|
80 } |
|
81 |
|
82 nsAutoCString notes; |
|
83 if (buffer[0]) { |
|
84 notes.Append(buffer); |
|
85 } else { |
|
86 notes.Append("Request "); |
|
87 notes.AppendInt(event->request_code); |
|
88 notes.Append('.'); |
|
89 notes.AppendInt(event->minor_code); |
|
90 } |
|
91 |
|
92 notes.Append(": "); |
|
93 |
|
94 // Get a string to describe the error. |
|
95 XGetErrorText(display, event->error_code, buffer, sizeof(buffer)); |
|
96 notes.Append(buffer); |
|
97 |
|
98 // For requests where Xlib gets the reply synchronously, |age| will be 1 |
|
99 // and the stack will include the function making the request. For |
|
100 // asynchronous requests, the current stack will often be unrelated to the |
|
101 // point of making the request, even if |age| is 1, but sometimes this may |
|
102 // help us count back to the point of the request. With XSynchronize on, |
|
103 // the stack will include the function making the request, even though |
|
104 // |age| will be 2 for asynchronous requests because XSynchronize is |
|
105 // implemented by an empty request from an XSync, which has not yet been |
|
106 // processed. |
|
107 if (age > 1) { |
|
108 // XSynchronize returns the previous "after function". If a second |
|
109 // XSynchronize call returns the same function after an enable call then |
|
110 // synchronization must have already been enabled. |
|
111 if (XSynchronize(display, True) == XSynchronize(display, False)) { |
|
112 notes.Append("; sync"); |
|
113 } else { |
|
114 notes.Append("; "); |
|
115 notes.AppendInt(uint32_t(age)); |
|
116 notes.Append(" requests ago"); |
|
117 } |
|
118 } |
|
119 |
|
120 #ifdef MOZ_CRASHREPORTER |
|
121 switch (XRE_GetProcessType()) { |
|
122 case GeckoProcessType_Default: |
|
123 case GeckoProcessType_Plugin: |
|
124 case GeckoProcessType_Content: |
|
125 CrashReporter::AppendAppNotesToCrashReport(notes); |
|
126 break; |
|
127 default: |
|
128 ; // crash report notes not supported. |
|
129 } |
|
130 #endif |
|
131 |
|
132 #ifdef DEBUG |
|
133 // The resource id is unlikely to be useful in a crash report without |
|
134 // context of other ids, but add it to the debug console output. |
|
135 notes.Append("; id=0x"); |
|
136 notes.AppendInt(uint32_t(event->resourceid), 16); |
|
137 #ifdef MOZ_X11 |
|
138 // Actually, for requests where Xlib gets the reply synchronously, |
|
139 // MOZ_X_SYNC=1 will not be necessary, but we don't have a table to tell us |
|
140 // which requests get a synchronous reply. |
|
141 if (!PR_GetEnv("MOZ_X_SYNC")) { |
|
142 notes.Append("\nRe-running with MOZ_X_SYNC=1 in the environment may give a more helpful backtrace."); |
|
143 } |
|
144 #endif |
|
145 #endif |
|
146 |
|
147 #ifdef MOZ_WIDGET_QT |
|
148 // We should not abort here if MOZ_X_SYNC is not set |
|
149 // until http://bugreports.qt.nokia.com/browse/QTBUG-4042 |
|
150 // not fixed, just print error value |
|
151 if (!PR_GetEnv("MOZ_X_SYNC")) { |
|
152 fprintf(stderr, "XError: %s\n", notes.get()); |
|
153 return 0; // temporary workaround for bug 161472 |
|
154 } |
|
155 #endif |
|
156 |
|
157 NS_RUNTIMEABORT(notes.get()); |
|
158 return 0; // not reached |
|
159 } |
|
160 } |
|
161 |
|
162 void |
|
163 InstallX11ErrorHandler() |
|
164 { |
|
165 XSetErrorHandler(X11Error); |
|
166 |
|
167 Display *display = mozilla::DefaultXDisplay(); |
|
168 NS_ASSERTION(display, "No X display"); |
|
169 if (PR_GetEnv("MOZ_X_SYNC")) { |
|
170 XSynchronize(display, True); |
|
171 } |
|
172 } |