Bug 737833 - Only do metro registration for metro builds. r=rstrong
[gecko.git] / toolkit / crashreporter / nsExceptionHandler.cpp
blob472a87270a91957499ff2f4892ba0b17d3c47c06
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 #include "mozilla/dom/CrashReporterChild.h"
7 #include "mozilla/Services.h"
8 #include "nsIObserverService.h"
9 #include "mozilla/Util.h"
11 #include "nsXULAppAPI.h"
13 #include "nsExceptionHandler.h"
14 #include "nsThreadUtils.h"
16 #if defined(XP_WIN32)
17 #ifdef WIN32_LEAN_AND_MEAN
18 #undef WIN32_LEAN_AND_MEAN
19 #endif
21 #include "nsIWindowsRegKey.h"
22 #include "client/windows/crash_generation/crash_generation_server.h"
23 #include "client/windows/handler/exception_handler.h"
24 #include <DbgHelp.h>
25 #include <string.h>
26 #include "nsDirectoryServiceUtils.h"
28 #include "nsWindowsDllInterceptor.h"
29 #elif defined(XP_MACOSX)
30 #include "client/mac/crash_generation/client_info.h"
31 #include "client/mac/crash_generation/crash_generation_server.h"
32 #include "client/mac/handler/exception_handler.h"
33 #include <string>
34 #include <Carbon/Carbon.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <crt_externs.h>
37 #include <fcntl.h>
38 #include <mach/mach.h>
39 #include <sys/types.h>
40 #include <spawn.h>
41 #include <unistd.h>
42 #include "mac_utils.h"
43 #elif defined(XP_LINUX)
44 #include "nsDirectoryServiceDefs.h"
45 #include "nsIINIParser.h"
46 #include "common/linux/linux_libc_support.h"
47 #include "common/linux/linux_syscall_support.h"
48 #include "client/linux/crash_generation/client_info.h"
49 #include "client/linux/crash_generation/crash_generation_server.h"
50 #include "client/linux/handler/exception_handler.h"
51 #include "client/linux/minidump_writer/linux_dumper.h"
52 #include "client/linux/minidump_writer/minidump_writer.h"
53 #include <fcntl.h>
54 #include <sys/types.h>
55 #include <unistd.h>
56 #elif defined(XP_SOLARIS)
57 #include "client/solaris/handler/exception_handler.h"
58 #include <fcntl.h>
59 #include <sys/types.h>
60 #include <unistd.h>
61 #else
62 #error "Not yet implemented for this platform"
63 #endif // defined(XP_WIN32)
65 #ifdef MOZ_CRASHREPORTER_INJECTOR
66 #include "InjectCrashReporter.h"
67 using mozilla::InjectCrashRunnable;
68 #endif
70 #include <stdlib.h>
71 #include <time.h>
72 #include <prenv.h>
73 #include <prio.h>
74 #include <prmem.h>
75 #include "mozilla/Mutex.h"
76 #include "nsDebug.h"
77 #include "nsCRT.h"
78 #include "nsIFile.h"
79 #include "nsIFileStreams.h"
80 #include "nsInterfaceHashtable.h"
81 #include "prprf.h"
82 #include "nsIXULAppInfo.h"
83 #include <map>
84 #include <vector>
86 #include "mozilla/mozalloc_oom.h"
87 #include "mozilla/mozPoisonWrite.h"
89 #if defined(XP_MACOSX)
90 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
91 #endif
93 #include "nsIUUIDGenerator.h"
95 using google_breakpad::CrashGenerationServer;
96 using google_breakpad::ClientInfo;
97 using namespace mozilla;
98 using mozilla::dom::CrashReporterChild;
99 using mozilla::dom::PCrashReporterChild;
101 namespace CrashReporter {
103 #ifdef XP_WIN32
104 typedef wchar_t XP_CHAR;
105 typedef std::wstring xpstring;
106 #define CONVERT_UTF16_TO_XP_CHAR(x) x
107 #define CONVERT_XP_CHAR_TO_UTF16(x) x
108 #define XP_STRLEN(x) wcslen(x)
109 #define my_strlen strlen
110 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
111 #define PATH_SEPARATOR "\\"
112 #define XP_PATH_SEPARATOR L"\\"
113 // sort of arbitrary, but MAX_PATH is kinda small
114 #define XP_PATH_MAX 4096
115 // "<reporter path>" "<minidump path>"
116 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
117 #ifdef _USE_32BIT_TIME_T
118 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
119 #else
120 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
121 #endif
122 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
123 #else
124 typedef char XP_CHAR;
125 typedef std::string xpstring;
126 #define CONVERT_UTF16_TO_XP_CHAR(x) NS_ConvertUTF16toUTF8(x)
127 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
128 #define CRASH_REPORTER_FILENAME "crashreporter"
129 #define PATH_SEPARATOR "/"
130 #define XP_PATH_SEPARATOR "/"
131 #define XP_PATH_MAX PATH_MAX
132 #ifdef XP_LINUX
133 #define XP_STRLEN(x) my_strlen(x)
134 #define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
135 #define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
136 #else
137 #define XP_STRLEN(x) strlen(x)
138 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
139 #define XP_STOA(size, buffer, base) sprintf(buffer, "%zu", size)
140 #define my_strlen strlen
141 #define sys_close close
142 #define sys_fork fork
143 #define sys_open open
144 #define sys_write write
145 #endif
146 #endif // XP_WIN32
148 static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
149 '\0'}; // .dmp
150 static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
151 'r', 'a', '\0'}; // .extra
153 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
155 static XP_CHAR* pendingDirectory;
156 static XP_CHAR* crashReporterPath;
158 // If this is false, we don't launch the crash reporter
159 static bool doReport = true;
161 // If this is true, we don't have a crash reporter
162 static bool headlessClient = false;
164 // if this is true, we pass the exception on to the OS crash reporter
165 static bool showOSCrashReporter = false;
167 // The time of the last recorded crash, as a time_t value.
168 static time_t lastCrashTime = 0;
169 // The pathname of a file to store the crash time in
170 static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
172 // A marker file to hold the path to the last dump written, which
173 // will be checked on startup.
174 static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
176 // Whether we've already looked for the marker file.
177 static bool lastRunCrashID_checked = false;
178 // The minidump ID contained in the marker file.
179 static nsString* lastRunCrashID = nullptr;
181 // these are just here for readability
182 static const char kCrashTimeParameter[] = "CrashTime=";
183 static const int kCrashTimeParameterLen = sizeof(kCrashTimeParameter)-1;
185 static const char kTimeSinceLastCrashParameter[] = "SecondsSinceLastCrash=";
186 static const int kTimeSinceLastCrashParameterLen =
187 sizeof(kTimeSinceLastCrashParameter)-1;
189 static const char kSysMemoryParameter[] = "SystemMemoryUsePercentage=";
190 static const int kSysMemoryParameterLen = sizeof(kSysMemoryParameter)-1;
192 static const char kTotalVirtualMemoryParameter[] = "TotalVirtualMemory=";
193 static const int kTotalVirtualMemoryParameterLen =
194 sizeof(kTotalVirtualMemoryParameter)-1;
196 static const char kAvailableVirtualMemoryParameter[] = "AvailableVirtualMemory=";
197 static const int kAvailableVirtualMemoryParameterLen =
198 sizeof(kAvailableVirtualMemoryParameter)-1;
200 static const char kOOMAllocationSizeParameter[] = "OOMAllocationSize=";
201 static const int kOOMAllocationSizeParameterLen =
202 sizeof(kOOMAllocationSizeParameter)-1;
204 static const char kAvailablePageFileParameter[] = "AvailablePageFile=";
205 static const int kAvailablePageFileParameterLen =
206 sizeof(kAvailablePageFileParameter)-1;
208 static const char kAvailablePhysicalMemoryParameter[] = "AvailablePhysicalMemory=";
209 static const int kAvailablePhysicalMemoryParameterLen =
210 sizeof(kAvailablePhysicalMemoryParameter)-1;
212 static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
213 static const int kIsGarbageCollectingParameterLen =
214 sizeof(kIsGarbageCollectingParameter)-1;
216 // this holds additional data sent via the API
217 static Mutex* crashReporterAPILock;
218 static Mutex* notesFieldLock;
219 static AnnotationTable* crashReporterAPIData_Hash;
220 static nsCString* crashReporterAPIData = nullptr;
221 static nsCString* notesField = nullptr;
222 static bool isGarbageCollecting;
224 // OOP crash reporting
225 static CrashGenerationServer* crashServer; // chrome process has this
227 # if defined(XP_WIN) || defined(XP_MACOSX)
228 // If crash reporting is disabled, we hand out this "null" pipe to the
229 // child process and don't attempt to connect to a parent server.
230 static const char kNullNotifyPipe[] = "-";
231 static char* childCrashNotifyPipe;
233 # elif defined(XP_LINUX)
234 static int serverSocketFd = -1;
235 static int clientSocketFd = -1;
236 static const int kMagicChildCrashReportFd = 4;
238 # endif
240 // |dumpMapLock| must protect all access to |pidToMinidump|.
241 static Mutex* dumpMapLock;
242 struct ChildProcessData : public nsUint32HashKey
244 ChildProcessData(KeyTypePointer aKey)
245 : nsUint32HashKey(aKey)
246 , sequence(0)
247 #ifdef MOZ_CRASHREPORTER_INJECTOR
248 , callback(NULL)
249 #endif
252 nsCOMPtr<nsIFile> minidump;
253 // Each crashing process is assigned an increasing sequence number to
254 // indicate which process crashed first.
255 uint32_t sequence;
256 #ifdef MOZ_CRASHREPORTER_INJECTOR
257 InjectorCrashCallback* callback;
258 #endif
261 typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
262 static ChildMinidumpMap* pidToMinidump;
263 static uint32_t crashSequence;
264 static bool OOPInitialized();
266 #ifdef MOZ_CRASHREPORTER_INJECTOR
267 static nsIThread* sInjectorThread;
269 class ReportInjectedCrash : public nsRunnable
271 public:
272 ReportInjectedCrash(uint32_t pid) : mPID(pid) { }
274 NS_IMETHOD Run();
276 private:
277 uint32_t mPID;
279 #endif // MOZ_CRASHREPORTER_INJECTOR
281 // Crashreporter annotations that we don't send along in subprocess
282 // reports
283 static const char* kSubprocessBlacklist[] = {
284 "FramePoisonBase",
285 "FramePoisonSize",
286 "StartupTime",
287 "URL"
290 // If annotations are attempted before the crash reporter is enabled,
291 // they queue up here.
292 class DelayedNote;
293 nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
295 #if defined(XP_WIN)
296 // the following are used to prevent other DLLs reverting the last chance
297 // exception handler to the windows default. Any attempt to change the
298 // unhandled exception filter or to reset it is ignored and our crash
299 // reporter is loaded instead (in case it became unloaded somehow)
300 typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
301 (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
302 static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
303 static WindowsDllInterceptor gKernel32Intercept;
304 static bool gBlockUnhandledExceptionFilter = true;
306 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
307 patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
309 if (!gBlockUnhandledExceptionFilter ||
310 lpTopLevelExceptionFilter == google_breakpad::ExceptionHandler::HandleException) {
311 // don't intercept
312 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
315 // intercept attempts to change the filter
316 return NULL;
318 #endif
320 #ifdef XP_MACOSX
321 static cpu_type_t pref_cpu_types[2] = {
322 #if defined(__i386__)
323 CPU_TYPE_X86,
324 #elif defined(__x86_64__)
325 CPU_TYPE_X86_64,
326 #elif defined(__ppc__)
327 CPU_TYPE_POWERPC,
328 #endif
329 CPU_TYPE_ANY };
331 static posix_spawnattr_t spawnattr;
332 #endif
334 #if defined(MOZ_WIDGET_ANDROID)
335 // Android builds use a custom library loader,
336 // so the embedding will provide a list of shared
337 // libraries that are mapped into anonymous mappings.
338 typedef struct {
339 std::string name;
340 std::string debug_id;
341 uintptr_t start_address;
342 size_t length;
343 size_t file_offset;
344 } mapping_info;
345 static std::vector<mapping_info> library_mappings;
346 typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
347 static MappingMap child_library_mappings;
349 void FileIDToGUID(const char* file_id, u_int8_t guid[sizeof(MDGUID)])
351 for (int i = 0; i < sizeof(MDGUID); i++) {
352 int c;
353 sscanf(file_id, "%02X", &c);
354 guid[i] = (u_int8_t)(c & 0xFF);
355 file_id += 2;
357 // GUIDs are stored in network byte order.
358 uint32_t* data1 = reinterpret_cast<uint32_t*>(guid);
359 *data1 = htonl(*data1);
360 uint16_t* data2 = reinterpret_cast<uint16_t*>(guid + 4);
361 *data2 = htons(*data2);
362 uint16_t* data3 = reinterpret_cast<uint16_t*>(guid + 6);
363 *data3 = htons(*data3);
365 #endif
367 #ifdef XP_LINUX
368 inline void
369 my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
371 my_memset(buffer, 0, buffer_length);
372 my_itos(buffer, t, my_int_len(t));
374 #endif
376 #ifdef XP_WIN
377 static void
378 CreateFileFromPath(const xpstring& path, nsIFile** file)
380 NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
382 #else
383 static void
384 CreateFileFromPath(const xpstring& path, nsIFile** file)
386 NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
388 #endif
390 static XP_CHAR*
391 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
393 int appendLen = XP_STRLEN(toAppend);
394 if (appendLen >= *size) appendLen = *size - 1;
396 memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
397 str += appendLen;
398 *str = '\0';
399 *size -= appendLen;
401 return str;
404 static size_t gOOMAllocationSize = 0;
406 void AnnotateOOMAllocationSize(size_t size)
408 gOOMAllocationSize = size;
411 bool MinidumpCallback(const XP_CHAR* dump_path,
412 const XP_CHAR* minidump_id,
413 void* context,
414 #ifdef XP_WIN32
415 EXCEPTION_POINTERS* exinfo,
416 MDRawAssertionInfo* assertion,
417 #endif
418 bool succeeded)
420 bool returnValue = showOSCrashReporter ? false : succeeded;
422 static XP_CHAR minidumpPath[XP_PATH_MAX];
423 int size = XP_PATH_MAX;
424 XP_CHAR* p = Concat(minidumpPath, dump_path, &size);
425 p = Concat(p, XP_PATH_SEPARATOR, &size);
426 p = Concat(p, minidump_id, &size);
427 Concat(p, dumpFileExtension, &size);
429 static XP_CHAR extraDataPath[XP_PATH_MAX];
430 size = XP_PATH_MAX;
431 p = Concat(extraDataPath, dump_path, &size);
432 p = Concat(p, XP_PATH_SEPARATOR, &size);
433 p = Concat(p, minidump_id, &size);
434 Concat(p, extraFileExtension, &size);
436 if (headlessClient) {
437 // Leave a marker indicating that there was a crash.
438 #if defined(XP_WIN32)
439 HANDLE hFile = CreateFile(crashMarkerFilename, GENERIC_WRITE, 0,
440 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
441 NULL);
442 if(hFile != INVALID_HANDLE_VALUE) {
443 DWORD nBytes;
444 WriteFile(hFile, minidumpPath, 2*wcslen(minidumpPath), &nBytes, NULL);
445 CloseHandle(hFile);
447 #elif defined(XP_UNIX)
448 int fd = sys_open(crashMarkerFilename,
449 O_WRONLY | O_CREAT | O_TRUNC,
450 0600);
451 if (fd != -1) {
452 ssize_t ignored = sys_write(fd, minidumpPath, my_strlen(minidumpPath));
453 (void)ignored;
454 sys_close(fd);
456 #endif
459 char oomAllocationSizeBuffer[32];
460 int oomAllocationSizeBufferLen = 0;
461 if (gOOMAllocationSize) {
462 XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
463 oomAllocationSizeBufferLen = my_strlen(oomAllocationSizeBuffer);
466 // calculate time since last crash (if possible), and store
467 // the time of this crash.
468 time_t crashTime;
469 #ifdef XP_LINUX
470 struct kernel_timeval tv;
471 sys_gettimeofday(&tv, NULL);
472 crashTime = tv.tv_sec;
473 #else
474 crashTime = time(NULL);
475 #endif
476 time_t timeSinceLastCrash = 0;
477 // stringified versions of the above
478 char crashTimeString[32];
479 int crashTimeStringLen = 0;
480 char timeSinceLastCrashString[32];
481 int timeSinceLastCrashStringLen = 0;
483 XP_TTOA(crashTime, crashTimeString, 10);
484 crashTimeStringLen = my_strlen(crashTimeString);
485 if (lastCrashTime != 0) {
486 timeSinceLastCrash = crashTime - lastCrashTime;
487 XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
488 timeSinceLastCrashStringLen = my_strlen(timeSinceLastCrashString);
490 // write crash time to file
491 if (lastCrashTimeFilename[0] != 0) {
492 #if defined(XP_WIN32)
493 HANDLE hFile = CreateFile(lastCrashTimeFilename, GENERIC_WRITE, 0,
494 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
495 NULL);
496 if(hFile != INVALID_HANDLE_VALUE) {
497 DWORD nBytes;
498 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, NULL);
499 CloseHandle(hFile);
501 #elif defined(XP_UNIX)
502 int fd = sys_open(lastCrashTimeFilename,
503 O_WRONLY | O_CREAT | O_TRUNC,
504 0600);
505 if (fd != -1) {
506 ssize_t ignored = sys_write(fd, crashTimeString, crashTimeStringLen);
507 (void)ignored;
508 sys_close(fd);
510 #endif
513 #if defined(XP_WIN32)
514 if (!crashReporterAPIData->IsEmpty()) {
515 // write out API data
516 HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
517 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
518 NULL);
519 if(hFile != INVALID_HANDLE_VALUE) {
520 DWORD nBytes;
521 WriteFile(hFile, crashReporterAPIData->get(),
522 crashReporterAPIData->Length(), &nBytes, NULL);
523 WriteFile(hFile, kCrashTimeParameter, kCrashTimeParameterLen,
524 &nBytes, NULL);
525 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, NULL);
526 WriteFile(hFile, "\n", 1, &nBytes, NULL);
527 if (timeSinceLastCrash != 0) {
528 WriteFile(hFile, kTimeSinceLastCrashParameter,
529 kTimeSinceLastCrashParameterLen, &nBytes, NULL);
530 WriteFile(hFile, timeSinceLastCrashString, timeSinceLastCrashStringLen,
531 &nBytes, NULL);
532 WriteFile(hFile, "\n", 1, &nBytes, NULL);
534 if (isGarbageCollecting) {
535 WriteFile(hFile, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen,
536 &nBytes, NULL);
537 WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, NULL);
538 WriteFile(hFile, "\n", 1, &nBytes, NULL);
541 // Try to get some information about memory.
542 MEMORYSTATUSEX statex;
543 statex.dwLength = sizeof(statex);
544 if (GlobalMemoryStatusEx(&statex)) {
545 char buffer[128];
546 int bufferLen;
548 #define WRITE_STATEX_FIELD(field, paramName, conversionFunc) \
549 WriteFile(hFile, k##paramName##Parameter, \
550 k##paramName##ParameterLen, &nBytes, NULL); \
551 conversionFunc(statex.field, buffer, 10); \
552 bufferLen = strlen(buffer); \
553 WriteFile(hFile, buffer, bufferLen, &nBytes, NULL); \
554 WriteFile(hFile, "\n", 1, &nBytes, NULL);
556 WRITE_STATEX_FIELD(dwMemoryLoad, SysMemory, ltoa);
557 WRITE_STATEX_FIELD(ullTotalVirtual, TotalVirtualMemory, _ui64toa);
558 WRITE_STATEX_FIELD(ullAvailVirtual, AvailableVirtualMemory, _ui64toa);
559 WRITE_STATEX_FIELD(ullAvailPageFile, AvailablePageFile, _ui64toa);
560 WRITE_STATEX_FIELD(ullAvailPhys, AvailablePhysicalMemory, _ui64toa);
562 #undef WRITE_STATEX_FIELD
565 if (oomAllocationSizeBufferLen) {
566 WriteFile(hFile, kOOMAllocationSizeParameter,
567 kOOMAllocationSizeParameterLen, &nBytes, NULL);
568 WriteFile(hFile, oomAllocationSizeBuffer, oomAllocationSizeBufferLen,
569 &nBytes, NULL);
570 WriteFile(hFile, "\n", 1, &nBytes, NULL);
572 CloseHandle(hFile);
576 if (!doReport) {
577 return returnValue;
580 XP_CHAR cmdLine[CMDLINE_SIZE];
581 size = CMDLINE_SIZE;
582 p = Concat(cmdLine, L"\"", &size);
583 p = Concat(p, crashReporterPath, &size);
584 p = Concat(p, L"\" \"", &size);
585 p = Concat(p, minidumpPath, &size);
586 Concat(p, L"\"", &size);
588 STARTUPINFO si;
589 PROCESS_INFORMATION pi;
591 ZeroMemory(&si, sizeof(si));
592 si.cb = sizeof(si);
593 si.dwFlags = STARTF_USESHOWWINDOW;
594 si.wShowWindow = SW_SHOWNORMAL;
595 ZeroMemory(&pi, sizeof(pi));
597 if (CreateProcess(NULL, (LPWSTR)cmdLine, NULL, NULL, FALSE, 0,
598 NULL, NULL, &si, &pi)) {
599 CloseHandle( pi.hProcess );
600 CloseHandle( pi.hThread );
602 // we're not really in a position to do anything if the CreateProcess fails
603 TerminateProcess(GetCurrentProcess(), 1);
604 #elif defined(XP_UNIX)
605 if (!crashReporterAPIData->IsEmpty()) {
606 // write out API data
607 int fd = sys_open(extraDataPath,
608 O_WRONLY | O_CREAT | O_TRUNC,
609 0666);
611 if (fd != -1) {
612 // not much we can do in case of error
613 ssize_t ignored = sys_write(fd, crashReporterAPIData->get(),
614 crashReporterAPIData->Length());
615 ignored = sys_write(fd, kCrashTimeParameter, kCrashTimeParameterLen);
616 ignored = sys_write(fd, crashTimeString, crashTimeStringLen);
617 ignored = sys_write(fd, "\n", 1);
618 if (timeSinceLastCrash != 0) {
619 ignored = sys_write(fd, kTimeSinceLastCrashParameter,
620 kTimeSinceLastCrashParameterLen);
621 ignored = sys_write(fd, timeSinceLastCrashString,
622 timeSinceLastCrashStringLen);
623 ignored = sys_write(fd, "\n", 1);
625 if (isGarbageCollecting) {
626 ignored = sys_write(fd, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen);
627 ignored = sys_write(fd, isGarbageCollecting ? "1" : "0", 1);
628 ignored = sys_write(fd, "\n", 1);
630 if (oomAllocationSizeBufferLen) {
631 sys_write(fd, kOOMAllocationSizeParameter,
632 kOOMAllocationSizeParameterLen);
633 sys_write(fd, oomAllocationSizeBuffer, oomAllocationSizeBufferLen);
634 sys_write(fd, "\n", 1);
636 sys_close(fd);
640 if (!doReport) {
641 return returnValue;
644 #ifdef XP_MACOSX
645 char* const my_argv[] = {
646 crashReporterPath,
647 minidumpPath,
648 NULL
651 char **env = NULL;
652 char ***nsEnv = _NSGetEnviron();
653 if (nsEnv)
654 env = *nsEnv;
655 int result = posix_spawnp(NULL,
656 my_argv[0],
657 NULL,
658 &spawnattr,
659 my_argv,
660 env);
662 if (result != 0)
663 return false;
665 #else // !XP_MACOSX
666 pid_t pid = sys_fork();
668 if (pid == -1)
669 return false;
670 else if (pid == 0) {
671 #if !defined(MOZ_WIDGET_ANDROID)
672 // need to clobber this, as libcurl might load NSS,
673 // and we want it to load the system NSS.
674 unsetenv("LD_LIBRARY_PATH");
675 (void) execl(crashReporterPath,
676 crashReporterPath, minidumpPath, (char*)0);
677 #else
678 // Invoke the reportCrash activity using am
679 (void) execlp("/system/bin/am",
680 "/system/bin/am",
681 "start",
682 "-a", "org.mozilla.gecko.reportCrash",
683 "-n", crashReporterPath,
684 "--es", "minidumpPath", minidumpPath,
685 (char*)0);
686 #endif
687 _exit(1);
689 #endif // XP_MACOSX
690 #endif // XP_UNIX
692 return returnValue;
695 #ifdef XP_WIN
697 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
698 * and should not be handled as crashes.
700 static bool FPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
701 MDRawAssertionInfo* assertion)
703 if (!exinfo)
704 return true;
706 PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)exinfo->ExceptionRecord;
707 switch (e->ExceptionCode) {
708 case STATUS_FLOAT_DENORMAL_OPERAND:
709 case STATUS_FLOAT_DIVIDE_BY_ZERO:
710 case STATUS_FLOAT_INEXACT_RESULT:
711 case STATUS_FLOAT_INVALID_OPERATION:
712 case STATUS_FLOAT_OVERFLOW:
713 case STATUS_FLOAT_STACK_CHECK:
714 case STATUS_FLOAT_UNDERFLOW:
715 case STATUS_FLOAT_MULTIPLE_FAULTS:
716 case STATUS_FLOAT_MULTIPLE_TRAPS:
717 return false; // Don't write minidump, continue exception search
719 return true;
721 #endif // XP_WIN
723 static bool ShouldReport()
725 // this environment variable prevents us from launching
726 // the crash reporter client
727 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
728 return !(envvar && *envvar);
731 namespace {
732 bool Filter(void* context) {
733 mozilla::DisableWritePoisoning();
734 return true;
739 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
740 bool force/*=false*/)
742 if (gExceptionHandler)
743 return NS_ERROR_ALREADY_INITIALIZED;
745 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
746 if (envvar && *envvar && !force)
747 return NS_OK;
749 #if defined(MOZ_WIDGET_GONK)
750 doReport = false;
751 headlessClient = true;
752 #elif defined(XP_WIN)
753 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
754 doReport = ShouldReport();
755 } else {
756 doReport = false;
757 headlessClient = true;
759 #else
760 // this environment variable prevents us from launching
761 // the crash reporter client
762 doReport = ShouldReport();
763 #endif
765 // allocate our strings
766 crashReporterAPIData = new nsCString();
767 NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY);
769 NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
770 crashReporterAPILock = new Mutex("crashReporterAPILock");
771 NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
772 notesFieldLock = new Mutex("notesFieldLock");
774 crashReporterAPIData_Hash =
775 new nsDataHashtable<nsCStringHashKey,nsCString>();
776 NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
778 crashReporterAPIData_Hash->Init();
780 notesField = new nsCString();
781 NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
783 if (!headlessClient) {
784 // locate crashreporter executable
785 nsCOMPtr<nsIFile> exePath;
786 nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
787 NS_ENSURE_SUCCESS(rv, rv);
789 #if defined(XP_MACOSX)
790 exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
791 exePath->Append(NS_LITERAL_STRING("Contents"));
792 exePath->Append(NS_LITERAL_STRING("MacOS"));
793 #endif
795 exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
797 #ifdef XP_WIN32
798 nsString crashReporterPath_temp;
800 exePath->GetPath(crashReporterPath_temp);
801 crashReporterPath = ToNewUnicode(crashReporterPath_temp);
802 #elif !defined(__ANDROID__)
803 nsCString crashReporterPath_temp;
805 exePath->GetNativePath(crashReporterPath_temp);
806 crashReporterPath = ToNewCString(crashReporterPath_temp);
807 #else
808 // On Android, we launch using the application package name
809 // instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
810 nsCString package(ANDROID_PACKAGE_NAME "/.CrashReporter");
811 crashReporterPath = ToNewCString(package);
812 #endif
815 // get temp path to use for minidump path
816 #if defined(XP_WIN32)
817 nsString tempPath;
819 // first figure out buffer size
820 int pathLen = GetTempPath(0, NULL);
821 if (pathLen == 0)
822 return NS_ERROR_FAILURE;
824 tempPath.SetLength(pathLen);
825 GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
826 #elif defined(XP_MACOSX)
827 nsCString tempPath;
828 FSRef fsRef;
829 OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
830 kCreateFolder, &fsRef);
831 if (err != noErr)
832 return NS_ERROR_FAILURE;
834 char path[PATH_MAX];
835 OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
836 if (status != noErr)
837 return NS_ERROR_FAILURE;
839 tempPath = path;
841 #elif defined(__ANDROID__)
842 // GeckoAppShell or Gonk's init.rc sets this in the environment
843 const char *tempenv = PR_GetEnv("TMPDIR");
844 if (!tempenv)
845 return NS_ERROR_FAILURE;
846 nsCString tempPath(tempenv);
847 #elif defined(XP_UNIX)
848 // we assume it's always /tmp on unix systems
849 nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
850 #else
851 #error "Implement this for your platform"
852 #endif
854 #ifdef XP_MACOSX
855 // Initialize spawn attributes, since this calls malloc.
856 if (posix_spawnattr_init(&spawnattr) != 0) {
857 return NS_ERROR_FAILURE;
860 // Set spawn attributes.
861 size_t attr_count = ArrayLength(pref_cpu_types);
862 size_t attr_ocount = 0;
863 if (posix_spawnattr_setbinpref_np(&spawnattr,
864 attr_count,
865 pref_cpu_types,
866 &attr_ocount) != 0 ||
867 attr_ocount != attr_count) {
868 posix_spawnattr_destroy(&spawnattr);
869 return NS_ERROR_FAILURE;
871 #endif
873 #ifdef XP_WIN32
874 MINIDUMP_TYPE minidump_type = MiniDumpNormal;
876 // Try to determine what version of dbghelp.dll we're using.
877 // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
879 DWORD version_size = GetFileVersionInfoSizeW(L"dbghelp.dll", NULL);
880 if (version_size > 0) {
881 std::vector<BYTE> buffer(version_size);
882 if (GetFileVersionInfoW(L"dbghelp.dll",
884 version_size,
885 &buffer[0])) {
886 UINT len;
887 VS_FIXEDFILEINFO* file_info;
888 VerQueryValue(&buffer[0], L"\\", (void**)&file_info, &len);
889 WORD major = HIWORD(file_info->dwFileVersionMS),
890 minor = LOWORD(file_info->dwFileVersionMS),
891 revision = HIWORD(file_info->dwFileVersionLS);
892 if (major > 6 || (major == 6 && minor > 1) ||
893 (major == 6 && minor == 1 && revision >= 7600)) {
894 minidump_type = MiniDumpWithFullMemoryInfo;
898 #endif // XP_WIN32
900 // now set the exception handler
901 gExceptionHandler = new google_breakpad::
902 ExceptionHandler(tempPath.get(),
903 #ifdef XP_WIN
904 FPEFilter,
905 #else
906 Filter,
907 #endif
908 MinidumpCallback,
909 nullptr,
910 #if defined(XP_WIN32)
911 google_breakpad::ExceptionHandler::HANDLER_ALL,
912 minidump_type,
913 (const wchar_t*) NULL,
914 NULL);
915 #else
916 true
917 #if defined(XP_MACOSX)
918 , NULL
919 #endif
921 #endif // XP_WIN32
923 if (!gExceptionHandler)
924 return NS_ERROR_OUT_OF_MEMORY;
926 #ifdef XP_WIN
927 gExceptionHandler->set_handle_debug_exceptions(true);
929 // protect the crash reporter from being unloaded
930 gKernel32Intercept.Init("kernel32.dll");
931 bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
932 reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
933 (void**) &stub_SetUnhandledExceptionFilter);
935 #ifdef DEBUG
936 if (!ok)
937 printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
938 #endif
939 #endif
941 // store application start time
942 char timeString[32];
943 time_t startupTime = time(NULL);
944 XP_TTOA(startupTime, timeString, 10);
945 AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
946 nsDependentCString(timeString));
948 #if defined(XP_MACOSX)
949 // On OS X, many testers like to see the OS crash reporting dialog
950 // since it offers immediate stack traces. We allow them to set
951 // a default to pass exceptions to the OS handler.
952 Boolean keyExistsAndHasValidFormat = false;
953 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
954 kCFPreferencesCurrentApplication,
955 &keyExistsAndHasValidFormat);
956 if (keyExistsAndHasValidFormat)
957 showOSCrashReporter = prefValue;
958 #endif
960 #if defined(MOZ_WIDGET_ANDROID)
961 for (unsigned int i = 0; i < library_mappings.size(); i++) {
962 u_int8_t guid[sizeof(MDGUID)];
963 FileIDToGUID(library_mappings[i].debug_id.c_str(), guid);
964 gExceptionHandler->AddMappingInfo(library_mappings[i].name,
965 guid,
966 library_mappings[i].start_address,
967 library_mappings[i].length,
968 library_mappings[i].file_offset);
970 #endif
972 mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
974 return NS_OK;
977 bool GetEnabled()
979 return gExceptionHandler != nullptr;
982 bool GetMinidumpPath(nsAString& aPath)
984 if (!gExceptionHandler)
985 return false;
987 aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
988 return true;
991 nsresult SetMinidumpPath(const nsAString& aPath)
993 if (!gExceptionHandler)
994 return NS_ERROR_NOT_INITIALIZED;
996 gExceptionHandler->set_dump_path(CONVERT_UTF16_TO_XP_CHAR(aPath).BeginReading());
998 return NS_OK;
1001 static nsresult
1002 WriteDataToFile(nsIFile* aFile, const nsACString& data)
1004 PRFileDesc* fd;
1005 nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 rv = NS_OK;
1009 if (PR_Write(fd, data.Data(), data.Length()) == -1) {
1010 rv = NS_ERROR_FAILURE;
1012 PR_Close(fd);
1013 return rv;
1016 static nsresult
1017 GetFileContents(nsIFile* aFile, nsACString& data)
1019 PRFileDesc* fd;
1020 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
1021 NS_ENSURE_SUCCESS(rv, rv);
1023 rv = NS_OK;
1024 int32_t filesize = PR_Available(fd);
1025 if (filesize <= 0) {
1026 rv = NS_ERROR_FILE_NOT_FOUND;
1028 else {
1029 data.SetLength(filesize);
1030 if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
1031 rv = NS_ERROR_FAILURE;
1034 PR_Close(fd);
1035 return rv;
1038 // Function typedef for initializing a piece of data that we
1039 // don't already have.
1040 typedef nsresult (*InitDataFunc)(nsACString&);
1042 // Attempt to read aFile's contents into aContents, if aFile
1043 // does not exist, create it and initialize its contents
1044 // by calling aInitFunc for the data.
1045 static nsresult
1046 GetOrInit(nsIFile* aDir, const nsACString& filename,
1047 nsACString& aContents, InitDataFunc aInitFunc)
1049 bool exists;
1051 nsCOMPtr<nsIFile> dataFile;
1052 nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
1053 NS_ENSURE_SUCCESS(rv, rv);
1055 rv = dataFile->AppendNative(filename);
1056 NS_ENSURE_SUCCESS(rv, rv);
1058 rv = dataFile->Exists(&exists);
1059 NS_ENSURE_SUCCESS(rv, rv);
1061 if (!exists) {
1062 if (aInitFunc) {
1063 // get the initial value and write it to the file
1064 rv = aInitFunc(aContents);
1065 NS_ENSURE_SUCCESS(rv, rv);
1066 rv = WriteDataToFile(dataFile, aContents);
1068 else {
1069 // didn't pass in an init func
1070 rv = NS_ERROR_FAILURE;
1073 else {
1074 // just get the file's contents
1075 rv = GetFileContents(dataFile, aContents);
1078 return rv;
1081 // Init the "install time" data. We're taking an easy way out here
1082 // and just setting this to "the time when this version was first run".
1083 static nsresult
1084 InitInstallTime(nsACString& aInstallTime)
1086 time_t t = time(NULL);
1087 char buf[16];
1088 sprintf(buf, "%ld", t);
1089 aInstallTime = buf;
1091 return NS_OK;
1094 // Annotate the crash report with a Unique User ID and time
1095 // since install. Also do some prep work for recording
1096 // time since last crash, which must be calculated at
1097 // crash time.
1098 // If any piece of data doesn't exist, initialize it first.
1099 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
1100 const nsACString& aBuildID)
1102 nsCOMPtr<nsIFile> dataDirectory;
1103 nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
1104 NS_ENSURE_SUCCESS(rv, rv);
1106 rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
1107 NS_ENSURE_SUCCESS(rv, rv);
1109 bool exists;
1110 rv = dataDirectory->Exists(&exists);
1111 NS_ENSURE_SUCCESS(rv, rv);
1113 if (!exists) {
1114 rv = dataDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
1115 NS_ENSURE_SUCCESS(rv, rv);
1118 #if defined(XP_WIN32)
1119 nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
1121 nsAutoString dataDirectoryPath;
1122 rv = dataDirectory->GetPath(dataDirectoryPath);
1123 NS_ENSURE_SUCCESS(rv, rv);
1125 dataDirEnv.Append(dataDirectoryPath);
1127 _wputenv(dataDirEnv.get());
1128 #else
1129 // Save this path in the environment for the crash reporter application.
1130 nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
1132 nsAutoCString dataDirectoryPath;
1133 rv = dataDirectory->GetNativePath(dataDirectoryPath);
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 dataDirEnv.Append(dataDirectoryPath);
1138 char* env = ToNewCString(dataDirEnv);
1139 NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
1141 PR_SetEnv(env);
1142 #endif
1144 nsAutoCString data;
1145 if(NS_SUCCEEDED(GetOrInit(dataDirectory,
1146 NS_LITERAL_CSTRING("InstallTime") + aBuildID,
1147 data, InitInstallTime)))
1148 AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
1150 // this is a little different, since we can't init it with anything,
1151 // since it's stored at crash time, and we can't annotate the
1152 // crash report with the stored value, since we really want
1153 // (now - LastCrash), so we just get a value if it exists,
1154 // and store it in a time_t value.
1155 if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
1156 data, NULL))) {
1157 lastCrashTime = (time_t)atol(data.get());
1160 // not really the best place to init this, but I have the path I need here
1161 nsCOMPtr<nsIFile> lastCrashFile;
1162 rv = dataDirectory->Clone(getter_AddRefs(lastCrashFile));
1163 NS_ENSURE_SUCCESS(rv, rv);
1165 rv = lastCrashFile->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
1166 NS_ENSURE_SUCCESS(rv, rv);
1167 memset(lastCrashTimeFilename, 0, sizeof(lastCrashTimeFilename));
1169 #if defined(XP_WIN32)
1170 nsAutoString filename;
1171 rv = lastCrashFile->GetPath(filename);
1172 NS_ENSURE_SUCCESS(rv, rv);
1174 if (filename.Length() < XP_PATH_MAX)
1175 wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
1176 #else
1177 nsAutoCString filename;
1178 rv = lastCrashFile->GetNativePath(filename);
1179 NS_ENSURE_SUCCESS(rv, rv);
1181 if (filename.Length() < XP_PATH_MAX)
1182 strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
1183 #endif
1185 if (headlessClient) {
1186 nsCOMPtr<nsIFile> markerFile;
1187 rv = dataDirectory->Clone(getter_AddRefs(markerFile));
1188 NS_ENSURE_SUCCESS(rv, rv);
1190 rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
1191 NS_ENSURE_SUCCESS(rv, rv);
1192 memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
1194 #if defined(XP_WIN32)
1195 nsAutoString markerFilename;
1196 rv = markerFile->GetPath(markerFilename);
1197 NS_ENSURE_SUCCESS(rv, rv);
1199 if (markerFilename.Length() < XP_PATH_MAX)
1200 wcsncpy(crashMarkerFilename, markerFilename.get(),
1201 markerFilename.Length());
1202 #else
1203 nsAutoCString markerFilename;
1204 rv = markerFile->GetNativePath(markerFilename);
1205 NS_ENSURE_SUCCESS(rv, rv);
1207 if (markerFilename.Length() < XP_PATH_MAX)
1208 strncpy(crashMarkerFilename, markerFilename.get(),
1209 markerFilename.Length());
1210 #endif
1213 return NS_OK;
1216 static void OOPDeinit();
1218 nsresult UnsetExceptionHandler()
1220 #ifdef XP_WIN
1221 // allow SetUnhandledExceptionFilter
1222 gBlockUnhandledExceptionFilter = false;
1223 #endif
1225 delete gExceptionHandler;
1227 // do this here in the unlikely case that we succeeded in allocating
1228 // our strings but failed to allocate gExceptionHandler.
1229 delete crashReporterAPIData_Hash;
1230 crashReporterAPIData_Hash = nullptr;
1232 delete crashReporterAPILock;
1233 crashReporterAPILock = nullptr;
1235 delete notesFieldLock;
1236 notesFieldLock = nullptr;
1238 delete crashReporterAPIData;
1239 crashReporterAPIData = nullptr;
1241 delete notesField;
1242 notesField = nullptr;
1244 delete lastRunCrashID;
1245 lastRunCrashID = nullptr;
1247 if (pendingDirectory) {
1248 NS_Free(pendingDirectory);
1249 pendingDirectory = nullptr;
1252 if (crashReporterPath) {
1253 NS_Free(crashReporterPath);
1254 crashReporterPath = nullptr;
1257 #ifdef XP_MACOSX
1258 posix_spawnattr_destroy(&spawnattr);
1259 #endif
1261 if (!gExceptionHandler)
1262 return NS_ERROR_NOT_INITIALIZED;
1264 gExceptionHandler = nullptr;
1266 OOPDeinit();
1268 return NS_OK;
1271 static void ReplaceChar(nsCString& str, const nsACString& character,
1272 const nsACString& replacement)
1274 nsCString::const_iterator start, end;
1276 str.BeginReading(start);
1277 str.EndReading(end);
1279 while (FindInReadable(character, start, end)) {
1280 int32_t pos = end.size_backward();
1281 str.Replace(pos - 1, 1, replacement);
1283 str.BeginReading(start);
1284 start.advance(pos + replacement.Length() - 1);
1285 str.EndReading(end);
1289 static bool DoFindInReadable(const nsACString& str, const nsACString& value)
1291 nsACString::const_iterator start, end;
1292 str.BeginReading(start);
1293 str.EndReading(end);
1295 return FindInReadable(value, start, end);
1298 static PLDHashOperator EnumerateEntries(const nsACString& key,
1299 nsCString entry,
1300 void* userData)
1302 crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
1303 NS_LITERAL_CSTRING("\n"));
1304 return PL_DHASH_NEXT;
1307 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
1308 #ifdef _MSC_VER
1309 #pragma optimize("", off)
1310 #endif
1311 static nsresult
1312 EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
1314 if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
1315 DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
1316 return NS_ERROR_INVALID_ARG;
1318 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
1319 return NS_ERROR_INVALID_ARG;
1321 escapedData = data;
1323 // escape backslashes
1324 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
1325 NS_LITERAL_CSTRING("\\\\"));
1326 // escape newlines
1327 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
1328 NS_LITERAL_CSTRING("\\n"));
1329 return NS_OK;
1331 #ifdef _MSC_VER
1332 #pragma optimize("", on)
1333 #endif
1335 class DelayedNote
1337 public:
1338 DelayedNote(const nsACString& aKey, const nsACString& aData)
1339 : mKey(aKey), mData(aData), mType(Annotation) {}
1341 DelayedNote(const nsACString& aData)
1342 : mData(aData), mType(AppNote) {}
1344 void Run()
1346 if (mType == Annotation) {
1347 AnnotateCrashReport(mKey, mData);
1348 } else {
1349 AppendAppNotesToCrashReport(mData);
1353 private:
1354 nsCString mKey;
1355 nsCString mData;
1356 enum AnnotationType { Annotation, AppNote } mType;
1359 static void
1360 EnqueueDelayedNote(DelayedNote* aNote)
1362 if (!gDelayedAnnotations) {
1363 gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
1365 gDelayedAnnotations->AppendElement(aNote);
1368 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
1370 if (!GetEnabled())
1371 return NS_ERROR_NOT_INITIALIZED;
1373 nsCString escapedData;
1374 nsresult rv = EscapeAnnotation(key, data, escapedData);
1375 if (NS_FAILED(rv))
1376 return rv;
1378 if (XRE_GetProcessType() != GeckoProcessType_Default) {
1379 if (!NS_IsMainThread()) {
1380 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
1381 return NS_ERROR_FAILURE;
1383 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
1384 if (!reporter) {
1385 EnqueueDelayedNote(new DelayedNote(key, data));
1386 return NS_OK;
1388 if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
1389 return NS_ERROR_FAILURE;
1390 return NS_OK;
1393 MutexAutoLock lock(*crashReporterAPILock);
1395 crashReporterAPIData_Hash->Put(key, escapedData);
1397 // now rebuild the file contents
1398 crashReporterAPIData->Truncate(0);
1399 crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
1400 crashReporterAPIData);
1402 return NS_OK;
1405 nsresult SetGarbageCollecting(bool collecting)
1407 if (!GetEnabled())
1408 return NS_ERROR_NOT_INITIALIZED;
1410 isGarbageCollecting = collecting;
1412 return NS_OK;
1415 nsresult AppendAppNotesToCrashReport(const nsACString& data)
1417 if (!GetEnabled())
1418 return NS_ERROR_NOT_INITIALIZED;
1420 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
1421 return NS_ERROR_INVALID_ARG;
1423 if (XRE_GetProcessType() != GeckoProcessType_Default) {
1424 if (!NS_IsMainThread()) {
1425 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
1426 return NS_ERROR_FAILURE;
1428 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
1429 if (!reporter) {
1430 EnqueueDelayedNote(new DelayedNote(data));
1431 return NS_OK;
1434 // Since we don't go through AnnotateCrashReport in the parent process,
1435 // we must ensure that the data is escaped and valid before the parent
1436 // sees it.
1437 nsCString escapedData;
1438 nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
1439 if (NS_FAILED(rv))
1440 return rv;
1442 if (!reporter->SendAppendAppNotes(escapedData))
1443 return NS_ERROR_FAILURE;
1444 return NS_OK;
1447 MutexAutoLock lock(*notesFieldLock);
1449 notesField->Append(data);
1450 return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
1453 // Returns true if found, false if not found.
1454 bool GetAnnotation(const nsACString& key, nsACString& data)
1456 if (!gExceptionHandler)
1457 return false;
1459 nsAutoCString entry;
1460 if (!crashReporterAPIData_Hash->Get(key, &entry))
1461 return false;
1463 data = entry;
1464 return true;
1467 nsresult RegisterAppMemory(void* ptr, size_t length)
1469 if (!GetEnabled())
1470 return NS_ERROR_NOT_INITIALIZED;
1472 #if defined(XP_LINUX) || defined(XP_WIN32)
1473 gExceptionHandler->RegisterAppMemory(ptr, length);
1474 return NS_OK;
1475 #else
1476 return NS_ERROR_NOT_IMPLEMENTED;
1477 #endif
1480 nsresult UnregisterAppMemory(void* ptr)
1482 if (!GetEnabled())
1483 return NS_ERROR_NOT_INITIALIZED;
1485 #if defined(XP_LINUX) || defined(XP_WIN32)
1486 gExceptionHandler->UnregisterAppMemory(ptr);
1487 return NS_OK;
1488 #else
1489 return NS_ERROR_NOT_IMPLEMENTED;
1490 #endif
1493 bool GetServerURL(nsACString& aServerURL)
1495 if (!gExceptionHandler)
1496 return false;
1498 return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
1501 nsresult SetServerURL(const nsACString& aServerURL)
1503 // store server URL with the API data
1504 // the client knows to handle this specially
1505 return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
1506 aServerURL);
1509 nsresult
1510 SetRestartArgs(int argc, char** argv)
1512 if (!gExceptionHandler)
1513 return NS_OK;
1515 int i;
1516 nsAutoCString envVar;
1517 char *env;
1518 char *argv0 = getenv("MOZ_APP_LAUNCHER");
1519 for (i = 0; i < argc; i++) {
1520 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
1521 envVar.AppendInt(i);
1522 envVar += "=";
1523 if (argv0 && i == 0) {
1524 // Is there a request to suppress default binary launcher?
1525 envVar += argv0;
1526 } else {
1527 envVar += argv[i];
1530 // PR_SetEnv() wants the string to be available for the lifetime
1531 // of the app, so dup it here
1532 env = ToNewCString(envVar);
1533 if (!env)
1534 return NS_ERROR_OUT_OF_MEMORY;
1536 PR_SetEnv(env);
1539 // make sure the arg list is terminated
1540 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
1541 envVar.AppendInt(i);
1542 envVar += "=";
1544 // PR_SetEnv() wants the string to be available for the lifetime
1545 // of the app, so dup it here
1546 env = ToNewCString(envVar);
1547 if (!env)
1548 return NS_ERROR_OUT_OF_MEMORY;
1550 PR_SetEnv(env);
1552 // make sure we save the info in XUL_APP_FILE for the reporter
1553 const char *appfile = PR_GetEnv("XUL_APP_FILE");
1554 if (appfile && *appfile) {
1555 envVar = "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
1556 envVar += appfile;
1557 env = ToNewCString(envVar);
1558 PR_SetEnv(env);
1561 return NS_OK;
1564 #ifdef XP_WIN32
1565 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
1567 if (!gExceptionHandler)
1568 return NS_ERROR_NOT_INITIALIZED;
1570 return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
1572 #endif
1574 #ifdef XP_MACOSX
1575 nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
1577 nsAutoCString excString;
1578 GetObjCExceptionInfo(inException, excString);
1579 AppendAppNotesToCrashReport(excString);
1580 return NS_OK;
1582 #endif
1585 * Combined code to get/set the crash reporter submission pref on
1586 * different platforms.
1588 static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
1590 nsresult rv;
1591 #if defined(XP_WIN32)
1593 * NOTE! This needs to stay in sync with the preference checking code
1594 * in toolkit/crashreporter/client/crashreporter_win.cpp
1596 nsCOMPtr<nsIXULAppInfo> appinfo =
1597 do_GetService("@mozilla.org/xre/app-info;1", &rv);
1598 NS_ENSURE_SUCCESS(rv, rv);
1600 nsAutoCString appVendor, appName;
1601 rv = appinfo->GetVendor(appVendor);
1602 NS_ENSURE_SUCCESS(rv, rv);
1603 rv = appinfo->GetName(appName);
1604 NS_ENSURE_SUCCESS(rv, rv);
1606 nsCOMPtr<nsIWindowsRegKey> regKey
1607 (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
1608 NS_ENSURE_SUCCESS(rv, rv);
1610 nsAutoCString regPath;
1612 regPath.AppendLiteral("Software\\");
1614 // We need to ensure the registry keys are created so we can properly
1615 // write values to it
1617 // Create appVendor key
1618 if(!appVendor.IsEmpty()) {
1619 regPath.Append(appVendor);
1620 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1621 NS_ConvertUTF8toUTF16(regPath),
1622 nsIWindowsRegKey::ACCESS_SET_VALUE);
1623 regPath.AppendLiteral("\\");
1626 // Create appName key
1627 regPath.Append(appName);
1628 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1629 NS_ConvertUTF8toUTF16(regPath),
1630 nsIWindowsRegKey::ACCESS_SET_VALUE);
1631 regPath.AppendLiteral("\\");
1633 // Create Crash Reporter key
1634 regPath.AppendLiteral("Crash Reporter");
1635 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1636 NS_ConvertUTF8toUTF16(regPath),
1637 nsIWindowsRegKey::ACCESS_SET_VALUE);
1639 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
1640 // and we're done.
1641 if (writePref) {
1642 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1643 NS_ConvertUTF8toUTF16(regPath),
1644 nsIWindowsRegKey::ACCESS_SET_VALUE);
1645 NS_ENSURE_SUCCESS(rv, rv);
1647 uint32_t value = *aSubmitReports ? 1 : 0;
1648 rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
1649 regKey->Close();
1650 return rv;
1653 // We're reading the pref value, so we need to first look under
1654 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
1655 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
1656 // to "true".
1657 uint32_t value;
1658 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
1659 NS_ConvertUTF8toUTF16(regPath),
1660 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
1661 if (NS_SUCCEEDED(rv)) {
1662 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
1663 regKey->Close();
1664 if (NS_SUCCEEDED(rv)) {
1665 *aSubmitReports = !!value;
1666 return NS_OK;
1670 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1671 NS_ConvertUTF8toUTF16(regPath),
1672 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
1673 if (NS_FAILED(rv)) {
1674 *aSubmitReports = true;
1675 return NS_OK;
1678 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
1679 // default to true on failure
1680 if (NS_FAILED(rv)) {
1681 value = 1;
1682 rv = NS_OK;
1684 regKey->Close();
1686 *aSubmitReports = !!value;
1687 return NS_OK;
1688 #elif defined(XP_MACOSX)
1689 rv = NS_OK;
1690 if (writePref) {
1691 CFPropertyListRef cfValue = (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
1692 ::CFPreferencesSetAppValue(CFSTR("submitReport"),
1693 cfValue,
1694 reporterClientAppID);
1695 if (!::CFPreferencesAppSynchronize(reporterClientAppID))
1696 rv = NS_ERROR_FAILURE;
1698 else {
1699 *aSubmitReports = true;
1700 Boolean keyExistsAndHasValidFormat = false;
1701 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
1702 reporterClientAppID,
1703 &keyExistsAndHasValidFormat);
1704 if (keyExistsAndHasValidFormat)
1705 *aSubmitReports = !!prefValue;
1707 return rv;
1708 #elif defined(XP_UNIX)
1710 * NOTE! This needs to stay in sync with the preference checking code
1711 * in toolkit/crashreporter/client/crashreporter_linux.cpp
1713 nsCOMPtr<nsIFile> reporterINI;
1714 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
1715 NS_ENSURE_SUCCESS(rv, rv);
1716 reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
1717 reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
1719 bool exists;
1720 rv = reporterINI->Exists(&exists);
1721 NS_ENSURE_SUCCESS(rv, rv);
1722 if (!exists) {
1723 if (!writePref) {
1724 // If reading the pref, default to true if .ini doesn't exist.
1725 *aSubmitReports = true;
1726 return NS_OK;
1728 // Create the file so the INI processor can write to it.
1729 rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
1730 NS_ENSURE_SUCCESS(rv, rv);
1733 nsCOMPtr<nsIINIParserFactory> iniFactory =
1734 do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
1735 NS_ENSURE_SUCCESS(rv, rv);
1737 nsCOMPtr<nsIINIParser> iniParser;
1738 rv = iniFactory->CreateINIParser(reporterINI,
1739 getter_AddRefs(iniParser));
1740 NS_ENSURE_SUCCESS(rv, rv);
1742 // If we're writing the pref, just set and we're done.
1743 if (writePref) {
1744 nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
1745 NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
1747 rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
1748 NS_LITERAL_CSTRING("SubmitReport"),
1749 *aSubmitReports ? NS_LITERAL_CSTRING("1") :
1750 NS_LITERAL_CSTRING("0"));
1751 NS_ENSURE_SUCCESS(rv, rv);
1752 rv = iniWriter->WriteFile(NULL, 0);
1753 return rv;
1756 nsAutoCString submitReportValue;
1757 rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
1758 NS_LITERAL_CSTRING("SubmitReport"),
1759 submitReportValue);
1761 // Default to "true" if the pref can't be found.
1762 if (NS_FAILED(rv))
1763 *aSubmitReports = true;
1764 else if (submitReportValue.EqualsASCII("0"))
1765 *aSubmitReports = false;
1766 else
1767 *aSubmitReports = true;
1769 return NS_OK;
1770 #else
1771 return NS_ERROR_NOT_IMPLEMENTED;
1772 #endif
1775 nsresult GetSubmitReports(bool* aSubmitReports)
1777 return PrefSubmitReports(aSubmitReports, false);
1780 nsresult SetSubmitReports(bool aSubmitReports)
1782 nsresult rv;
1784 nsCOMPtr<nsIObserverService> obsServ =
1785 mozilla::services::GetObserverService();
1786 if (!obsServ) {
1787 return NS_ERROR_FAILURE;
1790 rv = PrefSubmitReports(&aSubmitReports, true);
1791 if (NS_FAILED(rv)) {
1792 return rv;
1795 obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
1796 return NS_OK;
1799 static void
1800 FindPendingDir()
1802 if (pendingDirectory)
1803 return;
1805 nsCOMPtr<nsIFile> pendingDir;
1806 nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
1807 if (NS_FAILED(rv)) {
1808 NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
1810 else {
1811 pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
1812 pendingDir->Append(NS_LITERAL_STRING("pending"));
1814 #ifdef XP_WIN
1815 nsString path;
1816 pendingDir->GetPath(path);
1817 pendingDirectory = ToNewUnicode(path);
1818 #else
1819 nsCString path;
1820 pendingDir->GetNativePath(path);
1821 pendingDirectory = ToNewCString(path);
1822 #endif
1826 // The "pending" dir is Crash Reports/pending, from which minidumps
1827 // can be submitted. Because this method may be called off the main thread,
1828 // we store the pending directory as a path.
1829 static bool
1830 GetPendingDir(nsIFile** dir)
1832 MOZ_ASSERT(OOPInitialized());
1833 if (!pendingDirectory) {
1834 return false;
1837 nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
1838 if (!pending) {
1839 NS_WARNING("Can't set up pending directory during shutdown.");
1840 return false;
1842 #ifdef XP_WIN
1843 pending->InitWithPath(nsDependentString(pendingDirectory));
1844 #else
1845 pending->InitWithNativePath(nsDependentCString(pendingDirectory));
1846 #endif
1847 pending.swap(*dir);
1848 return true;
1851 // The "limbo" dir is where minidumps go to wait for something else to
1852 // use them. If we're |ShouldReport()|, then the "something else" is
1853 // a minidump submitter, and they're coming from the
1854 // Crash Reports/pending/ dir. Otherwise, we don't know what the
1855 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
1856 // limbo.
1857 static bool
1858 GetMinidumpLimboDir(nsIFile** dir)
1860 if (ShouldReport()) {
1861 return GetPendingDir(dir);
1863 else {
1864 CreateFileFromPath(gExceptionHandler->dump_path(), dir);
1865 return NULL != *dir;
1869 bool
1870 GetMinidumpForID(const nsAString& id, nsIFile** minidump)
1872 if (!GetMinidumpLimboDir(minidump))
1873 return false;
1874 (*minidump)->Append(id + NS_LITERAL_STRING(".dmp"));
1875 return true;
1878 bool
1879 GetIDFromMinidump(nsIFile* minidump, nsAString& id)
1881 if (NS_SUCCEEDED(minidump->GetLeafName(id))) {
1882 id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING(""));
1883 return true;
1885 return false;
1888 bool
1889 GetExtraFileForID(const nsAString& id, nsIFile** extraFile)
1891 if (!GetMinidumpLimboDir(extraFile))
1892 return false;
1893 (*extraFile)->Append(id + NS_LITERAL_STRING(".extra"));
1894 return true;
1897 bool
1898 GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile)
1900 nsAutoString leafName;
1901 nsresult rv = minidump->GetLeafName(leafName);
1902 if (NS_FAILED(rv))
1903 return false;
1905 nsCOMPtr<nsIFile> extraF;
1906 rv = minidump->Clone(getter_AddRefs(extraF));
1907 if (NS_FAILED(rv))
1908 return false;
1910 leafName.Replace(leafName.Length() - 3, 3,
1911 NS_LITERAL_STRING("extra"));
1912 rv = extraF->SetLeafName(leafName);
1913 if (NS_FAILED(rv))
1914 return false;
1916 *extraFile = NULL;
1917 extraF.swap(*extraFile);
1918 return true;
1921 bool
1922 AppendExtraData(const nsAString& id, const AnnotationTable& data)
1924 nsCOMPtr<nsIFile> extraFile;
1925 if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
1926 return false;
1927 return AppendExtraData(extraFile, data);
1930 //-----------------------------------------------------------------------------
1931 // Helpers for AppendExtraData()
1933 struct Blacklist {
1934 Blacklist() : mItems(NULL), mLen(0) { }
1935 Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
1937 bool Contains(const nsACString& key) const {
1938 for (int i = 0; i < mLen; ++i)
1939 if (key.EqualsASCII(mItems[i]))
1940 return true;
1941 return false;
1944 const char** mItems;
1945 const int mLen;
1948 struct EnumerateAnnotationsContext {
1949 const Blacklist& blacklist;
1950 PRFileDesc* fd;
1953 static void
1954 WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
1956 PR_Write(fd, key.BeginReading(), key.Length());
1957 PR_Write(fd, "=", 1);
1958 PR_Write(fd, value.BeginReading(), value.Length());
1959 PR_Write(fd, "\n", 1);
1962 static PLDHashOperator
1963 EnumerateAnnotations(const nsACString& key,
1964 nsCString entry,
1965 void* userData)
1967 EnumerateAnnotationsContext* ctx =
1968 static_cast<EnumerateAnnotationsContext*>(userData);
1969 const Blacklist& blacklist = ctx->blacklist;
1971 // skip entries in the blacklist
1972 if (blacklist.Contains(key))
1973 return PL_DHASH_NEXT;
1975 WriteAnnotation(ctx->fd, key, entry);
1977 return PL_DHASH_NEXT;
1980 static bool
1981 WriteExtraData(nsIFile* extraFile,
1982 const AnnotationTable& data,
1983 const Blacklist& blacklist,
1984 bool writeCrashTime=false,
1985 bool truncate=false)
1987 PRFileDesc* fd;
1988 int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
1989 nsresult rv =
1990 extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
1991 0600, &fd);
1992 if (NS_FAILED(rv))
1993 return false;
1995 EnumerateAnnotationsContext ctx = { blacklist, fd };
1996 data.EnumerateRead(EnumerateAnnotations, &ctx);
1998 if (writeCrashTime) {
1999 time_t crashTime = time(NULL);
2000 char crashTimeString[32];
2001 XP_TTOA(crashTime, crashTimeString, 10);
2003 WriteAnnotation(fd,
2004 nsDependentCString("CrashTime"),
2005 nsDependentCString(crashTimeString));
2008 PR_Close(fd);
2009 return true;
2012 bool
2013 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
2015 return WriteExtraData(extraFile, data, Blacklist());
2019 static bool
2020 WriteExtraForMinidump(nsIFile* minidump,
2021 const Blacklist& blacklist,
2022 nsIFile** extraFile)
2024 nsCOMPtr<nsIFile> extra;
2025 if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra)))
2026 return false;
2028 if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
2029 blacklist,
2030 true /*write crash time*/,
2031 true /*truncate*/))
2032 return false;
2034 *extraFile = NULL;
2035 extra.swap(*extraFile);
2037 return true;
2040 // It really only makes sense to call this function when
2041 // ShouldReport() is true.
2042 static bool
2043 MoveToPending(nsIFile* dumpFile, nsIFile* extraFile)
2045 nsCOMPtr<nsIFile> pendingDir;
2046 if (!GetPendingDir(getter_AddRefs(pendingDir)))
2047 return false;
2049 if (NS_FAILED(dumpFile->MoveTo(pendingDir, EmptyString()))) {
2050 return false;
2053 if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, EmptyString()))) {
2054 return false;
2057 return true;
2060 static void
2061 OnChildProcessDumpRequested(void* aContext,
2062 #ifdef XP_MACOSX
2063 const ClientInfo& aClientInfo,
2064 const xpstring& aFilePath
2065 #else
2066 const ClientInfo* aClientInfo,
2067 const xpstring* aFilePath
2068 #endif
2071 nsCOMPtr<nsIFile> minidump;
2072 nsCOMPtr<nsIFile> extraFile;
2074 CreateFileFromPath(
2075 #ifdef XP_MACOSX
2076 aFilePath,
2077 #else
2078 *aFilePath,
2079 #endif
2080 getter_AddRefs(minidump));
2082 #if defined(MOZ_WIDGET_ANDROID)
2083 // Do dump generation here since the CrashGenerationServer doesn't
2084 // have access to the library mappings.
2085 MappingMap::const_iterator iter =
2086 child_library_mappings.find(aClientInfo->pid_);
2087 google_breakpad::AppMemoryList a;
2088 if (iter == child_library_mappings.end()) {
2089 NS_WARNING("No library mappings found for child, can't write minidump!");
2090 return;
2093 if (!google_breakpad::WriteMinidump(aFilePath->c_str(),
2094 aClientInfo->pid_,
2095 aClientInfo->crash_context,
2096 aClientInfo->crash_context_size,
2097 iter->second,
2099 return;
2100 #endif
2102 if (!WriteExtraForMinidump(minidump,
2103 Blacklist(kSubprocessBlacklist,
2104 ArrayLength(kSubprocessBlacklist)),
2105 getter_AddRefs(extraFile)))
2106 return;
2108 if (ShouldReport())
2109 MoveToPending(minidump, extraFile);
2112 uint32_t pid =
2113 #ifdef XP_MACOSX
2114 aClientInfo.pid();
2115 #else
2116 aClientInfo->pid();
2117 #endif
2119 #ifdef MOZ_CRASHREPORTER_INJECTOR
2120 bool runCallback;
2121 #endif
2123 MutexAutoLock lock(*dumpMapLock);
2124 ChildProcessData* pd = pidToMinidump->PutEntry(pid);
2125 MOZ_ASSERT(!pd->minidump);
2126 pd->minidump = minidump;
2127 pd->sequence = ++crashSequence;
2128 #ifdef MOZ_CRASHREPORTER_INJECTOR
2129 runCallback = NULL != pd->callback;
2130 #endif
2132 #ifdef MOZ_CRASHREPORTER_INJECTOR
2133 if (runCallback)
2134 NS_DispatchToMainThread(new ReportInjectedCrash(pid));
2135 #endif
2139 static bool
2140 OOPInitialized()
2142 return pidToMinidump != NULL;
2145 static bool ChildFilter(void *context) {
2146 mozilla::DisableWritePoisoning();
2147 return true;
2150 void
2151 OOPInit()
2153 if (OOPInitialized())
2154 return;
2156 MOZ_ASSERT(NS_IsMainThread());
2158 NS_ABORT_IF_FALSE(gExceptionHandler != NULL,
2159 "attempt to initialize OOP crash reporter before in-process crashreporter!");
2161 #if defined(XP_WIN)
2162 childCrashNotifyPipe =
2163 PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
2164 static_cast<int>(::GetCurrentProcessId()));
2166 const std::wstring dumpPath = gExceptionHandler->dump_path();
2167 crashServer = new CrashGenerationServer(
2168 NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get(),
2169 NULL, // default security attributes
2170 NULL, NULL, // we don't care about process connect here
2171 OnChildProcessDumpRequested, NULL,
2172 NULL, NULL, // we don't care about process exit here
2173 true, // automatically generate dumps
2174 &dumpPath);
2176 #elif defined(XP_LINUX)
2177 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
2178 &clientSocketFd))
2179 NS_RUNTIMEABORT("can't create crash reporter socketpair()");
2181 const std::string dumpPath = gExceptionHandler->dump_path();
2182 bool generateDumps = true;
2183 #if defined(MOZ_WIDGET_ANDROID)
2184 // On Android, the callback will do dump generation, since it needs
2185 // to pass the library mappings.
2186 generateDumps = false;
2187 #endif
2188 crashServer = new CrashGenerationServer(
2189 serverSocketFd,
2190 OnChildProcessDumpRequested, NULL,
2191 NULL, NULL, // we don't care about process exit here
2192 generateDumps,
2193 &dumpPath);
2195 #elif defined(XP_MACOSX)
2196 childCrashNotifyPipe =
2197 PR_smprintf("gecko-crash-server-pipe.%i",
2198 static_cast<int>(getpid()));
2199 const std::string dumpPath = gExceptionHandler->dump_path();
2201 crashServer = new CrashGenerationServer(
2202 childCrashNotifyPipe,
2203 ChildFilter,
2204 NULL,
2205 OnChildProcessDumpRequested, NULL,
2206 NULL, NULL,
2207 true, // automatically generate dumps
2208 dumpPath);
2209 #endif
2211 if (!crashServer->Start())
2212 NS_RUNTIMEABORT("can't start crash reporter server()");
2214 pidToMinidump = new ChildMinidumpMap();
2215 pidToMinidump->Init();
2217 dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
2219 FindPendingDir();
2222 static void
2223 OOPDeinit()
2225 if (!OOPInitialized()) {
2226 NS_WARNING("OOPDeinit() without successful OOPInit()");
2227 return;
2230 #ifdef MOZ_CRASHREPORTER_INJECTOR
2231 if (sInjectorThread) {
2232 sInjectorThread->Shutdown();
2233 NS_RELEASE(sInjectorThread);
2235 #endif
2237 delete crashServer;
2238 crashServer = NULL;
2240 delete dumpMapLock;
2241 dumpMapLock = NULL;
2243 delete pidToMinidump;
2244 pidToMinidump = NULL;
2246 #if defined(XP_WIN)
2247 PR_Free(childCrashNotifyPipe);
2248 childCrashNotifyPipe = NULL;
2249 #endif
2252 #if defined(XP_WIN) || defined(XP_MACOSX)
2253 // Parent-side API for children
2254 const char*
2255 GetChildNotificationPipe()
2257 if (!GetEnabled())
2258 return kNullNotifyPipe;
2260 MOZ_ASSERT(OOPInitialized());
2262 return childCrashNotifyPipe;
2264 #endif
2266 #ifdef MOZ_CRASHREPORTER_INJECTOR
2267 void
2268 InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
2270 if (!GetEnabled())
2271 return;
2273 if (!OOPInitialized())
2274 OOPInit();
2276 if (!sInjectorThread) {
2277 if (NS_FAILED(NS_NewThread(&sInjectorThread)))
2278 return;
2282 MutexAutoLock lock(*dumpMapLock);
2283 ChildProcessData* pd = pidToMinidump->PutEntry(processID);
2284 MOZ_ASSERT(!pd->minidump && !pd->callback);
2285 pd->callback = cb;
2288 nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
2289 sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
2292 NS_IMETHODIMP
2293 ReportInjectedCrash::Run()
2295 // Crash reporting may have been disabled after this method was dispatched
2296 if (!OOPInitialized())
2297 return NS_OK;
2299 InjectorCrashCallback* cb;
2301 MutexAutoLock lock(*dumpMapLock);
2302 ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
2303 if (!pd || !pd->callback)
2304 return NS_OK;
2306 MOZ_ASSERT(pd->minidump);
2308 cb = pd->callback;
2311 cb->OnCrash(mPID);
2312 return NS_OK;
2315 void
2316 UnregisterInjectorCallback(DWORD processID)
2318 if (!OOPInitialized())
2319 return;
2321 MutexAutoLock lock(*dumpMapLock);
2322 pidToMinidump->RemoveEntry(processID);
2325 #endif // MOZ_CRASHREPORTER_INJECTOR
2327 bool
2328 CheckForLastRunCrash()
2330 if (lastRunCrashID)
2331 return true;
2333 // The exception handler callback leaves the filename of the
2334 // last minidump in a known file.
2335 nsCOMPtr<nsIFile> lastCrashFile;
2336 CreateFileFromPath(crashMarkerFilename,
2337 getter_AddRefs(lastCrashFile));
2339 bool exists;
2340 if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
2341 return false;
2344 nsAutoCString lastMinidump_contents;
2345 if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
2346 return false;
2348 lastCrashFile->Remove(false);
2350 #ifdef XP_WIN
2351 // Ugly but effective.
2352 nsDependentString lastMinidump(
2353 reinterpret_cast<const PRUnichar*>(lastMinidump_contents.get()));
2354 #else
2355 nsAutoCString lastMinidump = lastMinidump_contents;
2356 #endif
2357 nsCOMPtr<nsIFile> lastMinidumpFile;
2358 CreateFileFromPath(lastMinidump.get(),
2359 getter_AddRefs(lastMinidumpFile));
2361 if (NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
2362 return false;
2365 nsCOMPtr<nsIFile> lastExtraFile;
2366 if (!GetExtraFileForMinidump(lastMinidumpFile,
2367 getter_AddRefs(lastExtraFile))) {
2368 return false;
2371 FindPendingDir();
2373 // Move {dump,extra} to pending folder
2374 if (!MoveToPending(lastMinidumpFile, lastExtraFile)) {
2375 return false;
2378 lastRunCrashID = new nsString();
2379 return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
2382 bool
2383 GetLastRunCrashID(nsAString& id)
2385 if (!lastRunCrashID_checked) {
2386 CheckForLastRunCrash();
2387 lastRunCrashID_checked = true;
2390 if (!lastRunCrashID) {
2391 return false;
2394 id = *lastRunCrashID;
2395 return true;
2398 #if defined(XP_WIN)
2399 // Child-side API
2400 bool
2401 SetRemoteExceptionHandler(const nsACString& crashPipe)
2403 // crash reporting is disabled
2404 if (crashPipe.Equals(kNullNotifyPipe))
2405 return true;
2407 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2409 gExceptionHandler = new google_breakpad::
2410 ExceptionHandler(L"",
2411 FPEFilter,
2412 NULL, // no minidump callback
2413 NULL, // no callback context
2414 google_breakpad::ExceptionHandler::HANDLER_ALL,
2415 MiniDumpNormal,
2416 NS_ConvertASCIItoUTF16(crashPipe).BeginReading(),
2417 NULL);
2418 #ifdef XP_WIN
2419 gExceptionHandler->set_handle_debug_exceptions(true);
2420 #endif
2422 // we either do remote or nothing, no fallback to regular crash reporting
2423 return gExceptionHandler->IsOutOfProcess();
2426 //--------------------------------------------------
2427 #elif defined(XP_LINUX)
2429 // Parent-side API for children
2430 bool
2431 CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
2433 if (!GetEnabled()) {
2434 *childCrashFd = -1;
2435 *childCrashRemapFd = -1;
2436 return true;
2439 MOZ_ASSERT(OOPInitialized());
2441 *childCrashFd = clientSocketFd;
2442 *childCrashRemapFd = kMagicChildCrashReportFd;
2444 return true;
2447 // Child-side API
2448 bool
2449 SetRemoteExceptionHandler()
2451 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2453 gExceptionHandler = new google_breakpad::
2454 ExceptionHandler("",
2455 NULL, // no filter callback
2456 NULL, // no minidump callback
2457 NULL, // no callback context
2458 true, // install signal handlers
2459 kMagicChildCrashReportFd);
2461 if (gDelayedAnnotations) {
2462 for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
2463 gDelayedAnnotations->ElementAt(i)->Run();
2465 delete gDelayedAnnotations;
2468 // we either do remote or nothing, no fallback to regular crash reporting
2469 return gExceptionHandler->IsOutOfProcess();
2472 //--------------------------------------------------
2473 #elif defined(XP_MACOSX)
2474 // Child-side API
2475 bool
2476 SetRemoteExceptionHandler(const nsACString& crashPipe)
2478 // crash reporting is disabled
2479 if (crashPipe.Equals(kNullNotifyPipe))
2480 return true;
2482 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2484 gExceptionHandler = new google_breakpad::
2485 ExceptionHandler("",
2486 Filter,
2487 NULL, // no minidump callback
2488 NULL, // no callback context
2489 true, // install signal handlers
2490 crashPipe.BeginReading());
2492 // we either do remote or nothing, no fallback to regular crash reporting
2493 return gExceptionHandler->IsOutOfProcess();
2495 #endif // XP_WIN
2498 bool
2499 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
2501 if (!GetEnabled())
2502 return false;
2504 MutexAutoLock lock(*dumpMapLock);
2506 ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
2507 if (!pd)
2508 return false;
2510 NS_IF_ADDREF(*dump = pd->minidump);
2511 if (aSequence) {
2512 *aSequence = pd->sequence;
2515 pidToMinidump->RemoveEntry(childPid);
2517 return !!*dump;
2520 //-----------------------------------------------------------------------------
2521 // CreatePairedMinidumps() and helpers
2524 void
2525 RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
2526 const nsACString& name)
2528 nsCOMPtr<nsIFile> directory;
2529 childMinidump->GetParent(getter_AddRefs(directory));
2530 if (!directory)
2531 return;
2533 nsAutoCString leafName;
2534 childMinidump->GetNativeLeafName(leafName);
2536 // turn "<id>.dmp" into "<id>-<name>.dmp
2537 leafName.Insert(NS_LITERAL_CSTRING("-") + name, leafName.Length() - 4);
2539 minidump->MoveToNative(directory, leafName);
2542 static bool
2543 PairedDumpCallback(const XP_CHAR* dump_path,
2544 const XP_CHAR* minidump_id,
2545 void* context,
2546 #ifdef XP_WIN32
2547 EXCEPTION_POINTERS* /*unused*/,
2548 MDRawAssertionInfo* /*unused*/,
2549 #endif
2550 bool succeeded)
2552 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
2554 xpstring dump(dump_path);
2555 dump += XP_PATH_SEPARATOR;
2556 dump += minidump_id;
2557 dump += dumpFileExtension;
2559 CreateFileFromPath(dump, getter_AddRefs(minidump));
2560 return true;
2563 static bool
2564 PairedDumpCallbackExtra(const XP_CHAR* dump_path,
2565 const XP_CHAR* minidump_id,
2566 void* context,
2567 #ifdef XP_WIN32
2568 EXCEPTION_POINTERS* /*unused*/,
2569 MDRawAssertionInfo* /*unused*/,
2570 #endif
2571 bool succeeded)
2573 PairedDumpCallback(dump_path, minidump_id, context,
2574 #ifdef XP_WIN32
2575 nullptr, nullptr,
2576 #endif
2577 succeeded);
2579 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
2581 nsCOMPtr<nsIFile> extra;
2582 return WriteExtraForMinidump(minidump, Blacklist(), getter_AddRefs(extra));
2585 ThreadId
2586 CurrentThreadId()
2588 #if defined(XP_WIN)
2589 return ::GetCurrentThreadId();
2590 #elif defined(XP_LINUX)
2591 return sys_gettid();
2592 #elif defined(XP_MACOSX)
2593 // Just return an index, since Mach ports can't be directly serialized
2594 thread_act_port_array_t threads_for_task;
2595 mach_msg_type_number_t thread_count;
2597 if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
2598 return -1;
2600 for (unsigned int i = 0; i < thread_count; ++i) {
2601 if (threads_for_task[i] == mach_thread_self())
2602 return i;
2604 abort();
2605 #else
2606 # error "Unsupported platform"
2607 #endif
2610 bool
2611 CreatePairedMinidumps(ProcessHandle childPid,
2612 ThreadId childBlamedThread,
2613 nsIFile** childDump)
2615 if (!GetEnabled())
2616 return false;
2618 #ifdef XP_MACOSX
2619 mach_port_t childThread = MACH_PORT_NULL;
2620 thread_act_port_array_t threads_for_task;
2621 mach_msg_type_number_t thread_count;
2623 if (task_threads(childPid, &threads_for_task, &thread_count)
2624 == KERN_SUCCESS && childBlamedThread < thread_count) {
2625 childThread = threads_for_task[childBlamedThread];
2627 #else
2628 ThreadId childThread = childBlamedThread;
2629 #endif
2631 // dump the child
2632 nsCOMPtr<nsIFile> childMinidump;
2633 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
2634 childPid,
2635 childThread,
2636 gExceptionHandler->dump_path(),
2637 PairedDumpCallbackExtra,
2638 static_cast<void*>(&childMinidump)))
2639 return false;
2641 nsCOMPtr<nsIFile> childExtra;
2642 GetExtraFileForMinidump(childMinidump, getter_AddRefs(childExtra));
2644 // dump the parent
2645 nsCOMPtr<nsIFile> parentMinidump;
2646 if (!google_breakpad::ExceptionHandler::WriteMinidump(
2647 gExceptionHandler->dump_path(),
2648 true, // write exception stream
2649 PairedDumpCallback,
2650 static_cast<void*>(&parentMinidump))) {
2652 childMinidump->Remove(false);
2653 childExtra->Remove(false);
2655 return false;
2658 // success
2659 RenameAdditionalHangMinidump(parentMinidump, childMinidump,
2660 NS_LITERAL_CSTRING("browser"));
2662 if (ShouldReport()) {
2663 MoveToPending(childMinidump, childExtra);
2664 MoveToPending(parentMinidump, nullptr);
2667 childMinidump.forget(childDump);
2669 return true;
2672 bool
2673 UnsetRemoteExceptionHandler()
2675 delete gExceptionHandler;
2676 gExceptionHandler = NULL;
2677 return true;
2680 #if defined(MOZ_WIDGET_ANDROID)
2681 void AddLibraryMapping(const char* library_name,
2682 const char* file_id,
2683 uintptr_t start_address,
2684 size_t mapping_length,
2685 size_t file_offset)
2687 if (!gExceptionHandler) {
2688 mapping_info info;
2689 info.name = library_name;
2690 info.debug_id = file_id;
2691 info.start_address = start_address;
2692 info.length = mapping_length;
2693 info.file_offset = file_offset;
2694 library_mappings.push_back(info);
2696 else {
2697 u_int8_t guid[sizeof(MDGUID)];
2698 FileIDToGUID(file_id, guid);
2699 gExceptionHandler->AddMappingInfo(library_name,
2700 guid,
2701 start_address,
2702 mapping_length,
2703 file_offset);
2707 void AddLibraryMappingForChild(uint32_t childPid,
2708 const char* library_name,
2709 const char* file_id,
2710 uintptr_t start_address,
2711 size_t mapping_length,
2712 size_t file_offset)
2714 if (child_library_mappings.find(childPid) == child_library_mappings.end())
2715 child_library_mappings[childPid] = google_breakpad::MappingList();
2716 google_breakpad::MappingInfo info;
2717 info.start_addr = start_address;
2718 info.size = mapping_length;
2719 info.offset = file_offset;
2720 strcpy(info.name, library_name);
2722 std::pair<google_breakpad::MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
2723 mapping.first = info;
2724 u_int8_t guid[sizeof(MDGUID)];
2725 FileIDToGUID(file_id, guid);
2726 memcpy(mapping.second, guid, sizeof(MDGUID));
2727 child_library_mappings[childPid].push_back(mapping);
2730 void RemoveLibraryMappingsForChild(uint32_t childPid)
2732 MappingMap::iterator iter = child_library_mappings.find(childPid);
2733 if (iter != child_library_mappings.end())
2734 child_library_mappings.erase(iter);
2736 #endif
2738 } // namespace CrashReporter