1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Breakpad integration
17 * The Initial Developer of the Original Code is
18 * Ted Mielczarek <ted.mielczarek@gmail.com>
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
23 * Josh Aas <josh@mozilla.com>
24 * Justin Dolske <dolske@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsExceptionHandler.h"
43 #ifdef WIN32_LEAN_AND_MEAN
44 #undef WIN32_LEAN_AND_MEAN
47 #include "nsIWindowsRegKey.h"
49 # include "client/windows/crash_generation/crash_generation_server.h"
51 #include "client/windows/handler/exception_handler.h"
54 #elif defined(XP_MACOSX)
56 # include "client/mac/crash_generation/client_info.h"
57 # include "client/mac/crash_generation/crash_generation_server.h"
59 #include "client/mac/handler/exception_handler.h"
61 #include <Carbon/Carbon.h>
62 #include <CoreFoundation/CoreFoundation.h>
63 #include <crt_externs.h>
65 #include <mach/mach.h>
66 #include <sys/types.h>
69 #include "mac_utils.h"
70 #elif defined(XP_LINUX)
71 #include "nsDirectoryServiceUtils.h"
72 #include "nsDirectoryServiceDefs.h"
73 #include "nsIINIParser.h"
74 #include "common/linux/linux_libc_support.h"
75 #include "common/linux/linux_syscall_support.h"
77 # include "client/linux/crash_generation/client_info.h"
78 # include "client/linux/crash_generation/crash_generation_server.h"
80 #include "client/linux/handler/exception_handler.h"
81 #include "client/linux/minidump_writer/linux_dumper.h"
82 #include "client/linux/minidump_writer/minidump_writer.h"
84 #include <sys/types.h>
86 #elif defined(XP_SOLARIS)
87 #include "client/solaris/handler/exception_handler.h"
89 #include <sys/types.h>
92 #error "Not yet implemented for this platform"
93 #endif // defined(XP_WIN32)
100 #include "mozilla/Mutex.h"
103 #include "nsILocalFile.h"
104 #include "nsIFileStreams.h"
105 #include "nsInterfaceHashtable.h"
107 #include "nsIXULAppInfo.h"
111 #if defined(XP_MACOSX)
112 CFStringRef reporterClientAppID
= CFSTR("org.mozilla.crashreporter");
116 #include "nsIUUIDGenerator.h"
118 using google_breakpad::CrashGenerationServer
;
119 using google_breakpad::ClientInfo
;
120 using mozilla::Mutex
;
121 using mozilla::MutexAutoLock
;
124 namespace CrashReporter
{
127 typedef wchar_t XP_CHAR
;
128 typedef std::wstring xpstring
;
129 #define CONVERT_UTF16_TO_XP_CHAR(x) x
130 #define CONVERT_XP_CHAR_TO_UTF16(x) x
131 #define XP_STRLEN(x) wcslen(x)
132 #define my_strlen strlen
133 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
134 #define PATH_SEPARATOR "\\"
135 #define XP_PATH_SEPARATOR L"\\"
136 // sort of arbitrary, but MAX_PATH is kinda small
137 #define XP_PATH_MAX 4096
138 // "<reporter path>" "<minidump path>"
139 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
140 #ifdef _USE_32BIT_TIME_T
141 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
143 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
146 typedef char XP_CHAR
;
147 typedef std::string xpstring
;
148 #define CONVERT_UTF16_TO_XP_CHAR(x) NS_ConvertUTF16toUTF8(x)
149 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
150 #define CRASH_REPORTER_FILENAME "crashreporter"
151 #define PATH_SEPARATOR "/"
152 #define XP_PATH_SEPARATOR "/"
153 #define XP_PATH_MAX PATH_MAX
155 #define XP_STRLEN(x) my_strlen(x)
156 #define XP_TTOA(time, buffer, base) my_timetostring(time, buffer, sizeof(buffer))
158 #define XP_STRLEN(x) strlen(x)
159 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
160 #define my_strlen strlen
161 #define sys_close close
162 #define sys_fork fork
163 #define sys_open open
164 #define sys_write write
168 static const XP_CHAR dumpFileExtension
[] = {'.', 'd', 'm', 'p',
170 static const XP_CHAR extraFileExtension
[] = {'.', 'e', 'x', 't',
171 'r', 'a', '\0'}; // .extra
173 static google_breakpad::ExceptionHandler
* gExceptionHandler
= nsnull
;
175 static XP_CHAR
* crashReporterPath
;
177 // if this is false, we don't launch the crash reporter
178 static bool doReport
= true;
180 // if this is true, we pass the exception on to the OS crash reporter
181 static bool showOSCrashReporter
= false;
183 // The time of the last recorded crash, as a time_t value.
184 static time_t lastCrashTime
= 0;
185 // The pathname of a file to store the crash time in
186 static XP_CHAR lastCrashTimeFilename
[XP_PATH_MAX
] = {0};
188 // these are just here for readability
189 static const char kCrashTimeParameter
[] = "CrashTime=";
190 static const int kCrashTimeParameterLen
= sizeof(kCrashTimeParameter
)-1;
192 static const char kTimeSinceLastCrashParameter
[] = "SecondsSinceLastCrash=";
193 static const int kTimeSinceLastCrashParameterLen
=
194 sizeof(kTimeSinceLastCrashParameter
)-1;
196 static const char kSysMemoryParameter
[] = "SystemMemoryUsePercentage=";
197 static const int kSysMemoryParameterLen
= sizeof(kSysMemoryParameter
)-1;
199 static const char kTotalVirtualMemoryParameter
[] = "TotalVirtualMemory=";
200 static const int kTotalVirtualMemoryParameterLen
=
201 sizeof(kTotalVirtualMemoryParameter
)-1;
203 static const char kAvailableVirtualMemoryParameter
[] = "AvailableVirtualMemory=";
204 static const int kAvailableVirtualMemoryParameterLen
=
205 sizeof(kAvailableVirtualMemoryParameter
)-1;
207 // this holds additional data sent via the API
208 static AnnotationTable
* crashReporterAPIData_Hash
;
209 static nsCString
* crashReporterAPIData
= nsnull
;
210 static nsCString
* notesField
= nsnull
;
213 // OOP crash reporting
214 static CrashGenerationServer
* crashServer
; // chrome process has this
216 # if defined(XP_WIN) || defined(XP_MACOSX)
217 // If crash reporting is disabled, we hand out this "null" pipe to the
218 // child process and don't attempt to connect to a parent server.
219 static const char kNullNotifyPipe
[] = "-";
220 static char* childCrashNotifyPipe
;
222 # elif defined(XP_LINUX)
223 static int serverSocketFd
= -1;
224 static int clientSocketFd
= -1;
225 static const int kMagicChildCrashReportFd
= 4;
229 // |dumpMapLock| must protect all access to |pidToMinidump|.
230 static Mutex
* dumpMapLock
;
231 typedef nsInterfaceHashtable
<nsUint32HashKey
, nsILocalFile
> ChildMinidumpMap
;
232 static ChildMinidumpMap
* pidToMinidump
;
234 // Crashreporter annotations that we don't send along in subprocess
236 static const char* kSubprocessBlacklist
[] = {
247 static cpu_type_t pref_cpu_types
[2] = {
248 #if defined(__i386__)
250 #elif defined(__x86_64__)
252 #elif defined(__ppc__)
257 static posix_spawnattr_t spawnattr
;
260 #if defined(__ANDROID__)
261 // Android builds use a custom library loader,
262 // so the embedding will provide a list of shared
263 // libraries that are mapped into anonymous mappings.
266 std::string debug_id
;
267 uintptr_t start_address
;
271 static std::vector
<mapping_info
> library_mappings
;
272 typedef std::map
<PRUint32
,google_breakpad::MappingList
> MappingMap
;
273 static MappingMap child_library_mappings
;
275 void FileIDToGUID(const char* file_id
, u_int8_t guid
[sizeof(MDGUID
)])
277 for (int i
= 0; i
< sizeof(MDGUID
); i
++) {
279 sscanf(file_id
, "%02X", &c
);
280 guid
[i
] = (u_int8_t
)(c
& 0xFF);
283 // GUIDs are stored in network byte order.
284 uint32_t* data1
= reinterpret_cast<uint32_t*>(guid
);
285 *data1
= htonl(*data1
);
286 uint16_t* data2
= reinterpret_cast<uint16_t*>(guid
+ 4);
287 *data2
= htons(*data2
);
288 uint16_t* data3
= reinterpret_cast<uint16_t*>(guid
+ 6);
289 *data3
= htons(*data3
);
295 my_timetostring(time_t t
, char* buffer
, size_t buffer_length
)
297 my_memset(buffer
, 0, buffer_length
);
298 my_itos(buffer
, t
, my_int_len(t
));
304 CreateFileFromPath(const xpstring
& path
, nsILocalFile
** file
)
306 NS_NewLocalFile(nsDependentString(path
.c_str()), PR_FALSE
, file
);
310 CreateFileFromPath(const xpstring
& path
, nsILocalFile
** file
)
312 NS_NewNativeLocalFile(nsDependentCString(path
.c_str()), PR_FALSE
, file
);
317 Concat(XP_CHAR
* str
, const XP_CHAR
* toAppend
, int* size
)
319 int appendLen
= XP_STRLEN(toAppend
);
320 if (appendLen
>= *size
) appendLen
= *size
- 1;
322 memcpy(str
, toAppend
, appendLen
* sizeof(XP_CHAR
));
330 bool MinidumpCallback(const XP_CHAR
* dump_path
,
331 const XP_CHAR
* minidump_id
,
334 EXCEPTION_POINTERS
* exinfo
,
335 MDRawAssertionInfo
* assertion
,
339 bool returnValue
= showOSCrashReporter
? false : succeeded
;
341 static XP_CHAR minidumpPath
[XP_PATH_MAX
];
342 int size
= XP_PATH_MAX
;
343 XP_CHAR
* p
= Concat(minidumpPath
, dump_path
, &size
);
344 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
345 p
= Concat(p
, minidump_id
, &size
);
346 Concat(p
, dumpFileExtension
, &size
);
348 static XP_CHAR extraDataPath
[XP_PATH_MAX
];
350 p
= Concat(extraDataPath
, dump_path
, &size
);
351 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
352 p
= Concat(p
, minidump_id
, &size
);
353 Concat(p
, extraFileExtension
, &size
);
355 // calculate time since last crash (if possible), and store
356 // the time of this crash.
359 struct kernel_timeval tv
;
360 sys_gettimeofday(&tv
, NULL
);
361 crashTime
= tv
.tv_sec
;
363 crashTime
= time(NULL
);
365 time_t timeSinceLastCrash
= 0;
366 // stringified versions of the above
367 char crashTimeString
[32];
368 int crashTimeStringLen
= 0;
369 char timeSinceLastCrashString
[32];
370 int timeSinceLastCrashStringLen
= 0;
372 XP_TTOA(crashTime
, crashTimeString
, 10);
373 crashTimeStringLen
= my_strlen(crashTimeString
);
374 if (lastCrashTime
!= 0) {
375 timeSinceLastCrash
= crashTime
- lastCrashTime
;
376 XP_TTOA(timeSinceLastCrash
, timeSinceLastCrashString
, 10);
377 timeSinceLastCrashStringLen
= my_strlen(timeSinceLastCrashString
);
379 // write crash time to file
380 if (lastCrashTimeFilename
[0] != 0) {
381 #if defined(XP_WIN32)
382 HANDLE hFile
= CreateFile(lastCrashTimeFilename
, GENERIC_WRITE
, 0,
383 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
,
385 if(hFile
!= INVALID_HANDLE_VALUE
) {
387 WriteFile(hFile
, crashTimeString
, crashTimeStringLen
, &nBytes
, NULL
);
390 #elif defined(XP_UNIX)
391 int fd
= sys_open(lastCrashTimeFilename
,
392 O_WRONLY
| O_CREAT
| O_TRUNC
,
395 ssize_t ignored
= sys_write(fd
, crashTimeString
, crashTimeStringLen
);
402 #if defined(XP_WIN32)
403 XP_CHAR cmdLine
[CMDLINE_SIZE
];
405 p
= Concat(cmdLine
, L
"\"", &size
);
406 p
= Concat(p
, crashReporterPath
, &size
);
407 p
= Concat(p
, L
"\" \"", &size
);
408 p
= Concat(p
, minidumpPath
, &size
);
409 Concat(p
, L
"\"", &size
);
411 if (!crashReporterAPIData
->IsEmpty()) {
412 // write out API data
413 HANDLE hFile
= CreateFile(extraDataPath
, GENERIC_WRITE
, 0,
414 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
,
416 if(hFile
!= INVALID_HANDLE_VALUE
) {
418 WriteFile(hFile
, crashReporterAPIData
->get(),
419 crashReporterAPIData
->Length(), &nBytes
, NULL
);
420 WriteFile(hFile
, kCrashTimeParameter
, kCrashTimeParameterLen
,
422 WriteFile(hFile
, crashTimeString
, crashTimeStringLen
, &nBytes
, NULL
);
423 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
424 if (timeSinceLastCrash
!= 0) {
425 WriteFile(hFile
, kTimeSinceLastCrashParameter
,
426 kTimeSinceLastCrashParameterLen
, &nBytes
, NULL
);
427 WriteFile(hFile
, timeSinceLastCrashString
, timeSinceLastCrashStringLen
,
429 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
431 // Try to get some information about memory.
432 MEMORYSTATUSEX statex
;
433 statex
.dwLength
= sizeof(statex
);
434 if (GlobalMemoryStatusEx(&statex
)) {
437 WriteFile(hFile
, kSysMemoryParameter
,
438 kSysMemoryParameterLen
, &nBytes
, NULL
);
439 ltoa(statex
.dwMemoryLoad
, buffer
, 10);
440 bufferLen
= strlen(buffer
);
441 WriteFile(hFile
, buffer
, bufferLen
,
443 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
444 WriteFile(hFile
, kTotalVirtualMemoryParameter
,
445 kTotalVirtualMemoryParameterLen
, &nBytes
, NULL
);
446 _ui64toa(statex
.ullTotalVirtual
, buffer
, 10);
447 bufferLen
= strlen(buffer
);
448 WriteFile(hFile
, buffer
, bufferLen
,
450 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
451 WriteFile(hFile
, kAvailableVirtualMemoryParameter
,
452 kAvailableVirtualMemoryParameterLen
, &nBytes
, NULL
);
453 _ui64toa(statex
.ullAvailVirtual
, buffer
, 10);
454 bufferLen
= strlen(buffer
);
455 WriteFile(hFile
, buffer
, bufferLen
,
457 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
468 PROCESS_INFORMATION pi
;
470 ZeroMemory(&si
, sizeof(si
));
472 si
.dwFlags
= STARTF_USESHOWWINDOW
;
473 si
.wShowWindow
= SW_SHOWNORMAL
;
474 ZeroMemory(&pi
, sizeof(pi
));
476 if (CreateProcess(NULL
, (LPWSTR
)cmdLine
, NULL
, NULL
, FALSE
, 0,
477 NULL
, NULL
, &si
, &pi
)) {
478 CloseHandle( pi
.hProcess
);
479 CloseHandle( pi
.hThread
);
481 // we're not really in a position to do anything if the CreateProcess fails
482 TerminateProcess(GetCurrentProcess(), 1);
483 #elif defined(XP_UNIX)
484 if (!crashReporterAPIData
->IsEmpty()) {
485 // write out API data
486 int fd
= sys_open(extraDataPath
,
487 O_WRONLY
| O_CREAT
| O_TRUNC
,
491 // not much we can do in case of error
492 ssize_t ignored
= sys_write(fd
, crashReporterAPIData
->get(),
493 crashReporterAPIData
->Length());
494 ignored
= sys_write(fd
, kCrashTimeParameter
, kCrashTimeParameterLen
);
495 ignored
= sys_write(fd
, crashTimeString
, crashTimeStringLen
);
496 ignored
= sys_write(fd
, "\n", 1);
497 if (timeSinceLastCrash
!= 0) {
498 ignored
= sys_write(fd
, kTimeSinceLastCrashParameter
,
499 kTimeSinceLastCrashParameterLen
);
500 ignored
= sys_write(fd
, timeSinceLastCrashString
,
501 timeSinceLastCrashStringLen
);
502 ignored
= sys_write(fd
, "\n", 1);
513 char* const my_argv
[] = {
520 char ***nsEnv
= _NSGetEnviron();
523 int result
= posix_spawnp(NULL
,
534 pid_t pid
= sys_fork();
539 #if !defined(__ANDROID__)
540 // need to clobber this, as libcurl might load NSS,
541 // and we want it to load the system NSS.
542 unsetenv("LD_LIBRARY_PATH");
543 (void) execl(crashReporterPath
,
544 crashReporterPath
, minidumpPath
, (char*)0);
546 // Invoke the reportCrash activity using am
547 (void) execlp("/system/bin/am",
550 "-a", "org.mozilla.gecko.reportCrash",
551 "-n", crashReporterPath
,
552 "--es", "minidumpPath", minidumpPath
,
565 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
566 * and should not be handled as crashes.
568 static bool FPEFilter(void* context
, EXCEPTION_POINTERS
* exinfo
,
569 MDRawAssertionInfo
* assertion
)
574 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)exinfo
->ExceptionRecord
;
575 switch (e
->ExceptionCode
) {
576 case STATUS_FLOAT_DENORMAL_OPERAND
:
577 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
578 case STATUS_FLOAT_INEXACT_RESULT
:
579 case STATUS_FLOAT_INVALID_OPERATION
:
580 case STATUS_FLOAT_OVERFLOW
:
581 case STATUS_FLOAT_STACK_CHECK
:
582 case STATUS_FLOAT_UNDERFLOW
:
583 case STATUS_FLOAT_MULTIPLE_FAULTS
:
584 case STATUS_FLOAT_MULTIPLE_TRAPS
:
585 return false; // Don't write minidump, continue exception search
591 static bool ShouldReport()
593 // this environment variable prevents us from launching
594 // the crash reporter client
595 const char *envvar
= PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
596 return !(envvar
&& *envvar
);
599 nsresult
SetExceptionHandler(nsILocalFile
* aXREDirectory
,
600 bool force
/*=false*/)
604 if (gExceptionHandler
)
605 return NS_ERROR_ALREADY_INITIALIZED
;
607 const char *envvar
= PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
608 if (envvar
&& *envvar
&& !force
)
611 // this environment variable prevents us from launching
612 // the crash reporter client
613 doReport
= ShouldReport();
615 // allocate our strings
616 crashReporterAPIData
= new nsCString();
617 NS_ENSURE_TRUE(crashReporterAPIData
, NS_ERROR_OUT_OF_MEMORY
);
619 crashReporterAPIData_Hash
=
620 new nsDataHashtable
<nsCStringHashKey
,nsCString
>();
621 NS_ENSURE_TRUE(crashReporterAPIData_Hash
, NS_ERROR_OUT_OF_MEMORY
);
623 rv
= crashReporterAPIData_Hash
->Init();
624 NS_ENSURE_SUCCESS(rv
, rv
);
626 notesField
= new nsCString();
627 NS_ENSURE_TRUE(notesField
, NS_ERROR_OUT_OF_MEMORY
);
629 // locate crashreporter executable
630 nsCOMPtr
<nsIFile
> exePath
;
631 rv
= aXREDirectory
->Clone(getter_AddRefs(exePath
));
632 NS_ENSURE_SUCCESS(rv
, rv
);
634 #if defined(XP_MACOSX)
635 exePath
->Append(NS_LITERAL_STRING("crashreporter.app"));
636 exePath
->Append(NS_LITERAL_STRING("Contents"));
637 exePath
->Append(NS_LITERAL_STRING("MacOS"));
640 exePath
->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME
));
643 nsString crashReporterPath_temp
;
644 exePath
->GetPath(crashReporterPath_temp
);
646 crashReporterPath
= ToNewUnicode(crashReporterPath_temp
);
647 #elif !defined(__ANDROID__)
648 nsCString crashReporterPath_temp
;
649 exePath
->GetNativePath(crashReporterPath_temp
);
651 crashReporterPath
= ToNewCString(crashReporterPath_temp
);
653 // On Android, we launch using the application package name
654 // instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
655 //TODO: don't hardcode org.mozilla here, so other vendors can
656 // ship XUL apps with different package names on Android?
657 nsCString
package(ANDROID_PACKAGE_NAME
"/.CrashReporter");
658 crashReporterPath
= ToNewCString(package
);
661 // get temp path to use for minidump path
662 #if defined(XP_WIN32)
665 // first figure out buffer size
666 int pathLen
= GetTempPath(0, NULL
);
668 return NS_ERROR_FAILURE
;
670 tempPath
.SetLength(pathLen
);
671 GetTempPath(pathLen
, (LPWSTR
)tempPath
.BeginWriting());
672 #elif defined(XP_MACOSX)
675 OSErr err
= FSFindFolder(kUserDomain
, kTemporaryFolderType
,
676 kCreateFolder
, &fsRef
);
678 return NS_ERROR_FAILURE
;
681 OSStatus status
= FSRefMakePath(&fsRef
, (UInt8
*)path
, PATH_MAX
);
683 return NS_ERROR_FAILURE
;
687 #elif defined(__ANDROID__)
688 // GeckoAppShell sets this in the environment
689 const char *tempenv
= PR_GetEnv("TMPDIR");
691 return NS_ERROR_FAILURE
;
692 nsCString
tempPath(tempenv
);
694 #elif defined(XP_UNIX)
695 // we assume it's always /tmp on unix systems
696 nsCString tempPath
= NS_LITERAL_CSTRING("/tmp/");
698 #error "Implement this for your platform"
702 // Initialize spawn attributes, since this calls malloc.
703 if (posix_spawnattr_init(&spawnattr
) != 0) {
704 return NS_ERROR_FAILURE
;
707 // Set spawn attributes.
708 size_t attr_count
= NS_ARRAY_LENGTH(pref_cpu_types
);
709 size_t attr_ocount
= 0;
710 if (posix_spawnattr_setbinpref_np(&spawnattr
,
713 &attr_ocount
) != 0 ||
714 attr_ocount
!= attr_count
) {
715 posix_spawnattr_destroy(&spawnattr
);
716 return NS_ERROR_FAILURE
;
721 MINIDUMP_TYPE minidump_type
= MiniDumpNormal
;
723 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
724 // Try to determine what version of dbghelp.dll we're using.
725 // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
727 DWORD version_size
= GetFileVersionInfoSizeW(L
"dbghelp.dll", NULL
);
728 if (version_size
> 0) {
729 std::vector
<BYTE
> buffer(version_size
);
730 if (GetFileVersionInfoW(L
"dbghelp.dll",
735 VS_FIXEDFILEINFO
* file_info
;
736 VerQueryValue(&buffer
[0], L
"\\", (void**)&file_info
, &len
);
737 WORD major
= HIWORD(file_info
->dwFileVersionMS
),
738 minor
= LOWORD(file_info
->dwFileVersionMS
),
739 revision
= HIWORD(file_info
->dwFileVersionLS
);
740 if (major
> 6 || (major
== 6 && minor
> 1) ||
741 (major
== 6 && minor
== 1 && revision
>= 7600)) {
742 minidump_type
= MiniDumpWithFullMemoryInfo
;
746 #endif // MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
749 // now set the exception handler
750 gExceptionHandler
= new google_breakpad::
751 ExceptionHandler(tempPath
.get(),
759 #if defined(XP_WIN32)
760 google_breakpad::ExceptionHandler::HANDLER_ALL
,
766 #if defined(XP_MACOSX)
772 if (!gExceptionHandler
)
773 return NS_ERROR_OUT_OF_MEMORY
;
776 gExceptionHandler
->set_handle_debug_exceptions(true);
779 // store application start time
781 time_t startupTime
= time(NULL
);
782 XP_TTOA(startupTime
, timeString
, 10);
783 AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
784 nsDependentCString(timeString
));
786 #if defined(XP_MACOSX)
787 // On OS X, many testers like to see the OS crash reporting dialog
788 // since it offers immediate stack traces. We allow them to set
789 // a default to pass exceptions to the OS handler.
790 Boolean keyExistsAndHasValidFormat
= false;
791 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
792 kCFPreferencesCurrentApplication
,
793 &keyExistsAndHasValidFormat
);
794 if (keyExistsAndHasValidFormat
)
795 showOSCrashReporter
= prefValue
;
798 #if defined(__ANDROID__)
799 for (unsigned int i
= 0; i
< library_mappings
.size(); i
++) {
800 u_int8_t guid
[sizeof(MDGUID
)];
801 FileIDToGUID(library_mappings
[i
].debug_id
.c_str(), guid
);
802 gExceptionHandler
->AddMappingInfo(library_mappings
[i
].name
,
804 library_mappings
[i
].start_address
,
805 library_mappings
[i
].length
,
806 library_mappings
[i
].file_offset
);
815 return gExceptionHandler
!= nsnull
&& !gExceptionHandler
->IsOutOfProcess();
818 bool GetMinidumpPath(nsAString
& aPath
)
820 if (!gExceptionHandler
)
823 aPath
= CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler
->dump_path().c_str());
827 nsresult
SetMinidumpPath(const nsAString
& aPath
)
829 if (!gExceptionHandler
)
830 return NS_ERROR_NOT_INITIALIZED
;
832 gExceptionHandler
->set_dump_path(CONVERT_UTF16_TO_XP_CHAR(aPath
).BeginReading());
838 WriteDataToFile(nsIFile
* aFile
, const nsACString
& data
)
840 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(aFile
);
841 NS_ENSURE_TRUE(localFile
, NS_ERROR_FAILURE
);
844 nsresult rv
= localFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
, 00600,
846 NS_ENSURE_SUCCESS(rv
, rv
);
849 if (PR_Write(fd
, data
.Data(), data
.Length()) == -1) {
850 rv
= NS_ERROR_FAILURE
;
857 GetFileContents(nsIFile
* aFile
, nsACString
& data
)
859 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(aFile
);
860 NS_ENSURE_TRUE(localFile
, NS_ERROR_FAILURE
);
863 nsresult rv
= localFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
864 NS_ENSURE_SUCCESS(rv
, rv
);
867 PRInt32 filesize
= PR_Available(fd
);
869 rv
= NS_ERROR_FILE_NOT_FOUND
;
872 data
.SetLength(filesize
);
873 if (PR_Read(fd
, data
.BeginWriting(), filesize
) == -1) {
874 rv
= NS_ERROR_FAILURE
;
881 // Function typedef for initializing a piece of data that we
882 // don't already have.
883 typedef nsresult (*InitDataFunc
)(nsACString
&);
885 // Attempt to read aFile's contents into aContents, if aFile
886 // does not exist, create it and initialize its contents
887 // by calling aInitFunc for the data.
889 GetOrInit(nsIFile
* aDir
, const nsACString
& filename
,
890 nsACString
& aContents
, InitDataFunc aInitFunc
)
894 nsCOMPtr
<nsIFile
> dataFile
;
895 nsresult rv
= aDir
->Clone(getter_AddRefs(dataFile
));
896 NS_ENSURE_SUCCESS(rv
, rv
);
898 rv
= dataFile
->AppendNative(filename
);
899 NS_ENSURE_SUCCESS(rv
, rv
);
901 rv
= dataFile
->Exists(&exists
);
902 NS_ENSURE_SUCCESS(rv
, rv
);
906 // get the initial value and write it to the file
907 rv
= aInitFunc(aContents
);
908 NS_ENSURE_SUCCESS(rv
, rv
);
909 rv
= WriteDataToFile(dataFile
, aContents
);
912 // didn't pass in an init func
913 rv
= NS_ERROR_FAILURE
;
917 // just get the file's contents
918 rv
= GetFileContents(dataFile
, aContents
);
924 // Init the "install time" data. We're taking an easy way out here
925 // and just setting this to "the time when this version was first run".
927 InitInstallTime(nsACString
& aInstallTime
)
929 time_t t
= time(NULL
);
931 sprintf(buf
, "%ld", t
);
937 // Annotate the crash report with a Unique User ID and time
938 // since install. Also do some prep work for recording
939 // time since last crash, which must be calculated at
941 // If any piece of data doesn't exist, initialize it first.
942 nsresult
SetupExtraData(nsILocalFile
* aAppDataDirectory
,
943 const nsACString
& aBuildID
)
945 nsCOMPtr
<nsIFile
> dataDirectory
;
946 nsresult rv
= aAppDataDirectory
->Clone(getter_AddRefs(dataDirectory
));
947 NS_ENSURE_SUCCESS(rv
, rv
);
949 rv
= dataDirectory
->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
950 NS_ENSURE_SUCCESS(rv
, rv
);
953 rv
= dataDirectory
->Exists(&exists
);
954 NS_ENSURE_SUCCESS(rv
, rv
);
957 rv
= dataDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
958 NS_ENSURE_SUCCESS(rv
, rv
);
961 #if defined(XP_WIN32)
962 nsAutoString
dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
964 nsAutoString dataDirectoryPath
;
965 rv
= dataDirectory
->GetPath(dataDirectoryPath
);
966 NS_ENSURE_SUCCESS(rv
, rv
);
968 dataDirEnv
.Append(dataDirectoryPath
);
970 _wputenv(dataDirEnv
.get());
972 // Save this path in the environment for the crash reporter application.
973 nsCAutoString
dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
975 nsCAutoString dataDirectoryPath
;
976 rv
= dataDirectory
->GetNativePath(dataDirectoryPath
);
977 NS_ENSURE_SUCCESS(rv
, rv
);
979 dataDirEnv
.Append(dataDirectoryPath
);
981 char* env
= ToNewCString(dataDirEnv
);
982 NS_ENSURE_TRUE(env
, NS_ERROR_OUT_OF_MEMORY
);
988 if(NS_SUCCEEDED(GetOrInit(dataDirectory
,
989 NS_LITERAL_CSTRING("InstallTime") + aBuildID
,
990 data
, InitInstallTime
)))
991 AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data
);
993 // this is a little different, since we can't init it with anything,
994 // since it's stored at crash time, and we can't annotate the
995 // crash report with the stored value, since we really want
996 // (now - LastCrash), so we just get a value if it exists,
997 // and store it in a time_t value.
998 if(NS_SUCCEEDED(GetOrInit(dataDirectory
, NS_LITERAL_CSTRING("LastCrash"),
1000 lastCrashTime
= (time_t)atol(data
.get());
1003 // not really the best place to init this, but I have the path I need here
1004 nsCOMPtr
<nsIFile
> lastCrashFile
;
1005 rv
= dataDirectory
->Clone(getter_AddRefs(lastCrashFile
));
1006 NS_ENSURE_SUCCESS(rv
, rv
);
1008 rv
= lastCrashFile
->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
1009 NS_ENSURE_SUCCESS(rv
, rv
);
1010 memset(lastCrashTimeFilename
, 0, sizeof(lastCrashTimeFilename
));
1012 #if defined(XP_WIN32)
1013 nsAutoString filename
;
1014 rv
= lastCrashFile
->GetPath(filename
);
1015 NS_ENSURE_SUCCESS(rv
, rv
);
1017 if (filename
.Length() < XP_PATH_MAX
)
1018 wcsncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
1020 nsCAutoString filename
;
1021 rv
= lastCrashFile
->GetNativePath(filename
);
1022 NS_ENSURE_SUCCESS(rv
, rv
);
1024 if (filename
.Length() < XP_PATH_MAX
)
1025 strncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
1031 static void OOPDeinit();
1033 nsresult
UnsetExceptionHandler()
1035 delete gExceptionHandler
;
1037 // do this here in the unlikely case that we succeeded in allocating
1038 // our strings but failed to allocate gExceptionHandler.
1039 if (crashReporterAPIData_Hash
) {
1040 delete crashReporterAPIData_Hash
;
1041 crashReporterAPIData_Hash
= nsnull
;
1044 if (crashReporterAPIData
) {
1045 delete crashReporterAPIData
;
1046 crashReporterAPIData
= nsnull
;
1051 notesField
= nsnull
;
1054 if (crashReporterPath
) {
1055 NS_Free(crashReporterPath
);
1056 crashReporterPath
= nsnull
;
1060 posix_spawnattr_destroy(&spawnattr
);
1063 if (!gExceptionHandler
)
1064 return NS_ERROR_NOT_INITIALIZED
;
1066 gExceptionHandler
= nsnull
;
1075 static void ReplaceChar(nsCString
& str
, const nsACString
& character
,
1076 const nsACString
& replacement
)
1078 nsCString::const_iterator start
, end
;
1080 str
.BeginReading(start
);
1081 str
.EndReading(end
);
1083 while (FindInReadable(character
, start
, end
)) {
1084 PRInt32 pos
= end
.size_backward();
1085 str
.Replace(pos
- 1, 1, replacement
);
1087 str
.BeginReading(start
);
1088 start
.advance(pos
+ replacement
.Length() - 1);
1089 str
.EndReading(end
);
1093 static PRBool
DoFindInReadable(const nsACString
& str
, const nsACString
& value
)
1095 nsACString::const_iterator start
, end
;
1096 str
.BeginReading(start
);
1097 str
.EndReading(end
);
1099 return FindInReadable(value
, start
, end
);
1102 static PLDHashOperator
EnumerateEntries(const nsACString
& key
,
1106 crashReporterAPIData
->Append(key
+ NS_LITERAL_CSTRING("=") + entry
+
1107 NS_LITERAL_CSTRING("\n"));
1108 return PL_DHASH_NEXT
;
1111 nsresult
AnnotateCrashReport(const nsACString
& key
, const nsACString
& data
)
1114 return NS_ERROR_NOT_INITIALIZED
;
1116 if (DoFindInReadable(key
, NS_LITERAL_CSTRING("=")) ||
1117 DoFindInReadable(key
, NS_LITERAL_CSTRING("\n")))
1118 return NS_ERROR_INVALID_ARG
;
1120 if (DoFindInReadable(data
, NS_LITERAL_CSTRING("\0")))
1121 return NS_ERROR_INVALID_ARG
;
1123 nsCString
escapedData(data
);
1125 // escape backslashes
1126 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\\"),
1127 NS_LITERAL_CSTRING("\\\\"));
1129 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\n"),
1130 NS_LITERAL_CSTRING("\\n"));
1132 nsresult rv
= crashReporterAPIData_Hash
->Put(key
, escapedData
);
1133 NS_ENSURE_SUCCESS(rv
, rv
);
1135 // now rebuild the file contents
1136 crashReporterAPIData
->Truncate(0);
1137 crashReporterAPIData_Hash
->EnumerateRead(EnumerateEntries
,
1138 crashReporterAPIData
);
1143 nsresult
AppendAppNotesToCrashReport(const nsACString
& data
)
1146 return NS_ERROR_NOT_INITIALIZED
;
1148 if (DoFindInReadable(data
, NS_LITERAL_CSTRING("\0")))
1149 return NS_ERROR_INVALID_ARG
;
1151 notesField
->Append(data
);
1152 return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField
);
1155 // Returns true if found, false if not found.
1156 bool GetAnnotation(const nsACString
& key
, nsACString
& data
)
1158 if (!gExceptionHandler
)
1161 nsCAutoString entry
;
1162 if (!crashReporterAPIData_Hash
->Get(key
, &entry
))
1169 bool GetServerURL(nsACString
& aServerURL
)
1171 if (!gExceptionHandler
)
1174 return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL
);
1177 nsresult
SetServerURL(const nsACString
& aServerURL
)
1179 // store server URL with the API data
1180 // the client knows to handle this specially
1181 return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
1186 SetRestartArgs(int argc
, char** argv
)
1188 if (!gExceptionHandler
)
1192 nsCAutoString envVar
;
1194 for (i
= 0; i
< argc
; i
++) {
1195 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
1196 envVar
.AppendInt(i
);
1198 #if defined(XP_UNIX) && !defined(XP_MACOSX)
1199 // we'd like to run the script around the binary
1200 // instead of the binary itself, so remove the -bin
1201 // if it exists on the first argument
1204 (arg_len
= strlen(argv
[i
])) > 4 &&
1205 strcmp(argv
[i
] + arg_len
- 4, "-bin") == 0) {
1206 envVar
.Append(argv
[i
], arg_len
- 4);
1213 // PR_SetEnv() wants the string to be available for the lifetime
1214 // of the app, so dup it here
1215 env
= ToNewCString(envVar
);
1217 return NS_ERROR_OUT_OF_MEMORY
;
1222 // make sure the arg list is terminated
1223 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
1224 envVar
.AppendInt(i
);
1227 // PR_SetEnv() wants the string to be available for the lifetime
1228 // of the app, so dup it here
1229 env
= ToNewCString(envVar
);
1231 return NS_ERROR_OUT_OF_MEMORY
;
1235 // make sure we save the info in XUL_APP_FILE for the reporter
1236 const char *appfile
= PR_GetEnv("XUL_APP_FILE");
1237 if (appfile
&& *appfile
) {
1238 envVar
= "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
1240 env
= ToNewCString(envVar
);
1248 nsresult
WriteMinidumpForException(EXCEPTION_POINTERS
* aExceptionInfo
)
1250 if (!gExceptionHandler
)
1251 return NS_ERROR_NOT_INITIALIZED
;
1253 return gExceptionHandler
->WriteMinidumpForException(aExceptionInfo
) ? NS_OK
: NS_ERROR_FAILURE
;
1258 nsresult
AppendObjCExceptionInfoToAppNotes(void *inException
)
1260 nsCAutoString excString
;
1261 GetObjCExceptionInfo(inException
, excString
);
1262 AppendAppNotesToCrashReport(excString
);
1268 * Combined code to get/set the crash reporter submission pref on
1269 * different platforms.
1271 static nsresult
PrefSubmitReports(PRBool
* aSubmitReports
, bool writePref
)
1274 #if defined(XP_WIN32)
1276 * NOTE! This needs to stay in sync with the preference checking code
1277 * in toolkit/crashreporter/client/crashreporter_win.cpp
1279 nsCOMPtr
<nsIXULAppInfo
> appinfo
=
1280 do_GetService("@mozilla.org/xre/app-info;1", &rv
);
1281 NS_ENSURE_SUCCESS(rv
, rv
);
1283 nsCAutoString appVendor
, appName
;
1284 rv
= appinfo
->GetVendor(appVendor
);
1285 NS_ENSURE_SUCCESS(rv
, rv
);
1286 rv
= appinfo
->GetName(appName
);
1287 NS_ENSURE_SUCCESS(rv
, rv
);
1289 nsCOMPtr
<nsIWindowsRegKey
> regKey
1290 (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv
));
1291 NS_ENSURE_SUCCESS(rv
, rv
);
1293 nsCAutoString regPath
;
1295 regPath
.AppendLiteral("Software\\");
1296 if(!appVendor
.IsEmpty()) {
1297 regPath
.Append(appVendor
);
1298 regPath
.AppendLiteral("\\");
1300 regPath
.Append(appName
);
1301 regPath
.AppendLiteral("\\Crash Reporter");
1303 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
1306 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
1307 NS_ConvertUTF8toUTF16(regPath
),
1308 nsIWindowsRegKey::ACCESS_SET_VALUE
);
1309 NS_ENSURE_SUCCESS(rv
, rv
);
1311 PRUint32 value
= *aSubmitReports
? 1 : 0;
1312 rv
= regKey
->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value
);
1317 // We're reading the pref value, so we need to first look under
1318 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
1319 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
1322 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
1323 NS_ConvertUTF8toUTF16(regPath
),
1324 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
1325 if (NS_SUCCEEDED(rv
)) {
1326 rv
= regKey
->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value
);
1328 if (NS_SUCCEEDED(rv
)) {
1329 *aSubmitReports
= !!value
;
1334 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
1335 NS_ConvertUTF8toUTF16(regPath
),
1336 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
1337 if (NS_FAILED(rv
)) {
1338 *aSubmitReports
= PR_TRUE
;
1342 rv
= regKey
->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value
);
1343 // default to true on failure
1344 if (NS_FAILED(rv
)) {
1350 *aSubmitReports
= !!value
;
1352 #elif defined(XP_MACOSX)
1355 CFPropertyListRef cfValue
= (CFPropertyListRef
)(*aSubmitReports
? kCFBooleanTrue
: kCFBooleanFalse
);
1356 ::CFPreferencesSetAppValue(CFSTR("submitReport"),
1358 reporterClientAppID
);
1359 if (!::CFPreferencesAppSynchronize(reporterClientAppID
))
1360 rv
= NS_ERROR_FAILURE
;
1363 *aSubmitReports
= PR_TRUE
;
1364 Boolean keyExistsAndHasValidFormat
= false;
1365 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
1366 reporterClientAppID
,
1367 &keyExistsAndHasValidFormat
);
1368 if (keyExistsAndHasValidFormat
)
1369 *aSubmitReports
= !!prefValue
;
1372 #elif defined(XP_UNIX)
1374 * NOTE! This needs to stay in sync with the preference checking code
1375 * in toolkit/crashreporter/client/crashreporter_linux.cpp
1377 nsCOMPtr
<nsIFile
> reporterINI
;
1378 rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI
));
1379 NS_ENSURE_SUCCESS(rv
, rv
);
1380 reporterINI
->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
1381 reporterINI
->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
1384 rv
= reporterINI
->Exists(&exists
);
1385 NS_ENSURE_SUCCESS(rv
, rv
);
1388 // If reading the pref, default to true if .ini doesn't exist.
1389 *aSubmitReports
= PR_TRUE
;
1392 // Create the file so the INI processor can write to it.
1393 rv
= reporterINI
->Create(nsIFile::NORMAL_FILE_TYPE
, 0600);
1394 NS_ENSURE_SUCCESS(rv
, rv
);
1397 nsCOMPtr
<nsIINIParserFactory
> iniFactory
=
1398 do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv
);
1399 NS_ENSURE_SUCCESS(rv
, rv
);
1401 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(reporterINI
);
1402 NS_ENSURE_TRUE(localFile
, NS_ERROR_FAILURE
);
1403 nsCOMPtr
<nsIINIParser
> iniParser
;
1404 rv
= iniFactory
->CreateINIParser(localFile
,
1405 getter_AddRefs(iniParser
));
1406 NS_ENSURE_SUCCESS(rv
, rv
);
1408 // If we're writing the pref, just set and we're done.
1410 nsCOMPtr
<nsIINIParserWriter
> iniWriter
= do_QueryInterface(iniParser
);
1411 NS_ENSURE_TRUE(iniWriter
, NS_ERROR_FAILURE
);
1413 rv
= iniWriter
->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
1414 NS_LITERAL_CSTRING("SubmitReport"),
1415 *aSubmitReports
? NS_LITERAL_CSTRING("1") :
1416 NS_LITERAL_CSTRING("0"));
1417 NS_ENSURE_SUCCESS(rv
, rv
);
1418 rv
= iniWriter
->WriteFile(NULL
);
1422 nsCAutoString submitReportValue
;
1423 rv
= iniParser
->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
1424 NS_LITERAL_CSTRING("SubmitReport"),
1427 // Default to "true" if the pref can't be found.
1429 *aSubmitReports
= PR_TRUE
;
1430 else if (submitReportValue
.EqualsASCII("0"))
1431 *aSubmitReports
= PR_FALSE
;
1433 *aSubmitReports
= PR_TRUE
;
1437 return NS_ERROR_NOT_IMPLEMENTED
;
1441 nsresult
GetSubmitReports(PRBool
* aSubmitReports
)
1443 return PrefSubmitReports(aSubmitReports
, false);
1446 nsresult
SetSubmitReports(PRBool aSubmitReports
)
1448 return PrefSubmitReports(&aSubmitReports
, true);
1451 // The "pending" dir is Crash Reports/pending, from which minidumps
1454 GetPendingDir(nsILocalFile
** dir
)
1456 nsCOMPtr
<nsIProperties
> dirSvc
=
1457 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
1460 nsCOMPtr
<nsILocalFile
> pendingDir
;
1461 if (NS_FAILED(dirSvc
->Get("UAppData",
1462 NS_GET_IID(nsILocalFile
),
1463 getter_AddRefs(pendingDir
))) ||
1464 NS_FAILED(pendingDir
->Append(NS_LITERAL_STRING("Crash Reports"))) ||
1465 NS_FAILED(pendingDir
->Append(NS_LITERAL_STRING("pending"))))
1468 pendingDir
.swap(*dir
);
1472 // The "limbo" dir is where minidumps go to wait for something else to
1473 // use them. If we're |ShouldReport()|, then the "something else" is
1474 // a minidump submitter, and they're coming from the
1475 // Crash Reports/pending/ dir. Otherwise, we don't know what the
1476 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
1479 GetMinidumpLimboDir(nsILocalFile
** dir
)
1481 if (ShouldReport()) {
1482 return GetPendingDir(dir
);
1485 CreateFileFromPath(gExceptionHandler
->dump_path(), dir
);
1486 return NULL
!= *dir
;
1491 GetMinidumpForID(const nsAString
& id
, nsILocalFile
** minidump
)
1493 if (!GetMinidumpLimboDir(minidump
))
1495 (*minidump
)->Append(id
+ NS_LITERAL_STRING(".dmp"));
1500 GetIDFromMinidump(nsILocalFile
* minidump
, nsAString
& id
)
1502 if (NS_SUCCEEDED(minidump
->GetLeafName(id
))) {
1503 id
.Replace(id
.Length() - 4, 4, NS_LITERAL_STRING(""));
1510 GetExtraFileForID(const nsAString
& id
, nsILocalFile
** extraFile
)
1512 if (!GetMinidumpLimboDir(extraFile
))
1514 (*extraFile
)->Append(id
+ NS_LITERAL_STRING(".extra"));
1519 GetExtraFileForMinidump(nsILocalFile
* minidump
, nsILocalFile
** extraFile
)
1521 nsAutoString leafName
;
1522 nsresult rv
= minidump
->GetLeafName(leafName
);
1526 nsCOMPtr
<nsIFile
> extraF
;
1527 rv
= minidump
->Clone(getter_AddRefs(extraF
));
1531 nsCOMPtr
<nsILocalFile
> extra
= do_QueryInterface(extraF
);
1535 leafName
.Replace(leafName
.Length() - 3, 3,
1536 NS_LITERAL_STRING("extra"));
1537 rv
= extra
->SetLeafName(leafName
);
1542 extra
.swap(*extraFile
);
1547 AppendExtraData(const nsAString
& id
, const AnnotationTable
& data
)
1549 nsCOMPtr
<nsILocalFile
> extraFile
;
1550 if (!GetExtraFileForID(id
, getter_AddRefs(extraFile
)))
1552 return AppendExtraData(extraFile
, data
);
1555 //-----------------------------------------------------------------------------
1556 // Helpers for AppendExtraData()
1559 Blacklist() : mItems(NULL
), mLen(0) { }
1560 Blacklist(const char** items
, int len
) : mItems(items
), mLen(len
) { }
1562 bool Contains(const nsACString
& key
) const {
1563 for (int i
= 0; i
< mLen
; ++i
)
1564 if (key
.EqualsASCII(mItems
[i
]))
1569 const char** mItems
;
1573 struct EnumerateAnnotationsContext
{
1574 const Blacklist
& blacklist
;
1579 WriteAnnotation(PRFileDesc
* fd
, const nsACString
& key
, const nsACString
& value
)
1581 PR_Write(fd
, key
.BeginReading(), key
.Length());
1582 PR_Write(fd
, "=", 1);
1583 PR_Write(fd
, value
.BeginReading(), value
.Length());
1584 PR_Write(fd
, "\n", 1);
1587 static PLDHashOperator
1588 EnumerateAnnotations(const nsACString
& key
,
1592 EnumerateAnnotationsContext
* ctx
=
1593 static_cast<EnumerateAnnotationsContext
*>(userData
);
1594 const Blacklist
& blacklist
= ctx
->blacklist
;
1596 // skip entries in the blacklist
1597 if (blacklist
.Contains(key
))
1598 return PL_DHASH_NEXT
;
1600 WriteAnnotation(ctx
->fd
, key
, entry
);
1602 return PL_DHASH_NEXT
;
1606 WriteExtraData(nsILocalFile
* extraFile
,
1607 const AnnotationTable
& data
,
1608 const Blacklist
& blacklist
,
1609 bool writeCrashTime
=false,
1610 bool truncate
=false)
1613 PRIntn truncOrAppend
= truncate
? PR_TRUNCATE
: PR_APPEND
;
1615 extraFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
| truncOrAppend
,
1620 EnumerateAnnotationsContext ctx
= { blacklist
, fd
};
1621 data
.EnumerateRead(EnumerateAnnotations
, &ctx
);
1623 if (writeCrashTime
) {
1624 time_t crashTime
= time(NULL
);
1625 char crashTimeString
[32];
1626 XP_TTOA(crashTime
, crashTimeString
, 10);
1629 nsDependentCString("CrashTime"),
1630 nsDependentCString(crashTimeString
));
1638 AppendExtraData(nsILocalFile
* extraFile
, const AnnotationTable
& data
)
1640 return WriteExtraData(extraFile
, data
, Blacklist());
1644 #if defined(MOZ_IPC)
1647 WriteExtraForMinidump(nsILocalFile
* minidump
,
1648 const Blacklist
& blacklist
,
1649 nsILocalFile
** extraFile
)
1651 nsCOMPtr
<nsILocalFile
> extra
;
1652 if (!GetExtraFileForMinidump(minidump
, getter_AddRefs(extra
)))
1655 if (!WriteExtraData(extra
, *crashReporterAPIData_Hash
,
1657 true /*write crash time*/,
1662 extra
.swap(*extraFile
);
1667 // It really only makes sense to call this function when
1668 // ShouldReport() is true.
1670 MoveToPending(nsIFile
* dumpFile
, nsIFile
* extraFile
)
1672 nsCOMPtr
<nsILocalFile
> pendingDir
;
1673 if (!GetPendingDir(getter_AddRefs(pendingDir
)))
1676 return NS_SUCCEEDED(dumpFile
->MoveTo(pendingDir
, EmptyString())) &&
1677 NS_SUCCEEDED(extraFile
->MoveTo(pendingDir
, EmptyString()));
1681 OnChildProcessDumpRequested(void* aContext
,
1683 const ClientInfo
& aClientInfo
,
1684 const xpstring
& aFilePath
1686 const ClientInfo
* aClientInfo
,
1687 const xpstring
* aFilePath
1691 nsCOMPtr
<nsILocalFile
> minidump
;
1692 nsCOMPtr
<nsILocalFile
> extraFile
;
1700 getter_AddRefs(minidump
));
1702 #if defined(__ANDROID__)
1703 // Do dump generation here since the CrashGenerationServer doesn't
1704 // have access to the library mappings.
1705 MappingMap::const_iterator iter
=
1706 child_library_mappings
.find(aClientInfo
->pid_
);
1707 if (iter
== child_library_mappings
.end()) {
1708 NS_WARNING("No library mappings found for child, can't write minidump!");
1712 if (!google_breakpad::WriteMinidump(aFilePath
->c_str(),
1714 aClientInfo
->crash_context
,
1715 aClientInfo
->crash_context_size
,
1720 if (!WriteExtraForMinidump(minidump
,
1721 Blacklist(kSubprocessBlacklist
,
1722 NS_ARRAY_LENGTH(kSubprocessBlacklist
)),
1723 getter_AddRefs(extraFile
)))
1727 MoveToPending(minidump
, extraFile
);
1737 MutexAutoLock
lock(*dumpMapLock
);
1738 pidToMinidump
->Put(pid
, minidump
);
1745 return pidToMinidump
!= NULL
;
1751 NS_ABORT_IF_FALSE(!OOPInitialized(),
1752 "OOP crash reporter initialized more than once!");
1753 NS_ABORT_IF_FALSE(gExceptionHandler
!= NULL
,
1754 "attempt to initialize OOP crash reporter before in-process crashreporter!");
1757 childCrashNotifyPipe
=
1758 PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
1759 static_cast<int>(::GetCurrentProcessId()));
1761 const std::wstring dumpPath
= gExceptionHandler
->dump_path();
1762 crashServer
= new CrashGenerationServer(
1763 NS_ConvertASCIItoUTF16(childCrashNotifyPipe
).get(),
1764 NULL
, // default security attributes
1765 NULL
, NULL
, // we don't care about process connect here
1766 OnChildProcessDumpRequested
, NULL
,
1767 NULL
, NULL
, // we don't care about process exit here
1768 true, // automatically generate dumps
1771 #elif defined(XP_LINUX)
1772 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd
,
1774 NS_RUNTIMEABORT("can't create crash reporter socketpair()");
1776 const std::string dumpPath
= gExceptionHandler
->dump_path();
1777 bool generateDumps
= true;
1778 #if defined(__ANDROID__)
1779 // On Android, the callback will do dump generation, since it needs
1780 // to pass the library mappings.
1781 generateDumps
= false;
1783 crashServer
= new CrashGenerationServer(
1785 OnChildProcessDumpRequested
, NULL
,
1786 NULL
, NULL
, // we don't care about process exit here
1790 #elif defined(XP_MACOSX)
1791 childCrashNotifyPipe
=
1792 PR_smprintf("gecko-crash-server-pipe.%i",
1793 static_cast<int>(getpid()));
1794 const std::string dumpPath
= gExceptionHandler
->dump_path();
1796 crashServer
= new CrashGenerationServer(
1797 childCrashNotifyPipe
,
1798 OnChildProcessDumpRequested
, NULL
,
1800 true, // automatically generate dumps
1804 if (!crashServer
->Start())
1805 NS_RUNTIMEABORT("can't start crash reporter server()");
1807 pidToMinidump
= new ChildMinidumpMap();
1808 pidToMinidump
->Init();
1810 dumpMapLock
= new Mutex("CrashReporter::dumpMapLock");
1816 if (!OOPInitialized()) {
1817 NS_WARNING("OOPDeinit() without successful OOPInit()");
1827 delete pidToMinidump
;
1828 pidToMinidump
= NULL
;
1831 PR_Free(childCrashNotifyPipe
);
1832 childCrashNotifyPipe
= NULL
;
1836 #if defined(XP_WIN) || defined(XP_MACOSX)
1837 // Parent-side API for children
1839 GetChildNotificationPipe()
1842 return kNullNotifyPipe
;
1844 if (!OOPInitialized())
1847 return childCrashNotifyPipe
;
1854 SetRemoteExceptionHandler(const nsACString
& crashPipe
)
1856 // crash reporting is disabled
1857 if (crashPipe
.Equals(kNullNotifyPipe
))
1860 NS_ABORT_IF_FALSE(!gExceptionHandler
, "crash client already init'd");
1862 gExceptionHandler
= new google_breakpad::
1863 ExceptionHandler(L
"",
1864 NULL
, // no filter callback
1865 NULL
, // no minidump callback
1866 NULL
, // no callback context
1867 google_breakpad::ExceptionHandler::HANDLER_ALL
,
1869 NS_ConvertASCIItoUTF16(crashPipe
).BeginReading(),
1872 gExceptionHandler
->set_handle_debug_exceptions(true);
1875 // we either do remote or nothing, no fallback to regular crash reporting
1876 return gExceptionHandler
->IsOutOfProcess();
1879 //--------------------------------------------------
1880 #elif defined(XP_LINUX)
1882 // Parent-side API for children
1884 CreateNotificationPipeForChild(int* childCrashFd
, int* childCrashRemapFd
)
1886 if (!GetEnabled()) {
1888 *childCrashRemapFd
= -1;
1892 if (!OOPInitialized())
1895 *childCrashFd
= clientSocketFd
;
1896 *childCrashRemapFd
= kMagicChildCrashReportFd
;
1903 SetRemoteExceptionHandler()
1905 NS_ABORT_IF_FALSE(!gExceptionHandler
, "crash client already init'd");
1907 gExceptionHandler
= new google_breakpad::
1908 ExceptionHandler("",
1909 NULL
, // no filter callback
1910 NULL
, // no minidump callback
1911 NULL
, // no callback context
1912 true, // install signal handlers
1913 kMagicChildCrashReportFd
);
1915 // we either do remote or nothing, no fallback to regular crash reporting
1916 return gExceptionHandler
->IsOutOfProcess();
1919 //--------------------------------------------------
1920 #elif defined(XP_MACOSX)
1923 SetRemoteExceptionHandler(const nsACString
& crashPipe
)
1925 // crash reporting is disabled
1926 if (crashPipe
.Equals(kNullNotifyPipe
))
1929 NS_ABORT_IF_FALSE(!gExceptionHandler
, "crash client already init'd");
1931 gExceptionHandler
= new google_breakpad::
1932 ExceptionHandler("",
1933 NULL
, // no filter callback
1934 NULL
, // no minidump callback
1935 NULL
, // no callback context
1936 true, // install signal handlers
1937 crashPipe
.BeginReading());
1939 // we either do remote or nothing, no fallback to regular crash reporting
1940 return gExceptionHandler
->IsOutOfProcess();
1946 TakeMinidumpForChild(PRUint32 childPid
, nsILocalFile
** dump
)
1951 MutexAutoLock
lock(*dumpMapLock
);
1953 nsCOMPtr
<nsILocalFile
> d
;
1954 bool found
= pidToMinidump
->Get(childPid
, getter_AddRefs(d
));
1956 pidToMinidump
->Remove(childPid
);
1964 //-----------------------------------------------------------------------------
1965 // CreatePairedMinidumps() and helpers
1967 struct PairedDumpContext
{
1968 nsCOMPtr
<nsILocalFile
>* minidump
;
1969 nsCOMPtr
<nsILocalFile
>* extra
;
1970 const Blacklist
& blacklist
;
1974 PairedDumpCallback(const XP_CHAR
* dump_path
,
1975 const XP_CHAR
* minidump_id
,
1978 EXCEPTION_POINTERS
* /*unused*/,
1979 MDRawAssertionInfo
* /*unused*/,
1983 PairedDumpContext
* ctx
= static_cast<PairedDumpContext
*>(context
);
1984 nsCOMPtr
<nsILocalFile
>& minidump
= *ctx
->minidump
;
1985 nsCOMPtr
<nsILocalFile
>& extra
= *ctx
->extra
;
1986 const Blacklist
& blacklist
= ctx
->blacklist
;
1988 xpstring
dump(dump_path
);
1989 dump
+= XP_PATH_SEPARATOR
;
1990 dump
+= minidump_id
;
1991 dump
+= dumpFileExtension
;
1993 CreateFileFromPath(dump
, getter_AddRefs(minidump
));
1994 return WriteExtraForMinidump(minidump
, blacklist
, getter_AddRefs(extra
));
2001 return ::GetCurrentThreadId();
2002 #elif defined(XP_LINUX)
2003 return sys_gettid();
2004 #elif defined(XP_MACOSX)
2005 // Just return an index, since Mach ports can't be directly serialized
2006 thread_act_port_array_t threads_for_task
;
2007 mach_msg_type_number_t thread_count
;
2009 if (task_threads(mach_task_self(), &threads_for_task
, &thread_count
))
2012 for (unsigned int i
= 0; i
< thread_count
; ++i
) {
2013 if (threads_for_task
[i
] == mach_thread_self())
2018 # error "Unsupported platform"
2023 CreatePairedMinidumps(ProcessHandle childPid
,
2024 ThreadId childBlamedThread
,
2025 nsAString
* pairGUID
,
2026 nsILocalFile
** childDump
,
2027 nsILocalFile
** parentDump
)
2032 // create the UUID for the hang dump as a pair
2034 nsCOMPtr
<nsIUUIDGenerator
> uuidgen
=
2035 do_GetService("@mozilla.org/uuid-generator;1", &rv
);
2036 NS_ENSURE_SUCCESS(rv
, false);
2039 rv
= uuidgen
->GenerateUUIDInPlace(&id
);
2040 NS_ENSURE_SUCCESS(rv
, false);
2042 char chars
[NSID_LENGTH
];
2043 id
.ToProvidedString(chars
);
2044 CopyASCIItoUTF16(chars
, *pairGUID
);
2047 pairGUID
->Cut(0, 1);
2048 pairGUID
->Cut(pairGUID
->Length()-1, 1);
2051 mach_port_t childThread
= MACH_PORT_NULL
;
2052 thread_act_port_array_t threads_for_task
;
2053 mach_msg_type_number_t thread_count
;
2055 if (task_threads(childPid
, &threads_for_task
, &thread_count
)
2056 == KERN_SUCCESS
&& childBlamedThread
< thread_count
) {
2057 childThread
= threads_for_task
[childBlamedThread
];
2060 ThreadId childThread
= childBlamedThread
;
2064 nsCOMPtr
<nsILocalFile
> childMinidump
;
2065 nsCOMPtr
<nsILocalFile
> childExtra
;
2066 Blacklist
childBlacklist(kSubprocessBlacklist
,
2067 NS_ARRAY_LENGTH(kSubprocessBlacklist
));
2068 PairedDumpContext childCtx
=
2069 { &childMinidump
, &childExtra
, childBlacklist
};
2070 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
2073 gExceptionHandler
->dump_path(),
2079 nsCOMPtr
<nsILocalFile
> parentMinidump
;
2080 nsCOMPtr
<nsILocalFile
> parentExtra
;
2081 // nothing's blacklisted for this process
2082 Blacklist parentBlacklist
;
2083 PairedDumpContext parentCtx
=
2084 { &parentMinidump
, &parentExtra
, parentBlacklist
};
2085 if (!google_breakpad::ExceptionHandler::WriteMinidump(
2086 gExceptionHandler
->dump_path(),
2087 true, // write exception stream
2093 if (ShouldReport()) {
2094 MoveToPending(childMinidump
, childExtra
);
2095 MoveToPending(parentMinidump
, parentExtra
);
2100 childMinidump
.swap(*childDump
);
2101 parentMinidump
.swap(*parentDump
);
2107 UnsetRemoteExceptionHandler()
2109 delete gExceptionHandler
;
2110 gExceptionHandler
= NULL
;
2116 #if defined(__ANDROID__)
2117 void AddLibraryMapping(const char* library_name
,
2118 const char* file_id
,
2119 uintptr_t start_address
,
2120 size_t mapping_length
,
2123 if (!gExceptionHandler
) {
2125 info
.name
= library_name
;
2126 info
.debug_id
= file_id
;
2127 info
.start_address
= start_address
;
2128 info
.length
= mapping_length
;
2129 info
.file_offset
= file_offset
;
2130 library_mappings
.push_back(info
);
2133 u_int8_t guid
[sizeof(MDGUID
)];
2134 FileIDToGUID(file_id
, guid
);
2135 gExceptionHandler
->AddMappingInfo(library_name
,
2144 void AddLibraryMappingForChild(PRUint32 childPid
,
2145 const char* library_name
,
2146 const char* file_id
,
2147 uintptr_t start_address
,
2148 size_t mapping_length
,
2151 if (child_library_mappings
.find(childPid
) == child_library_mappings
.end())
2152 child_library_mappings
[childPid
] = google_breakpad::MappingList();
2153 google_breakpad::MappingInfo info
;
2154 info
.start_addr
= start_address
;
2155 info
.size
= mapping_length
;
2156 info
.offset
= file_offset
;
2157 strcpy(info
.name
, library_name
);
2159 std::pair
<google_breakpad::MappingInfo
, u_int8_t
[sizeof(MDGUID
)]> mapping
;
2160 mapping
.first
= info
;
2161 u_int8_t guid
[sizeof(MDGUID
)];
2162 FileIDToGUID(file_id
, guid
);
2163 memcpy(mapping
.second
, guid
, sizeof(MDGUID
));
2164 child_library_mappings
[childPid
].push_back(mapping
);
2167 void RemoveLibraryMappingsForChild(PRUint32 childPid
)
2169 MappingMap::iterator iter
= child_library_mappings
.find(childPid
);
2170 if (iter
!= child_library_mappings
.end())
2171 child_library_mappings
.erase(iter
);
2176 } // namespace CrashReporter