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"
17 #ifdef WIN32_LEAN_AND_MEAN
18 #undef WIN32_LEAN_AND_MEAN
21 #include "nsIWindowsRegKey.h"
22 #include "client/windows/crash_generation/crash_generation_server.h"
23 #include "client/windows/handler/exception_handler.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"
34 #include <Carbon/Carbon.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <crt_externs.h>
38 #include <mach/mach.h>
39 #include <sys/types.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"
54 #include <sys/types.h>
56 #elif defined(XP_SOLARIS)
57 #include "client/solaris/handler/exception_handler.h"
59 #include <sys/types.h>
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
;
75 #include "mozilla/Mutex.h"
79 #include "nsIFileStreams.h"
80 #include "nsInterfaceHashtable.h"
82 #include "nsIXULAppInfo.h"
86 #include "mozilla/mozalloc_oom.h"
87 #include "mozilla/mozPoisonWrite.h"
89 #if defined(XP_MACOSX)
90 CFStringRef reporterClientAppID
= CFSTR("org.mozilla.crashreporter");
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
{
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)
120 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
122 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
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
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))
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
148 static const XP_CHAR dumpFileExtension
[] = {'.', 'd', 'm', 'p',
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;
240 // |dumpMapLock| must protect all access to |pidToMinidump|.
241 static Mutex
* dumpMapLock
;
242 struct ChildProcessData
: public nsUint32HashKey
244 ChildProcessData(KeyTypePointer aKey
)
245 : nsUint32HashKey(aKey
)
247 #ifdef MOZ_CRASHREPORTER_INJECTOR
252 nsCOMPtr
<nsIFile
> minidump
;
253 // Each crashing process is assigned an increasing sequence number to
254 // indicate which process crashed first.
256 #ifdef MOZ_CRASHREPORTER_INJECTOR
257 InjectorCrashCallback
* callback
;
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
272 ReportInjectedCrash(uint32_t pid
) : mPID(pid
) { }
279 #endif // MOZ_CRASHREPORTER_INJECTOR
281 // Crashreporter annotations that we don't send along in subprocess
283 static const char* kSubprocessBlacklist
[] = {
290 // If annotations are attempted before the crash reporter is enabled,
291 // they queue up here.
293 nsTArray
<nsAutoPtr
<DelayedNote
> >* gDelayedAnnotations
;
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
) {
312 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter
);
315 // intercept attempts to change the filter
321 static cpu_type_t pref_cpu_types
[2] = {
322 #if defined(__i386__)
324 #elif defined(__x86_64__)
326 #elif defined(__ppc__)
331 static posix_spawnattr_t spawnattr
;
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.
340 std::string debug_id
;
341 uintptr_t start_address
;
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
++) {
353 sscanf(file_id
, "%02X", &c
);
354 guid
[i
] = (u_int8_t
)(c
& 0xFF);
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
);
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
));
378 CreateFileFromPath(const xpstring
& path
, nsIFile
** file
)
380 NS_NewLocalFile(nsDependentString(path
.c_str()), false, file
);
384 CreateFileFromPath(const xpstring
& path
, nsIFile
** file
)
386 NS_NewNativeLocalFile(nsDependentCString(path
.c_str()), false, file
);
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
));
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
,
415 EXCEPTION_POINTERS
* exinfo
,
416 MDRawAssertionInfo
* assertion
,
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
];
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
,
442 if(hFile
!= INVALID_HANDLE_VALUE
) {
444 WriteFile(hFile
, minidumpPath
, 2*wcslen(minidumpPath
), &nBytes
, NULL
);
447 #elif defined(XP_UNIX)
448 int fd
= sys_open(crashMarkerFilename
,
449 O_WRONLY
| O_CREAT
| O_TRUNC
,
452 ssize_t ignored
= sys_write(fd
, minidumpPath
, my_strlen(minidumpPath
));
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.
470 struct kernel_timeval tv
;
471 sys_gettimeofday(&tv
, NULL
);
472 crashTime
= tv
.tv_sec
;
474 crashTime
= time(NULL
);
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
,
496 if(hFile
!= INVALID_HANDLE_VALUE
) {
498 WriteFile(hFile
, crashTimeString
, crashTimeStringLen
, &nBytes
, NULL
);
501 #elif defined(XP_UNIX)
502 int fd
= sys_open(lastCrashTimeFilename
,
503 O_WRONLY
| O_CREAT
| O_TRUNC
,
506 ssize_t ignored
= sys_write(fd
, crashTimeString
, crashTimeStringLen
);
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
,
519 if(hFile
!= INVALID_HANDLE_VALUE
) {
521 WriteFile(hFile
, crashReporterAPIData
->get(),
522 crashReporterAPIData
->Length(), &nBytes
, NULL
);
523 WriteFile(hFile
, kCrashTimeParameter
, kCrashTimeParameterLen
,
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
,
532 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
534 if (isGarbageCollecting
) {
535 WriteFile(hFile
, kIsGarbageCollectingParameter
, kIsGarbageCollectingParameterLen
,
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
)) {
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
,
570 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
580 XP_CHAR cmdLine
[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
);
589 PROCESS_INFORMATION pi
;
591 ZeroMemory(&si
, 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
,
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);
645 char* const my_argv
[] = {
652 char ***nsEnv
= _NSGetEnviron();
655 int result
= posix_spawnp(NULL
,
666 pid_t pid
= sys_fork();
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);
678 // Invoke the reportCrash activity using am
679 (void) execlp("/system/bin/am",
682 "-a", "org.mozilla.gecko.reportCrash",
683 "-n", crashReporterPath
,
684 "--es", "minidumpPath", minidumpPath
,
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
)
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
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
);
732 bool Filter(void* context
) {
733 mozilla::DisableWritePoisoning();
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
)
749 #if defined(MOZ_WIDGET_GONK)
751 headlessClient
= true;
752 #elif defined(XP_WIN)
753 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop
) {
754 doReport
= ShouldReport();
757 headlessClient
= true;
760 // this environment variable prevents us from launching
761 // the crash reporter client
762 doReport
= ShouldReport();
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"));
795 exePath
->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME
));
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
);
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
);
815 // get temp path to use for minidump path
816 #if defined(XP_WIN32)
819 // first figure out buffer size
820 int pathLen
= GetTempPath(0, NULL
);
822 return NS_ERROR_FAILURE
;
824 tempPath
.SetLength(pathLen
);
825 GetTempPath(pathLen
, (LPWSTR
)tempPath
.BeginWriting());
826 #elif defined(XP_MACOSX)
829 OSErr err
= FSFindFolder(kUserDomain
, kTemporaryFolderType
,
830 kCreateFolder
, &fsRef
);
832 return NS_ERROR_FAILURE
;
835 OSStatus status
= FSRefMakePath(&fsRef
, (UInt8
*)path
, PATH_MAX
);
837 return NS_ERROR_FAILURE
;
841 #elif defined(__ANDROID__)
842 // GeckoAppShell or Gonk's init.rc sets this in the environment
843 const char *tempenv
= PR_GetEnv("TMPDIR");
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/");
851 #error "Implement this for your platform"
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
,
866 &attr_ocount
) != 0 ||
867 attr_ocount
!= attr_count
) {
868 posix_spawnattr_destroy(&spawnattr
);
869 return NS_ERROR_FAILURE
;
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",
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
;
900 // now set the exception handler
901 gExceptionHandler
= new google_breakpad::
902 ExceptionHandler(tempPath
.get(),
910 #if defined(XP_WIN32)
911 google_breakpad::ExceptionHandler::HANDLER_ALL
,
913 (const wchar_t*) NULL
,
917 #if defined(XP_MACOSX)
923 if (!gExceptionHandler
)
924 return NS_ERROR_OUT_OF_MEMORY
;
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
);
937 printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
941 // store application start time
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
;
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
,
966 library_mappings
[i
].start_address
,
967 library_mappings
[i
].length
,
968 library_mappings
[i
].file_offset
);
972 mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize
);
979 return gExceptionHandler
!= nullptr;
982 bool GetMinidumpPath(nsAString
& aPath
)
984 if (!gExceptionHandler
)
987 aPath
= CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler
->dump_path().c_str());
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());
1002 WriteDataToFile(nsIFile
* aFile
, const nsACString
& data
)
1005 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
, 00600, &fd
);
1006 NS_ENSURE_SUCCESS(rv
, rv
);
1009 if (PR_Write(fd
, data
.Data(), data
.Length()) == -1) {
1010 rv
= NS_ERROR_FAILURE
;
1017 GetFileContents(nsIFile
* aFile
, nsACString
& data
)
1020 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
1021 NS_ENSURE_SUCCESS(rv
, rv
);
1024 int32_t filesize
= PR_Available(fd
);
1025 if (filesize
<= 0) {
1026 rv
= NS_ERROR_FILE_NOT_FOUND
;
1029 data
.SetLength(filesize
);
1030 if (PR_Read(fd
, data
.BeginWriting(), filesize
) == -1) {
1031 rv
= NS_ERROR_FAILURE
;
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.
1046 GetOrInit(nsIFile
* aDir
, const nsACString
& filename
,
1047 nsACString
& aContents
, InitDataFunc aInitFunc
)
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
);
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
);
1069 // didn't pass in an init func
1070 rv
= NS_ERROR_FAILURE
;
1074 // just get the file's contents
1075 rv
= GetFileContents(dataFile
, aContents
);
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".
1084 InitInstallTime(nsACString
& aInstallTime
)
1086 time_t t
= time(NULL
);
1088 sprintf(buf
, "%ld", t
);
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
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
);
1110 rv
= dataDirectory
->Exists(&exists
);
1111 NS_ENSURE_SUCCESS(rv
, rv
);
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());
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
);
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"),
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());
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());
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());
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());
1216 static void OOPDeinit();
1218 nsresult
UnsetExceptionHandler()
1221 // allow SetUnhandledExceptionFilter
1222 gBlockUnhandledExceptionFilter
= false;
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;
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;
1258 posix_spawnattr_destroy(&spawnattr
);
1261 if (!gExceptionHandler
)
1262 return NS_ERROR_NOT_INITIALIZED
;
1264 gExceptionHandler
= nullptr;
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
,
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.
1309 #pragma optimize("", off)
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
;
1323 // escape backslashes
1324 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\\"),
1325 NS_LITERAL_CSTRING("\\\\"));
1327 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\n"),
1328 NS_LITERAL_CSTRING("\\n"));
1332 #pragma optimize("", on)
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
) {}
1346 if (mType
== Annotation
) {
1347 AnnotateCrashReport(mKey
, mData
);
1349 AppendAppNotesToCrashReport(mData
);
1356 enum AnnotationType
{ Annotation
, AppNote
} mType
;
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
)
1371 return NS_ERROR_NOT_INITIALIZED
;
1373 nsCString escapedData
;
1374 nsresult rv
= EscapeAnnotation(key
, data
, escapedData
);
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();
1385 EnqueueDelayedNote(new DelayedNote(key
, data
));
1388 if (!reporter
->SendAnnotateCrashReport(nsCString(key
), escapedData
))
1389 return NS_ERROR_FAILURE
;
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
);
1405 nsresult
SetGarbageCollecting(bool collecting
)
1408 return NS_ERROR_NOT_INITIALIZED
;
1410 isGarbageCollecting
= collecting
;
1415 nsresult
AppendAppNotesToCrashReport(const nsACString
& data
)
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();
1430 EnqueueDelayedNote(new DelayedNote(data
));
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
1437 nsCString escapedData
;
1438 nsresult rv
= EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data
, escapedData
);
1442 if (!reporter
->SendAppendAppNotes(escapedData
))
1443 return NS_ERROR_FAILURE
;
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
)
1459 nsAutoCString entry
;
1460 if (!crashReporterAPIData_Hash
->Get(key
, &entry
))
1467 nsresult
RegisterAppMemory(void* ptr
, size_t length
)
1470 return NS_ERROR_NOT_INITIALIZED
;
1472 #if defined(XP_LINUX) || defined(XP_WIN32)
1473 gExceptionHandler
->RegisterAppMemory(ptr
, length
);
1476 return NS_ERROR_NOT_IMPLEMENTED
;
1480 nsresult
UnregisterAppMemory(void* ptr
)
1483 return NS_ERROR_NOT_INITIALIZED
;
1485 #if defined(XP_LINUX) || defined(XP_WIN32)
1486 gExceptionHandler
->UnregisterAppMemory(ptr
);
1489 return NS_ERROR_NOT_IMPLEMENTED
;
1493 bool GetServerURL(nsACString
& aServerURL
)
1495 if (!gExceptionHandler
)
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"),
1510 SetRestartArgs(int argc
, char** argv
)
1512 if (!gExceptionHandler
)
1516 nsAutoCString envVar
;
1518 char *argv0
= getenv("MOZ_APP_LAUNCHER");
1519 for (i
= 0; i
< argc
; i
++) {
1520 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
1521 envVar
.AppendInt(i
);
1523 if (argv0
&& i
== 0) {
1524 // Is there a request to suppress default binary launcher?
1530 // PR_SetEnv() wants the string to be available for the lifetime
1531 // of the app, so dup it here
1532 env
= ToNewCString(envVar
);
1534 return NS_ERROR_OUT_OF_MEMORY
;
1539 // make sure the arg list is terminated
1540 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
1541 envVar
.AppendInt(i
);
1544 // PR_SetEnv() wants the string to be available for the lifetime
1545 // of the app, so dup it here
1546 env
= ToNewCString(envVar
);
1548 return NS_ERROR_OUT_OF_MEMORY
;
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=";
1557 env
= ToNewCString(envVar
);
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
;
1575 nsresult
AppendObjCExceptionInfoToAppNotes(void *inException
)
1577 nsAutoCString excString
;
1578 GetObjCExceptionInfo(inException
, excString
);
1579 AppendAppNotesToCrashReport(excString
);
1585 * Combined code to get/set the crash reporter submission pref on
1586 * different platforms.
1588 static nsresult
PrefSubmitReports(bool* aSubmitReports
, bool writePref
)
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
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
);
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
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
);
1664 if (NS_SUCCEEDED(rv
)) {
1665 *aSubmitReports
= !!value
;
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;
1678 rv
= regKey
->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value
);
1679 // default to true on failure
1680 if (NS_FAILED(rv
)) {
1686 *aSubmitReports
= !!value
;
1688 #elif defined(XP_MACOSX)
1691 CFPropertyListRef cfValue
= (CFPropertyListRef
)(*aSubmitReports
? kCFBooleanTrue
: kCFBooleanFalse
);
1692 ::CFPreferencesSetAppValue(CFSTR("submitReport"),
1694 reporterClientAppID
);
1695 if (!::CFPreferencesAppSynchronize(reporterClientAppID
))
1696 rv
= NS_ERROR_FAILURE
;
1699 *aSubmitReports
= true;
1700 Boolean keyExistsAndHasValidFormat
= false;
1701 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
1702 reporterClientAppID
,
1703 &keyExistsAndHasValidFormat
);
1704 if (keyExistsAndHasValidFormat
)
1705 *aSubmitReports
= !!prefValue
;
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"));
1720 rv
= reporterINI
->Exists(&exists
);
1721 NS_ENSURE_SUCCESS(rv
, rv
);
1724 // If reading the pref, default to true if .ini doesn't exist.
1725 *aSubmitReports
= true;
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.
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);
1756 nsAutoCString submitReportValue
;
1757 rv
= iniParser
->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
1758 NS_LITERAL_CSTRING("SubmitReport"),
1761 // Default to "true" if the pref can't be found.
1763 *aSubmitReports
= true;
1764 else if (submitReportValue
.EqualsASCII("0"))
1765 *aSubmitReports
= false;
1767 *aSubmitReports
= true;
1771 return NS_ERROR_NOT_IMPLEMENTED
;
1775 nsresult
GetSubmitReports(bool* aSubmitReports
)
1777 return PrefSubmitReports(aSubmitReports
, false);
1780 nsresult
SetSubmitReports(bool aSubmitReports
)
1784 nsCOMPtr
<nsIObserverService
> obsServ
=
1785 mozilla::services::GetObserverService();
1787 return NS_ERROR_FAILURE
;
1790 rv
= PrefSubmitReports(&aSubmitReports
, true);
1791 if (NS_FAILED(rv
)) {
1795 obsServ
->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
1802 if (pendingDirectory
)
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");
1811 pendingDir
->Append(NS_LITERAL_STRING("Crash Reports"));
1812 pendingDir
->Append(NS_LITERAL_STRING("pending"));
1816 pendingDir
->GetPath(path
);
1817 pendingDirectory
= ToNewUnicode(path
);
1820 pendingDir
->GetNativePath(path
);
1821 pendingDirectory
= ToNewCString(path
);
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.
1830 GetPendingDir(nsIFile
** dir
)
1832 MOZ_ASSERT(OOPInitialized());
1833 if (!pendingDirectory
) {
1837 nsCOMPtr
<nsIFile
> pending
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
1839 NS_WARNING("Can't set up pending directory during shutdown.");
1843 pending
->InitWithPath(nsDependentString(pendingDirectory
));
1845 pending
->InitWithNativePath(nsDependentCString(pendingDirectory
));
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/
1858 GetMinidumpLimboDir(nsIFile
** dir
)
1860 if (ShouldReport()) {
1861 return GetPendingDir(dir
);
1864 CreateFileFromPath(gExceptionHandler
->dump_path(), dir
);
1865 return NULL
!= *dir
;
1870 GetMinidumpForID(const nsAString
& id
, nsIFile
** minidump
)
1872 if (!GetMinidumpLimboDir(minidump
))
1874 (*minidump
)->Append(id
+ NS_LITERAL_STRING(".dmp"));
1879 GetIDFromMinidump(nsIFile
* minidump
, nsAString
& id
)
1881 if (NS_SUCCEEDED(minidump
->GetLeafName(id
))) {
1882 id
.Replace(id
.Length() - 4, 4, NS_LITERAL_STRING(""));
1889 GetExtraFileForID(const nsAString
& id
, nsIFile
** extraFile
)
1891 if (!GetMinidumpLimboDir(extraFile
))
1893 (*extraFile
)->Append(id
+ NS_LITERAL_STRING(".extra"));
1898 GetExtraFileForMinidump(nsIFile
* minidump
, nsIFile
** extraFile
)
1900 nsAutoString leafName
;
1901 nsresult rv
= minidump
->GetLeafName(leafName
);
1905 nsCOMPtr
<nsIFile
> extraF
;
1906 rv
= minidump
->Clone(getter_AddRefs(extraF
));
1910 leafName
.Replace(leafName
.Length() - 3, 3,
1911 NS_LITERAL_STRING("extra"));
1912 rv
= extraF
->SetLeafName(leafName
);
1917 extraF
.swap(*extraFile
);
1922 AppendExtraData(const nsAString
& id
, const AnnotationTable
& data
)
1924 nsCOMPtr
<nsIFile
> extraFile
;
1925 if (!GetExtraFileForID(id
, getter_AddRefs(extraFile
)))
1927 return AppendExtraData(extraFile
, data
);
1930 //-----------------------------------------------------------------------------
1931 // Helpers for AppendExtraData()
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
]))
1944 const char** mItems
;
1948 struct EnumerateAnnotationsContext
{
1949 const Blacklist
& blacklist
;
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
,
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
;
1981 WriteExtraData(nsIFile
* extraFile
,
1982 const AnnotationTable
& data
,
1983 const Blacklist
& blacklist
,
1984 bool writeCrashTime
=false,
1985 bool truncate
=false)
1988 int truncOrAppend
= truncate
? PR_TRUNCATE
: PR_APPEND
;
1990 extraFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
| truncOrAppend
,
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);
2004 nsDependentCString("CrashTime"),
2005 nsDependentCString(crashTimeString
));
2013 AppendExtraData(nsIFile
* extraFile
, const AnnotationTable
& data
)
2015 return WriteExtraData(extraFile
, data
, Blacklist());
2020 WriteExtraForMinidump(nsIFile
* minidump
,
2021 const Blacklist
& blacklist
,
2022 nsIFile
** extraFile
)
2024 nsCOMPtr
<nsIFile
> extra
;
2025 if (!GetExtraFileForMinidump(minidump
, getter_AddRefs(extra
)))
2028 if (!WriteExtraData(extra
, *crashReporterAPIData_Hash
,
2030 true /*write crash time*/,
2035 extra
.swap(*extraFile
);
2040 // It really only makes sense to call this function when
2041 // ShouldReport() is true.
2043 MoveToPending(nsIFile
* dumpFile
, nsIFile
* extraFile
)
2045 nsCOMPtr
<nsIFile
> pendingDir
;
2046 if (!GetPendingDir(getter_AddRefs(pendingDir
)))
2049 if (NS_FAILED(dumpFile
->MoveTo(pendingDir
, EmptyString()))) {
2053 if (extraFile
&& NS_FAILED(extraFile
->MoveTo(pendingDir
, EmptyString()))) {
2061 OnChildProcessDumpRequested(void* aContext
,
2063 const ClientInfo
& aClientInfo
,
2064 const xpstring
& aFilePath
2066 const ClientInfo
* aClientInfo
,
2067 const xpstring
* aFilePath
2071 nsCOMPtr
<nsIFile
> minidump
;
2072 nsCOMPtr
<nsIFile
> extraFile
;
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!");
2093 if (!google_breakpad::WriteMinidump(aFilePath
->c_str(),
2095 aClientInfo
->crash_context
,
2096 aClientInfo
->crash_context_size
,
2102 if (!WriteExtraForMinidump(minidump
,
2103 Blacklist(kSubprocessBlacklist
,
2104 ArrayLength(kSubprocessBlacklist
)),
2105 getter_AddRefs(extraFile
)))
2109 MoveToPending(minidump
, extraFile
);
2119 #ifdef MOZ_CRASHREPORTER_INJECTOR
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
;
2132 #ifdef MOZ_CRASHREPORTER_INJECTOR
2134 NS_DispatchToMainThread(new ReportInjectedCrash(pid
));
2142 return pidToMinidump
!= NULL
;
2145 static bool ChildFilter(void *context
) {
2146 mozilla::DisableWritePoisoning();
2153 if (OOPInitialized())
2156 MOZ_ASSERT(NS_IsMainThread());
2158 NS_ABORT_IF_FALSE(gExceptionHandler
!= NULL
,
2159 "attempt to initialize OOP crash reporter before in-process crashreporter!");
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
2176 #elif defined(XP_LINUX)
2177 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd
,
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;
2188 crashServer
= new CrashGenerationServer(
2190 OnChildProcessDumpRequested
, NULL
,
2191 NULL
, NULL
, // we don't care about process exit here
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
,
2205 OnChildProcessDumpRequested
, NULL
,
2207 true, // automatically generate dumps
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");
2225 if (!OOPInitialized()) {
2226 NS_WARNING("OOPDeinit() without successful OOPInit()");
2230 #ifdef MOZ_CRASHREPORTER_INJECTOR
2231 if (sInjectorThread
) {
2232 sInjectorThread
->Shutdown();
2233 NS_RELEASE(sInjectorThread
);
2243 delete pidToMinidump
;
2244 pidToMinidump
= NULL
;
2247 PR_Free(childCrashNotifyPipe
);
2248 childCrashNotifyPipe
= NULL
;
2252 #if defined(XP_WIN) || defined(XP_MACOSX)
2253 // Parent-side API for children
2255 GetChildNotificationPipe()
2258 return kNullNotifyPipe
;
2260 MOZ_ASSERT(OOPInitialized());
2262 return childCrashNotifyPipe
;
2266 #ifdef MOZ_CRASHREPORTER_INJECTOR
2268 InjectCrashReporterIntoProcess(DWORD processID
, InjectorCrashCallback
* cb
)
2273 if (!OOPInitialized())
2276 if (!sInjectorThread
) {
2277 if (NS_FAILED(NS_NewThread(&sInjectorThread
)))
2282 MutexAutoLock
lock(*dumpMapLock
);
2283 ChildProcessData
* pd
= pidToMinidump
->PutEntry(processID
);
2284 MOZ_ASSERT(!pd
->minidump
&& !pd
->callback
);
2288 nsCOMPtr
<nsIRunnable
> r
= new InjectCrashRunnable(processID
);
2289 sInjectorThread
->Dispatch(r
, nsIEventTarget::DISPATCH_NORMAL
);
2293 ReportInjectedCrash::Run()
2295 // Crash reporting may have been disabled after this method was dispatched
2296 if (!OOPInitialized())
2299 InjectorCrashCallback
* cb
;
2301 MutexAutoLock
lock(*dumpMapLock
);
2302 ChildProcessData
* pd
= pidToMinidump
->GetEntry(mPID
);
2303 if (!pd
|| !pd
->callback
)
2306 MOZ_ASSERT(pd
->minidump
);
2316 UnregisterInjectorCallback(DWORD processID
)
2318 if (!OOPInitialized())
2321 MutexAutoLock
lock(*dumpMapLock
);
2322 pidToMinidump
->RemoveEntry(processID
);
2325 #endif // MOZ_CRASHREPORTER_INJECTOR
2328 CheckForLastRunCrash()
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
));
2340 if (NS_FAILED(lastCrashFile
->Exists(&exists
)) || !exists
) {
2344 nsAutoCString lastMinidump_contents
;
2345 if (NS_FAILED(GetFileContents(lastCrashFile
, lastMinidump_contents
))) {
2348 lastCrashFile
->Remove(false);
2351 // Ugly but effective.
2352 nsDependentString
lastMinidump(
2353 reinterpret_cast<const PRUnichar
*>(lastMinidump_contents
.get()));
2355 nsAutoCString lastMinidump
= lastMinidump_contents
;
2357 nsCOMPtr
<nsIFile
> lastMinidumpFile
;
2358 CreateFileFromPath(lastMinidump
.get(),
2359 getter_AddRefs(lastMinidumpFile
));
2361 if (NS_FAILED(lastMinidumpFile
->Exists(&exists
)) || !exists
) {
2365 nsCOMPtr
<nsIFile
> lastExtraFile
;
2366 if (!GetExtraFileForMinidump(lastMinidumpFile
,
2367 getter_AddRefs(lastExtraFile
))) {
2373 // Move {dump,extra} to pending folder
2374 if (!MoveToPending(lastMinidumpFile
, lastExtraFile
)) {
2378 lastRunCrashID
= new nsString();
2379 return GetIDFromMinidump(lastMinidumpFile
, *lastRunCrashID
);
2383 GetLastRunCrashID(nsAString
& id
)
2385 if (!lastRunCrashID_checked
) {
2386 CheckForLastRunCrash();
2387 lastRunCrashID_checked
= true;
2390 if (!lastRunCrashID
) {
2394 id
= *lastRunCrashID
;
2401 SetRemoteExceptionHandler(const nsACString
& crashPipe
)
2403 // crash reporting is disabled
2404 if (crashPipe
.Equals(kNullNotifyPipe
))
2407 NS_ABORT_IF_FALSE(!gExceptionHandler
, "crash client already init'd");
2409 gExceptionHandler
= new google_breakpad::
2410 ExceptionHandler(L
"",
2412 NULL
, // no minidump callback
2413 NULL
, // no callback context
2414 google_breakpad::ExceptionHandler::HANDLER_ALL
,
2416 NS_ConvertASCIItoUTF16(crashPipe
).BeginReading(),
2419 gExceptionHandler
->set_handle_debug_exceptions(true);
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
2431 CreateNotificationPipeForChild(int* childCrashFd
, int* childCrashRemapFd
)
2433 if (!GetEnabled()) {
2435 *childCrashRemapFd
= -1;
2439 MOZ_ASSERT(OOPInitialized());
2441 *childCrashFd
= clientSocketFd
;
2442 *childCrashRemapFd
= kMagicChildCrashReportFd
;
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)
2476 SetRemoteExceptionHandler(const nsACString
& crashPipe
)
2478 // crash reporting is disabled
2479 if (crashPipe
.Equals(kNullNotifyPipe
))
2482 NS_ABORT_IF_FALSE(!gExceptionHandler
, "crash client already init'd");
2484 gExceptionHandler
= new google_breakpad::
2485 ExceptionHandler("",
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();
2499 TakeMinidumpForChild(uint32_t childPid
, nsIFile
** dump
, uint32_t* aSequence
)
2504 MutexAutoLock
lock(*dumpMapLock
);
2506 ChildProcessData
* pd
= pidToMinidump
->GetEntry(childPid
);
2510 NS_IF_ADDREF(*dump
= pd
->minidump
);
2512 *aSequence
= pd
->sequence
;
2515 pidToMinidump
->RemoveEntry(childPid
);
2520 //-----------------------------------------------------------------------------
2521 // CreatePairedMinidumps() and helpers
2525 RenameAdditionalHangMinidump(nsIFile
* minidump
, nsIFile
* childMinidump
,
2526 const nsACString
& name
)
2528 nsCOMPtr
<nsIFile
> directory
;
2529 childMinidump
->GetParent(getter_AddRefs(directory
));
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
);
2543 PairedDumpCallback(const XP_CHAR
* dump_path
,
2544 const XP_CHAR
* minidump_id
,
2547 EXCEPTION_POINTERS
* /*unused*/,
2548 MDRawAssertionInfo
* /*unused*/,
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
));
2564 PairedDumpCallbackExtra(const XP_CHAR
* dump_path
,
2565 const XP_CHAR
* minidump_id
,
2568 EXCEPTION_POINTERS
* /*unused*/,
2569 MDRawAssertionInfo
* /*unused*/,
2573 PairedDumpCallback(dump_path
, minidump_id
, context
,
2579 nsCOMPtr
<nsIFile
>& minidump
= *static_cast< nsCOMPtr
<nsIFile
>* >(context
);
2581 nsCOMPtr
<nsIFile
> extra
;
2582 return WriteExtraForMinidump(minidump
, Blacklist(), getter_AddRefs(extra
));
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
))
2600 for (unsigned int i
= 0; i
< thread_count
; ++i
) {
2601 if (threads_for_task
[i
] == mach_thread_self())
2606 # error "Unsupported platform"
2611 CreatePairedMinidumps(ProcessHandle childPid
,
2612 ThreadId childBlamedThread
,
2613 nsIFile
** childDump
)
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
];
2628 ThreadId childThread
= childBlamedThread
;
2632 nsCOMPtr
<nsIFile
> childMinidump
;
2633 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
2636 gExceptionHandler
->dump_path(),
2637 PairedDumpCallbackExtra
,
2638 static_cast<void*>(&childMinidump
)))
2641 nsCOMPtr
<nsIFile
> childExtra
;
2642 GetExtraFileForMinidump(childMinidump
, getter_AddRefs(childExtra
));
2645 nsCOMPtr
<nsIFile
> parentMinidump
;
2646 if (!google_breakpad::ExceptionHandler::WriteMinidump(
2647 gExceptionHandler
->dump_path(),
2648 true, // write exception stream
2650 static_cast<void*>(&parentMinidump
))) {
2652 childMinidump
->Remove(false);
2653 childExtra
->Remove(false);
2659 RenameAdditionalHangMinidump(parentMinidump
, childMinidump
,
2660 NS_LITERAL_CSTRING("browser"));
2662 if (ShouldReport()) {
2663 MoveToPending(childMinidump
, childExtra
);
2664 MoveToPending(parentMinidump
, nullptr);
2667 childMinidump
.forget(childDump
);
2673 UnsetRemoteExceptionHandler()
2675 delete gExceptionHandler
;
2676 gExceptionHandler
= NULL
;
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
,
2687 if (!gExceptionHandler
) {
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
);
2697 u_int8_t guid
[sizeof(MDGUID
)];
2698 FileIDToGUID(file_id
, guid
);
2699 gExceptionHandler
->AddMappingInfo(library_name
,
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
,
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
);
2738 } // namespace CrashReporter