Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 "nsExceptionHandler.h"
7 #include "nsDataHashtable.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/dom/CrashReporterChild.h"
10 #include "mozilla/Services.h"
11 #include "nsIObserverService.h"
12 #include "mozilla/unused.h"
14 #include "nsThreadUtils.h"
15 #include "nsXULAppAPI.h"
17 #if defined(XP_WIN32)
18 #ifdef WIN32_LEAN_AND_MEAN
19 #undef WIN32_LEAN_AND_MEAN
20 #endif
22 #include "nsXULAppAPI.h"
23 #include "nsIXULAppInfo.h"
24 #include "nsIWindowsRegKey.h"
25 #include "client/windows/crash_generation/client_info.h"
26 #include "client/windows/crash_generation/crash_generation_server.h"
27 #include "client/windows/handler/exception_handler.h"
28 #include <dbghelp.h>
29 #include <string.h>
30 #include "nsDirectoryServiceUtils.h"
32 #include "nsWindowsDllInterceptor.h"
33 #elif defined(XP_MACOSX)
34 #include "client/mac/crash_generation/client_info.h"
35 #include "client/mac/crash_generation/crash_generation_server.h"
36 #include "client/mac/handler/exception_handler.h"
37 #include <string>
38 #include <Carbon/Carbon.h>
39 #include <CoreFoundation/CoreFoundation.h>
40 #include <crt_externs.h>
41 #include <fcntl.h>
42 #include <mach/mach.h>
43 #include <sys/types.h>
44 #include <spawn.h>
45 #include <unistd.h>
46 #include "mac_utils.h"
47 #elif defined(XP_LINUX)
48 #include "nsIINIParser.h"
49 #include "common/linux/linux_libc_support.h"
50 #include "third_party/lss/linux_syscall_support.h"
51 #include "client/linux/crash_generation/client_info.h"
52 #include "client/linux/crash_generation/crash_generation_server.h"
53 #include "client/linux/handler/exception_handler.h"
54 #include <fcntl.h>
55 #include <sys/types.h>
56 #include <unistd.h>
57 #elif defined(XP_SOLARIS)
58 #include "client/solaris/handler/exception_handler.h"
59 #include <fcntl.h>
60 #include <sys/types.h>
61 #include <unistd.h>
62 #else
63 #error "Not yet implemented for this platform"
64 #endif // defined(XP_WIN32)
66 #ifdef MOZ_CRASHREPORTER_INJECTOR
67 #include "InjectCrashReporter.h"
68 using mozilla::InjectCrashRunnable;
69 #endif
71 #include <stdlib.h>
72 #include <time.h>
73 #include <prenv.h>
74 #include <prio.h>
75 #include <prmem.h>
76 #include "mozilla/Mutex.h"
77 #include "nsDebug.h"
78 #include "nsCRT.h"
79 #include "nsIFile.h"
80 #include "prprf.h"
81 #include <map>
82 #include <vector>
84 #include "mozilla/IOInterposer.h"
85 #include "mozilla/mozalloc_oom.h"
86 #include "mozilla/WindowsDllBlocklist.h"
88 #if defined(XP_MACOSX)
89 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
90 #endif
91 #if defined(MOZ_WIDGET_ANDROID)
92 #include "common/linux/file_id.h"
93 #endif
95 using google_breakpad::CrashGenerationServer;
96 using google_breakpad::ClientInfo;
97 #ifdef XP_LINUX
98 using google_breakpad::MinidumpDescriptor;
99 #endif
100 using namespace mozilla;
101 using mozilla::dom::CrashReporterChild;
102 using mozilla::dom::PCrashReporterChild;
104 namespace CrashReporter {
106 #ifdef XP_WIN32
107 typedef wchar_t XP_CHAR;
108 typedef std::wstring xpstring;
109 #define CONVERT_XP_CHAR_TO_UTF16(x) x
110 #define XP_STRLEN(x) wcslen(x)
111 #define my_strlen strlen
112 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
113 #define PATH_SEPARATOR "\\"
114 #define XP_PATH_SEPARATOR L"\\"
115 // sort of arbitrary, but MAX_PATH is kinda small
116 #define XP_PATH_MAX 4096
117 // "<reporter path>" "<minidump path>"
118 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
119 #ifdef _USE_32BIT_TIME_T
120 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
121 #else
122 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
123 #endif
124 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
125 #else
126 typedef char XP_CHAR;
127 typedef std::string xpstring;
128 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
129 #define CRASH_REPORTER_FILENAME "crashreporter"
130 #define PATH_SEPARATOR "/"
131 #define XP_PATH_SEPARATOR "/"
132 #define XP_PATH_MAX PATH_MAX
133 #ifdef XP_LINUX
134 #define XP_STRLEN(x) my_strlen(x)
135 #define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
136 #define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
137 #else
138 #define XP_STRLEN(x) strlen(x)
139 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
140 #define XP_STOA(size, buffer, base) sprintf(buffer, "%zu", size)
141 #define my_strlen strlen
142 #define sys_close close
143 #define sys_fork fork
144 #define sys_open open
145 #define sys_write write
146 #endif
147 #endif // XP_WIN32
149 static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
150 '\0'}; // .dmp
151 static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
152 'r', 'a', '\0'}; // .extra
154 static const char kCrashMainID[] = "crash.main.1\n";
156 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
158 static XP_CHAR* pendingDirectory;
159 static XP_CHAR* crashReporterPath;
161 // Where crash events should go.
162 static XP_CHAR* eventsDirectory;
164 // If this is false, we don't launch the crash reporter
165 static bool doReport = true;
167 // If this is true, we don't have a crash reporter
168 static bool headlessClient = false;
170 // if this is true, we pass the exception on to the OS crash reporter
171 static bool showOSCrashReporter = false;
173 // The time of the last recorded crash, as a time_t value.
174 static time_t lastCrashTime = 0;
175 // The pathname of a file to store the crash time in
176 static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
178 // A marker file to hold the path to the last dump written, which
179 // will be checked on startup.
180 static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
182 // Whether we've already looked for the marker file.
183 static bool lastRunCrashID_checked = false;
184 // The minidump ID contained in the marker file.
185 static nsString* lastRunCrashID = nullptr;
187 #if defined(MOZ_WIDGET_ANDROID)
188 // on Android 4.2 and above there is a user serial number associated
189 // with the current process that gets lost when we fork so we need to
190 // explicitly pass it to am
191 static char* androidUserSerial = nullptr;
192 #endif
194 // these are just here for readability
195 static const char kCrashTimeParameter[] = "CrashTime=";
196 static const int kCrashTimeParameterLen = sizeof(kCrashTimeParameter)-1;
198 static const char kTimeSinceLastCrashParameter[] = "SecondsSinceLastCrash=";
199 static const int kTimeSinceLastCrashParameterLen =
200 sizeof(kTimeSinceLastCrashParameter)-1;
202 static const char kSysMemoryParameter[] = "SystemMemoryUsePercentage=";
203 static const int kSysMemoryParameterLen = sizeof(kSysMemoryParameter)-1;
205 static const char kTotalVirtualMemoryParameter[] = "TotalVirtualMemory=";
206 static const int kTotalVirtualMemoryParameterLen =
207 sizeof(kTotalVirtualMemoryParameter)-1;
209 static const char kAvailableVirtualMemoryParameter[] = "AvailableVirtualMemory=";
210 static const int kAvailableVirtualMemoryParameterLen =
211 sizeof(kAvailableVirtualMemoryParameter)-1;
213 static const char kOOMAllocationSizeParameter[] = "OOMAllocationSize=";
214 static const int kOOMAllocationSizeParameterLen =
215 sizeof(kOOMAllocationSizeParameter)-1;
217 static const char kTotalPageFileParameter[] = "TotalPageFile=";
218 static const int kTotalPageFileParameterLen =
219 sizeof(kTotalPageFileParameter)-1;
221 static const char kAvailablePageFileParameter[] = "AvailablePageFile=";
222 static const int kAvailablePageFileParameterLen =
223 sizeof(kAvailablePageFileParameter)-1;
225 static const char kTotalPhysicalMemoryParameter[] = "TotalPhysicalMemory=";
226 static const int kTotalPhysicalMemoryParameterLen =
227 sizeof(kTotalPhysicalMemoryParameter)-1;
229 static const char kAvailablePhysicalMemoryParameter[] = "AvailablePhysicalMemory=";
230 static const int kAvailablePhysicalMemoryParameterLen =
231 sizeof(kAvailablePhysicalMemoryParameter)-1;
233 static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
234 static const int kIsGarbageCollectingParameterLen =
235 sizeof(kIsGarbageCollectingParameter)-1;
237 static const char kEventLoopNestingLevelParameter[] = "EventLoopNestingLevel=";
238 static const int kEventLoopNestingLevelParameterLen =
239 sizeof(kEventLoopNestingLevelParameter)-1;
241 #ifdef XP_WIN
242 static const char kBreakpadReserveAddressParameter[] = "BreakpadReserveAddress=";
243 static const int kBreakpadReserveAddressParameterLen =
244 sizeof(kBreakpadReserveAddressParameter)-1;
246 static const char kBreakpadReserveSizeParameter[] = "BreakpadReserveSize=";
247 static const int kBreakpadReserveSizeParameterLen =
248 sizeof(kBreakpadReserveSizeParameter)-1;
249 #endif
251 // this holds additional data sent via the API
252 static Mutex* crashReporterAPILock;
253 static Mutex* notesFieldLock;
254 static AnnotationTable* crashReporterAPIData_Hash;
255 static nsCString* crashReporterAPIData = nullptr;
256 static nsCString* notesField = nullptr;
257 static bool isGarbageCollecting;
258 static uint32_t eventloopNestingLevel = 0;
260 // Avoid a race during application termination.
261 static Mutex* dumpSafetyLock;
262 static bool isSafeToDump = false;
264 // OOP crash reporting
265 static CrashGenerationServer* crashServer; // chrome process has this
267 # if defined(XP_WIN) || defined(XP_MACOSX)
268 // If crash reporting is disabled, we hand out this "null" pipe to the
269 // child process and don't attempt to connect to a parent server.
270 static const char kNullNotifyPipe[] = "-";
271 static char* childCrashNotifyPipe;
273 # elif defined(XP_LINUX)
274 static int serverSocketFd = -1;
275 static int clientSocketFd = -1;
276 static const int kMagicChildCrashReportFd = 4;
278 # endif
280 // |dumpMapLock| must protect all access to |pidToMinidump|.
281 static Mutex* dumpMapLock;
282 struct ChildProcessData : public nsUint32HashKey
283 {
284 ChildProcessData(KeyTypePointer aKey)
285 : nsUint32HashKey(aKey)
286 , sequence(0)
287 #ifdef MOZ_CRASHREPORTER_INJECTOR
288 , callback(nullptr)
289 #endif
290 { }
292 nsCOMPtr<nsIFile> minidump;
293 // Each crashing process is assigned an increasing sequence number to
294 // indicate which process crashed first.
295 uint32_t sequence;
296 #ifdef MOZ_CRASHREPORTER_INJECTOR
297 InjectorCrashCallback* callback;
298 #endif
299 };
301 typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
302 static ChildMinidumpMap* pidToMinidump;
303 static uint32_t crashSequence;
304 static bool OOPInitialized();
306 #ifdef MOZ_CRASHREPORTER_INJECTOR
307 static nsIThread* sInjectorThread;
309 class ReportInjectedCrash : public nsRunnable
310 {
311 public:
312 ReportInjectedCrash(uint32_t pid) : mPID(pid) { }
314 NS_IMETHOD Run();
316 private:
317 uint32_t mPID;
318 };
319 #endif // MOZ_CRASHREPORTER_INJECTOR
321 // Crashreporter annotations that we don't send along in subprocess
322 // reports
323 static const char* kSubprocessBlacklist[] = {
324 "FramePoisonBase",
325 "FramePoisonSize",
326 "StartupTime",
327 "URL"
328 };
330 // If annotations are attempted before the crash reporter is enabled,
331 // they queue up here.
332 class DelayedNote;
333 nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
335 #if defined(XP_WIN)
336 // the following are used to prevent other DLLs reverting the last chance
337 // exception handler to the windows default. Any attempt to change the
338 // unhandled exception filter or to reset it is ignored and our crash
339 // reporter is loaded instead (in case it became unloaded somehow)
340 typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
341 (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
342 static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
343 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = nullptr;
344 static WindowsDllInterceptor gKernel32Intercept;
345 static bool gBlockUnhandledExceptionFilter = true;
347 static void NotePreviousUnhandledExceptionFilter()
348 {
349 // Set a dummy value to get the previous filter, then restore
350 previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(nullptr);
351 SetUnhandledExceptionFilter(previousUnhandledExceptionFilter);
352 }
354 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
355 patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
356 {
357 if (!gBlockUnhandledExceptionFilter) {
358 // don't intercept
359 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
360 }
362 if (lpTopLevelExceptionFilter == previousUnhandledExceptionFilter) {
363 // OK to swap back and forth between the previous filter
364 previousUnhandledExceptionFilter =
365 stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
366 return previousUnhandledExceptionFilter;
367 }
369 // intercept attempts to change the filter
370 return nullptr;
371 }
373 /**
374 * Reserve some VM space. In the event that we crash because VM space is
375 * being leaked without leaking memory, freeing this space before taking
376 * the minidump will allow us to collect a minidump.
377 *
378 * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
379 * allocations.
380 */
381 static const SIZE_T kReserveSize = 0x2800000; // 40 MB
382 static void* gBreakpadReservedVM;
383 #endif
385 #ifdef XP_MACOSX
386 static cpu_type_t pref_cpu_types[2] = {
387 #if defined(__i386__)
388 CPU_TYPE_X86,
389 #elif defined(__x86_64__)
390 CPU_TYPE_X86_64,
391 #elif defined(__ppc__)
392 CPU_TYPE_POWERPC,
393 #endif
394 CPU_TYPE_ANY };
396 static posix_spawnattr_t spawnattr;
397 #endif
399 #if defined(MOZ_WIDGET_ANDROID)
400 // Android builds use a custom library loader,
401 // so the embedding will provide a list of shared
402 // libraries that are mapped into anonymous mappings.
403 typedef struct {
404 std::string name;
405 uintptr_t start_address;
406 size_t length;
407 size_t file_offset;
408 } mapping_info;
409 static std::vector<mapping_info> library_mappings;
410 typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
411 #endif
413 #ifdef XP_LINUX
414 inline void
415 my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
416 {
417 my_memset(buffer, 0, buffer_length);
418 my_uitos(buffer, t, my_uint_len(t));
419 }
420 #endif
422 #ifdef XP_WIN
423 static void
424 CreateFileFromPath(const xpstring& path, nsIFile** file)
425 {
426 NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
427 }
428 #else
429 static void
430 CreateFileFromPath(const xpstring& path, nsIFile** file)
431 {
432 NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
433 }
434 #endif
436 static XP_CHAR*
437 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
438 {
439 int appendLen = XP_STRLEN(toAppend);
440 if (appendLen >= *size) appendLen = *size - 1;
442 memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
443 str += appendLen;
444 *str = '\0';
445 *size -= appendLen;
447 return str;
448 }
450 static size_t gOOMAllocationSize = 0;
452 void AnnotateOOMAllocationSize(size_t size)
453 {
454 gOOMAllocationSize = size;
455 }
457 bool MinidumpCallback(
458 #ifdef XP_LINUX
459 const MinidumpDescriptor& descriptor,
460 #else
461 const XP_CHAR* dump_path,
462 const XP_CHAR* minidump_id,
463 #endif
464 void* context,
465 #ifdef XP_WIN32
466 EXCEPTION_POINTERS* exinfo,
467 MDRawAssertionInfo* assertion,
468 #endif
469 bool succeeded)
470 {
471 bool returnValue = showOSCrashReporter ? false : succeeded;
473 static XP_CHAR minidumpPath[XP_PATH_MAX];
474 int size = XP_PATH_MAX;
475 XP_CHAR* p;
476 #ifndef XP_LINUX
477 p = Concat(minidumpPath, dump_path, &size);
478 p = Concat(p, XP_PATH_SEPARATOR, &size);
479 p = Concat(p, minidump_id, &size);
480 Concat(p, dumpFileExtension, &size);
481 #else
482 Concat(minidumpPath, descriptor.path(), &size);
483 #endif
485 static XP_CHAR extraDataPath[XP_PATH_MAX];
486 size = XP_PATH_MAX;
487 #ifndef XP_LINUX
488 p = Concat(extraDataPath, dump_path, &size);
489 p = Concat(p, XP_PATH_SEPARATOR, &size);
490 p = Concat(p, minidump_id, &size);
491 #else
492 p = Concat(extraDataPath, descriptor.path(), &size);
493 // Skip back past the .dmp extension.
494 p -= 4;
495 #endif
496 Concat(p, extraFileExtension, &size);
498 if (headlessClient) {
499 // Leave a marker indicating that there was a crash.
500 #if defined(XP_WIN32)
501 HANDLE hFile = CreateFile(crashMarkerFilename, GENERIC_WRITE, 0,
502 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
503 nullptr);
504 if(hFile != INVALID_HANDLE_VALUE) {
505 DWORD nBytes;
506 WriteFile(hFile, minidumpPath, 2*wcslen(minidumpPath), &nBytes, nullptr);
507 CloseHandle(hFile);
508 }
509 #elif defined(XP_UNIX)
510 int fd = sys_open(crashMarkerFilename,
511 O_WRONLY | O_CREAT | O_TRUNC,
512 0600);
513 if (fd != -1) {
514 unused << sys_write(fd, minidumpPath, my_strlen(minidumpPath));
515 sys_close(fd);
516 }
517 #endif
518 }
520 char oomAllocationSizeBuffer[32];
521 int oomAllocationSizeBufferLen = 0;
522 if (gOOMAllocationSize) {
523 XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
524 oomAllocationSizeBufferLen = my_strlen(oomAllocationSizeBuffer);
525 }
527 // calculate time since last crash (if possible), and store
528 // the time of this crash.
529 time_t crashTime;
530 #ifdef XP_LINUX
531 struct kernel_timeval tv;
532 sys_gettimeofday(&tv, nullptr);
533 crashTime = tv.tv_sec;
534 #else
535 crashTime = time(nullptr);
536 #endif
537 time_t timeSinceLastCrash = 0;
538 // stringified versions of the above
539 char crashTimeString[32];
540 int crashTimeStringLen = 0;
541 char timeSinceLastCrashString[32];
542 int timeSinceLastCrashStringLen = 0;
544 XP_TTOA(crashTime, crashTimeString, 10);
545 crashTimeStringLen = my_strlen(crashTimeString);
546 if (lastCrashTime != 0) {
547 timeSinceLastCrash = crashTime - lastCrashTime;
548 XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
549 timeSinceLastCrashStringLen = my_strlen(timeSinceLastCrashString);
550 }
551 // write crash time to file
552 if (lastCrashTimeFilename[0] != 0) {
553 #if defined(XP_WIN32)
554 HANDLE hFile = CreateFile(lastCrashTimeFilename, GENERIC_WRITE, 0,
555 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
556 nullptr);
557 if(hFile != INVALID_HANDLE_VALUE) {
558 DWORD nBytes;
559 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
560 CloseHandle(hFile);
561 }
562 #elif defined(XP_UNIX)
563 int fd = sys_open(lastCrashTimeFilename,
564 O_WRONLY | O_CREAT | O_TRUNC,
565 0600);
566 if (fd != -1) {
567 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
568 sys_close(fd);
569 }
570 #endif
571 }
573 // Write crash event file.
575 // Minidump IDs are UUIDs (36) + NULL.
576 static char id_ascii[37];
577 #ifdef XP_LINUX
578 const char * index = strrchr(descriptor.path(), '/');
579 MOZ_ASSERT(index);
580 MOZ_ASSERT(strlen(index) == 1 + 36 + 4); // "/" + UUID + ".dmp"
581 for (uint32_t i = 0; i < 36; i++) {
582 id_ascii[i] = *(index + 1 + i);
583 }
584 #else
585 MOZ_ASSERT(XP_STRLEN(minidump_id) == 36);
586 for (uint32_t i = 0; i < 36; i++) {
587 id_ascii[i] = *((char *)(minidump_id + i));
588 }
589 #endif
591 if (eventsDirectory) {
592 static XP_CHAR crashEventPath[XP_PATH_MAX];
593 int size = XP_PATH_MAX;
594 XP_CHAR* p;
595 p = Concat(crashEventPath, eventsDirectory, &size);
596 p = Concat(p, XP_PATH_SEPARATOR, &size);
597 #ifdef XP_LINUX
598 p = Concat(p, id_ascii, &size);
599 #else
600 p = Concat(p, minidump_id, &size);
601 #endif
603 #if defined(XP_WIN32)
604 HANDLE hFile = CreateFile(crashEventPath, GENERIC_WRITE, 0,
605 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
606 nullptr);
607 if (hFile != INVALID_HANDLE_VALUE) {
608 DWORD nBytes;
609 WriteFile(hFile, kCrashMainID, sizeof(kCrashMainID) - 1, &nBytes,
610 nullptr);
611 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
612 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
613 WriteFile(hFile, id_ascii, strlen(id_ascii), &nBytes, nullptr);
614 CloseHandle(hFile);
615 }
616 #elif defined(XP_UNIX)
617 int fd = sys_open(crashEventPath,
618 O_WRONLY | O_CREAT | O_TRUNC,
619 0600);
620 if (fd != -1) {
621 unused << sys_write(fd, kCrashMainID, sizeof(kCrashMainID) - 1);
622 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
623 unused << sys_write(fd, "\n", 1);
624 unused << sys_write(fd, id_ascii, strlen(id_ascii));
625 sys_close(fd);
626 }
627 #endif
628 }
630 #if defined(XP_WIN32)
631 if (!crashReporterAPIData->IsEmpty()) {
632 // write out API data
633 HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
634 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
635 nullptr);
636 if(hFile != INVALID_HANDLE_VALUE) {
637 DWORD nBytes;
638 WriteFile(hFile, crashReporterAPIData->get(),
639 crashReporterAPIData->Length(), &nBytes, nullptr);
640 WriteFile(hFile, kCrashTimeParameter, kCrashTimeParameterLen,
641 &nBytes, nullptr);
642 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
643 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
644 if (timeSinceLastCrash != 0) {
645 WriteFile(hFile, kTimeSinceLastCrashParameter,
646 kTimeSinceLastCrashParameterLen, &nBytes, nullptr);
647 WriteFile(hFile, timeSinceLastCrashString, timeSinceLastCrashStringLen,
648 &nBytes, nullptr);
649 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
650 }
651 if (isGarbageCollecting) {
652 WriteFile(hFile, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen,
653 &nBytes, nullptr);
654 WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, nullptr);
655 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
656 }
658 char buffer[128];
659 int bufferLen;
661 if (eventloopNestingLevel > 0) {
662 WriteFile(hFile, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen,
663 &nBytes, nullptr);
664 _ultoa(eventloopNestingLevel, buffer, 10);
665 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
666 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
667 }
669 if (gBreakpadReservedVM) {
670 WriteFile(hFile, kBreakpadReserveAddressParameter, kBreakpadReserveAddressParameterLen, &nBytes, nullptr);
671 _ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
672 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
673 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
674 WriteFile(hFile, kBreakpadReserveSizeParameter, kBreakpadReserveSizeParameterLen, &nBytes, nullptr);
675 _ui64toa(kReserveSize, buffer, 10);
676 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
677 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
678 }
680 #ifdef HAS_DLL_BLOCKLIST
681 DllBlocklist_WriteNotes(hFile);
682 #endif
684 // Try to get some information about memory.
685 MEMORYSTATUSEX statex;
686 statex.dwLength = sizeof(statex);
687 if (GlobalMemoryStatusEx(&statex)) {
689 #define WRITE_STATEX_FIELD(field, paramName, conversionFunc) \
690 WriteFile(hFile, k##paramName##Parameter, \
691 k##paramName##ParameterLen, &nBytes, nullptr); \
692 conversionFunc(statex.field, buffer, 10); \
693 bufferLen = strlen(buffer); \
694 WriteFile(hFile, buffer, bufferLen, &nBytes, nullptr); \
695 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
697 WRITE_STATEX_FIELD(dwMemoryLoad, SysMemory, ltoa);
698 WRITE_STATEX_FIELD(ullTotalVirtual, TotalVirtualMemory, _ui64toa);
699 WRITE_STATEX_FIELD(ullAvailVirtual, AvailableVirtualMemory, _ui64toa);
700 WRITE_STATEX_FIELD(ullTotalPageFile, TotalPageFile, _ui64toa);
701 WRITE_STATEX_FIELD(ullAvailPageFile, AvailablePageFile, _ui64toa);
702 WRITE_STATEX_FIELD(ullTotalPhys, TotalPhysicalMemory, _ui64toa);
703 WRITE_STATEX_FIELD(ullAvailPhys, AvailablePhysicalMemory, _ui64toa);
705 #undef WRITE_STATEX_FIELD
706 }
708 if (oomAllocationSizeBufferLen) {
709 WriteFile(hFile, kOOMAllocationSizeParameter,
710 kOOMAllocationSizeParameterLen, &nBytes, nullptr);
711 WriteFile(hFile, oomAllocationSizeBuffer, oomAllocationSizeBufferLen,
712 &nBytes, nullptr);
713 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
714 }
715 CloseHandle(hFile);
716 }
717 }
719 if (!doReport) {
720 return returnValue;
721 }
723 XP_CHAR cmdLine[CMDLINE_SIZE];
724 size = CMDLINE_SIZE;
725 p = Concat(cmdLine, L"\"", &size);
726 p = Concat(p, crashReporterPath, &size);
727 p = Concat(p, L"\" \"", &size);
728 p = Concat(p, minidumpPath, &size);
729 Concat(p, L"\"", &size);
731 STARTUPINFO si;
732 PROCESS_INFORMATION pi;
734 ZeroMemory(&si, sizeof(si));
735 si.cb = sizeof(si);
736 si.dwFlags = STARTF_USESHOWWINDOW;
737 si.wShowWindow = SW_SHOWNORMAL;
738 ZeroMemory(&pi, sizeof(pi));
740 if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
741 nullptr, nullptr, &si, &pi)) {
742 CloseHandle( pi.hProcess );
743 CloseHandle( pi.hThread );
744 }
745 // we're not really in a position to do anything if the CreateProcess fails
746 TerminateProcess(GetCurrentProcess(), 1);
747 #elif defined(XP_UNIX)
748 if (!crashReporterAPIData->IsEmpty()) {
749 // write out API data
750 int fd = sys_open(extraDataPath,
751 O_WRONLY | O_CREAT | O_TRUNC,
752 0666);
754 if (fd != -1) {
755 // not much we can do in case of error
756 unused << sys_write(fd, crashReporterAPIData->get(),
757 crashReporterAPIData->Length());
758 unused << sys_write(fd, kCrashTimeParameter, kCrashTimeParameterLen);
759 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
760 unused << sys_write(fd, "\n", 1);
761 if (timeSinceLastCrash != 0) {
762 unused << sys_write(fd, kTimeSinceLastCrashParameter,
763 kTimeSinceLastCrashParameterLen);
764 unused << sys_write(fd, timeSinceLastCrashString,
765 timeSinceLastCrashStringLen);
766 unused << sys_write(fd, "\n", 1);
767 }
768 if (isGarbageCollecting) {
769 unused << sys_write(fd, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen);
770 unused << sys_write(fd, isGarbageCollecting ? "1" : "0", 1);
771 unused << sys_write(fd, "\n", 1);
772 }
773 if (eventloopNestingLevel > 0) {
774 unused << sys_write(fd, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen);
775 char buffer[16];
776 XP_TTOA(eventloopNestingLevel, buffer, 10);
777 unused << sys_write(fd, buffer, my_strlen(buffer));
778 unused << sys_write(fd, "\n", 1);
779 }
780 if (oomAllocationSizeBufferLen) {
781 unused << sys_write(fd, kOOMAllocationSizeParameter,
782 kOOMAllocationSizeParameterLen);
783 unused << sys_write(fd, oomAllocationSizeBuffer,
784 oomAllocationSizeBufferLen);
785 unused << sys_write(fd, "\n", 1);
786 }
787 sys_close(fd);
788 }
789 }
791 if (!doReport) {
792 return returnValue;
793 }
795 #ifdef XP_MACOSX
796 char* const my_argv[] = {
797 crashReporterPath,
798 minidumpPath,
799 nullptr
800 };
802 char **env = nullptr;
803 char ***nsEnv = _NSGetEnviron();
804 if (nsEnv)
805 env = *nsEnv;
806 int result = posix_spawnp(nullptr,
807 my_argv[0],
808 nullptr,
809 &spawnattr,
810 my_argv,
811 env);
813 if (result != 0)
814 return false;
816 #else // !XP_MACOSX
817 pid_t pid = sys_fork();
819 if (pid == -1)
820 return false;
821 else if (pid == 0) {
822 #if !defined(MOZ_WIDGET_ANDROID)
823 // need to clobber this, as libcurl might load NSS,
824 // and we want it to load the system NSS.
825 unsetenv("LD_LIBRARY_PATH");
826 unused << execl(crashReporterPath,
827 crashReporterPath, minidumpPath, (char*)0);
828 #else
829 // Invoke the reportCrash activity using am
830 if (androidUserSerial) {
831 unused << execlp("/system/bin/am",
832 "/system/bin/am",
833 "start",
834 "--user", androidUserSerial,
835 "-a", "org.mozilla.gecko.reportCrash",
836 "-n", crashReporterPath,
837 "--es", "minidumpPath", minidumpPath,
838 (char*)0);
839 } else {
840 unused << execlp("/system/bin/am",
841 "/system/bin/am",
842 "start",
843 "-a", "org.mozilla.gecko.reportCrash",
844 "-n", crashReporterPath,
845 "--es", "minidumpPath", minidumpPath,
846 (char*)0);
847 }
848 #endif
849 _exit(1);
850 }
851 #endif // XP_MACOSX
852 #endif // XP_UNIX
854 return returnValue;
855 }
857 #ifdef XP_WIN
858 static void
859 ReserveBreakpadVM()
860 {
861 if (!gBreakpadReservedVM) {
862 gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
863 PAGE_NOACCESS);
864 }
865 }
867 static void
868 FreeBreakpadVM()
869 {
870 if (gBreakpadReservedVM) {
871 VirtualFree(gBreakpadReservedVM, 0, MEM_RELEASE);
872 }
873 }
875 /**
876 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
877 * and should not be handled as crashes.
878 *
879 * Also calls FreeBreakpadVM if appropriate.
880 */
881 static bool FPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
882 MDRawAssertionInfo* assertion)
883 {
884 if (!exinfo) {
885 mozilla::IOInterposer::Disable();
886 FreeBreakpadVM();
887 return true;
888 }
890 PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)exinfo->ExceptionRecord;
891 switch (e->ExceptionCode) {
892 case STATUS_FLOAT_DENORMAL_OPERAND:
893 case STATUS_FLOAT_DIVIDE_BY_ZERO:
894 case STATUS_FLOAT_INEXACT_RESULT:
895 case STATUS_FLOAT_INVALID_OPERATION:
896 case STATUS_FLOAT_OVERFLOW:
897 case STATUS_FLOAT_STACK_CHECK:
898 case STATUS_FLOAT_UNDERFLOW:
899 case STATUS_FLOAT_MULTIPLE_FAULTS:
900 case STATUS_FLOAT_MULTIPLE_TRAPS:
901 return false; // Don't write minidump, continue exception search
902 }
903 mozilla::IOInterposer::Disable();
904 FreeBreakpadVM();
905 return true;
906 }
907 #endif // XP_WIN
909 static bool ShouldReport()
910 {
911 // this environment variable prevents us from launching
912 // the crash reporter client
913 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
914 if (envvar && *envvar) {
915 return false;
916 }
918 envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
919 if (envvar && *envvar) {
920 return false;
921 }
923 return true;
924 }
926 namespace {
927 bool Filter(void* context) {
928 mozilla::IOInterposer::Disable();
929 return true;
930 }
931 }
934 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
935 bool force/*=false*/)
936 {
937 if (gExceptionHandler)
938 return NS_ERROR_ALREADY_INITIALIZED;
940 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
941 // In non-debug builds, enable the crash reporter by default, and allow
942 // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
943 // Also enable it by default in debug gonk builds as it is difficult to
944 // set environment on startup.
945 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
946 if (envvar && *envvar && !force)
947 return NS_OK;
948 #else
949 // In debug builds, disable the crash reporter by default, and allow to
950 // enable it with the MOZ_CRASHREPORTER environment variable.
951 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER");
952 if ((!envvar || !*envvar) && !force)
953 return NS_OK;
954 #endif
956 #if defined(MOZ_WIDGET_GONK)
957 doReport = false;
958 headlessClient = true;
959 #elif defined(XP_WIN)
960 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
961 doReport = ShouldReport();
962 } else {
963 doReport = false;
964 headlessClient = true;
965 }
966 #else
967 // this environment variable prevents us from launching
968 // the crash reporter client
969 doReport = ShouldReport();
970 #endif
972 // allocate our strings
973 crashReporterAPIData = new nsCString();
974 NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY);
976 NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
977 crashReporterAPILock = new Mutex("crashReporterAPILock");
978 NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
979 notesFieldLock = new Mutex("notesFieldLock");
981 crashReporterAPIData_Hash =
982 new nsDataHashtable<nsCStringHashKey,nsCString>();
983 NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
985 notesField = new nsCString();
986 NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
988 if (!headlessClient) {
989 // locate crashreporter executable
990 nsCOMPtr<nsIFile> exePath;
991 nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
992 NS_ENSURE_SUCCESS(rv, rv);
994 #if defined(XP_MACOSX)
995 exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
996 exePath->Append(NS_LITERAL_STRING("Contents"));
997 exePath->Append(NS_LITERAL_STRING("MacOS"));
998 #endif
1000 exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
1002 #ifdef XP_WIN32
1003 nsString crashReporterPath_temp;
1005 exePath->GetPath(crashReporterPath_temp);
1006 crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
1007 #elif !defined(__ANDROID__)
1008 nsCString crashReporterPath_temp;
1010 exePath->GetNativePath(crashReporterPath_temp);
1011 crashReporterPath = ToNewCString(crashReporterPath_temp);
1012 #else
1013 // On Android, we launch using the application package name
1014 // instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
1015 nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
1016 crashReporterPath = ToNewCString(package);
1017 #endif
1018 }
1020 // get temp path to use for minidump path
1021 #if defined(XP_WIN32)
1022 nsString tempPath;
1024 // first figure out buffer size
1025 int pathLen = GetTempPath(0, nullptr);
1026 if (pathLen == 0)
1027 return NS_ERROR_FAILURE;
1029 tempPath.SetLength(pathLen);
1030 GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
1031 #elif defined(XP_MACOSX)
1032 nsCString tempPath;
1033 FSRef fsRef;
1034 OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
1035 kCreateFolder, &fsRef);
1036 if (err != noErr)
1037 return NS_ERROR_FAILURE;
1039 char path[PATH_MAX];
1040 OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
1041 if (status != noErr)
1042 return NS_ERROR_FAILURE;
1044 tempPath = path;
1046 #elif defined(__ANDROID__)
1047 // GeckoAppShell or Gonk's init.rc sets this in the environment
1048 const char *tempenv = PR_GetEnv("TMPDIR");
1049 if (!tempenv)
1050 return NS_ERROR_FAILURE;
1051 nsCString tempPath(tempenv);
1052 #elif defined(XP_UNIX)
1053 // we assume it's always /tmp on unix systems
1054 nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
1055 #else
1056 #error "Implement this for your platform"
1057 #endif
1059 #ifdef XP_MACOSX
1060 // Initialize spawn attributes, since this calls malloc.
1061 if (posix_spawnattr_init(&spawnattr) != 0) {
1062 return NS_ERROR_FAILURE;
1063 }
1065 // Set spawn attributes.
1066 size_t attr_count = ArrayLength(pref_cpu_types);
1067 size_t attr_ocount = 0;
1068 if (posix_spawnattr_setbinpref_np(&spawnattr,
1069 attr_count,
1070 pref_cpu_types,
1071 &attr_ocount) != 0 ||
1072 attr_ocount != attr_count) {
1073 posix_spawnattr_destroy(&spawnattr);
1074 return NS_ERROR_FAILURE;
1075 }
1076 #endif
1078 #ifdef XP_WIN32
1079 ReserveBreakpadVM();
1081 MINIDUMP_TYPE minidump_type = MiniDumpNormal;
1083 // Try to determine what version of dbghelp.dll we're using.
1084 // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
1086 DWORD version_size = GetFileVersionInfoSizeW(L"dbghelp.dll", nullptr);
1087 if (version_size > 0) {
1088 std::vector<BYTE> buffer(version_size);
1089 if (GetFileVersionInfoW(L"dbghelp.dll",
1090 0,
1091 version_size,
1092 &buffer[0])) {
1093 UINT len;
1094 VS_FIXEDFILEINFO* file_info;
1095 VerQueryValue(&buffer[0], L"\\", (void**)&file_info, &len);
1096 WORD major = HIWORD(file_info->dwFileVersionMS),
1097 minor = LOWORD(file_info->dwFileVersionMS),
1098 revision = HIWORD(file_info->dwFileVersionLS);
1099 if (major > 6 || (major == 6 && minor > 1) ||
1100 (major == 6 && minor == 1 && revision >= 7600)) {
1101 minidump_type = MiniDumpWithFullMemoryInfo;
1102 }
1103 }
1104 }
1106 const char* e = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1107 if (e && *e) {
1108 minidump_type = MiniDumpWithFullMemory;
1109 }
1110 #endif // XP_WIN32
1112 #ifdef MOZ_WIDGET_ANDROID
1113 androidUserSerial = getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
1114 #endif
1116 // Initialize the flag and mutex used to avoid dump processing
1117 // once browser termination has begun.
1118 NS_ASSERTION(!dumpSafetyLock, "Shouldn't have a lock yet");
1119 // Do not deallocate this lock while it is still possible for
1120 // isSafeToDump to be tested on another thread.
1121 dumpSafetyLock = new Mutex("dumpSafetyLock");
1122 MutexAutoLock lock(*dumpSafetyLock);
1123 isSafeToDump = true;
1125 // now set the exception handler
1126 #ifdef XP_LINUX
1127 MinidumpDescriptor descriptor(tempPath.get());
1128 #endif
1130 #ifdef XP_WIN
1131 NotePreviousUnhandledExceptionFilter();
1132 #endif
1134 gExceptionHandler = new google_breakpad::
1135 ExceptionHandler(
1136 #ifdef XP_LINUX
1137 descriptor,
1138 #else
1139 tempPath.get(),
1140 #endif
1142 #ifdef XP_WIN
1143 FPEFilter,
1144 #else
1145 Filter,
1146 #endif
1147 MinidumpCallback,
1148 nullptr,
1149 #ifdef XP_WIN32
1150 google_breakpad::ExceptionHandler::HANDLER_ALL,
1151 minidump_type,
1152 (const wchar_t*) nullptr,
1153 nullptr);
1154 #else
1155 true
1156 #ifdef XP_MACOSX
1157 , nullptr
1158 #endif
1159 #ifdef XP_LINUX
1160 , -1
1161 #endif
1162 );
1163 #endif // XP_WIN32
1165 if (!gExceptionHandler)
1166 return NS_ERROR_OUT_OF_MEMORY;
1168 #ifdef XP_WIN
1169 gExceptionHandler->set_handle_debug_exceptions(true);
1171 // protect the crash reporter from being unloaded
1172 gBlockUnhandledExceptionFilter = true;
1173 gKernel32Intercept.Init("kernel32.dll");
1174 bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
1175 reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
1176 (void**) &stub_SetUnhandledExceptionFilter);
1178 #ifdef DEBUG
1179 if (!ok)
1180 printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
1181 #endif
1182 #endif
1184 // store application start time
1185 char timeString[32];
1186 time_t startupTime = time(nullptr);
1187 XP_TTOA(startupTime, timeString, 10);
1188 AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
1189 nsDependentCString(timeString));
1191 #if defined(XP_MACOSX)
1192 // On OS X, many testers like to see the OS crash reporting dialog
1193 // since it offers immediate stack traces. We allow them to set
1194 // a default to pass exceptions to the OS handler.
1195 Boolean keyExistsAndHasValidFormat = false;
1196 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
1197 kCFPreferencesCurrentApplication,
1198 &keyExistsAndHasValidFormat);
1199 if (keyExistsAndHasValidFormat)
1200 showOSCrashReporter = prefValue;
1201 #endif
1203 #if defined(MOZ_WIDGET_ANDROID)
1204 for (unsigned int i = 0; i < library_mappings.size(); i++) {
1205 u_int8_t guid[sizeof(MDGUID)];
1206 google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)library_mappings[i].start_address, guid);
1207 gExceptionHandler->AddMappingInfo(library_mappings[i].name,
1208 guid,
1209 library_mappings[i].start_address,
1210 library_mappings[i].length,
1211 library_mappings[i].file_offset);
1212 }
1213 #endif
1215 mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
1217 return NS_OK;
1218 }
1220 bool GetEnabled()
1221 {
1222 return gExceptionHandler != nullptr;
1223 }
1225 bool GetMinidumpPath(nsAString& aPath)
1226 {
1227 if (!gExceptionHandler)
1228 return false;
1230 #ifndef XP_LINUX
1231 aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
1232 #else
1233 aPath = CONVERT_XP_CHAR_TO_UTF16(
1234 gExceptionHandler->minidump_descriptor().directory().c_str());
1235 #endif
1236 return true;
1237 }
1239 nsresult SetMinidumpPath(const nsAString& aPath)
1240 {
1241 if (!gExceptionHandler)
1242 return NS_ERROR_NOT_INITIALIZED;
1244 #ifdef XP_WIN32
1245 gExceptionHandler->set_dump_path(char16ptr_t(aPath.BeginReading()));
1246 #elif defined(XP_LINUX)
1247 gExceptionHandler->set_minidump_descriptor(
1248 MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath).BeginReading()));
1249 #else
1250 gExceptionHandler->set_dump_path(NS_ConvertUTF16toUTF8(aPath).BeginReading());
1251 #endif
1252 return NS_OK;
1253 }
1255 static nsresult
1256 WriteDataToFile(nsIFile* aFile, const nsACString& data)
1257 {
1258 PRFileDesc* fd;
1259 nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
1260 NS_ENSURE_SUCCESS(rv, rv);
1262 rv = NS_OK;
1263 if (PR_Write(fd, data.Data(), data.Length()) == -1) {
1264 rv = NS_ERROR_FAILURE;
1265 }
1266 PR_Close(fd);
1267 return rv;
1268 }
1270 static nsresult
1271 GetFileContents(nsIFile* aFile, nsACString& data)
1272 {
1273 PRFileDesc* fd;
1274 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
1275 NS_ENSURE_SUCCESS(rv, rv);
1277 rv = NS_OK;
1278 int32_t filesize = PR_Available(fd);
1279 if (filesize <= 0) {
1280 rv = NS_ERROR_FILE_NOT_FOUND;
1281 }
1282 else {
1283 data.SetLength(filesize);
1284 if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
1285 rv = NS_ERROR_FAILURE;
1286 }
1287 }
1288 PR_Close(fd);
1289 return rv;
1290 }
1292 // Function typedef for initializing a piece of data that we
1293 // don't already have.
1294 typedef nsresult (*InitDataFunc)(nsACString&);
1296 // Attempt to read aFile's contents into aContents, if aFile
1297 // does not exist, create it and initialize its contents
1298 // by calling aInitFunc for the data.
1299 static nsresult
1300 GetOrInit(nsIFile* aDir, const nsACString& filename,
1301 nsACString& aContents, InitDataFunc aInitFunc)
1302 {
1303 bool exists;
1305 nsCOMPtr<nsIFile> dataFile;
1306 nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
1307 NS_ENSURE_SUCCESS(rv, rv);
1309 rv = dataFile->AppendNative(filename);
1310 NS_ENSURE_SUCCESS(rv, rv);
1312 rv = dataFile->Exists(&exists);
1313 NS_ENSURE_SUCCESS(rv, rv);
1315 if (!exists) {
1316 if (aInitFunc) {
1317 // get the initial value and write it to the file
1318 rv = aInitFunc(aContents);
1319 NS_ENSURE_SUCCESS(rv, rv);
1320 rv = WriteDataToFile(dataFile, aContents);
1321 }
1322 else {
1323 // didn't pass in an init func
1324 rv = NS_ERROR_FAILURE;
1325 }
1326 }
1327 else {
1328 // just get the file's contents
1329 rv = GetFileContents(dataFile, aContents);
1330 }
1332 return rv;
1333 }
1335 // Init the "install time" data. We're taking an easy way out here
1336 // and just setting this to "the time when this version was first run".
1337 static nsresult
1338 InitInstallTime(nsACString& aInstallTime)
1339 {
1340 time_t t = time(nullptr);
1341 char buf[16];
1342 sprintf(buf, "%ld", t);
1343 aInstallTime = buf;
1345 return NS_OK;
1346 }
1348 // Ensure a directory exists and create it if missing.
1349 static nsresult
1350 EnsureDirectoryExists(nsIFile* dir)
1351 {
1352 nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
1354 if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
1355 return rv;
1356 }
1358 return NS_OK;
1359 }
1361 // Annotate the crash report with a Unique User ID and time
1362 // since install. Also do some prep work for recording
1363 // time since last crash, which must be calculated at
1364 // crash time.
1365 // If any piece of data doesn't exist, initialize it first.
1366 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
1367 const nsACString& aBuildID)
1368 {
1369 nsCOMPtr<nsIFile> dataDirectory;
1370 nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
1371 NS_ENSURE_SUCCESS(rv, rv);
1373 rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
1374 NS_ENSURE_SUCCESS(rv, rv);
1376 EnsureDirectoryExists(dataDirectory);
1378 #if defined(XP_WIN32)
1379 nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
1381 nsAutoString dataDirectoryPath;
1382 rv = dataDirectory->GetPath(dataDirectoryPath);
1383 NS_ENSURE_SUCCESS(rv, rv);
1385 dataDirEnv.Append(dataDirectoryPath);
1387 _wputenv(dataDirEnv.get());
1388 #else
1389 // Save this path in the environment for the crash reporter application.
1390 nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
1392 nsAutoCString dataDirectoryPath;
1393 rv = dataDirectory->GetNativePath(dataDirectoryPath);
1394 NS_ENSURE_SUCCESS(rv, rv);
1396 dataDirEnv.Append(dataDirectoryPath);
1398 char* env = ToNewCString(dataDirEnv);
1399 NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
1401 PR_SetEnv(env);
1402 #endif
1404 nsAutoCString data;
1405 if(NS_SUCCEEDED(GetOrInit(dataDirectory,
1406 NS_LITERAL_CSTRING("InstallTime") + aBuildID,
1407 data, InitInstallTime)))
1408 AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
1410 // this is a little different, since we can't init it with anything,
1411 // since it's stored at crash time, and we can't annotate the
1412 // crash report with the stored value, since we really want
1413 // (now - LastCrash), so we just get a value if it exists,
1414 // and store it in a time_t value.
1415 if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
1416 data, nullptr))) {
1417 lastCrashTime = (time_t)atol(data.get());
1418 }
1420 // not really the best place to init this, but I have the path I need here
1421 nsCOMPtr<nsIFile> lastCrashFile;
1422 rv = dataDirectory->Clone(getter_AddRefs(lastCrashFile));
1423 NS_ENSURE_SUCCESS(rv, rv);
1425 rv = lastCrashFile->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
1426 NS_ENSURE_SUCCESS(rv, rv);
1427 memset(lastCrashTimeFilename, 0, sizeof(lastCrashTimeFilename));
1429 #if defined(XP_WIN32)
1430 nsAutoString filename;
1431 rv = lastCrashFile->GetPath(filename);
1432 NS_ENSURE_SUCCESS(rv, rv);
1434 if (filename.Length() < XP_PATH_MAX)
1435 wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
1436 #else
1437 nsAutoCString filename;
1438 rv = lastCrashFile->GetNativePath(filename);
1439 NS_ENSURE_SUCCESS(rv, rv);
1441 if (filename.Length() < XP_PATH_MAX)
1442 strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
1443 #endif
1445 if (headlessClient) {
1446 nsCOMPtr<nsIFile> markerFile;
1447 rv = dataDirectory->Clone(getter_AddRefs(markerFile));
1448 NS_ENSURE_SUCCESS(rv, rv);
1450 rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
1451 NS_ENSURE_SUCCESS(rv, rv);
1452 memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
1454 #if defined(XP_WIN32)
1455 nsAutoString markerFilename;
1456 rv = markerFile->GetPath(markerFilename);
1457 NS_ENSURE_SUCCESS(rv, rv);
1459 if (markerFilename.Length() < XP_PATH_MAX)
1460 wcsncpy(crashMarkerFilename, markerFilename.get(),
1461 markerFilename.Length());
1462 #else
1463 nsAutoCString markerFilename;
1464 rv = markerFile->GetNativePath(markerFilename);
1465 NS_ENSURE_SUCCESS(rv, rv);
1467 if (markerFilename.Length() < XP_PATH_MAX)
1468 strncpy(crashMarkerFilename, markerFilename.get(),
1469 markerFilename.Length());
1470 #endif
1471 }
1473 return NS_OK;
1474 }
1476 static void OOPDeinit();
1478 nsresult UnsetExceptionHandler()
1479 {
1480 if (isSafeToDump) {
1481 MutexAutoLock lock(*dumpSafetyLock);
1482 isSafeToDump = false;
1483 }
1485 #ifdef XP_WIN
1486 // allow SetUnhandledExceptionFilter
1487 gBlockUnhandledExceptionFilter = false;
1488 #endif
1490 delete gExceptionHandler;
1492 // do this here in the unlikely case that we succeeded in allocating
1493 // our strings but failed to allocate gExceptionHandler.
1494 delete crashReporterAPIData_Hash;
1495 crashReporterAPIData_Hash = nullptr;
1497 delete crashReporterAPILock;
1498 crashReporterAPILock = nullptr;
1500 delete notesFieldLock;
1501 notesFieldLock = nullptr;
1503 delete crashReporterAPIData;
1504 crashReporterAPIData = nullptr;
1506 delete notesField;
1507 notesField = nullptr;
1509 delete lastRunCrashID;
1510 lastRunCrashID = nullptr;
1512 if (pendingDirectory) {
1513 NS_Free(pendingDirectory);
1514 pendingDirectory = nullptr;
1515 }
1517 if (crashReporterPath) {
1518 NS_Free(crashReporterPath);
1519 crashReporterPath = nullptr;
1520 }
1522 if (eventsDirectory) {
1523 NS_Free(eventsDirectory);
1524 eventsDirectory = nullptr;
1525 }
1527 #ifdef XP_MACOSX
1528 posix_spawnattr_destroy(&spawnattr);
1529 #endif
1531 if (!gExceptionHandler)
1532 return NS_ERROR_NOT_INITIALIZED;
1534 gExceptionHandler = nullptr;
1536 OOPDeinit();
1538 delete dumpSafetyLock;
1539 dumpSafetyLock = nullptr;
1541 return NS_OK;
1542 }
1544 static void ReplaceChar(nsCString& str, const nsACString& character,
1545 const nsACString& replacement)
1546 {
1547 nsCString::const_iterator start, end;
1549 str.BeginReading(start);
1550 str.EndReading(end);
1552 while (FindInReadable(character, start, end)) {
1553 int32_t pos = end.size_backward();
1554 str.Replace(pos - 1, 1, replacement);
1556 str.BeginReading(start);
1557 start.advance(pos + replacement.Length() - 1);
1558 str.EndReading(end);
1559 }
1560 }
1562 static bool DoFindInReadable(const nsACString& str, const nsACString& value)
1563 {
1564 nsACString::const_iterator start, end;
1565 str.BeginReading(start);
1566 str.EndReading(end);
1568 return FindInReadable(value, start, end);
1569 }
1571 static PLDHashOperator EnumerateEntries(const nsACString& key,
1572 nsCString entry,
1573 void* userData)
1574 {
1575 crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
1576 NS_LITERAL_CSTRING("\n"));
1577 return PL_DHASH_NEXT;
1578 }
1580 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
1581 #ifdef _MSC_VER
1582 #pragma optimize("", off)
1583 #endif
1584 static nsresult
1585 EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
1586 {
1587 if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
1588 DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
1589 return NS_ERROR_INVALID_ARG;
1591 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
1592 return NS_ERROR_INVALID_ARG;
1594 escapedData = data;
1596 // escape backslashes
1597 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
1598 NS_LITERAL_CSTRING("\\\\"));
1599 // escape newlines
1600 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
1601 NS_LITERAL_CSTRING("\\n"));
1602 return NS_OK;
1603 }
1604 #ifdef _MSC_VER
1605 #pragma optimize("", on)
1606 #endif
1608 class DelayedNote
1609 {
1610 public:
1611 DelayedNote(const nsACString& aKey, const nsACString& aData)
1612 : mKey(aKey), mData(aData), mType(Annotation) {}
1614 DelayedNote(const nsACString& aData)
1615 : mData(aData), mType(AppNote) {}
1617 void Run()
1618 {
1619 if (mType == Annotation) {
1620 AnnotateCrashReport(mKey, mData);
1621 } else {
1622 AppendAppNotesToCrashReport(mData);
1623 }
1624 }
1626 private:
1627 nsCString mKey;
1628 nsCString mData;
1629 enum AnnotationType { Annotation, AppNote } mType;
1630 };
1632 static void
1633 EnqueueDelayedNote(DelayedNote* aNote)
1634 {
1635 if (!gDelayedAnnotations) {
1636 gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
1637 }
1638 gDelayedAnnotations->AppendElement(aNote);
1639 }
1641 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
1642 {
1643 if (!GetEnabled())
1644 return NS_ERROR_NOT_INITIALIZED;
1646 nsCString escapedData;
1647 nsresult rv = EscapeAnnotation(key, data, escapedData);
1648 if (NS_FAILED(rv))
1649 return rv;
1651 if (XRE_GetProcessType() != GeckoProcessType_Default) {
1652 if (!NS_IsMainThread()) {
1653 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
1654 return NS_ERROR_FAILURE;
1655 }
1656 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
1657 if (!reporter) {
1658 EnqueueDelayedNote(new DelayedNote(key, data));
1659 return NS_OK;
1660 }
1661 if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
1662 return NS_ERROR_FAILURE;
1663 return NS_OK;
1664 }
1666 MutexAutoLock lock(*crashReporterAPILock);
1668 crashReporterAPIData_Hash->Put(key, escapedData);
1670 // now rebuild the file contents
1671 crashReporterAPIData->Truncate(0);
1672 crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
1673 crashReporterAPIData);
1675 return NS_OK;
1676 }
1678 nsresult SetGarbageCollecting(bool collecting)
1679 {
1680 if (!GetEnabled())
1681 return NS_ERROR_NOT_INITIALIZED;
1683 isGarbageCollecting = collecting;
1685 return NS_OK;
1686 }
1688 void SetEventloopNestingLevel(uint32_t level)
1689 {
1690 eventloopNestingLevel = level;
1691 }
1693 nsresult AppendAppNotesToCrashReport(const nsACString& data)
1694 {
1695 if (!GetEnabled())
1696 return NS_ERROR_NOT_INITIALIZED;
1698 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
1699 return NS_ERROR_INVALID_ARG;
1701 if (XRE_GetProcessType() != GeckoProcessType_Default) {
1702 if (!NS_IsMainThread()) {
1703 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
1704 return NS_ERROR_FAILURE;
1705 }
1706 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
1707 if (!reporter) {
1708 EnqueueDelayedNote(new DelayedNote(data));
1709 return NS_OK;
1710 }
1712 // Since we don't go through AnnotateCrashReport in the parent process,
1713 // we must ensure that the data is escaped and valid before the parent
1714 // sees it.
1715 nsCString escapedData;
1716 nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
1717 if (NS_FAILED(rv))
1718 return rv;
1720 if (!reporter->SendAppendAppNotes(escapedData))
1721 return NS_ERROR_FAILURE;
1722 return NS_OK;
1723 }
1725 MutexAutoLock lock(*notesFieldLock);
1727 notesField->Append(data);
1728 return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
1729 }
1731 // Returns true if found, false if not found.
1732 bool GetAnnotation(const nsACString& key, nsACString& data)
1733 {
1734 if (!gExceptionHandler)
1735 return false;
1737 nsAutoCString entry;
1738 if (!crashReporterAPIData_Hash->Get(key, &entry))
1739 return false;
1741 data = entry;
1742 return true;
1743 }
1745 nsresult RegisterAppMemory(void* ptr, size_t length)
1746 {
1747 if (!GetEnabled())
1748 return NS_ERROR_NOT_INITIALIZED;
1750 #if defined(XP_LINUX) || defined(XP_WIN32)
1751 gExceptionHandler->RegisterAppMemory(ptr, length);
1752 return NS_OK;
1753 #else
1754 return NS_ERROR_NOT_IMPLEMENTED;
1755 #endif
1756 }
1758 nsresult UnregisterAppMemory(void* ptr)
1759 {
1760 if (!GetEnabled())
1761 return NS_ERROR_NOT_INITIALIZED;
1763 #if defined(XP_LINUX) || defined(XP_WIN32)
1764 gExceptionHandler->UnregisterAppMemory(ptr);
1765 return NS_OK;
1766 #else
1767 return NS_ERROR_NOT_IMPLEMENTED;
1768 #endif
1769 }
1771 bool GetServerURL(nsACString& aServerURL)
1772 {
1773 if (!gExceptionHandler)
1774 return false;
1776 return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
1777 }
1779 nsresult SetServerURL(const nsACString& aServerURL)
1780 {
1781 // store server URL with the API data
1782 // the client knows to handle this specially
1783 return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
1784 aServerURL);
1785 }
1787 nsresult
1788 SetRestartArgs(int argc, char** argv)
1789 {
1790 if (!gExceptionHandler)
1791 return NS_OK;
1793 int i;
1794 nsAutoCString envVar;
1795 char *env;
1796 char *argv0 = getenv("MOZ_APP_LAUNCHER");
1797 for (i = 0; i < argc; i++) {
1798 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
1799 envVar.AppendInt(i);
1800 envVar += "=";
1801 if (argv0 && i == 0) {
1802 // Is there a request to suppress default binary launcher?
1803 envVar += argv0;
1804 } else {
1805 envVar += argv[i];
1806 }
1808 // PR_SetEnv() wants the string to be available for the lifetime
1809 // of the app, so dup it here
1810 env = ToNewCString(envVar);
1811 if (!env)
1812 return NS_ERROR_OUT_OF_MEMORY;
1814 PR_SetEnv(env);
1815 }
1817 // make sure the arg list is terminated
1818 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
1819 envVar.AppendInt(i);
1820 envVar += "=";
1822 // PR_SetEnv() wants the string to be available for the lifetime
1823 // of the app, so dup it here
1824 env = ToNewCString(envVar);
1825 if (!env)
1826 return NS_ERROR_OUT_OF_MEMORY;
1828 PR_SetEnv(env);
1830 // make sure we save the info in XUL_APP_FILE for the reporter
1831 const char *appfile = PR_GetEnv("XUL_APP_FILE");
1832 if (appfile && *appfile) {
1833 envVar = "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
1834 envVar += appfile;
1835 env = ToNewCString(envVar);
1836 PR_SetEnv(env);
1837 }
1839 return NS_OK;
1840 }
1842 #ifdef XP_WIN32
1843 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
1844 {
1845 if (!gExceptionHandler)
1846 return NS_ERROR_NOT_INITIALIZED;
1848 return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
1849 }
1850 #endif
1852 #ifdef XP_LINUX
1853 bool WriteMinidumpForSigInfo(int signo, siginfo_t* info, void* uc)
1854 {
1855 return gExceptionHandler->HandleSignal(signo, info, uc);
1856 }
1857 #endif
1859 #ifdef XP_MACOSX
1860 nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
1861 {
1862 nsAutoCString excString;
1863 GetObjCExceptionInfo(inException, excString);
1864 AppendAppNotesToCrashReport(excString);
1865 return NS_OK;
1866 }
1867 #endif
1869 /*
1870 * Combined code to get/set the crash reporter submission pref on
1871 * different platforms.
1872 */
1873 static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
1874 {
1875 nsresult rv;
1876 #if defined(XP_WIN32)
1877 /*
1878 * NOTE! This needs to stay in sync with the preference checking code
1879 * in toolkit/crashreporter/client/crashreporter_win.cpp
1880 */
1881 nsCOMPtr<nsIXULAppInfo> appinfo =
1882 do_GetService("@mozilla.org/xre/app-info;1", &rv);
1883 NS_ENSURE_SUCCESS(rv, rv);
1885 nsAutoCString appVendor, appName;
1886 rv = appinfo->GetVendor(appVendor);
1887 NS_ENSURE_SUCCESS(rv, rv);
1888 rv = appinfo->GetName(appName);
1889 NS_ENSURE_SUCCESS(rv, rv);
1891 nsCOMPtr<nsIWindowsRegKey> regKey
1892 (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
1893 NS_ENSURE_SUCCESS(rv, rv);
1895 nsAutoCString regPath;
1897 regPath.AppendLiteral("Software\\");
1899 // We need to ensure the registry keys are created so we can properly
1900 // write values to it
1902 // Create appVendor key
1903 if(!appVendor.IsEmpty()) {
1904 regPath.Append(appVendor);
1905 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1906 NS_ConvertUTF8toUTF16(regPath),
1907 nsIWindowsRegKey::ACCESS_SET_VALUE);
1908 regPath.AppendLiteral("\\");
1909 }
1911 // Create appName key
1912 regPath.Append(appName);
1913 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1914 NS_ConvertUTF8toUTF16(regPath),
1915 nsIWindowsRegKey::ACCESS_SET_VALUE);
1916 regPath.AppendLiteral("\\");
1918 // Create Crash Reporter key
1919 regPath.AppendLiteral("Crash Reporter");
1920 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1921 NS_ConvertUTF8toUTF16(regPath),
1922 nsIWindowsRegKey::ACCESS_SET_VALUE);
1924 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
1925 // and we're done.
1926 if (writePref) {
1927 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1928 NS_ConvertUTF8toUTF16(regPath),
1929 nsIWindowsRegKey::ACCESS_SET_VALUE);
1930 NS_ENSURE_SUCCESS(rv, rv);
1932 uint32_t value = *aSubmitReports ? 1 : 0;
1933 rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
1934 regKey->Close();
1935 return rv;
1936 }
1938 // We're reading the pref value, so we need to first look under
1939 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
1940 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
1941 // to "true".
1942 uint32_t value;
1943 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
1944 NS_ConvertUTF8toUTF16(regPath),
1945 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
1946 if (NS_SUCCEEDED(rv)) {
1947 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
1948 regKey->Close();
1949 if (NS_SUCCEEDED(rv)) {
1950 *aSubmitReports = !!value;
1951 return NS_OK;
1952 }
1953 }
1955 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1956 NS_ConvertUTF8toUTF16(regPath),
1957 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
1958 if (NS_FAILED(rv)) {
1959 *aSubmitReports = true;
1960 return NS_OK;
1961 }
1963 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
1964 // default to true on failure
1965 if (NS_FAILED(rv)) {
1966 value = 1;
1967 rv = NS_OK;
1968 }
1969 regKey->Close();
1971 *aSubmitReports = !!value;
1972 return NS_OK;
1973 #elif defined(XP_MACOSX)
1974 rv = NS_OK;
1975 if (writePref) {
1976 CFPropertyListRef cfValue = (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
1977 ::CFPreferencesSetAppValue(CFSTR("submitReport"),
1978 cfValue,
1979 reporterClientAppID);
1980 if (!::CFPreferencesAppSynchronize(reporterClientAppID))
1981 rv = NS_ERROR_FAILURE;
1982 }
1983 else {
1984 *aSubmitReports = true;
1985 Boolean keyExistsAndHasValidFormat = false;
1986 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
1987 reporterClientAppID,
1988 &keyExistsAndHasValidFormat);
1989 if (keyExistsAndHasValidFormat)
1990 *aSubmitReports = !!prefValue;
1991 }
1992 return rv;
1993 #elif defined(XP_UNIX)
1994 /*
1995 * NOTE! This needs to stay in sync with the preference checking code
1996 * in toolkit/crashreporter/client/crashreporter_linux.cpp
1997 */
1998 nsCOMPtr<nsIFile> reporterINI;
1999 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
2000 NS_ENSURE_SUCCESS(rv, rv);
2001 reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
2002 reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
2004 bool exists;
2005 rv = reporterINI->Exists(&exists);
2006 NS_ENSURE_SUCCESS(rv, rv);
2007 if (!exists) {
2008 if (!writePref) {
2009 // If reading the pref, default to true if .ini doesn't exist.
2010 *aSubmitReports = true;
2011 return NS_OK;
2012 }
2013 // Create the file so the INI processor can write to it.
2014 rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
2015 NS_ENSURE_SUCCESS(rv, rv);
2016 }
2018 nsCOMPtr<nsIINIParserFactory> iniFactory =
2019 do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
2020 NS_ENSURE_SUCCESS(rv, rv);
2022 nsCOMPtr<nsIINIParser> iniParser;
2023 rv = iniFactory->CreateINIParser(reporterINI,
2024 getter_AddRefs(iniParser));
2025 NS_ENSURE_SUCCESS(rv, rv);
2027 // If we're writing the pref, just set and we're done.
2028 if (writePref) {
2029 nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
2030 NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
2032 rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
2033 NS_LITERAL_CSTRING("SubmitReport"),
2034 *aSubmitReports ? NS_LITERAL_CSTRING("1") :
2035 NS_LITERAL_CSTRING("0"));
2036 NS_ENSURE_SUCCESS(rv, rv);
2037 rv = iniWriter->WriteFile(nullptr, 0);
2038 return rv;
2039 }
2041 nsAutoCString submitReportValue;
2042 rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
2043 NS_LITERAL_CSTRING("SubmitReport"),
2044 submitReportValue);
2046 // Default to "true" if the pref can't be found.
2047 if (NS_FAILED(rv))
2048 *aSubmitReports = true;
2049 else if (submitReportValue.EqualsASCII("0"))
2050 *aSubmitReports = false;
2051 else
2052 *aSubmitReports = true;
2054 return NS_OK;
2055 #else
2056 return NS_ERROR_NOT_IMPLEMENTED;
2057 #endif
2058 }
2060 nsresult GetSubmitReports(bool* aSubmitReports)
2061 {
2062 return PrefSubmitReports(aSubmitReports, false);
2063 }
2065 nsresult SetSubmitReports(bool aSubmitReports)
2066 {
2067 nsresult rv;
2069 nsCOMPtr<nsIObserverService> obsServ =
2070 mozilla::services::GetObserverService();
2071 if (!obsServ) {
2072 return NS_ERROR_FAILURE;
2073 }
2075 rv = PrefSubmitReports(&aSubmitReports, true);
2076 if (NS_FAILED(rv)) {
2077 return rv;
2078 }
2080 obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
2081 return NS_OK;
2082 }
2084 void
2085 UpdateCrashEventsDir()
2086 {
2087 nsCOMPtr<nsIFile> eventsDir;
2089 // We prefer the following locations in order:
2090 //
2091 // 1. If environment variable is present, use it. We don't expect
2092 // the environment variable except for tests and other atypical setups.
2093 // 2. Inside the profile directory.
2094 // 3. Inside the user application data directory (no profile available).
2095 // 4. A temporary directory (setup likely is invalid / application is buggy).
2096 const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
2097 if (env) {
2098 eventsDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
2099 if (!eventsDir) {
2100 return;
2101 }
2102 eventsDir->InitWithNativePath(nsDependentCString(env));
2103 EnsureDirectoryExists(eventsDir);
2104 } else {
2105 nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
2106 if (NS_SUCCEEDED(rv)) {
2107 eventsDir->Append(NS_LITERAL_STRING("crashes"));
2108 EnsureDirectoryExists(eventsDir);
2109 eventsDir->Append(NS_LITERAL_STRING("events"));
2110 EnsureDirectoryExists(eventsDir);
2111 } else {
2112 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
2113 if (NS_SUCCEEDED(rv)) {
2114 eventsDir->Append(NS_LITERAL_STRING("Crash Reports"));
2115 EnsureDirectoryExists(eventsDir);
2116 eventsDir->Append(NS_LITERAL_STRING("events"));
2117 EnsureDirectoryExists(eventsDir);
2118 } else {
2119 NS_WARNING("Couldn't get the user appdata directory. Crash events may not be produced.");
2120 return;
2121 }
2122 }
2123 }
2125 #ifdef XP_WIN
2126 nsString path;
2127 eventsDir->GetPath(path);
2128 eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
2129 #else
2130 nsCString path;
2131 eventsDir->GetNativePath(path);
2132 eventsDirectory = ToNewCString(path);
2133 #endif
2134 }
2136 bool GetCrashEventsDir(nsAString& aPath)
2137 {
2138 if (!eventsDirectory) {
2139 return false;
2140 }
2142 aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory);
2143 return true;
2144 }
2146 static void
2147 FindPendingDir()
2148 {
2149 if (pendingDirectory)
2150 return;
2152 nsCOMPtr<nsIFile> pendingDir;
2153 nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
2154 if (NS_FAILED(rv)) {
2155 NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
2156 }
2157 else {
2158 pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
2159 pendingDir->Append(NS_LITERAL_STRING("pending"));
2161 #ifdef XP_WIN
2162 nsString path;
2163 pendingDir->GetPath(path);
2164 pendingDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
2165 #else
2166 nsCString path;
2167 pendingDir->GetNativePath(path);
2168 pendingDirectory = ToNewCString(path);
2169 #endif
2170 }
2171 }
2173 // The "pending" dir is Crash Reports/pending, from which minidumps
2174 // can be submitted. Because this method may be called off the main thread,
2175 // we store the pending directory as a path.
2176 static bool
2177 GetPendingDir(nsIFile** dir)
2178 {
2179 MOZ_ASSERT(OOPInitialized());
2180 if (!pendingDirectory) {
2181 return false;
2182 }
2184 nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
2185 if (!pending) {
2186 NS_WARNING("Can't set up pending directory during shutdown.");
2187 return false;
2188 }
2189 #ifdef XP_WIN
2190 pending->InitWithPath(nsDependentString(pendingDirectory));
2191 #else
2192 pending->InitWithNativePath(nsDependentCString(pendingDirectory));
2193 #endif
2194 pending.swap(*dir);
2195 return true;
2196 }
2198 // The "limbo" dir is where minidumps go to wait for something else to
2199 // use them. If we're |ShouldReport()|, then the "something else" is
2200 // a minidump submitter, and they're coming from the
2201 // Crash Reports/pending/ dir. Otherwise, we don't know what the
2202 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
2203 // limbo.
2204 static bool
2205 GetMinidumpLimboDir(nsIFile** dir)
2206 {
2207 if (ShouldReport()) {
2208 return GetPendingDir(dir);
2209 }
2210 else {
2211 #ifndef XP_LINUX
2212 CreateFileFromPath(gExceptionHandler->dump_path(), dir);
2213 #else
2214 CreateFileFromPath(gExceptionHandler->minidump_descriptor().directory(),
2215 dir);
2216 #endif
2217 return nullptr != *dir;
2218 }
2219 }
2221 bool
2222 GetMinidumpForID(const nsAString& id, nsIFile** minidump)
2223 {
2224 if (!GetMinidumpLimboDir(minidump))
2225 return false;
2226 (*minidump)->Append(id + NS_LITERAL_STRING(".dmp"));
2227 return true;
2228 }
2230 bool
2231 GetIDFromMinidump(nsIFile* minidump, nsAString& id)
2232 {
2233 if (NS_SUCCEEDED(minidump->GetLeafName(id))) {
2234 id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING(""));
2235 return true;
2236 }
2237 return false;
2238 }
2240 bool
2241 GetExtraFileForID(const nsAString& id, nsIFile** extraFile)
2242 {
2243 if (!GetMinidumpLimboDir(extraFile))
2244 return false;
2245 (*extraFile)->Append(id + NS_LITERAL_STRING(".extra"));
2246 return true;
2247 }
2249 bool
2250 GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile)
2251 {
2252 nsAutoString leafName;
2253 nsresult rv = minidump->GetLeafName(leafName);
2254 if (NS_FAILED(rv))
2255 return false;
2257 nsCOMPtr<nsIFile> extraF;
2258 rv = minidump->Clone(getter_AddRefs(extraF));
2259 if (NS_FAILED(rv))
2260 return false;
2262 leafName.Replace(leafName.Length() - 3, 3,
2263 NS_LITERAL_STRING("extra"));
2264 rv = extraF->SetLeafName(leafName);
2265 if (NS_FAILED(rv))
2266 return false;
2268 *extraFile = nullptr;
2269 extraF.swap(*extraFile);
2270 return true;
2271 }
2273 bool
2274 AppendExtraData(const nsAString& id, const AnnotationTable& data)
2275 {
2276 nsCOMPtr<nsIFile> extraFile;
2277 if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
2278 return false;
2279 return AppendExtraData(extraFile, data);
2280 }
2282 //-----------------------------------------------------------------------------
2283 // Helpers for AppendExtraData()
2284 //
2285 struct Blacklist {
2286 Blacklist() : mItems(nullptr), mLen(0) { }
2287 Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
2289 bool Contains(const nsACString& key) const {
2290 for (int i = 0; i < mLen; ++i)
2291 if (key.EqualsASCII(mItems[i]))
2292 return true;
2293 return false;
2294 }
2296 const char** mItems;
2297 const int mLen;
2298 };
2300 struct EnumerateAnnotationsContext {
2301 const Blacklist& blacklist;
2302 PRFileDesc* fd;
2303 };
2305 static void
2306 WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
2307 {
2308 PR_Write(fd, key.BeginReading(), key.Length());
2309 PR_Write(fd, "=", 1);
2310 PR_Write(fd, value.BeginReading(), value.Length());
2311 PR_Write(fd, "\n", 1);
2312 }
2314 static PLDHashOperator
2315 EnumerateAnnotations(const nsACString& key,
2316 nsCString entry,
2317 void* userData)
2318 {
2319 EnumerateAnnotationsContext* ctx =
2320 static_cast<EnumerateAnnotationsContext*>(userData);
2321 const Blacklist& blacklist = ctx->blacklist;
2323 // skip entries in the blacklist
2324 if (blacklist.Contains(key))
2325 return PL_DHASH_NEXT;
2327 WriteAnnotation(ctx->fd, key, entry);
2329 return PL_DHASH_NEXT;
2330 }
2332 static bool
2333 WriteExtraData(nsIFile* extraFile,
2334 const AnnotationTable& data,
2335 const Blacklist& blacklist,
2336 bool writeCrashTime=false,
2337 bool truncate=false)
2338 {
2339 PRFileDesc* fd;
2340 int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
2341 nsresult rv =
2342 extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
2343 0600, &fd);
2344 if (NS_FAILED(rv))
2345 return false;
2347 EnumerateAnnotationsContext ctx = { blacklist, fd };
2348 data.EnumerateRead(EnumerateAnnotations, &ctx);
2350 if (writeCrashTime) {
2351 time_t crashTime = time(nullptr);
2352 char crashTimeString[32];
2353 XP_TTOA(crashTime, crashTimeString, 10);
2355 WriteAnnotation(fd,
2356 nsDependentCString("CrashTime"),
2357 nsDependentCString(crashTimeString));
2358 }
2360 PR_Close(fd);
2361 return true;
2362 }
2364 bool
2365 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
2366 {
2367 return WriteExtraData(extraFile, data, Blacklist());
2368 }
2371 static bool
2372 WriteExtraForMinidump(nsIFile* minidump,
2373 const Blacklist& blacklist,
2374 nsIFile** extraFile)
2375 {
2376 nsCOMPtr<nsIFile> extra;
2377 if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra)))
2378 return false;
2380 if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
2381 blacklist,
2382 true /*write crash time*/,
2383 true /*truncate*/))
2384 return false;
2386 *extraFile = nullptr;
2387 extra.swap(*extraFile);
2389 return true;
2390 }
2392 // It really only makes sense to call this function when
2393 // ShouldReport() is true.
2394 static bool
2395 MoveToPending(nsIFile* dumpFile, nsIFile* extraFile)
2396 {
2397 nsCOMPtr<nsIFile> pendingDir;
2398 if (!GetPendingDir(getter_AddRefs(pendingDir)))
2399 return false;
2401 if (NS_FAILED(dumpFile->MoveTo(pendingDir, EmptyString()))) {
2402 return false;
2403 }
2405 if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, EmptyString()))) {
2406 return false;
2407 }
2409 return true;
2410 }
2412 static void
2413 OnChildProcessDumpRequested(void* aContext,
2414 #ifdef XP_MACOSX
2415 const ClientInfo& aClientInfo,
2416 const xpstring& aFilePath
2417 #else
2418 const ClientInfo* aClientInfo,
2419 const xpstring* aFilePath
2420 #endif
2421 )
2422 {
2423 nsCOMPtr<nsIFile> minidump;
2424 nsCOMPtr<nsIFile> extraFile;
2426 // Hold the mutex until the current dump request is complete, to
2427 // prevent UnsetExceptionHandler() from pulling the rug out from
2428 // under us.
2429 MutexAutoLock lock(*dumpSafetyLock);
2430 if (!isSafeToDump)
2431 return;
2433 CreateFileFromPath(
2434 #ifdef XP_MACOSX
2435 aFilePath,
2436 #else
2437 *aFilePath,
2438 #endif
2439 getter_AddRefs(minidump));
2441 if (!WriteExtraForMinidump(minidump,
2442 Blacklist(kSubprocessBlacklist,
2443 ArrayLength(kSubprocessBlacklist)),
2444 getter_AddRefs(extraFile)))
2445 return;
2447 if (ShouldReport())
2448 MoveToPending(minidump, extraFile);
2450 {
2451 uint32_t pid =
2452 #ifdef XP_MACOSX
2453 aClientInfo.pid();
2454 #else
2455 aClientInfo->pid();
2456 #endif
2458 #ifdef MOZ_CRASHREPORTER_INJECTOR
2459 bool runCallback;
2460 #endif
2461 {
2462 MutexAutoLock lock(*dumpMapLock);
2463 ChildProcessData* pd = pidToMinidump->PutEntry(pid);
2464 MOZ_ASSERT(!pd->minidump);
2465 pd->minidump = minidump;
2466 pd->sequence = ++crashSequence;
2467 #ifdef MOZ_CRASHREPORTER_INJECTOR
2468 runCallback = nullptr != pd->callback;
2469 #endif
2470 }
2471 #ifdef MOZ_CRASHREPORTER_INJECTOR
2472 if (runCallback)
2473 NS_DispatchToMainThread(new ReportInjectedCrash(pid));
2474 #endif
2475 }
2476 }
2478 static bool
2479 OOPInitialized()
2480 {
2481 return pidToMinidump != nullptr;
2482 }
2484 #ifdef XP_MACOSX
2485 static bool ChildFilter(void *context) {
2486 mozilla::IOInterposer::Disable();
2487 return true;
2488 }
2489 #endif
2491 void
2492 OOPInit()
2493 {
2494 if (OOPInitialized())
2495 return;
2497 MOZ_ASSERT(NS_IsMainThread());
2499 NS_ABORT_IF_FALSE(gExceptionHandler != nullptr,
2500 "attempt to initialize OOP crash reporter before in-process crashreporter!");
2502 #if defined(XP_WIN)
2503 childCrashNotifyPipe =
2504 PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
2505 static_cast<int>(::GetCurrentProcessId()));
2507 const std::wstring dumpPath = gExceptionHandler->dump_path();
2508 crashServer = new CrashGenerationServer(
2509 NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get(),
2510 nullptr, // default security attributes
2511 nullptr, nullptr, // we don't care about process connect here
2512 OnChildProcessDumpRequested, nullptr,
2513 nullptr, nullptr, // we don't care about process exit here
2514 nullptr, nullptr, // we don't care about upload request here
2515 true, // automatically generate dumps
2516 &dumpPath);
2518 #elif defined(XP_LINUX)
2519 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
2520 &clientSocketFd))
2521 NS_RUNTIMEABORT("can't create crash reporter socketpair()");
2523 const std::string dumpPath =
2524 gExceptionHandler->minidump_descriptor().directory();
2525 crashServer = new CrashGenerationServer(
2526 serverSocketFd,
2527 OnChildProcessDumpRequested, nullptr,
2528 nullptr, nullptr, // we don't care about process exit here
2529 true,
2530 &dumpPath);
2532 #elif defined(XP_MACOSX)
2533 childCrashNotifyPipe =
2534 PR_smprintf("gecko-crash-server-pipe.%i",
2535 static_cast<int>(getpid()));
2536 const std::string dumpPath = gExceptionHandler->dump_path();
2538 crashServer = new CrashGenerationServer(
2539 childCrashNotifyPipe,
2540 ChildFilter,
2541 nullptr,
2542 OnChildProcessDumpRequested, nullptr,
2543 nullptr, nullptr,
2544 true, // automatically generate dumps
2545 dumpPath);
2546 #endif
2548 if (!crashServer->Start())
2549 NS_RUNTIMEABORT("can't start crash reporter server()");
2551 pidToMinidump = new ChildMinidumpMap();
2553 dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
2555 FindPendingDir();
2556 UpdateCrashEventsDir();
2557 }
2559 static void
2560 OOPDeinit()
2561 {
2562 if (!OOPInitialized()) {
2563 NS_WARNING("OOPDeinit() without successful OOPInit()");
2564 return;
2565 }
2567 #ifdef MOZ_CRASHREPORTER_INJECTOR
2568 if (sInjectorThread) {
2569 sInjectorThread->Shutdown();
2570 NS_RELEASE(sInjectorThread);
2571 }
2572 #endif
2574 delete crashServer;
2575 crashServer = nullptr;
2577 delete dumpMapLock;
2578 dumpMapLock = nullptr;
2580 delete pidToMinidump;
2581 pidToMinidump = nullptr;
2583 #if defined(XP_WIN)
2584 PR_Free(childCrashNotifyPipe);
2585 childCrashNotifyPipe = nullptr;
2586 #endif
2587 }
2589 #if defined(XP_WIN) || defined(XP_MACOSX)
2590 // Parent-side API for children
2591 const char*
2592 GetChildNotificationPipe()
2593 {
2594 if (!GetEnabled())
2595 return kNullNotifyPipe;
2597 MOZ_ASSERT(OOPInitialized());
2599 return childCrashNotifyPipe;
2600 }
2601 #endif
2603 #ifdef MOZ_CRASHREPORTER_INJECTOR
2604 void
2605 InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
2606 {
2607 if (!GetEnabled())
2608 return;
2610 if (!OOPInitialized())
2611 OOPInit();
2613 if (!sInjectorThread) {
2614 if (NS_FAILED(NS_NewThread(&sInjectorThread)))
2615 return;
2616 }
2618 {
2619 MutexAutoLock lock(*dumpMapLock);
2620 ChildProcessData* pd = pidToMinidump->PutEntry(processID);
2621 MOZ_ASSERT(!pd->minidump && !pd->callback);
2622 pd->callback = cb;
2623 }
2625 nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
2626 sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
2627 }
2629 NS_IMETHODIMP
2630 ReportInjectedCrash::Run()
2631 {
2632 // Crash reporting may have been disabled after this method was dispatched
2633 if (!OOPInitialized())
2634 return NS_OK;
2636 InjectorCrashCallback* cb;
2637 {
2638 MutexAutoLock lock(*dumpMapLock);
2639 ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
2640 if (!pd || !pd->callback)
2641 return NS_OK;
2643 MOZ_ASSERT(pd->minidump);
2645 cb = pd->callback;
2646 }
2648 cb->OnCrash(mPID);
2649 return NS_OK;
2650 }
2652 void
2653 UnregisterInjectorCallback(DWORD processID)
2654 {
2655 if (!OOPInitialized())
2656 return;
2658 MutexAutoLock lock(*dumpMapLock);
2659 pidToMinidump->RemoveEntry(processID);
2660 }
2662 #endif // MOZ_CRASHREPORTER_INJECTOR
2664 bool
2665 CheckForLastRunCrash()
2666 {
2667 if (lastRunCrashID)
2668 return true;
2670 // The exception handler callback leaves the filename of the
2671 // last minidump in a known file.
2672 nsCOMPtr<nsIFile> lastCrashFile;
2673 CreateFileFromPath(crashMarkerFilename,
2674 getter_AddRefs(lastCrashFile));
2676 bool exists;
2677 if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
2678 return false;
2679 }
2681 nsAutoCString lastMinidump_contents;
2682 if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
2683 return false;
2684 }
2685 lastCrashFile->Remove(false);
2687 #ifdef XP_WIN
2688 // Ugly but effective.
2689 nsDependentString lastMinidump(
2690 reinterpret_cast<const char16_t*>(lastMinidump_contents.get()));
2691 #else
2692 nsAutoCString lastMinidump = lastMinidump_contents;
2693 #endif
2694 nsCOMPtr<nsIFile> lastMinidumpFile;
2695 CreateFileFromPath(lastMinidump.get(),
2696 getter_AddRefs(lastMinidumpFile));
2698 if (!lastMinidumpFile || NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
2699 return false;
2700 }
2702 nsCOMPtr<nsIFile> lastExtraFile;
2703 if (!GetExtraFileForMinidump(lastMinidumpFile,
2704 getter_AddRefs(lastExtraFile))) {
2705 return false;
2706 }
2708 FindPendingDir();
2710 // Move {dump,extra} to pending folder
2711 if (!MoveToPending(lastMinidumpFile, lastExtraFile)) {
2712 return false;
2713 }
2715 lastRunCrashID = new nsString();
2716 return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
2717 }
2719 bool
2720 GetLastRunCrashID(nsAString& id)
2721 {
2722 if (!lastRunCrashID_checked) {
2723 CheckForLastRunCrash();
2724 lastRunCrashID_checked = true;
2725 }
2727 if (!lastRunCrashID) {
2728 return false;
2729 }
2731 id = *lastRunCrashID;
2732 return true;
2733 }
2735 #if defined(XP_WIN)
2736 // Child-side API
2737 bool
2738 SetRemoteExceptionHandler(const nsACString& crashPipe)
2739 {
2740 // crash reporting is disabled
2741 if (crashPipe.Equals(kNullNotifyPipe))
2742 return true;
2744 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2746 gExceptionHandler = new google_breakpad::
2747 ExceptionHandler(L"",
2748 FPEFilter,
2749 nullptr, // no minidump callback
2750 nullptr, // no callback context
2751 google_breakpad::ExceptionHandler::HANDLER_ALL,
2752 MiniDumpNormal,
2753 NS_ConvertASCIItoUTF16(crashPipe).get(),
2754 nullptr);
2755 #ifdef XP_WIN
2756 gExceptionHandler->set_handle_debug_exceptions(true);
2757 #endif
2759 // we either do remote or nothing, no fallback to regular crash reporting
2760 return gExceptionHandler->IsOutOfProcess();
2761 }
2763 //--------------------------------------------------
2764 #elif defined(XP_LINUX)
2766 // Parent-side API for children
2767 bool
2768 CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
2769 {
2770 if (!GetEnabled()) {
2771 *childCrashFd = -1;
2772 *childCrashRemapFd = -1;
2773 return true;
2774 }
2776 MOZ_ASSERT(OOPInitialized());
2778 *childCrashFd = clientSocketFd;
2779 *childCrashRemapFd = kMagicChildCrashReportFd;
2781 return true;
2782 }
2784 // Child-side API
2785 bool
2786 SetRemoteExceptionHandler()
2787 {
2788 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2790 #ifndef XP_LINUX
2791 xpstring path = "";
2792 #else
2793 // MinidumpDescriptor requires a non-empty path.
2794 google_breakpad::MinidumpDescriptor path(".");
2795 #endif
2796 gExceptionHandler = new google_breakpad::
2797 ExceptionHandler(path,
2798 nullptr, // no filter callback
2799 nullptr, // no minidump callback
2800 nullptr, // no callback context
2801 true, // install signal handlers
2802 kMagicChildCrashReportFd);
2804 if (gDelayedAnnotations) {
2805 for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
2806 gDelayedAnnotations->ElementAt(i)->Run();
2807 }
2808 delete gDelayedAnnotations;
2809 }
2811 // we either do remote or nothing, no fallback to regular crash reporting
2812 return gExceptionHandler->IsOutOfProcess();
2813 }
2815 //--------------------------------------------------
2816 #elif defined(XP_MACOSX)
2817 // Child-side API
2818 bool
2819 SetRemoteExceptionHandler(const nsACString& crashPipe)
2820 {
2821 // crash reporting is disabled
2822 if (crashPipe.Equals(kNullNotifyPipe))
2823 return true;
2825 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
2827 gExceptionHandler = new google_breakpad::
2828 ExceptionHandler("",
2829 Filter,
2830 nullptr, // no minidump callback
2831 nullptr, // no callback context
2832 true, // install signal handlers
2833 crashPipe.BeginReading());
2835 // we either do remote or nothing, no fallback to regular crash reporting
2836 return gExceptionHandler->IsOutOfProcess();
2837 }
2838 #endif // XP_WIN
2841 bool
2842 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
2843 {
2844 if (!GetEnabled())
2845 return false;
2847 MutexAutoLock lock(*dumpMapLock);
2849 ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
2850 if (!pd)
2851 return false;
2853 NS_IF_ADDREF(*dump = pd->minidump);
2854 if (aSequence) {
2855 *aSequence = pd->sequence;
2856 }
2858 pidToMinidump->RemoveEntry(childPid);
2860 return !!*dump;
2861 }
2863 //-----------------------------------------------------------------------------
2864 // CreatePairedMinidumps() and helpers
2865 //
2867 void
2868 RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
2869 const nsACString& name)
2870 {
2871 nsCOMPtr<nsIFile> directory;
2872 childMinidump->GetParent(getter_AddRefs(directory));
2873 if (!directory)
2874 return;
2876 nsAutoCString leafName;
2877 childMinidump->GetNativeLeafName(leafName);
2879 // turn "<id>.dmp" into "<id>-<name>.dmp
2880 leafName.Insert(NS_LITERAL_CSTRING("-") + name, leafName.Length() - 4);
2882 minidump->MoveToNative(directory, leafName);
2883 }
2885 static bool
2886 PairedDumpCallback(
2887 #ifdef XP_LINUX
2888 const MinidumpDescriptor& descriptor,
2889 #else
2890 const XP_CHAR* dump_path,
2891 const XP_CHAR* minidump_id,
2892 #endif
2893 void* context,
2894 #ifdef XP_WIN32
2895 EXCEPTION_POINTERS* /*unused*/,
2896 MDRawAssertionInfo* /*unused*/,
2897 #endif
2898 bool succeeded)
2899 {
2900 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
2902 xpstring dump;
2903 #ifdef XP_LINUX
2904 dump = descriptor.path();
2905 #else
2906 dump = dump_path;
2907 dump += XP_PATH_SEPARATOR;
2908 dump += minidump_id;
2909 dump += dumpFileExtension;
2910 #endif
2912 CreateFileFromPath(dump, getter_AddRefs(minidump));
2913 return true;
2914 }
2916 static bool
2917 PairedDumpCallbackExtra(
2918 #ifdef XP_LINUX
2919 const MinidumpDescriptor& descriptor,
2920 #else
2921 const XP_CHAR* dump_path,
2922 const XP_CHAR* minidump_id,
2923 #endif
2924 void* context,
2925 #ifdef XP_WIN32
2926 EXCEPTION_POINTERS* /*unused*/,
2927 MDRawAssertionInfo* /*unused*/,
2928 #endif
2929 bool succeeded)
2930 {
2931 PairedDumpCallback(
2932 #ifdef XP_LINUX
2933 descriptor,
2934 #else
2935 dump_path, minidump_id,
2936 #endif
2937 context,
2938 #ifdef XP_WIN32
2939 nullptr, nullptr,
2940 #endif
2941 succeeded);
2943 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
2945 nsCOMPtr<nsIFile> extra;
2946 return WriteExtraForMinidump(minidump, Blacklist(), getter_AddRefs(extra));
2947 }
2949 ThreadId
2950 CurrentThreadId()
2951 {
2952 #if defined(XP_WIN)
2953 return ::GetCurrentThreadId();
2954 #elif defined(XP_LINUX)
2955 return sys_gettid();
2956 #elif defined(XP_MACOSX)
2957 // Just return an index, since Mach ports can't be directly serialized
2958 thread_act_port_array_t threads_for_task;
2959 mach_msg_type_number_t thread_count;
2961 if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
2962 return -1;
2964 for (unsigned int i = 0; i < thread_count; ++i) {
2965 if (threads_for_task[i] == mach_thread_self())
2966 return i;
2967 }
2968 abort();
2969 #else
2970 # error "Unsupported platform"
2971 #endif
2972 }
2974 #ifdef XP_MACOSX
2975 static mach_port_t
2976 GetChildThread(ProcessHandle childPid, ThreadId childBlamedThread)
2977 {
2978 mach_port_t childThread = MACH_PORT_NULL;
2979 thread_act_port_array_t threads_for_task;
2980 mach_msg_type_number_t thread_count;
2982 if (task_threads(childPid, &threads_for_task, &thread_count)
2983 == KERN_SUCCESS && childBlamedThread < thread_count) {
2984 childThread = threads_for_task[childBlamedThread];
2985 }
2987 return childThread;
2988 }
2989 #endif
2991 bool
2992 CreatePairedMinidumps(ProcessHandle childPid,
2993 ThreadId childBlamedThread,
2994 nsIFile** childDump)
2995 {
2996 if (!GetEnabled())
2997 return false;
2999 #ifdef XP_MACOSX
3000 mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
3001 #else
3002 ThreadId childThread = childBlamedThread;
3003 #endif
3005 xpstring dump_path;
3006 #ifndef XP_LINUX
3007 dump_path = gExceptionHandler->dump_path();
3008 #else
3009 dump_path = gExceptionHandler->minidump_descriptor().directory();
3010 #endif
3012 // dump the child
3013 nsCOMPtr<nsIFile> childMinidump;
3014 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
3015 childPid,
3016 childThread,
3017 dump_path,
3018 PairedDumpCallbackExtra,
3019 static_cast<void*>(&childMinidump)))
3020 return false;
3022 nsCOMPtr<nsIFile> childExtra;
3023 GetExtraFileForMinidump(childMinidump, getter_AddRefs(childExtra));
3025 // dump the parent
3026 nsCOMPtr<nsIFile> parentMinidump;
3027 if (!google_breakpad::ExceptionHandler::WriteMinidump(
3028 dump_path,
3029 #ifdef XP_MACOSX
3030 true, // write exception stream
3031 #endif
3032 PairedDumpCallback,
3033 static_cast<void*>(&parentMinidump))) {
3035 childMinidump->Remove(false);
3036 childExtra->Remove(false);
3038 return false;
3039 }
3041 // success
3042 RenameAdditionalHangMinidump(parentMinidump, childMinidump,
3043 NS_LITERAL_CSTRING("browser"));
3045 if (ShouldReport()) {
3046 MoveToPending(childMinidump, childExtra);
3047 MoveToPending(parentMinidump, nullptr);
3048 }
3050 childMinidump.forget(childDump);
3052 return true;
3053 }
3055 bool
3056 CreateAdditionalChildMinidump(ProcessHandle childPid,
3057 ThreadId childBlamedThread,
3058 nsIFile* parentMinidump,
3059 const nsACString& name)
3060 {
3061 if (!GetEnabled())
3062 return false;
3064 #ifdef XP_MACOSX
3065 mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
3066 #else
3067 ThreadId childThread = childBlamedThread;
3068 #endif
3070 xpstring dump_path;
3071 #ifndef XP_LINUX
3072 dump_path = gExceptionHandler->dump_path();
3073 #else
3074 dump_path = gExceptionHandler->minidump_descriptor().directory();
3075 #endif
3077 // dump the child
3078 nsCOMPtr<nsIFile> childMinidump;
3079 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
3080 childPid,
3081 childThread,
3082 dump_path,
3083 PairedDumpCallback,
3084 static_cast<void*>(&childMinidump))) {
3085 return false;
3086 }
3088 RenameAdditionalHangMinidump(childMinidump, parentMinidump, name);
3090 return true;
3091 }
3093 bool
3094 UnsetRemoteExceptionHandler()
3095 {
3096 delete gExceptionHandler;
3097 gExceptionHandler = nullptr;
3098 return true;
3099 }
3101 #if defined(MOZ_WIDGET_ANDROID)
3102 void AddLibraryMapping(const char* library_name,
3103 uintptr_t start_address,
3104 size_t mapping_length,
3105 size_t file_offset)
3106 {
3107 if (!gExceptionHandler) {
3108 mapping_info info;
3109 info.name = library_name;
3110 info.start_address = start_address;
3111 info.length = mapping_length;
3112 info.file_offset = file_offset;
3113 library_mappings.push_back(info);
3114 }
3115 else {
3116 u_int8_t guid[sizeof(MDGUID)];
3117 google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)start_address, guid);
3118 gExceptionHandler->AddMappingInfo(library_name,
3119 guid,
3120 start_address,
3121 mapping_length,
3122 file_offset);
3123 }
3124 }
3125 #endif
3127 } // namespace CrashReporter