1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsExceptionHandler.h"
8 #include "nsExceptionHandlerUtils.h"
10 #include "nsAppDirectoryServiceDefs.h"
11 #include "nsComponentManagerUtils.h"
12 #include "nsDirectoryServiceDefs.h"
13 #include "nsDirectoryService.h"
15 #include "nsTHashMap.h"
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/EnumeratedRange.h"
19 #include "mozilla/Services.h"
20 #include "nsIObserverService.h"
21 #include "mozilla/Unused.h"
22 #include "mozilla/UniquePtr.h"
23 #include "mozilla/Printf.h"
24 #include "mozilla/RuntimeExceptionModule.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/Sprintf.h"
27 #include "mozilla/StaticMutex.h"
28 #include "mozilla/SyncRunnable.h"
29 #include "mozilla/TimeStamp.h"
31 #include "nsPrintfCString.h"
32 #include "nsThreadUtils.h"
34 #include "jsfriendapi.h"
35 #include "private/pprio.h"
36 #include "base/process_util.h"
37 #include "common/basictypes.h"
39 #include "mozilla/toolkit/crashreporter/mozannotation_client_ffi_generated.h"
40 #include "mozilla/toolkit/crashreporter/mozannotation_server_ffi_generated.h"
43 # ifdef WIN32_LEAN_AND_MEAN
44 # undef WIN32_LEAN_AND_MEAN
47 # include "nsXULAppAPI.h"
48 # include "nsIXULAppInfo.h"
49 # include "nsIWindowsRegKey.h"
50 # include "breakpad-client/windows/crash_generation/client_info.h"
51 # include "breakpad-client/windows/crash_generation/crash_generation_server.h"
52 # include "breakpad-client/windows/handler/exception_handler.h"
55 # include "nsDirectoryServiceUtils.h"
57 # include "nsWindowsDllInterceptor.h"
58 # include "mozilla/WindowsDllBlocklist.h"
59 # include "psapi.h" // For PERFORMANCE_INFORMATION and K32GetPerformanceInfo()
60 #elif defined(XP_MACOSX)
61 # include "breakpad-client/mac/crash_generation/client_info.h"
62 # include "breakpad-client/mac/crash_generation/crash_generation_server.h"
63 # include "breakpad-client/mac/handler/exception_handler.h"
65 # include <Carbon/Carbon.h>
66 # include <CoreFoundation/CoreFoundation.h>
67 # include <crt_externs.h>
69 # include <mach/mach.h>
70 # include <mach/vm_statistics.h>
71 # include <sys/sysctl.h>
72 # include <sys/types.h>
75 # include "mac_utils.h"
76 #elif defined(XP_LINUX)
77 # include "nsIINIParser.h"
78 # include "common/linux/linux_libc_support.h"
79 # include "third_party/lss/linux_syscall_support.h"
80 # include "breakpad-client/linux/crash_generation/client_info.h"
81 # include "breakpad-client/linux/crash_generation/crash_generation_server.h"
82 # include "breakpad-client/linux/handler/exception_handler.h"
83 # include "common/linux/eintr_wrapper.h"
85 # include <sys/types.h>
86 # include "sys/sysinfo.h"
87 # include <sys/wait.h>
90 # error "Not yet implemented for this platform"
91 #endif // defined(XP_WIN)
93 #ifdef MOZ_CRASHREPORTER_INJECTOR
94 # include "InjectCrashReporter.h"
95 using mozilla::InjectCrashRunnable
;
102 #include "mozilla/Mutex.h"
107 #include "mozilla/IOInterposer.h"
108 #include "mozilla/mozalloc_oom.h"
110 #if defined(XP_MACOSX)
111 CFStringRef reporterClientAppID
= CFSTR("org.mozilla.crashreporter");
113 #if defined(MOZ_WIDGET_ANDROID)
114 # include "common/linux/file_id.h"
117 using google_breakpad::ClientInfo
;
118 using google_breakpad::CrashGenerationServer
;
120 using google_breakpad::MinidumpDescriptor
;
121 #elif defined(XP_WIN)
122 using google_breakpad::ExceptionHandler
;
124 #if defined(MOZ_WIDGET_ANDROID)
125 using google_breakpad::auto_wasteful_vector
;
126 using google_breakpad::FileID
;
127 using google_breakpad::kDefaultBuildIdSize
;
128 using google_breakpad::PageAllocator
;
130 using namespace mozilla
;
132 namespace mozilla::phc
{
134 // Global instance that is retrieved by the process generating the crash report
135 mozilla::phc::AddrInfo gAddrInfo
;
137 } // namespace mozilla::phc
139 namespace CrashReporter
{
142 typedef wchar_t XP_CHAR
;
143 typedef std::wstring xpstring
;
144 # define XP_TEXT(x) L##x
145 # define CONVERT_XP_CHAR_TO_UTF16(x) x
146 # define XP_STRLEN(x) wcslen(x)
147 # define my_strlen strlen
148 # define my_memchr memchr
149 # define CRASH_REPORTER_FILENAME u"crashreporter.exe"_ns
150 # define XP_PATH_SEPARATOR L"\\"
151 # define XP_PATH_SEPARATOR_CHAR L'\\'
152 # define XP_PATH_MAX (MAX_PATH + 1)
153 // "<reporter path>" "<minidump path>"
154 # define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
155 # define XP_TTOA(time, buffer) _i64toa((time), (buffer), 10)
156 # define XP_STOA(size, buffer) _ui64toa((size), (buffer), 10)
158 typedef char XP_CHAR
;
159 typedef std::string xpstring
;
160 # define XP_TEXT(x) x
161 # define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
162 # define CRASH_REPORTER_FILENAME u"crashreporter"_ns
163 # define XP_PATH_SEPARATOR "/"
164 # define XP_PATH_SEPARATOR_CHAR '/'
165 # define XP_PATH_MAX PATH_MAX
167 # define XP_STRLEN(x) my_strlen(x)
168 # define XP_TTOA(time, buffer) \
169 my_u64tostring(uint64_t(time), (buffer), sizeof(buffer))
170 # define XP_STOA(size, buffer) \
171 my_u64tostring((size), (buffer), sizeof(buffer))
173 # define XP_STRLEN(x) strlen(x)
174 # define XP_TTOA(time, buffer) sprintf(buffer, "%" PRIu64, uint64_t(time))
175 # define XP_STOA(size, buffer) sprintf(buffer, "%zu", size_t(size))
176 # define my_strlen strlen
177 # define my_memchr memchr
178 # define sys_close close
179 # define sys_fork fork
180 # define sys_open open
181 # define sys_read read
182 # define sys_write write
186 #if defined(__GNUC__)
187 # define MAYBE_UNUSED __attribute__((unused))
189 # define MAYBE_UNUSED
190 #endif // defined(__GNUC__)
193 static const XP_CHAR dumpFileExtension
[] = XP_TEXT(".dmp");
196 static const XP_CHAR extraFileExtension
[] = XP_TEXT(".extra");
197 static const XP_CHAR memoryReportExtension
[] = XP_TEXT(".memory.json.gz");
198 static xpstring
* defaultMemoryReportPath
= nullptr;
200 static const char kCrashMainID
[] = "crash.main.3\n";
202 static google_breakpad::ExceptionHandler
* gExceptionHandler
= nullptr;
203 static mozilla::Atomic
<bool> gEncounteredChildException(false);
205 static xpstring pendingDirectory
;
206 static xpstring crashReporterPath
;
207 static xpstring memoryReportPath
;
209 // Where crash events should go.
210 static xpstring eventsDirectory
;
212 // If this is false, we don't launch the crash reporter
213 static bool doReport
= true;
215 // if this is true, we pass the exception on to the OS crash reporter
216 static bool showOSCrashReporter
= false;
218 // The time of the last recorded crash, as a time_t value.
219 static time_t lastCrashTime
= 0;
220 // The pathname of a file to store the crash time in
221 static XP_CHAR lastCrashTimeFilename
[XP_PATH_MAX
] = {0};
223 #if defined(MOZ_WIDGET_ANDROID)
224 // on Android 4.2 and above there is a user serial number associated
225 // with the current process that gets lost when we fork so we need to
226 // explicitly pass it to am
227 static char* androidUserSerial
= nullptr;
229 // Before Android 8 we needed to use "startservice" to start the crash reporting
230 // service. After Android 8 we need to use "start-foreground-service"
231 static const char* androidStartServiceCommand
= nullptr;
234 // this holds additional data sent via the API
235 static Mutex
* crashReporterAPILock
;
236 static Mutex
* notesFieldLock
;
237 static AnnotationTable crashReporterAPIData_Table
;
238 static nsCString
* notesField
= nullptr;
239 static bool isGarbageCollecting
;
240 static uint32_t eventloopNestingLevel
= 0;
241 static time_t inactiveStateStart
= 0;
246 #elif defined(XP_WIN) // defined(XP_UNIX)
248 #endif // defined(XP_WIN)
251 // Avoid a race during application termination.
252 static Mutex
* dumpSafetyLock
;
253 static bool isSafeToDump
= false;
255 // Whether to include heap regions of the crash context.
256 static bool sIncludeContextHeap
= false;
258 // OOP crash reporting
259 static CrashGenerationServer
* crashServer
; // chrome process has this
261 static std::terminate_handler oldTerminateHandler
= nullptr;
263 #if defined(XP_WIN) || defined(XP_MACOSX)
264 // If crash reporting is disabled, we hand out this "null" pipe to the
265 // child process and don't attempt to connect to a parent server.
266 static const char kNullNotifyPipe
[] = "-";
267 static char* childCrashNotifyPipe
;
269 #elif defined(XP_LINUX)
270 static int serverSocketFd
= -1;
271 static int clientSocketFd
= -1;
273 // On Linux these file descriptors are created in the parent process and
274 // remapped in the child ones. See PosixProcessLauncher::DoSetup() for more
276 static FileHandle gMagicChildCrashReportFd
=
277 # if defined(MOZ_WIDGET_ANDROID)
278 // On android the fd is set at the time of child creation.
282 # endif // defined(MOZ_WIDGET_ANDROID)
286 // |dumpMapLock| must protect all access to |pidToMinidump|.
287 static Mutex
* dumpMapLock
;
288 struct ChildProcessData
: public nsUint32HashKey
{
289 explicit ChildProcessData(KeyTypePointer aKey
)
290 : nsUint32HashKey(aKey
),
292 annotations(nullptr),
294 #ifdef MOZ_CRASHREPORTER_INJECTOR
301 nsCOMPtr
<nsIFile
> minidump
;
302 // Each crashing process is assigned an increasing sequence number to
303 // indicate which process crashed first.
305 UniquePtr
<AnnotationTable
> annotations
;
306 bool minidumpOnly
; // If true then no annotations are present
307 #ifdef MOZ_CRASHREPORTER_INJECTOR
308 InjectorCrashCallback
* callback
;
312 typedef nsTHashtable
<ChildProcessData
> ChildMinidumpMap
;
313 static ChildMinidumpMap
* pidToMinidump
;
314 static uint32_t crashSequence
;
315 static bool OOPInitialized();
317 #ifdef MOZ_CRASHREPORTER_INJECTOR
318 static nsIThread
* sInjectorThread
;
320 class ReportInjectedCrash
: public Runnable
{
322 explicit ReportInjectedCrash(uint32_t pid
)
323 : Runnable("ReportInjectedCrash"), mPID(pid
) {}
325 NS_IMETHOD
Run() override
;
330 #endif // MOZ_CRASHREPORTER_INJECTOR
332 void RecordMainThreadId() {
336 #elif defined(XP_WIN) // defined(XP_UNIX)
338 #endif // defined(XP_WIN)
342 bool SignalSafeIsMainThread() {
343 // We can't rely on NS_IsMainThread() because we are in a signal handler, and
344 // sTLSIsMainThread is a thread local variable and it can be lazy allocated
345 // i.e., we could hit code path where this variable has not been accessed
346 // before and needs to be allocated right now, which will lead to spinlock
347 // deadlock effectively hanging the process, as in bug 1756407.
350 pthread_t th
= pthread_self();
351 return pthread_equal(th
, gMainThreadId
);
352 #elif defined(XP_WIN) // defined(XP_UNIX)
353 DWORD th
= GetCurrentThreadId();
354 return th
== gMainThreadId
;
355 #endif // defined(XP_WIN)
359 // the following are used to prevent other DLLs reverting the last chance
360 // exception handler to the windows default. Any attempt to change the
361 // unhandled exception filter or to reset it is ignored and our crash
362 // reporter is loaded instead (in case it became unloaded somehow)
363 typedef LPTOP_LEVEL_EXCEPTION_FILTER(WINAPI
* SetUnhandledExceptionFilter_func
)(
364 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
365 static WindowsDllInterceptor::FuncHookType
<SetUnhandledExceptionFilter_func
>
366 stub_SetUnhandledExceptionFilter
;
367 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter
= nullptr;
368 static WindowsDllInterceptor gKernel32Intercept
;
369 static bool gBlockUnhandledExceptionFilter
= true;
371 static LPTOP_LEVEL_EXCEPTION_FILTER
GetUnhandledExceptionFilter() {
372 // Set a dummy value to get the current filter, then restore
373 LPTOP_LEVEL_EXCEPTION_FILTER current
= SetUnhandledExceptionFilter(nullptr);
374 SetUnhandledExceptionFilter(current
);
378 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
patched_SetUnhandledExceptionFilter(
379 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
) {
380 if (!gBlockUnhandledExceptionFilter
) {
382 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter
);
385 if (lpTopLevelExceptionFilter
== previousUnhandledExceptionFilter
) {
386 // OK to swap back and forth between the previous filter
387 previousUnhandledExceptionFilter
=
388 stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter
);
389 return previousUnhandledExceptionFilter
;
392 // intercept attempts to change the filter
396 # if defined(HAVE_64BIT_BUILD)
397 static LPTOP_LEVEL_EXCEPTION_FILTER sUnhandledExceptionFilter
= nullptr;
399 static long JitExceptionHandler(void* exceptionRecord
, void* context
) {
400 EXCEPTION_POINTERS pointers
= {(PEXCEPTION_RECORD
)exceptionRecord
,
402 return sUnhandledExceptionFilter(&pointers
);
405 static void SetJitExceptionHandler() {
406 sUnhandledExceptionFilter
= GetUnhandledExceptionFilter();
407 if (sUnhandledExceptionFilter
)
408 js::SetJitExceptionHandler(JitExceptionHandler
);
413 * Reserve some VM space. In the event that we crash because VM space is
414 * being leaked without leaking memory, freeing this space before taking
415 * the minidump will allow us to collect a minidump.
417 * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
420 static const SIZE_T kReserveSize
= 0x5000000; // 80 MB
421 static void* gBreakpadReservedVM
;
425 static inline void my_u64tostring(uint64_t aValue
, char* aBuffer
,
426 size_t aBufferLength
) {
427 my_memset(aBuffer
, 0, aBufferLength
);
428 my_uitos(aBuffer
, aValue
, my_uint_len(aValue
));
433 static void CreateFileFromPath(const xpstring
& path
, nsIFile
** file
) {
434 NS_NewLocalFile(nsDependentString(path
.c_str()), false, file
);
437 static xpstring
* CreatePathFromFile(nsIFile
* file
) {
439 nsresult rv
= file
->GetPath(path
);
443 return new xpstring(static_cast<wchar_t*>(path
.get()), path
.Length());
446 static void CreateFileFromPath(const xpstring
& path
, nsIFile
** file
) {
447 NS_NewNativeLocalFile(nsDependentCString(path
.c_str()), false, file
);
450 MAYBE_UNUSED
static xpstring
* CreatePathFromFile(nsIFile
* file
) {
452 nsresult rv
= file
->GetNativePath(path
);
456 return new xpstring(path
.get(), path
.Length());
460 static time_t GetCurrentTimeForCrashTime() {
462 struct kernel_timeval tv
;
463 sys_gettimeofday(&tv
, nullptr);
466 return time(nullptr);
470 static XP_CHAR
* Concat(XP_CHAR
* str
, const XP_CHAR
* toAppend
, size_t* size
) {
471 size_t appendLen
= XP_STRLEN(toAppend
);
472 if (appendLen
>= *size
) {
473 appendLen
= *size
- 1;
476 memcpy(str
, toAppend
, appendLen
* sizeof(XP_CHAR
));
484 void AnnotateOOMAllocationSize(size_t size
) { gOOMAllocationSize
= size
; }
486 static size_t gTexturesSize
= 0;
488 void AnnotateTexturesSize(size_t size
) { gTexturesSize
= size
; }
491 // Like Windows CopyFile for *nix
493 // This function is not declared static even though it's not used outside of
494 // this file because of an issue in Fennec which prevents breakpad's exception
495 // handler from invoking the MinidumpCallback function. See bug 1424304.
496 bool copy_file(const char* from
, const char* to
) {
497 const int kBufSize
= 4096;
498 int fdfrom
= sys_open(from
, O_RDONLY
, 0);
505 int fdto
= sys_open(to
, O_WRONLY
| O_CREAT
, 0666);
513 int r
= sys_read(fdfrom
, buf
, kBufSize
);
523 int w
= sys_write(fdto
, wbuf
, r
);
527 } else if (errno
!= EINTR
) {
544 * The PlatformWriter class provides a tool to create and write to a file that
545 * is safe to call from within an exception handler. To use it this way the
546 * file path needs to be provided as a bare C string.
548 class PlatformWriter
{
550 PlatformWriter() : mBuffer
{}, mPos(0), mFD(kInvalidFileHandle
) {}
551 explicit PlatformWriter(const XP_CHAR
* aPath
) : PlatformWriter() {
560 #elif defined(XP_UNIX)
566 void Open(const XP_CHAR
* aPath
) {
568 mFD
= CreateFile(aPath
, GENERIC_WRITE
, 0, nullptr, CREATE_ALWAYS
,
569 FILE_ATTRIBUTE_NORMAL
, nullptr);
570 #elif defined(XP_UNIX)
571 mFD
= sys_open(aPath
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
575 void OpenHandle(FileHandle aFD
) { mFD
= aFD
; }
576 bool Valid() { return mFD
!= kInvalidFileHandle
; }
578 void WriteBuffer(const char* aBuffer
, size_t aLen
) {
584 WriteChar(*aBuffer
++);
588 void WriteString(const char* aStr
) { WriteBuffer(aStr
, my_strlen(aStr
)); }
591 void WriteLiteral(const char (&aStr
)[N
]) {
592 WriteBuffer(aStr
, N
- 1);
595 FileHandle
FileDesc() { return mFD
; }
598 PlatformWriter(const PlatformWriter
&) = delete;
600 const PlatformWriter
& operator=(const PlatformWriter
&) = delete;
602 void WriteChar(char aChar
) {
603 if (mPos
== kBufferSize
) {
607 mBuffer
[mPos
++] = aChar
;
612 char* buffer
= mBuffer
;
613 size_t length
= mPos
;
616 DWORD written_bytes
= 0;
617 if (!WriteFile(mFD
, buffer
, length
, &written_bytes
, nullptr)) {
620 #elif defined(XP_UNIX)
621 ssize_t written_bytes
= sys_write(mFD
, buffer
, length
);
622 if (written_bytes
< 0) {
623 if (errno
== EAGAIN
) {
630 buffer
+= written_bytes
;
631 length
-= written_bytes
;
638 static const size_t kBufferSize
= 512;
640 char mBuffer
[kBufferSize
];
645 class JSONAnnotationWriter
: public AnnotationWriter
{
647 explicit JSONAnnotationWriter(PlatformWriter
& aPlatformWriter
)
648 : mWriter(aPlatformWriter
), mEmpty(true) {
649 mWriter
.WriteBuffer("{", 1);
652 ~JSONAnnotationWriter() { mWriter
.WriteBuffer("}", 1); }
654 void Write(Annotation aAnnotation
, const char* aValue
,
655 size_t aLen
= 0) override
{
656 size_t len
= aLen
? aLen
: my_strlen(aValue
);
657 const char* annotationStr
= AnnotationToString(aAnnotation
);
660 mWriter
.WriteBuffer(annotationStr
, my_strlen(annotationStr
));
662 WriteEscapedString(aValue
, len
);
666 void Write(Annotation aAnnotation
, uint64_t aValue
) override
{
667 char buffer
[32] = {};
668 XP_STOA(aValue
, buffer
);
669 Write(aAnnotation
, buffer
);
675 mWriter
.WriteBuffer("\"", 1);
678 mWriter
.WriteBuffer(",\"", 2);
682 void WriteSeparator() { mWriter
.WriteBuffer("\":\"", 3); }
683 void WriteSuffix() { mWriter
.WriteBuffer("\"", 1); }
684 void WriteEscapedString(const char* aStr
, size_t aLen
) {
685 for (size_t i
= 0; i
< aLen
; i
++) {
687 if (c
<= 0x1f || c
== '\\' || c
== '\"') {
688 mWriter
.WriteBuffer("\\u00", 4);
689 WriteHexDigitAsAsciiChar((c
& 0x00f0) >> 4);
690 WriteHexDigitAsAsciiChar(c
& 0x000f);
692 mWriter
.WriteBuffer(aStr
+ i
, 1);
697 void WriteHexDigitAsAsciiChar(uint8_t u
) {
699 buf
[0] = static_cast<unsigned>((u
< 10) ? '0' + u
: 'a' + (u
- 10));
700 mWriter
.WriteBuffer(buf
, 1);
703 PlatformWriter
& mWriter
;
707 class BinaryAnnotationWriter
: public AnnotationWriter
{
709 explicit BinaryAnnotationWriter(PlatformWriter
& aPlatformWriter
)
710 : mPlatformWriter(aPlatformWriter
) {}
712 void Write(Annotation aAnnotation
, const char* aValue
,
713 size_t aLen
= 0) override
{
714 uint64_t len
= aLen
? aLen
: my_strlen(aValue
);
715 mPlatformWriter
.WriteBuffer((const char*)&aAnnotation
, sizeof(aAnnotation
));
716 mPlatformWriter
.WriteBuffer((const char*)&len
, sizeof(len
));
717 mPlatformWriter
.WriteBuffer(aValue
, len
);
720 void Write(Annotation aAnnotation
, uint64_t aValue
) override
{
721 char buffer
[32] = {};
722 XP_STOA(aValue
, buffer
);
723 Write(aAnnotation
, buffer
);
727 PlatformWriter
& mPlatformWriter
;
732 // 21 is the max length of a 64-bit decimal address entry, including the
733 // trailing comma or '\0'. And then we add another 32 just to be safe.
734 const size_t phcStringifiedAnnotationSize
=
735 (mozilla::phc::StackTrace::kMaxFrames
* 21) + 32;
737 static void PHCStackTraceToString(char* aBuffer
, size_t aBufferLen
,
738 const phc::StackTrace
& aStack
) {
741 for (size_t i
= 0; i
< aStack
.mLength
; i
++) {
743 strcat(aBuffer
, ",");
745 XP_STOA(uintptr_t(aStack
.mPcs
[i
]), addrString
);
746 strncat(aBuffer
, addrString
, aBufferLen
);
750 // The stack traces are encoded as a comma-separated list of decimal
751 // (not hexadecimal!) addresses, e.g. "12345678,12345679,12345680".
752 static void WritePHCStackTrace(AnnotationWriter
& aWriter
,
753 const Annotation aName
,
754 const Maybe
<phc::StackTrace
>& aStack
) {
755 if (aStack
.isNothing()) {
759 // 21 is the max length of a 64-bit decimal address entry, including the
760 // trailing comma or '\0'. And then we add another 32 just to be safe.
761 char addrsString
[phcStringifiedAnnotationSize
];
762 PHCStackTraceToString(addrsString
, sizeof(addrsString
), *aStack
);
763 aWriter
.Write(aName
, addrsString
);
766 static void WritePHCAddrInfo(AnnotationWriter
& writer
,
767 const phc::AddrInfo
* aAddrInfo
) {
768 // Is this a PHC allocation needing special treatment?
769 if (aAddrInfo
&& aAddrInfo
->mKind
!= phc::AddrInfo::Kind::Unknown
) {
770 const char* kindString
;
771 switch (aAddrInfo
->mKind
) {
772 case phc::AddrInfo::Kind::Unknown
:
773 kindString
= "Unknown(?!)";
775 case phc::AddrInfo::Kind::NeverAllocatedPage
:
776 kindString
= "NeverAllocatedPage";
778 case phc::AddrInfo::Kind::InUsePage
:
779 kindString
= "InUsePage(?!)";
781 case phc::AddrInfo::Kind::FreedPage
:
782 kindString
= "FreedPage";
784 case phc::AddrInfo::Kind::GuardPage
:
785 kindString
= "GuardPage";
788 kindString
= "Unmatched(?!)";
791 writer
.Write(Annotation::PHCKind
, kindString
);
792 writer
.Write(Annotation::PHCBaseAddress
, uintptr_t(aAddrInfo
->mBaseAddr
));
793 writer
.Write(Annotation::PHCUsableSize
, aAddrInfo
->mUsableSize
);
795 WritePHCStackTrace(writer
, Annotation::PHCAllocStack
,
796 aAddrInfo
->mAllocStack
);
797 WritePHCStackTrace(writer
, Annotation::PHCFreeStack
, aAddrInfo
->mFreeStack
);
801 static void PopulatePHCStackTraceAnnotation(
802 AnnotationTable
& aAnnotations
, const Annotation aName
,
803 const Maybe
<phc::StackTrace
>& aStack
) {
804 if (aStack
.isNothing()) {
808 char addrsString
[phcStringifiedAnnotationSize
];
809 PHCStackTraceToString(addrsString
, sizeof(addrsString
), *aStack
);
810 aAnnotations
[aName
] = addrsString
;
813 static void PopulatePHCAnnotations(AnnotationTable
& aAnnotations
,
814 const phc::AddrInfo
* aAddrInfo
) {
815 // Is this a PHC allocation needing special treatment?
816 if (aAddrInfo
&& aAddrInfo
->mKind
!= phc::AddrInfo::Kind::Unknown
) {
817 const char* kindString
;
818 switch (aAddrInfo
->mKind
) {
819 case phc::AddrInfo::Kind::Unknown
:
820 kindString
= "Unknown(?!)";
822 case phc::AddrInfo::Kind::NeverAllocatedPage
:
823 kindString
= "NeverAllocatedPage";
825 case phc::AddrInfo::Kind::InUsePage
:
826 kindString
= "InUsePage(?!)";
828 case phc::AddrInfo::Kind::FreedPage
:
829 kindString
= "FreedPage";
831 case phc::AddrInfo::Kind::GuardPage
:
832 kindString
= "GuardPage";
835 kindString
= "Unmatched(?!)";
839 aAnnotations
[Annotation::PHCKind
] = kindString
;
840 aAnnotations
[Annotation::PHCBaseAddress
] =
841 nsPrintfCString("%zu", uintptr_t(aAddrInfo
->mBaseAddr
));
842 aAnnotations
[Annotation::PHCUsableSize
] =
843 nsPrintfCString("%zu", aAddrInfo
->mUsableSize
);
844 PopulatePHCStackTraceAnnotation(aAnnotations
, Annotation::PHCAllocStack
,
845 aAddrInfo
->mAllocStack
);
846 PopulatePHCStackTraceAnnotation(aAnnotations
, Annotation::PHCFreeStack
,
847 aAddrInfo
->mFreeStack
);
853 * If minidump_id is null, we assume that dump_path contains the full
856 static void OpenAPIData(PlatformWriter
& aWriter
, const XP_CHAR
* dump_path
,
857 const XP_CHAR
* minidump_id
= nullptr) {
858 static XP_CHAR extraDataPath
[XP_PATH_MAX
];
859 size_t size
= XP_PATH_MAX
;
862 p
= Concat(extraDataPath
, dump_path
, &size
);
863 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
864 p
= Concat(p
, minidump_id
, &size
);
866 p
= Concat(extraDataPath
, dump_path
, &size
);
867 // Skip back past the .dmp extension, if any.
868 if (*(p
- 4) == XP_TEXT('.')) {
873 Concat(p
, extraFileExtension
, &size
);
874 aWriter
.Open(extraDataPath
);
878 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
879 MEMORYSTATUSEX statex
;
880 statex
.dwLength
= sizeof(statex
);
881 if (GlobalMemoryStatusEx(&statex
)) {
882 aWriter
.Write(Annotation::SystemMemoryUsePercentage
, statex
.dwMemoryLoad
);
883 aWriter
.Write(Annotation::TotalVirtualMemory
, statex
.ullTotalVirtual
);
884 aWriter
.Write(Annotation::AvailableVirtualMemory
, statex
.ullAvailVirtual
);
885 aWriter
.Write(Annotation::TotalPhysicalMemory
, statex
.ullTotalPhys
);
886 aWriter
.Write(Annotation::AvailablePhysicalMemory
, statex
.ullAvailPhys
);
889 PERFORMANCE_INFORMATION info
;
890 if (K32GetPerformanceInfo(&info
, sizeof(info
))) {
891 aWriter
.Write(Annotation::TotalPageFile
, info
.CommitLimit
* info
.PageSize
);
892 aWriter
.Write(Annotation::AvailablePageFile
,
893 (info
.CommitLimit
- info
.CommitTotal
) * info
.PageSize
);
897 // Extract the total physical memory of the system.
898 static void WritePhysicalMemoryStatus(AnnotationWriter
& aWriter
) {
899 uint64_t physicalMemoryByteSize
= 0;
900 const size_t NAME_LEN
= 2;
901 int name
[NAME_LEN
] = {/* Hardware */ CTL_HW
,
902 /* 64-bit physical memory size */ HW_MEMSIZE
};
903 size_t infoByteSize
= sizeof(physicalMemoryByteSize
);
904 if (sysctl(name
, NAME_LEN
, &physicalMemoryByteSize
, &infoByteSize
,
905 /* We do not replace data */ nullptr,
906 /* We do not replace data */ 0) != -1) {
907 aWriter
.Write(Annotation::TotalPhysicalMemory
, physicalMemoryByteSize
);
911 // Extract available and purgeable physical memory.
912 static void WriteAvailableMemoryStatus(AnnotationWriter
& aWriter
) {
913 auto host
= mach_host_self();
914 vm_statistics64_data_t stats
;
915 unsigned int count
= HOST_VM_INFO64_COUNT
;
916 if (host_statistics64(host
, HOST_VM_INFO64
, (host_info64_t
)&stats
, &count
) ==
918 aWriter
.Write(Annotation::AvailablePhysicalMemory
,
919 stats
.free_count
* vm_page_size
);
920 aWriter
.Write(Annotation::PurgeablePhysicalMemory
,
921 stats
.purgeable_count
* vm_page_size
);
925 // Extract the status of the swap.
926 static void WriteSwapFileStatus(AnnotationWriter
& aWriter
) {
927 const size_t NAME_LEN
= 2;
928 int name
[] = {/* Hardware */ CTL_VM
,
929 /* 64-bit physical memory size */ VM_SWAPUSAGE
};
930 struct xsw_usage swapUsage
;
931 size_t infoByteSize
= sizeof(swapUsage
);
932 if (sysctl(name
, NAME_LEN
, &swapUsage
, &infoByteSize
,
933 /* We do not replace data */ nullptr,
934 /* We do not replace data */ 0) != -1) {
935 aWriter
.Write(Annotation::AvailableSwapMemory
, swapUsage
.xsu_avail
);
938 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
939 WritePhysicalMemoryStatus(aWriter
);
940 WriteAvailableMemoryStatus(aWriter
);
941 WriteSwapFileStatus(aWriter
);
946 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
947 // We can't simply call `sysinfo` as this requires libc.
948 // So we need to parse /proc/meminfo.
950 // We read the entire file to memory prior to parsing
951 // as it makes the parser code a little bit simpler.
952 // As /proc/meminfo is synchronized via `proc_create_single`,
953 // there's no risk of race condition regardless of how we
956 // The buffer in which we're going to load the entire file.
957 // A typical size for /proc/meminfo is 1KiB, so 4KiB should
958 // be large enough until further notice.
959 const size_t BUFFER_SIZE_BYTES
= 4096;
960 char buffer
[BUFFER_SIZE_BYTES
];
962 size_t bufferLen
= 0;
964 // Read and load into memory.
965 int fd
= sys_open("/proc/meminfo", O_RDONLY
, /* chmod */ 0);
967 // No /proc/meminfo? Well, fail silently.
970 auto Guard
= MakeScopeExit([fd
]() { mozilla::Unused
<< sys_close(fd
); });
972 ssize_t bytesRead
= 0;
974 if ((bytesRead
= sys_read(fd
, buffer
+ bufferLen
,
975 BUFFER_SIZE_BYTES
- bufferLen
)) < 0) {
976 if ((errno
== EAGAIN
) || (errno
== EINTR
)) {
980 // Cannot read for some reason. Let's give up.
984 bufferLen
+= bytesRead
;
986 if (bufferLen
== BUFFER_SIZE_BYTES
) {
987 // The file is too large, bail out
990 } while (bytesRead
!= 0);
993 // Each line of /proc/meminfo looks like
994 // SomeLabel: number unit
995 // The last line is empty.
996 // Let's write a parser.
997 // Note that we don't care about writing a normative parser, so
998 // we happily skip whitespaces without checking that it's necessary.
1000 // A stack-allocated structure containing a 0-terminated string.
1001 // We could avoid the memory copies and make it a slice at the cost
1002 // of a slightly more complicated parser. Since we're not in a
1003 // performance-critical section, we didn't.
1005 DataBuffer() : data
{0}, pos(0) {}
1006 // Clear the buffer.
1011 // Append a character.
1013 // In case of error (if c is '\0' or the buffer is full), does nothing.
1014 void append(char c
) {
1015 if (c
== 0 || pos
>= sizeof(data
) - 1) {
1021 // Compare the buffer against a nul-terminated string.
1022 bool operator==(const char* s
) const {
1023 for (size_t i
= 0; i
< pos
; ++i
) {
1024 if (s
[i
] != data
[i
]) {
1025 // Note: Since `data` never contains a '0' in positions [0,pos)
1026 // this will bailout once we have reached the end of `s`.
1033 // A NUL-terminated string of `pos + 1` chars (the +1 is for the 0).
1036 // Invariant: < 256.
1040 // A DataBuffer holding the string representation of a non-negative number.
1041 struct NumberBuffer
: DataBuffer
{
1042 // If possible, convert the string into a number.
1043 // Returns `true` in case of success, `false` in case of failure.
1044 bool asNumber(size_t* number
) {
1046 if (!my_strtoui(&result
, data
)) {
1054 // A DataBuffer holding the string representation of a unit. As of this
1055 // writing, we only support unit `kB`, which seems to be the only unit used in
1057 struct UnitBuffer
: DataBuffer
{
1058 // If possible, convert the string into a multiplier, e.g. `kB => 1024`.
1059 // Return `true` in case of success, `false` in case of failure.
1060 bool asMultiplier(size_t* multiplier
) {
1061 if (*this == "kB") {
1065 // Other units don't seem to be specified/used.
1070 // The state of the mini-parser.
1072 // Reading the label, including the trailing ':'.
1074 // Reading the number, ignoring any whitespace.
1076 // Reading the unit, ignoring any whitespace.
1080 // A single measure being read from /proc/meminfo, e.g.
1081 // the total physical memory available on the system.
1083 Measure() : state(State::Label
) {}
1084 // Reset the measure for a new read.
1086 state
= State::Label
;
1091 // Attempt to convert the measure into a number.
1092 // Return `true` if both the number and the multiplier could be
1093 // converted, `false` otherwise.
1094 // In case of overflow, produces the maximal possible `size_t`.
1095 bool asValue(size_t* result
) {
1096 size_t numberAsSize
= 0;
1097 if (!number
.asNumber(&numberAsSize
)) {
1100 size_t unitAsMultiplier
= 0;
1101 if (!unit
.asMultiplier(&unitAsMultiplier
)) {
1104 if (numberAsSize
* unitAsMultiplier
>= numberAsSize
) {
1105 *result
= numberAsSize
* unitAsMultiplier
;
1107 // Overflow. Unlikely, but just in case, let's return
1108 // the maximal possible value.
1109 *result
= size_t(-1);
1114 // The label being read, e.g. `MemFree`. Does not include the trailing ':'.
1117 // The number being read, e.g. "1024".
1118 NumberBuffer number
;
1120 // The unit being read, e.g. "kB".
1123 // What we're reading at the moment.
1127 // A value we wish to store for later processing.
1128 // e.g. to compute `AvailablePageFile`, we need to
1129 // store `CommitLimit` and `Committed_AS`.
1131 ValueStore() : value(0), found(false) {}
1135 ValueStore commitLimit
;
1136 ValueStore committedAS
;
1137 ValueStore memTotal
;
1138 ValueStore swapTotal
;
1140 // The current measure.
1143 for (size_t pos
= 0; pos
< size_t(bufferLen
); ++pos
) {
1144 const char c
= buffer
[pos
];
1145 switch (measure
.state
) {
1148 // We have finished reading the label.
1149 measure
.state
= State::Number
;
1151 measure
.label
.append(c
);
1156 // Ignore whitespace
1157 } else if ('0' <= c
&& c
<= '9') {
1158 // Accumulate numbers.
1159 measure
.number
.append(c
);
1161 // We have jumped to the unit.
1162 measure
.unit
.append(c
);
1163 measure
.state
= State::Unit
;
1168 // Ignore whitespace
1169 } else if (c
== '\n') {
1171 // - If this one of the measures we're interested in, write it.
1172 // - Once we're done, reset the parser.
1173 auto Guard
= MakeScopeExit([&measure
]() { measure
.reset(); });
1175 struct PointOfInterest
{
1176 // The label we're looking for, e.g. "MemTotal".
1178 // If non-nullptr, store the value at this address.
1180 // If other than Annotation::Count, write the value for this
1182 Annotation annotation
;
1184 const PointOfInterest POINTS_OF_INTEREST
[] = {
1185 {"MemTotal", &memTotal
, Annotation::TotalPhysicalMemory
},
1186 {"MemFree", nullptr, Annotation::AvailablePhysicalMemory
},
1187 {"MemAvailable", nullptr, Annotation::AvailableVirtualMemory
},
1188 {"SwapFree", nullptr, Annotation::AvailableSwapMemory
},
1189 {"SwapTotal", &swapTotal
, Annotation::Count
},
1190 {"CommitLimit", &commitLimit
, Annotation::Count
},
1191 {"Committed_AS", &committedAS
, Annotation::Count
},
1193 for (const auto& pointOfInterest
: POINTS_OF_INTEREST
) {
1194 if (measure
.label
== pointOfInterest
.label
) {
1196 if (measure
.asValue(&value
)) {
1197 if (pointOfInterest
.dest
!= nullptr) {
1198 pointOfInterest
.dest
->found
= true;
1199 pointOfInterest
.dest
->value
= value
;
1201 if (pointOfInterest
.annotation
!= Annotation::Count
) {
1202 aWriter
.Write(pointOfInterest
.annotation
, value
);
1208 // Otherwise, ignore.
1210 measure
.unit
.append(c
);
1216 if (commitLimit
.found
&& committedAS
.found
) {
1217 // If available, attempt to determine the available virtual memory.
1218 // As `commitLimit` is not guaranteed to be larger than `committedAS`,
1219 // we return `0` in case the commit limit has already been exceeded.
1220 uint64_t availablePageFile
= (committedAS
.value
<= commitLimit
.value
)
1221 ? (commitLimit
.value
- committedAS
.value
)
1223 aWriter
.Write(Annotation::AvailablePageFile
, availablePageFile
);
1225 if (memTotal
.found
&& swapTotal
.found
) {
1226 // If available, attempt to determine the available virtual memory.
1227 aWriter
.Write(Annotation::TotalPageFile
, memTotal
.value
+ swapTotal
.value
);
1233 static void AnnotateMemoryStatus(AnnotationTable
&) {
1234 // No memory data for other platforms yet.
1237 #endif // XP_WIN || XP_MACOSX || XP_LINUX || else
1239 #if !defined(MOZ_WIDGET_ANDROID)
1242 * Launches the program specified in aProgramPath with aMinidumpPath as its
1245 * @param aProgramPath The path of the program to be launched
1246 * @param aMinidumpPath The path of the minidump file, passed as an argument
1247 * to the launched program
1249 static bool LaunchProgram(const XP_CHAR
* aProgramPath
,
1250 const XP_CHAR
* aMinidumpPath
) {
1252 XP_CHAR cmdLine
[CMDLINE_SIZE
];
1255 size_t size
= CMDLINE_SIZE
;
1256 p
= Concat(cmdLine
, L
"\"", &size
);
1257 p
= Concat(p
, aProgramPath
, &size
);
1258 p
= Concat(p
, L
"\" \"", &size
);
1259 p
= Concat(p
, aMinidumpPath
, &size
);
1260 Concat(p
, L
"\"", &size
);
1262 PROCESS_INFORMATION pi
= {};
1263 STARTUPINFO si
= {};
1266 // If CreateProcess() fails don't do anything.
1268 /* lpApplicationName */ nullptr, (LPWSTR
)cmdLine
,
1269 /* lpProcessAttributes */ nullptr, /* lpThreadAttributes */ nullptr,
1270 /* bInheritHandles */ FALSE
,
1271 NORMAL_PRIORITY_CLASS
| CREATE_NO_WINDOW
| CREATE_BREAKAWAY_FROM_JOB
,
1272 /* lpEnvironment */ nullptr, /* lpCurrentDirectory */ nullptr, &si
,
1274 CloseHandle(pi
.hProcess
);
1275 CloseHandle(pi
.hThread
);
1277 # elif defined(XP_MACOSX)
1279 char* const my_argv
[] = {const_cast<char*>(aProgramPath
),
1280 const_cast<char*>(aMinidumpPath
), nullptr};
1282 char** env
= nullptr;
1283 char*** nsEnv
= _NSGetEnviron();
1288 int rv
= posix_spawnp(&pid
, my_argv
[0], nullptr, nullptr, my_argv
, env
);
1293 # else // !XP_MACOSX
1294 pid_t pid
= sys_fork();
1298 } else if (pid
== 0) {
1299 Unused
<< execl(aProgramPath
, aProgramPath
, aMinidumpPath
, nullptr);
1302 # endif // XP_MACOSX
1310 * Launch the crash reporter activity on Android
1312 * @param aProgramPath The path of the program to be launched
1313 * @param aMinidumpPath The path to the crash minidump file
1316 static bool LaunchCrashHandlerService(const XP_CHAR
* aProgramPath
,
1317 const XP_CHAR
* aMinidumpPath
) {
1318 static XP_CHAR extrasPath
[XP_PATH_MAX
];
1319 size_t size
= XP_PATH_MAX
;
1321 XP_CHAR
* p
= Concat(extrasPath
, aMinidumpPath
, &size
);
1322 p
= Concat(p
- 3, "extra", &size
);
1324 pid_t pid
= sys_fork();
1328 else if (pid
== 0) {
1329 // Invoke the crash handler service using am
1330 if (androidUserSerial
) {
1331 Unused
<< execlp("/system/bin/am", "/system/bin/am",
1332 androidStartServiceCommand
, "--user", androidUserSerial
,
1333 "-a", "org.mozilla.gecko.ACTION_CRASHED", "-n",
1334 aProgramPath
, "--es", "minidumpPath", aMinidumpPath
,
1335 "--es", "extrasPath", extrasPath
, "--ez", "fatal",
1336 "true", "--es", "processType", "MAIN", (char*)0);
1339 "/system/bin/am", "/system/bin/am", androidStartServiceCommand
, "-a",
1340 "org.mozilla.gecko.ACTION_CRASHED", "-n", aProgramPath
, "--es",
1341 "minidumpPath", aMinidumpPath
, "--es", "extrasPath", extrasPath
,
1342 "--ez", "fatal", "true", "--es", "processType", "MAIN", (char*)0);
1347 // We need to wait on the 'am start' command above to finish, otherwise
1348 // everything will be killed by the ActivityManager as soon as the signal
1351 Unused
<< HANDLE_EINTR(sys_waitpid(pid
, &status
, __WALL
));
1359 static void WriteMainThreadRunnableName(AnnotationWriter
& aWriter
) {
1360 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1361 // Only try to collect this information if the main thread is crashing.
1362 if (!SignalSafeIsMainThread()) {
1366 // NOTE: Use `my_memchr` over `strlen` to ensure we don't run off the end of
1367 // the buffer if it contains no null bytes. This is used instead of `strnlen`,
1368 // as breakpad's linux support library doesn't export a `my_strnlen` function.
1369 const char* buf
= nsThread::sMainThreadRunnableName
.begin();
1370 size_t len
= nsThread::kRunnableNameBufSize
;
1371 if (const void* end
= my_memchr(buf
, '\0', len
)) {
1372 len
= static_cast<const char*>(end
) - buf
;
1376 aWriter
.Write(Annotation::MainThreadRunnableName
, buf
, len
);
1381 static void WriteOOMAllocationSize(AnnotationWriter
& aWriter
) {
1382 if (gOOMAllocationSize
) {
1383 aWriter
.Write(Annotation::OOMAllocationSize
, gOOMAllocationSize
);
1387 static void WriteMozCrashReason(AnnotationWriter
& aWriter
) {
1388 if (gMozCrashReason
!= nullptr) {
1389 aWriter
.Write(Annotation::MozCrashReason
, gMozCrashReason
);
1393 static void WriteAnnotations(AnnotationWriter
& aWriter
,
1394 const AnnotationTable
& aAnnotations
) {
1395 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
1396 const nsCString
& value
= aAnnotations
[key
];
1397 if (!value
.IsEmpty()) {
1398 aWriter
.Write(key
, value
.get(), value
.Length());
1403 static void WriteSynthesizedAnnotations(AnnotationWriter
& aWriter
) {
1404 AnnotateMemoryStatus(aWriter
);
1407 static void WriteAnnotationsForMainProcessCrash(PlatformWriter
& pw
,
1408 const phc::AddrInfo
* addrInfo
,
1410 JSONAnnotationWriter
writer(pw
);
1411 WriteAnnotations(writer
, crashReporterAPIData_Table
);
1412 WriteSynthesizedAnnotations(writer
);
1413 writer
.Write(Annotation::CrashTime
, uint64_t(crashTime
));
1415 if (inactiveStateStart
) {
1416 writer
.Write(Annotation::LastInteractionDuration
,
1417 crashTime
- inactiveStateStart
);
1420 double uptimeTS
= (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation())
1421 .ToSecondsSigDigits();
1422 char uptimeTSString
[64] = {};
1423 SimpleNoCLibDtoA(uptimeTS
, uptimeTSString
, sizeof(uptimeTSString
));
1424 writer
.Write(Annotation::UptimeTS
, uptimeTSString
);
1426 // calculate time since last crash (if possible).
1427 if (lastCrashTime
!= 0) {
1428 uint64_t timeSinceLastCrash
= crashTime
- lastCrashTime
;
1430 if (timeSinceLastCrash
!= 0) {
1431 writer
.Write(Annotation::SecondsSinceLastCrash
, timeSinceLastCrash
);
1435 if (isGarbageCollecting
) {
1436 writer
.Write(Annotation::IsGarbageCollecting
, "1");
1439 if (eventloopNestingLevel
> 0) {
1440 writer
.Write(Annotation::EventLoopNestingLevel
, eventloopNestingLevel
);
1443 #if defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST)
1444 // HACK: The DLL blocklist code will manually write its annotations as JSON
1445 DllBlocklist_WriteNotes(writer
);
1446 #endif // defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST)
1448 WriteMozCrashReason(writer
);
1450 WriteMainThreadRunnableName(writer
);
1452 WriteOOMAllocationSize(writer
);
1454 if (gTexturesSize
) {
1455 writer
.Write(Annotation::TextureUsage
, gTexturesSize
);
1459 WritePHCAddrInfo(writer
, addrInfo
);
1463 static void WriteCrashEventFile(time_t crashTime
, const char* crashTimeString
,
1464 const phc::AddrInfo
* addrInfo
,
1466 const MinidumpDescriptor
& descriptor
1468 const XP_CHAR
* minidump_id
1471 // Minidump IDs are UUIDs (36) + NULL.
1472 static char id_ascii
[37] = {};
1474 const char* index
= strrchr(descriptor
.path(), '/');
1476 MOZ_ASSERT(strlen(index
) == 1 + 36 + 4); // "/" + UUID + ".dmp"
1477 for (uint32_t i
= 0; i
< 36; i
++) {
1478 id_ascii
[i
] = *(index
+ 1 + i
);
1481 MOZ_ASSERT(XP_STRLEN(minidump_id
) == 36);
1482 for (uint32_t i
= 0; i
< 36; i
++) {
1483 id_ascii
[i
] = *((char*)(minidump_id
+ i
));
1487 PlatformWriter eventFile
;
1489 if (!eventsDirectory
.empty()) {
1490 static XP_CHAR crashEventPath
[XP_PATH_MAX
];
1491 size_t size
= XP_PATH_MAX
;
1493 p
= Concat(crashEventPath
, eventsDirectory
.c_str(), &size
);
1494 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1496 Concat(p
, id_ascii
, &size
);
1498 Concat(p
, minidump_id
, &size
);
1501 eventFile
.Open(crashEventPath
);
1502 eventFile
.WriteLiteral(kCrashMainID
);
1503 eventFile
.WriteString(crashTimeString
);
1504 eventFile
.WriteLiteral("\n");
1505 eventFile
.WriteString(id_ascii
);
1506 eventFile
.WriteLiteral("\n");
1507 WriteAnnotationsForMainProcessCrash(eventFile
, addrInfo
, crashTime
);
1511 // Callback invoked from breakpad's exception handler, this writes out the
1512 // last annotations after a crash occurs and launches the crash reporter client.
1514 // This function is not declared static even though it's not used outside of
1515 // this file because of an issue in Fennec which prevents breakpad's exception
1516 // handler from invoking it. See bug 1424304.
1517 bool MinidumpCallback(
1519 const MinidumpDescriptor
& descriptor
,
1521 const XP_CHAR
* dump_path
, const XP_CHAR
* minidump_id
,
1525 EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
,
1527 const phc::AddrInfo
* addrInfo
, bool succeeded
) {
1528 bool returnValue
= showOSCrashReporter
? false : succeeded
;
1530 static XP_CHAR minidumpPath
[XP_PATH_MAX
];
1531 size_t size
= XP_PATH_MAX
;
1534 p
= Concat(minidumpPath
, dump_path
, &size
);
1535 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1536 p
= Concat(p
, minidump_id
, &size
);
1537 Concat(p
, dumpFileExtension
, &size
);
1539 Concat(minidumpPath
, descriptor
.path(), &size
);
1542 static XP_CHAR memoryReportLocalPath
[XP_PATH_MAX
];
1545 p
= Concat(memoryReportLocalPath
, dump_path
, &size
);
1546 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1547 p
= Concat(p
, minidump_id
, &size
);
1549 p
= Concat(memoryReportLocalPath
, descriptor
.path(), &size
);
1550 // Skip back past the .dmp extension
1553 Concat(p
, memoryReportExtension
, &size
);
1555 if (!memoryReportPath
.empty()) {
1557 CopyFile(memoryReportPath
.c_str(), memoryReportLocalPath
, false);
1559 copy_file(memoryReportPath
.c_str(), memoryReportLocalPath
);
1563 time_t crashTime
= GetCurrentTimeForCrashTime();
1564 char crashTimeString
[32];
1565 XP_TTOA(crashTime
, crashTimeString
);
1567 // write crash time to file
1568 if (lastCrashTimeFilename
[0] != 0) {
1569 PlatformWriter
lastCrashFile(lastCrashTimeFilename
);
1570 lastCrashFile
.WriteString(crashTimeString
);
1573 WriteCrashEventFile(crashTime
, crashTimeString
, addrInfo
,
1582 PlatformWriter apiData
;
1584 OpenAPIData(apiData
, descriptor
.path());
1586 OpenAPIData(apiData
, dump_path
, minidump_id
);
1588 WriteAnnotationsForMainProcessCrash(apiData
, addrInfo
, crashTime
);
1593 TerminateProcess(GetCurrentProcess(), 1);
1598 #if defined(MOZ_WIDGET_ANDROID) // Android
1600 LaunchCrashHandlerService(crashReporterPath
.c_str(), minidumpPath
);
1601 #else // Windows, Mac, Linux, etc...
1602 returnValue
= LaunchProgram(crashReporterPath
.c_str(), minidumpPath
);
1604 TerminateProcess(GetCurrentProcess(), 1);
1611 #if defined(XP_MACOSX) || defined(__ANDROID__) || defined(XP_LINUX)
1612 static size_t EnsureTrailingSlash(XP_CHAR
* aBuf
, size_t aBufLen
) {
1613 size_t len
= XP_STRLEN(aBuf
);
1614 if ((len
+ 1) < aBufLen
&& len
> 0 &&
1615 aBuf
[len
- 1] != XP_PATH_SEPARATOR_CHAR
) {
1616 aBuf
[len
] = XP_PATH_SEPARATOR_CHAR
;
1626 static size_t BuildTempPath(wchar_t* aBuf
, size_t aBufLen
) {
1627 // first figure out buffer size
1628 DWORD pathLen
= GetTempPath(0, nullptr);
1629 if (pathLen
== 0 || pathLen
>= aBufLen
) {
1633 return GetTempPath(pathLen
, aBuf
);
1636 static size_t BuildTempPath(char16_t
* aBuf
, size_t aBufLen
) {
1637 return BuildTempPath(reinterpret_cast<wchar_t*>(aBuf
), aBufLen
);
1640 #elif defined(XP_MACOSX)
1642 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1643 if (aBufLen
< PATH_MAX
) {
1649 FSFindFolder(kUserDomain
, kTemporaryFolderType
, kCreateFolder
, &fsRef
);
1654 OSStatus status
= FSRefMakePath(&fsRef
, (UInt8
*)aBuf
, PATH_MAX
);
1655 if (status
!= noErr
) {
1659 return EnsureTrailingSlash(aBuf
, aBufLen
);
1662 #elif defined(__ANDROID__)
1664 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1665 // GeckoAppShell sets this in the environment
1666 const char* tempenv
= PR_GetEnv("TMPDIR");
1670 size_t size
= aBufLen
;
1671 Concat(aBuf
, tempenv
, &size
);
1672 return EnsureTrailingSlash(aBuf
, aBufLen
);
1675 #elif defined(XP_UNIX)
1677 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1678 const char* tempenv
= PR_GetEnv("TMPDIR");
1679 const char* tmpPath
= "/tmp/";
1683 size_t size
= aBufLen
;
1684 Concat(aBuf
, tempenv
, &size
);
1685 return EnsureTrailingSlash(aBuf
, aBufLen
);
1689 # error "Implement this for your platform"
1692 template <typename CharT
, size_t N
>
1693 static size_t BuildTempPath(CharT (&aBuf
)[N
]) {
1694 static_assert(N
>= XP_PATH_MAX
, "char array length is too small");
1695 return BuildTempPath(&aBuf
[0], N
);
1698 template <typename PathStringT
>
1699 static bool BuildTempPath(PathStringT
& aResult
) {
1700 aResult
.SetLength(XP_PATH_MAX
);
1701 size_t actualLen
= BuildTempPath(aResult
.BeginWriting(), XP_PATH_MAX
);
1705 aResult
.SetLength(actualLen
);
1711 static void ReserveBreakpadVM() {
1712 if (!gBreakpadReservedVM
) {
1713 gBreakpadReservedVM
=
1714 VirtualAlloc(nullptr, kReserveSize
, MEM_RESERVE
, PAGE_NOACCESS
);
1718 static void FreeBreakpadVM() {
1719 if (gBreakpadReservedVM
) {
1720 VirtualFree(gBreakpadReservedVM
, 0, MEM_RELEASE
);
1724 static bool IsCrashingException(EXCEPTION_POINTERS
* exinfo
) {
1729 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)exinfo
->ExceptionRecord
;
1730 switch (e
->ExceptionCode
) {
1731 case STATUS_FLOAT_DENORMAL_OPERAND
:
1732 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
1733 case STATUS_FLOAT_INEXACT_RESULT
:
1734 case STATUS_FLOAT_INVALID_OPERATION
:
1735 case STATUS_FLOAT_OVERFLOW
:
1736 case STATUS_FLOAT_STACK_CHECK
:
1737 case STATUS_FLOAT_UNDERFLOW
:
1738 case STATUS_FLOAT_MULTIPLE_FAULTS
:
1739 case STATUS_FLOAT_MULTIPLE_TRAPS
:
1740 return false; // Don't write minidump, continue exception search
1748 // Do various actions to prepare the child process for minidump generation.
1749 // This includes disabling the I/O interposer and DLL blocklist which both
1750 // would get in the way. We also free the address space we had reserved in
1751 // 32-bit builds to free room for the minidump generation to do its work.
1752 static void PrepareForMinidump() {
1753 mozilla::IOInterposer::Disable();
1755 # if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
1756 DllBlocklist_Shutdown();
1765 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
1766 * and should not be handled as crashes.
1768 static ExceptionHandler::FilterResult
Filter(void* context
,
1769 EXCEPTION_POINTERS
* exinfo
,
1770 MDRawAssertionInfo
* assertion
) {
1771 if (!IsCrashingException(exinfo
)) {
1772 return ExceptionHandler::FilterResult::ContinueSearch
;
1775 PrepareForMinidump();
1776 return ExceptionHandler::FilterResult::HandleException
;
1779 static ExceptionHandler::FilterResult
ChildFilter(
1780 void* context
, EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
) {
1781 if (!IsCrashingException(exinfo
)) {
1782 return ExceptionHandler::FilterResult::ContinueSearch
;
1785 if (gEncounteredChildException
.exchange(true)) {
1786 return ExceptionHandler::FilterResult::AbortWithoutMinidump
;
1789 PrepareForMinidump();
1790 return ExceptionHandler::FilterResult::HandleException
;
1793 static MINIDUMP_TYPE
GetMinidumpType() {
1794 MINIDUMP_TYPE minidump_type
= static_cast<MINIDUMP_TYPE
>(
1795 MiniDumpWithFullMemoryInfo
| MiniDumpWithUnloadedModules
);
1797 # ifdef NIGHTLY_BUILD
1798 minidump_type
= static_cast<MINIDUMP_TYPE
>(
1800 // This is Nightly only because this doubles the size of minidumps based
1801 // on the experimental data.
1802 MiniDumpWithProcessThreadData
|
1803 // This allows us to examine heap objects referenced from stack objects
1804 // at the cost of further doubling the size of minidumps.
1805 MiniDumpWithIndirectlyReferencedMemory
);
1808 const char* e
= PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1810 minidump_type
= MiniDumpWithFullMemory
;
1813 return minidump_type
;
1818 static bool Filter(void* context
) {
1819 PrepareForMinidump();
1823 static bool ChildFilter(void* context
) {
1824 if (gEncounteredChildException
.exchange(true)) {
1828 PrepareForMinidump();
1832 #endif // !defined(XP_WIN)
1834 static bool ChildMinidumpCallback(
1836 const wchar_t* dump_path
, const wchar_t* minidump_id
,
1837 #elif defined(XP_LINUX)
1838 const MinidumpDescriptor
& descriptor
,
1839 #else // defined(XP_MACOSX)
1840 const char* dump_dir
, const char* minidump_id
,
1844 EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
,
1845 #endif // defined(XP_WIN)
1846 const mozilla::phc::AddrInfo
* addr_info
, bool succeeded
) {
1850 static bool ShouldReport() {
1851 // this environment variable prevents us from launching
1852 // the crash reporter client
1853 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
1854 if (envvar
&& *envvar
) {
1858 envvar
= PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1859 if (envvar
&& *envvar
) {
1866 static void TerminateHandler() { MOZ_CRASH("Unhandled exception"); }
1868 #if !defined(MOZ_WIDGET_ANDROID)
1870 // Locate the specified executable and store its path as a native string in
1871 // the |aPath| so we can later invoke it from within the exception handler.
1872 static nsresult
LocateExecutable(nsIFile
* aXREDirectory
, const nsAString
& aName
,
1873 PathString
& aPath
) {
1874 nsCOMPtr
<nsIFile
> exePath
;
1875 nsresult rv
= aXREDirectory
->Clone(getter_AddRefs(exePath
));
1876 NS_ENSURE_SUCCESS(rv
, rv
);
1879 exePath
->SetNativeLeafName("MacOS"_ns
);
1880 exePath
->Append(u
"crashreporter.app"_ns
);
1881 exePath
->Append(u
"Contents"_ns
);
1882 exePath
->Append(u
"MacOS"_ns
);
1885 exePath
->Append(aName
);
1886 aPath
= exePath
->NativePath();
1890 #endif // !defined(MOZ_WIDGET_ANDROID)
1892 static void InitializeAnnotationFacilities() {
1893 crashReporterAPILock
= new Mutex("crashReporterAPILock");
1894 notesFieldLock
= new Mutex("notesFieldLock");
1895 notesField
= new nsCString();
1898 static void TeardownAnnotationFacilities() {
1899 std::fill(crashReporterAPIData_Table
.begin(),
1900 crashReporterAPIData_Table
.end(), ""_ns
);
1902 delete crashReporterAPILock
;
1903 crashReporterAPILock
= nullptr;
1905 delete notesFieldLock
;
1906 notesFieldLock
= nullptr;
1909 notesField
= nullptr;
1912 nsresult
SetExceptionHandler(nsIFile
* aXREDirectory
, bool force
/*=false*/) {
1913 if (gExceptionHandler
) return NS_ERROR_ALREADY_INITIALIZED
;
1916 // In debug builds, disable the crash reporter by default, and allow to
1917 // enable it with the MOZ_CRASHREPORTER environment variable.
1918 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER");
1919 if ((!envvar
|| !*envvar
) && !force
) return NS_OK
;
1921 // In other builds, enable the crash reporter by default, and allow
1922 // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
1923 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
1924 if (envvar
&& *envvar
&& !force
) return NS_OK
;
1927 // this environment variable prevents us from launching
1928 // the crash reporter client
1929 doReport
= ShouldReport();
1931 RegisterRuntimeExceptionModule();
1932 InitializeAnnotationFacilities();
1934 #if !defined(MOZ_WIDGET_ANDROID)
1935 // Locate the crash reporter executable
1936 PathString crashReporterPath_temp
;
1937 nsresult rv
= LocateExecutable(aXREDirectory
, CRASH_REPORTER_FILENAME
,
1938 crashReporterPath_temp
);
1939 if (NS_WARN_IF(NS_FAILED(rv
))) {
1943 crashReporterPath
= crashReporterPath_temp
.get();
1945 // On Android, we launch a service defined via MOZ_ANDROID_CRASH_HANDLER
1946 const char* androidCrashHandler
= PR_GetEnv("MOZ_ANDROID_CRASH_HANDLER");
1947 if (androidCrashHandler
) {
1948 crashReporterPath
= xpstring(androidCrashHandler
);
1950 NS_WARNING("No Android crash handler set");
1953 const char* deviceAndroidVersion
=
1954 PR_GetEnv("MOZ_ANDROID_DEVICE_SDK_VERSION");
1955 if (deviceAndroidVersion
!= nullptr) {
1956 const int deviceSdkVersion
= atol(deviceAndroidVersion
);
1957 if (deviceSdkVersion
>= 26) {
1958 androidStartServiceCommand
= (char*)"start-foreground-service";
1960 androidStartServiceCommand
= (char*)"startservice";
1963 #endif // !defined(MOZ_WIDGET_ANDROID)
1965 // get temp path to use for minidump path
1966 PathString tempPath
;
1967 if (!BuildTempPath(tempPath
)) {
1968 return NS_ERROR_FAILURE
;
1972 ReserveBreakpadVM();
1974 // Pre-load psapi.dll to prevent it from being loaded during exception
1976 ::LoadLibraryW(L
"psapi.dll");
1979 #ifdef MOZ_WIDGET_ANDROID
1980 androidUserSerial
= getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
1983 // Initialize the flag and mutex used to avoid dump processing
1984 // once browser termination has begun.
1985 NS_ASSERTION(!dumpSafetyLock
, "Shouldn't have a lock yet");
1986 // Do not deallocate this lock while it is still possible for
1987 // isSafeToDump to be tested on another thread.
1988 dumpSafetyLock
= new Mutex("dumpSafetyLock");
1989 MutexAutoLock
lock(*dumpSafetyLock
);
1990 isSafeToDump
= true;
1992 // now set the exception handler
1994 MinidumpDescriptor
descriptor(tempPath
.get());
1998 previousUnhandledExceptionFilter
= GetUnhandledExceptionFilter();
2001 gExceptionHandler
= new google_breakpad::ExceptionHandler(
2004 #elif defined(XP_WIN)
2005 std::wstring(tempPath
.get()),
2010 Filter
, MinidumpCallback
, nullptr,
2012 google_breakpad::ExceptionHandler::HANDLER_ALL
, GetMinidumpType(),
2013 (const wchar_t*)nullptr, nullptr);
2027 if (!gExceptionHandler
) return NS_ERROR_OUT_OF_MEMORY
;
2030 gExceptionHandler
->set_handle_debug_exceptions(true);
2032 // Initially set sIncludeContextHeap to true for debugging startup crashes
2033 // even if the controlling pref value is false.
2034 SetIncludeContextHeap(true);
2035 # if defined(HAVE_64BIT_BUILD)
2036 // Tell JS about the new filter before we disable SetUnhandledExceptionFilter
2037 SetJitExceptionHandler();
2040 RecordMainThreadId();
2042 // protect the crash reporter from being unloaded
2043 gBlockUnhandledExceptionFilter
= true;
2044 gKernel32Intercept
.Init("kernel32.dll");
2045 DebugOnly
<bool> ok
= stub_SetUnhandledExceptionFilter
.Set(
2046 gKernel32Intercept
, "SetUnhandledExceptionFilter",
2047 &patched_SetUnhandledExceptionFilter
);
2052 "SetUnhandledExceptionFilter hook failed; crash reporter is "
2057 // store application start time
2058 char timeString
[32];
2059 time_t startupTime
= time(nullptr);
2060 XP_TTOA(startupTime
, timeString
);
2061 AnnotateCrashReport(Annotation::StartupTime
, nsDependentCString(timeString
));
2063 #if defined(XP_MACOSX)
2064 // On OS X, many testers like to see the OS crash reporting dialog
2065 // since it offers immediate stack traces. We allow them to set
2066 // a default to pass exceptions to the OS handler.
2067 Boolean keyExistsAndHasValidFormat
= false;
2068 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(
2069 CFSTR("OSCrashReporter"), kCFPreferencesCurrentApplication
,
2070 &keyExistsAndHasValidFormat
);
2071 if (keyExistsAndHasValidFormat
) showOSCrashReporter
= prefValue
;
2074 oldTerminateHandler
= std::set_terminate(&TerminateHandler
);
2079 bool GetEnabled() { return gExceptionHandler
!= nullptr; }
2081 bool GetMinidumpPath(nsAString
& aPath
) {
2082 if (!gExceptionHandler
) return false;
2085 aPath
= CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler
->dump_path().c_str());
2087 aPath
= CONVERT_XP_CHAR_TO_UTF16(
2088 gExceptionHandler
->minidump_descriptor().directory().c_str());
2093 nsresult
SetMinidumpPath(const nsAString
& aPath
) {
2094 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2097 gExceptionHandler
->set_dump_path(
2098 std::wstring(char16ptr_t(aPath
.BeginReading())));
2099 #elif defined(XP_LINUX)
2100 gExceptionHandler
->set_minidump_descriptor(
2101 MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath
).BeginReading()));
2103 gExceptionHandler
->set_dump_path(NS_ConvertUTF16toUTF8(aPath
).BeginReading());
2108 static nsresult
WriteDataToFile(nsIFile
* aFile
, const nsACString
& data
) {
2110 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
, 00600, &fd
);
2111 NS_ENSURE_SUCCESS(rv
, rv
);
2114 if (PR_Write(fd
, data
.Data(), data
.Length()) == -1) {
2115 rv
= NS_ERROR_FAILURE
;
2121 static nsresult
GetFileContents(nsIFile
* aFile
, nsACString
& data
) {
2123 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
2124 NS_ENSURE_SUCCESS(rv
, rv
);
2127 int32_t filesize
= PR_Available(fd
);
2128 if (filesize
<= 0) {
2129 rv
= NS_ERROR_FILE_NOT_FOUND
;
2131 data
.SetLength(filesize
);
2132 if (PR_Read(fd
, data
.BeginWriting(), filesize
) == -1) {
2133 rv
= NS_ERROR_FAILURE
;
2140 // Function typedef for initializing a piece of data that we
2141 // don't already have.
2142 typedef nsresult (*InitDataFunc
)(nsACString
&);
2144 // Attempt to read aFile's contents into aContents, if aFile
2145 // does not exist, create it and initialize its contents
2146 // by calling aInitFunc for the data.
2147 static nsresult
GetOrInit(nsIFile
* aDir
, const nsACString
& filename
,
2148 nsACString
& aContents
, InitDataFunc aInitFunc
) {
2151 nsCOMPtr
<nsIFile
> dataFile
;
2152 nsresult rv
= aDir
->Clone(getter_AddRefs(dataFile
));
2153 NS_ENSURE_SUCCESS(rv
, rv
);
2155 rv
= dataFile
->AppendNative(filename
);
2156 NS_ENSURE_SUCCESS(rv
, rv
);
2158 rv
= dataFile
->Exists(&exists
);
2159 NS_ENSURE_SUCCESS(rv
, rv
);
2163 // get the initial value and write it to the file
2164 rv
= aInitFunc(aContents
);
2165 NS_ENSURE_SUCCESS(rv
, rv
);
2166 rv
= WriteDataToFile(dataFile
, aContents
);
2168 // didn't pass in an init func
2169 rv
= NS_ERROR_FAILURE
;
2172 // just get the file's contents
2173 rv
= GetFileContents(dataFile
, aContents
);
2179 // Init the "install time" data. We're taking an easy way out here
2180 // and just setting this to "the time when this version was first run".
2181 static nsresult
InitInstallTime(nsACString
& aInstallTime
) {
2182 time_t t
= time(nullptr);
2183 aInstallTime
= nsPrintfCString("%" PRIu64
, static_cast<uint64_t>(t
));
2188 // Ensure a directory exists and create it if missing.
2189 static nsresult
EnsureDirectoryExists(nsIFile
* dir
) {
2190 nsresult rv
= dir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
2192 if (NS_WARN_IF(NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
2199 // Creates a directory that will be accessible by the crash reporter. The
2200 // directory will live under Firefox default data directory and will use the
2201 // specified name. The directory path will be passed to the crashreporter via
2202 // the specified environment variable.
2203 static nsresult
SetupCrashReporterDirectory(nsIFile
* aAppDataDirectory
,
2204 const char* aDirName
,
2205 const XP_CHAR
* aEnvVarName
,
2206 nsIFile
** aDirectory
= nullptr) {
2207 nsCOMPtr
<nsIFile
> directory
;
2208 nsresult rv
= aAppDataDirectory
->Clone(getter_AddRefs(directory
));
2209 NS_ENSURE_SUCCESS(rv
, rv
);
2211 rv
= directory
->AppendNative(nsDependentCString(aDirName
));
2212 NS_ENSURE_SUCCESS(rv
, rv
);
2214 EnsureDirectoryExists(directory
);
2215 xpstring
* directoryPath
= CreatePathFromFile(directory
);
2217 if (!directoryPath
) {
2218 return NS_ERROR_FAILURE
;
2222 SetEnvironmentVariableW(aEnvVarName
, directoryPath
->c_str());
2224 setenv(aEnvVarName
, directoryPath
->c_str(), /* overwrite */ 1);
2227 delete directoryPath
;
2230 directory
.forget(aDirectory
);
2236 // Annotate the crash report with a Unique User ID and time
2237 // since install. Also do some prep work for recording
2238 // time since last crash, which must be calculated at
2240 // If any piece of data doesn't exist, initialize it first.
2241 nsresult
SetupExtraData(nsIFile
* aAppDataDirectory
,
2242 const nsACString
& aBuildID
) {
2243 nsCOMPtr
<nsIFile
> dataDirectory
;
2245 SetupCrashReporterDirectory(aAppDataDirectory
, "Crash Reports",
2246 XP_TEXT("MOZ_CRASHREPORTER_DATA_DIRECTORY"),
2247 getter_AddRefs(dataDirectory
));
2249 if (NS_WARN_IF(NS_FAILED(rv
))) {
2253 rv
= SetupCrashReporterDirectory(aAppDataDirectory
, "Pending Pings",
2254 XP_TEXT("MOZ_CRASHREPORTER_PING_DIRECTORY"));
2256 if (NS_WARN_IF(NS_FAILED(rv
))) {
2261 if (NS_SUCCEEDED(GetOrInit(dataDirectory
, "InstallTime"_ns
+ aBuildID
, data
,
2263 AnnotateCrashReport(Annotation::InstallTime
, data
);
2265 // this is a little different, since we can't init it with anything,
2266 // since it's stored at crash time, and we can't annotate the
2267 // crash report with the stored value, since we really want
2268 // (now - LastCrash), so we just get a value if it exists,
2269 // and store it in a time_t value.
2270 if (NS_SUCCEEDED(GetOrInit(dataDirectory
, "LastCrash"_ns
, data
, nullptr))) {
2271 lastCrashTime
= (time_t)atol(data
.get());
2274 // not really the best place to init this, but I have the path I need here
2275 nsCOMPtr
<nsIFile
> lastCrashFile
;
2276 rv
= dataDirectory
->Clone(getter_AddRefs(lastCrashFile
));
2277 NS_ENSURE_SUCCESS(rv
, rv
);
2279 rv
= lastCrashFile
->AppendNative("LastCrash"_ns
);
2280 NS_ENSURE_SUCCESS(rv
, rv
);
2281 memset(lastCrashTimeFilename
, 0, sizeof(lastCrashTimeFilename
));
2283 PathString filename
;
2285 rv
= lastCrashFile
->GetPath(filename
);
2287 rv
= lastCrashFile
->GetNativePath(filename
);
2289 NS_ENSURE_SUCCESS(rv
, rv
);
2291 if (filename
.Length() < XP_PATH_MAX
) {
2293 wcsncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
2295 strncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
2302 static void OOPDeinit();
2304 nsresult
UnsetExceptionHandler() {
2306 MutexAutoLock
lock(*dumpSafetyLock
);
2307 isSafeToDump
= false;
2311 // allow SetUnhandledExceptionFilter
2312 gBlockUnhandledExceptionFilter
= false;
2315 delete gExceptionHandler
;
2317 TeardownAnnotationFacilities();
2319 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2321 gExceptionHandler
= nullptr;
2325 delete dumpSafetyLock
;
2326 dumpSafetyLock
= nullptr;
2328 std::set_terminate(oldTerminateHandler
);
2333 nsresult
AnnotateCrashReport(Annotation key
, bool data
) {
2334 return AnnotateCrashReport(key
, data
? "1"_ns
: "0"_ns
);
2337 nsresult
AnnotateCrashReport(Annotation key
, int data
) {
2338 nsAutoCString dataString
;
2339 dataString
.AppendInt(data
);
2341 return AnnotateCrashReport(key
, dataString
);
2344 nsresult
AnnotateCrashReport(Annotation key
, unsigned int data
) {
2345 nsAutoCString dataString
;
2346 dataString
.AppendInt(data
);
2348 return AnnotateCrashReport(key
, dataString
);
2351 nsresult
AnnotateCrashReport(Annotation key
, const nsACString
& data
) {
2352 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2354 MutexAutoLock
lock(*crashReporterAPILock
);
2355 crashReporterAPIData_Table
[key
] = data
;
2360 nsresult
AppendToCrashReportAnnotation(Annotation key
, const nsACString
& data
) {
2361 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2363 MutexAutoLock
lock(*crashReporterAPILock
);
2364 nsAutoCString
newString(crashReporterAPIData_Table
[key
]);
2365 newString
.Append(" - "_ns
);
2366 newString
.Append(data
);
2367 crashReporterAPIData_Table
[key
] = newString
;
2372 nsresult
RemoveCrashReportAnnotation(Annotation key
) {
2373 return AnnotateCrashReport(key
, ""_ns
);
2376 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, bool data
)
2377 : AutoAnnotateCrashReport(key
, data
? "1"_ns
: "0"_ns
) {}
2379 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, int data
)
2380 : AutoAnnotateCrashReport(key
, nsPrintfCString("%d", data
)) {}
2382 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, unsigned data
)
2383 : AutoAnnotateCrashReport(key
, nsPrintfCString("%u", data
)) {}
2385 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
,
2386 const nsACString
& data
)
2389 MutexAutoLock
lock(*crashReporterAPILock
);
2390 auto& entry
= crashReporterAPIData_Table
[mKey
];
2391 mPrevious
= std::move(entry
);
2396 AutoAnnotateCrashReport::~AutoAnnotateCrashReport() {
2398 MutexAutoLock
lock(*crashReporterAPILock
);
2399 crashReporterAPIData_Table
[mKey
] = std::move(mPrevious
);
2403 void MergeCrashAnnotations(AnnotationTable
& aDst
, const AnnotationTable
& aSrc
) {
2404 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
2405 const nsCString
& value
= aSrc
[key
];
2406 if (!value
.IsEmpty()) {
2412 static void MergeContentCrashAnnotations(AnnotationTable
& aDst
) {
2413 MutexAutoLock
lock(*crashReporterAPILock
);
2414 MergeCrashAnnotations(aDst
, crashReporterAPIData_Table
);
2417 // Adds crash time, uptime and memory report annotations
2418 static void AddCommonAnnotations(AnnotationTable
& aAnnotations
) {
2419 const time_t crashTime
= time(nullptr);
2420 nsAutoCString crashTimeStr
;
2421 crashTimeStr
.AppendInt(static_cast<uint64_t>(crashTime
));
2422 aAnnotations
[Annotation::CrashTime
] = crashTimeStr
;
2424 if (inactiveStateStart
) {
2425 nsAutoCString inactiveDuration
;
2426 inactiveDuration
.AppendInt(
2427 static_cast<uint64_t>(crashTime
- inactiveStateStart
));
2428 aAnnotations
[Annotation::LastInteractionDuration
] = inactiveDuration
;
2431 double uptimeTS
= (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation())
2432 .ToSecondsSigDigits();
2433 nsAutoCString uptimeStr
;
2434 uptimeStr
.AppendFloat(uptimeTS
);
2435 aAnnotations
[Annotation::UptimeTS
] = uptimeStr
;
2438 nsresult
SetGarbageCollecting(bool collecting
) {
2439 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2441 isGarbageCollecting
= collecting
;
2446 void SetEventloopNestingLevel(uint32_t level
) { eventloopNestingLevel
= level
; }
2448 void ClearInactiveStateStart() { inactiveStateStart
= 0; }
2449 void SetInactiveStateStart() {
2450 if (!inactiveStateStart
) {
2451 inactiveStateStart
= GetCurrentTimeForCrashTime();
2455 void SetMinidumpAnalysisAllThreads() {
2456 char* env
= strdup("MOZ_CRASHREPORTER_DUMP_ALL_THREADS=1");
2460 nsresult
AppendAppNotesToCrashReport(const nsACString
& data
) {
2461 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2463 MutexAutoLock
lock(*notesFieldLock
);
2465 notesField
->Append(data
);
2466 return AnnotateCrashReport(Annotation::Notes
, *notesField
);
2469 // Returns true if found, false if not found.
2470 static bool GetAnnotation(CrashReporter::Annotation key
, nsACString
& data
) {
2471 if (!gExceptionHandler
) return false;
2473 MutexAutoLock
lock(*crashReporterAPILock
);
2474 const nsCString
& entry
= crashReporterAPIData_Table
[key
];
2475 if (entry
.IsEmpty()) {
2483 nsresult
RegisterAppMemory(void* ptr
, size_t length
) {
2484 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2486 #if defined(XP_LINUX) || defined(XP_WIN)
2487 gExceptionHandler
->RegisterAppMemory(ptr
, length
);
2490 return NS_ERROR_NOT_IMPLEMENTED
;
2494 nsresult
UnregisterAppMemory(void* ptr
) {
2495 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2497 #if defined(XP_LINUX) || defined(XP_WIN)
2498 gExceptionHandler
->UnregisterAppMemory(ptr
);
2501 return NS_ERROR_NOT_IMPLEMENTED
;
2505 void SetIncludeContextHeap(bool aValue
) {
2506 sIncludeContextHeap
= aValue
;
2509 if (gExceptionHandler
) {
2510 gExceptionHandler
->set_include_context_heap(sIncludeContextHeap
);
2515 bool GetServerURL(nsACString
& aServerURL
) {
2516 if (!gExceptionHandler
) return false;
2518 return GetAnnotation(CrashReporter::Annotation::ServerURL
, aServerURL
);
2521 nsresult
SetServerURL(const nsACString
& aServerURL
) {
2522 // store server URL with the API data
2523 // the client knows to handle this specially
2524 return AnnotateCrashReport(Annotation::ServerURL
, aServerURL
);
2527 nsresult
SetRestartArgs(int argc
, char** argv
) {
2528 if (!gExceptionHandler
) return NS_OK
;
2531 nsAutoCString envVar
;
2533 char* argv0
= getenv("MOZ_APP_LAUNCHER");
2534 for (i
= 0; i
< argc
; i
++) {
2535 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
2536 envVar
.AppendInt(i
);
2538 if (argv0
&& i
== 0) {
2539 // Is there a request to suppress default binary launcher?
2545 // PR_SetEnv() wants the string to be available for the lifetime
2546 // of the app, so dup it here. This conversion is not lossy.
2547 env
= ToNewCString(envVar
, mozilla::fallible
);
2548 if (!env
) return NS_ERROR_OUT_OF_MEMORY
;
2553 // make sure the arg list is terminated
2554 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
2555 envVar
.AppendInt(i
);
2558 // PR_SetEnv() wants the string to be available for the lifetime
2559 // of the app, so dup it here. This conversion is not lossy.
2560 env
= ToNewCString(envVar
, mozilla::fallible
);
2561 if (!env
) return NS_ERROR_OUT_OF_MEMORY
;
2565 // make sure we save the info in XUL_APP_FILE for the reporter
2566 const char* appfile
= PR_GetEnv("XUL_APP_FILE");
2567 if (appfile
&& *appfile
) {
2568 envVar
= "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
2571 // PR_SetEnv() wants the string to be available for the lifetime
2572 // of the app, so dup it here. This conversion is not lossy.
2573 env
= ToNewCString(envVar
);
2581 nsresult
WriteMinidumpForException(EXCEPTION_POINTERS
* aExceptionInfo
) {
2582 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2584 return gExceptionHandler
->WriteMinidumpForException(aExceptionInfo
)
2591 bool WriteMinidumpForSigInfo(int signo
, siginfo_t
* info
, void* uc
) {
2592 if (!gExceptionHandler
) {
2593 // Crash reporting is disabled.
2596 return gExceptionHandler
->HandleSignal(signo
, info
, uc
);
2601 nsresult
AppendObjCExceptionInfoToAppNotes(void* inException
) {
2602 nsAutoCString excString
;
2603 GetObjCExceptionInfo(inException
, excString
);
2604 AppendAppNotesToCrashReport(excString
);
2610 * Combined code to get/set the crash reporter submission pref on
2611 * different platforms.
2613 static nsresult
PrefSubmitReports(bool* aSubmitReports
, bool writePref
) {
2617 * NOTE! This needs to stay in sync with the preference checking code
2618 * in toolkit/crashreporter/client/crashreporter_win.cpp
2620 nsCOMPtr
<nsIXULAppInfo
> appinfo
=
2621 do_GetService("@mozilla.org/xre/app-info;1", &rv
);
2622 NS_ENSURE_SUCCESS(rv
, rv
);
2624 nsAutoCString appVendor
, appName
;
2625 rv
= appinfo
->GetVendor(appVendor
);
2626 NS_ENSURE_SUCCESS(rv
, rv
);
2627 rv
= appinfo
->GetName(appName
);
2628 NS_ENSURE_SUCCESS(rv
, rv
);
2630 nsCOMPtr
<nsIWindowsRegKey
> regKey(
2631 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv
));
2632 NS_ENSURE_SUCCESS(rv
, rv
);
2634 nsAutoCString regPath
;
2636 regPath
.AppendLiteral("Software\\");
2638 // We need to ensure the registry keys are created so we can properly
2639 // write values to it
2641 // Create appVendor key
2642 if (!appVendor
.IsEmpty()) {
2643 regPath
.Append(appVendor
);
2644 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2645 NS_ConvertUTF8toUTF16(regPath
),
2646 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2647 regPath
.Append('\\');
2650 // Create appName key
2651 regPath
.Append(appName
);
2652 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2653 NS_ConvertUTF8toUTF16(regPath
),
2654 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2655 regPath
.Append('\\');
2657 // Create Crash Reporter key
2658 regPath
.AppendLiteral("Crash Reporter");
2659 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2660 NS_ConvertUTF8toUTF16(regPath
),
2661 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2663 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
2666 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2667 NS_ConvertUTF8toUTF16(regPath
),
2668 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2669 NS_ENSURE_SUCCESS(rv
, rv
);
2671 uint32_t value
= *aSubmitReports
? 1 : 0;
2672 rv
= regKey
->WriteIntValue(u
"SubmitCrashReport"_ns
, value
);
2677 // We're reading the pref value, so we need to first look under
2678 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
2679 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
2682 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
2683 NS_ConvertUTF8toUTF16(regPath
),
2684 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
2685 if (NS_SUCCEEDED(rv
)) {
2686 rv
= regKey
->ReadIntValue(u
"SubmitCrashReport"_ns
, &value
);
2688 if (NS_SUCCEEDED(rv
)) {
2689 *aSubmitReports
= !!value
;
2694 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2695 NS_ConvertUTF8toUTF16(regPath
),
2696 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
2697 if (NS_FAILED(rv
)) {
2698 *aSubmitReports
= true;
2702 rv
= regKey
->ReadIntValue(u
"SubmitCrashReport"_ns
, &value
);
2703 // default to true on failure
2704 if (NS_FAILED(rv
)) {
2710 *aSubmitReports
= !!value
;
2712 #elif defined(XP_MACOSX)
2715 CFPropertyListRef cfValue
=
2716 (CFPropertyListRef
)(*aSubmitReports
? kCFBooleanTrue
: kCFBooleanFalse
);
2717 ::CFPreferencesSetAppValue(CFSTR("submitReport"), cfValue
,
2718 reporterClientAppID
);
2719 if (!::CFPreferencesAppSynchronize(reporterClientAppID
))
2720 rv
= NS_ERROR_FAILURE
;
2722 *aSubmitReports
= true;
2723 Boolean keyExistsAndHasValidFormat
= false;
2724 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(
2725 CFSTR("submitReport"), reporterClientAppID
,
2726 &keyExistsAndHasValidFormat
);
2727 if (keyExistsAndHasValidFormat
) *aSubmitReports
= !!prefValue
;
2730 #elif defined(XP_UNIX)
2732 * NOTE! This needs to stay in sync with the preference checking code
2733 * in toolkit/crashreporter/client/crashreporter_linux.cpp
2735 nsCOMPtr
<nsIFile
> reporterINI
;
2736 rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI
));
2737 NS_ENSURE_SUCCESS(rv
, rv
);
2738 reporterINI
->AppendNative("Crash Reports"_ns
);
2739 reporterINI
->AppendNative("crashreporter.ini"_ns
);
2742 rv
= reporterINI
->Exists(&exists
);
2743 NS_ENSURE_SUCCESS(rv
, rv
);
2746 // If reading the pref, default to true if .ini doesn't exist.
2747 *aSubmitReports
= true;
2750 // Create the file so the INI processor can write to it.
2751 rv
= reporterINI
->Create(nsIFile::NORMAL_FILE_TYPE
, 0600);
2752 NS_ENSURE_SUCCESS(rv
, rv
);
2755 nsCOMPtr
<nsIINIParserFactory
> iniFactory
=
2756 do_GetService("@mozilla.org/xpcom/ini-parser-factory;1", &rv
);
2757 NS_ENSURE_SUCCESS(rv
, rv
);
2759 nsCOMPtr
<nsIINIParser
> iniParser
;
2760 rv
= iniFactory
->CreateINIParser(reporterINI
, getter_AddRefs(iniParser
));
2761 NS_ENSURE_SUCCESS(rv
, rv
);
2763 // If we're writing the pref, just set and we're done.
2765 nsCOMPtr
<nsIINIParserWriter
> iniWriter
= do_QueryInterface(iniParser
);
2766 NS_ENSURE_TRUE(iniWriter
, NS_ERROR_FAILURE
);
2768 rv
= iniWriter
->SetString("Crash Reporter"_ns
, "SubmitReport"_ns
,
2769 *aSubmitReports
? "1"_ns
: "0"_ns
);
2770 NS_ENSURE_SUCCESS(rv
, rv
);
2771 rv
= iniWriter
->WriteFile(reporterINI
);
2775 nsAutoCString submitReportValue
;
2776 rv
= iniParser
->GetString("Crash Reporter"_ns
, "SubmitReport"_ns
,
2779 // Default to "true" if the pref can't be found.
2781 *aSubmitReports
= true;
2782 else if (submitReportValue
.EqualsASCII("0"))
2783 *aSubmitReports
= false;
2785 *aSubmitReports
= true;
2789 return NS_ERROR_NOT_IMPLEMENTED
;
2793 nsresult
GetSubmitReports(bool* aSubmitReports
) {
2794 return PrefSubmitReports(aSubmitReports
, false);
2797 nsresult
SetSubmitReports(bool aSubmitReports
) {
2800 nsCOMPtr
<nsIObserverService
> obsServ
=
2801 mozilla::services::GetObserverService();
2803 return NS_ERROR_FAILURE
;
2806 rv
= PrefSubmitReports(&aSubmitReports
, true);
2807 if (NS_FAILED(rv
)) {
2811 obsServ
->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
2815 static void SetCrashEventsDir(nsIFile
* aDir
) {
2816 static const XP_CHAR eventsDirectoryEnv
[] =
2817 XP_TEXT("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
2819 nsCOMPtr
<nsIFile
> eventsDir
= aDir
;
2821 const char* env
= PR_GetEnv("CRASHES_EVENTS_DIR");
2823 NS_NewNativeLocalFile(nsDependentCString(env
), false,
2824 getter_AddRefs(eventsDir
));
2825 EnsureDirectoryExists(eventsDir
);
2828 xpstring
* path
= CreatePathFromFile(eventsDir
);
2830 return; // There's no clean failure from this
2833 eventsDirectory
= xpstring(*path
);
2835 SetEnvironmentVariableW(eventsDirectoryEnv
, path
->c_str());
2837 setenv(eventsDirectoryEnv
, path
->c_str(), /* overwrite */ 1);
2843 void SetProfileDirectory(nsIFile
* aDir
) {
2844 nsCOMPtr
<nsIFile
> dir
;
2845 aDir
->Clone(getter_AddRefs(dir
));
2847 dir
->Append(u
"crashes"_ns
);
2848 EnsureDirectoryExists(dir
);
2849 dir
->Append(u
"events"_ns
);
2850 EnsureDirectoryExists(dir
);
2851 SetCrashEventsDir(dir
);
2854 void SetUserAppDataDirectory(nsIFile
* aDir
) {
2855 nsCOMPtr
<nsIFile
> dir
;
2856 aDir
->Clone(getter_AddRefs(dir
));
2858 dir
->Append(u
"Crash Reports"_ns
);
2859 EnsureDirectoryExists(dir
);
2860 dir
->Append(u
"events"_ns
);
2861 EnsureDirectoryExists(dir
);
2862 SetCrashEventsDir(dir
);
2865 void UpdateCrashEventsDir() {
2866 const char* env
= PR_GetEnv("CRASHES_EVENTS_DIR");
2868 SetCrashEventsDir(nullptr);
2871 nsCOMPtr
<nsIFile
> eventsDir
;
2872 nsresult rv
= NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir
));
2873 if (NS_SUCCEEDED(rv
)) {
2874 SetProfileDirectory(eventsDir
);
2878 rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir
));
2879 if (NS_SUCCEEDED(rv
)) {
2880 SetUserAppDataDirectory(eventsDir
);
2885 "Couldn't get the user appdata directory. Crash events may not be "
2889 bool GetCrashEventsDir(nsAString
& aPath
) {
2890 if (eventsDirectory
.empty()) {
2893 aPath
= CONVERT_XP_CHAR_TO_UTF16(eventsDirectory
.c_str());
2897 void SetMemoryReportFile(nsIFile
* aFile
) {
2898 if (!gExceptionHandler
) {
2904 aFile
->GetPath(path
);
2906 aFile
->GetNativePath(path
);
2908 memoryReportPath
= xpstring(path
.get());
2911 nsresult
GetDefaultMemoryReportFile(nsIFile
** aFile
) {
2912 nsCOMPtr
<nsIFile
> defaultMemoryReportFile
;
2913 if (!defaultMemoryReportPath
) {
2914 nsresult rv
= NS_GetSpecialDirectory(
2915 NS_APP_PROFILE_DIR_STARTUP
, getter_AddRefs(defaultMemoryReportFile
));
2916 if (NS_FAILED(rv
)) {
2919 defaultMemoryReportFile
->AppendNative("memory-report.json.gz"_ns
);
2920 defaultMemoryReportPath
= CreatePathFromFile(defaultMemoryReportFile
);
2921 if (!defaultMemoryReportPath
) {
2922 return NS_ERROR_FAILURE
;
2925 CreateFileFromPath(*defaultMemoryReportPath
,
2926 getter_AddRefs(defaultMemoryReportFile
));
2927 if (!defaultMemoryReportFile
) {
2928 return NS_ERROR_FAILURE
;
2931 defaultMemoryReportFile
.forget(aFile
);
2935 static void FindPendingDir() {
2936 if (!pendingDirectory
.empty()) {
2939 nsCOMPtr
<nsIFile
> pendingDir
;
2940 nsresult rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir
));
2941 if (NS_FAILED(rv
)) {
2943 "Couldn't get the user appdata directory, crash dumps will go in an "
2944 "unusual location");
2946 pendingDir
->Append(u
"Crash Reports"_ns
);
2947 pendingDir
->Append(u
"pending"_ns
);
2951 pendingDir
->GetPath(path
);
2953 pendingDir
->GetNativePath(path
);
2955 pendingDirectory
= xpstring(path
.get());
2959 // The "pending" dir is Crash Reports/pending, from which minidumps
2960 // can be submitted. Because this method may be called off the main thread,
2961 // we store the pending directory as a path.
2962 static bool GetPendingDir(nsIFile
** dir
) {
2963 // MOZ_ASSERT(OOPInitialized());
2964 if (pendingDirectory
.empty()) {
2968 nsCOMPtr
<nsIFile
> pending
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
2970 NS_WARNING("Can't set up pending directory during shutdown.");
2974 pending
->InitWithPath(nsDependentString(pendingDirectory
.c_str()));
2976 pending
->InitWithNativePath(nsDependentCString(pendingDirectory
.c_str()));
2982 // The "limbo" dir is where minidumps go to wait for something else to
2983 // use them. If we're |ShouldReport()|, then the "something else" is
2984 // a minidump submitter, and they're coming from the
2985 // Crash Reports/pending/ dir. Otherwise, we don't know what the
2986 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
2988 static bool GetMinidumpLimboDir(nsIFile
** dir
) {
2989 if (ShouldReport()) {
2990 return GetPendingDir(dir
);
2993 CreateFileFromPath(gExceptionHandler
->dump_path(), dir
);
2995 CreateFileFromPath(gExceptionHandler
->minidump_descriptor().directory(),
2998 return nullptr != *dir
;
3002 void DeleteMinidumpFilesForID(const nsAString
& aId
,
3003 const Maybe
<nsString
>& aAdditionalMinidump
) {
3004 nsCOMPtr
<nsIFile
> minidumpFile
;
3005 if (GetMinidumpForID(aId
, getter_AddRefs(minidumpFile
))) {
3006 minidumpFile
->Remove(false);
3009 nsCOMPtr
<nsIFile
> extraFile
;
3010 if (GetExtraFileForID(aId
, getter_AddRefs(extraFile
))) {
3011 extraFile
->Remove(false);
3014 if (aAdditionalMinidump
&& GetMinidumpForID(aId
, getter_AddRefs(minidumpFile
),
3015 aAdditionalMinidump
)) {
3016 minidumpFile
->Remove(false);
3020 bool GetMinidumpForID(const nsAString
& id
, nsIFile
** minidump
,
3021 const Maybe
<nsString
>& aAdditionalMinidump
) {
3022 if (!GetMinidumpLimboDir(minidump
)) {
3026 nsAutoString
fileName(id
);
3028 if (aAdditionalMinidump
) {
3029 fileName
.Append('-');
3030 fileName
.Append(*aAdditionalMinidump
);
3033 fileName
.Append(u
".dmp"_ns
);
3034 (*minidump
)->Append(fileName
);
3037 if (NS_FAILED((*minidump
)->Exists(&exists
)) || !exists
) {
3044 bool GetIDFromMinidump(nsIFile
* minidump
, nsAString
& id
) {
3045 if (minidump
&& NS_SUCCEEDED(minidump
->GetLeafName(id
))) {
3046 id
.ReplaceLiteral(id
.Length() - 4, 4, u
"");
3052 bool GetExtraFileForID(const nsAString
& id
, nsIFile
** extraFile
) {
3053 if (!GetMinidumpLimboDir(extraFile
)) {
3057 (*extraFile
)->Append(id
+ u
".extra"_ns
);
3060 if (NS_FAILED((*extraFile
)->Exists(&exists
)) || !exists
) {
3067 bool GetExtraFileForMinidump(nsIFile
* minidump
, nsIFile
** extraFile
) {
3068 nsAutoString leafName
;
3069 nsresult rv
= minidump
->GetLeafName(leafName
);
3070 if (NS_FAILED(rv
)) return false;
3072 nsCOMPtr
<nsIFile
> extraF
;
3073 rv
= minidump
->Clone(getter_AddRefs(extraF
));
3074 if (NS_FAILED(rv
)) return false;
3076 leafName
.Replace(leafName
.Length() - 3, 3, u
"extra"_ns
);
3077 rv
= extraF
->SetLeafName(leafName
);
3078 if (NS_FAILED(rv
)) return false;
3080 *extraFile
= nullptr;
3081 extraF
.swap(*extraFile
);
3085 static bool WriteExtraFile(PlatformWriter
& pw
,
3086 const AnnotationTable
& aAnnotations
) {
3091 JSONAnnotationWriter
writer(pw
);
3092 WriteAnnotations(writer
, aAnnotations
);
3093 WriteSynthesizedAnnotations(writer
);
3098 bool WriteExtraFile(const nsAString
& id
, const AnnotationTable
& annotations
) {
3099 nsCOMPtr
<nsIFile
> extra
;
3100 if (!GetMinidumpLimboDir(getter_AddRefs(extra
))) {
3104 extra
->Append(id
+ u
".extra"_ns
);
3107 NS_ENSURE_SUCCESS(extra
->GetPath(path
), false);
3108 #elif defined(XP_UNIX)
3109 NS_ENSURE_SUCCESS(extra
->GetNativePath(path
), false);
3112 PlatformWriter
pw(path
.get());
3113 return WriteExtraFile(pw
, annotations
);
3116 // This adds annotations that were populated in the main process but are not
3117 // present among the ones that were passed in. Additionally common annotations
3118 // which are present in every crash report are added, including crash time,
3120 static void AddSharedAnnotations(AnnotationTable
& aAnnotations
) {
3121 MergeContentCrashAnnotations(aAnnotations
);
3122 AddCommonAnnotations(aAnnotations
);
3125 static void AddChildProcessAnnotations(
3126 AnnotationTable
& aAnnotations
, nsTArray
<CAnnotation
>* aChildAnnotations
) {
3127 if (!aChildAnnotations
) {
3128 // TODO: We should probably make a list of errors that occurred when
3129 // generating a crash report as more than one can occurr.
3130 aAnnotations
[Annotation::DumperError
] = "MissingAnnotations";
3134 for (const auto& annotation
: *aChildAnnotations
) {
3135 switch (annotation
.data
.tag
) {
3136 case AnnotationData::Tag::Empty
:
3139 case AnnotationData::Tag::UsizeData
:
3140 if (annotation
.id
==
3141 static_cast<uint32_t>(Annotation::OOMAllocationSize
)) {
3142 // We need to special-case OOMAllocationSize here because it should
3143 // not be added if its value is 0. We'll come up with a more general
3144 // method of skipping ignored values for crash annotations in the
3146 if (annotation
.data
.usize_data
._0
!= 0) {
3147 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3148 nsPrintfCString("%zu", annotation
.data
.usize_data
._0
);
3151 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3152 nsPrintfCString("%zu", annotation
.data
.usize_data
._0
);
3156 case AnnotationData::Tag::NSCStringData
: {
3157 const auto& string
= annotation
.data
.nsc_string_data
._0
;
3158 if (!string
.IsEmpty()) {
3159 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3160 annotation
.data
.nsc_string_data
._0
;
3164 case AnnotationData::Tag::ByteBuffer
: {
3165 if (annotation
.id
==
3166 static_cast<uint32_t>(Annotation::PHCBaseAddress
)) {
3168 const auto& buffer
= annotation
.data
.byte_buffer
._0
;
3169 mozilla::phc::AddrInfo addr_info
;
3170 memcpy(&addr_info
, buffer
.Elements(), sizeof(addr_info
));
3171 PopulatePHCAnnotations(aAnnotations
, &addr_info
);
3179 // It really only makes sense to call this function when
3180 // ShouldReport() is true.
3181 // Uses dumpFile's filename to generate memoryReport's filename (same name with
3182 // a different extension)
3183 static bool MoveToPending(nsIFile
* dumpFile
, nsIFile
* extraFile
,
3184 nsIFile
* memoryReport
) {
3185 nsCOMPtr
<nsIFile
> pendingDir
;
3186 if (!GetPendingDir(getter_AddRefs(pendingDir
))) return false;
3188 if (NS_FAILED(dumpFile
->MoveTo(pendingDir
, u
""_ns
))) {
3192 if (extraFile
&& NS_FAILED(extraFile
->MoveTo(pendingDir
, u
""_ns
))) {
3197 nsAutoString leafName
;
3198 nsresult rv
= dumpFile
->GetLeafName(leafName
);
3199 if (NS_FAILED(rv
)) {
3202 // Generate the correct memory report filename from the dumpFile's name
3204 leafName
.Length() - 4, 4,
3205 static_cast<nsString
>(CONVERT_XP_CHAR_TO_UTF16(memoryReportExtension
)));
3206 if (NS_FAILED(memoryReport
->MoveTo(pendingDir
, leafName
))) {
3214 static void MaybeAnnotateDumperError(const ClientInfo
& aClientInfo
,
3215 AnnotationTable
& aAnnotations
) {
3216 #if defined(MOZ_OXIDIZED_BREAKPAD)
3217 if (aClientInfo
.had_error()) {
3218 aAnnotations
[Annotation::DumperError
] = *aClientInfo
.error_msg();
3223 static void OnChildProcessDumpRequested(
3224 void* aContext
, const ClientInfo
& aClientInfo
,
3225 const xpstring
& aFilePath
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
3226 nsCOMPtr
<nsIFile
> minidump
;
3228 // Hold the mutex until the current dump request is complete, to
3229 // prevent UnsetExceptionHandler() from pulling the rug out from
3231 MutexAutoLock
lock(*dumpSafetyLock
);
3232 if (!isSafeToDump
) return;
3234 CreateFileFromPath(aFilePath
, getter_AddRefs(minidump
));
3236 ProcessId pid
= aClientInfo
.pid();
3237 if (ShouldReport()) {
3238 nsCOMPtr
<nsIFile
> memoryReport
;
3239 if (!memoryReportPath
.empty()) {
3240 CreateFileFromPath(memoryReportPath
, getter_AddRefs(memoryReport
));
3241 MOZ_ASSERT(memoryReport
);
3243 MoveToPending(minidump
, nullptr, memoryReport
);
3247 nsTArray
<CAnnotation
>* child_annotations
= mozannotation_retrieve(
3248 reinterpret_cast<uintptr_t>(aClientInfo
.process_handle()),
3249 static_cast<size_t>(Annotation::Count
));
3250 #elif defined(XP_MACOSX)
3251 nsTArray
<CAnnotation
>* child_annotations
= mozannotation_retrieve(
3252 aClientInfo
.task(), static_cast<size_t>(Annotation::Count
));
3254 nsTArray
<CAnnotation
>* child_annotations
=
3255 mozannotation_retrieve(pid
, static_cast<size_t>(Annotation::Count
));
3258 // TODO: Write a minimal set of annotations if we fail to read them, and
3259 // add an error to the minidump to highlight this fact.
3262 #ifdef MOZ_CRASHREPORTER_INJECTOR
3266 MutexAutoLock
lock(*dumpMapLock
);
3267 ChildProcessData
* pd
= pidToMinidump
->PutEntry(pid
);
3268 MOZ_ASSERT(!pd
->minidump
);
3269 pd
->minidump
= minidump
;
3270 pd
->sequence
= ++crashSequence
;
3271 pd
->annotations
= MakeUnique
<AnnotationTable
>();
3272 AnnotationTable
& annotations
= *(pd
->annotations
);
3273 AddSharedAnnotations(annotations
);
3274 AddChildProcessAnnotations(annotations
, child_annotations
);
3276 MaybeAnnotateDumperError(aClientInfo
, annotations
);
3278 #ifdef MOZ_CRASHREPORTER_INJECTOR
3279 runCallback
= nullptr != pd
->callback
;
3282 #ifdef MOZ_CRASHREPORTER_INJECTOR
3283 if (runCallback
) NS_DispatchToMainThread(new ReportInjectedCrash(pid
));
3287 if (child_annotations
) {
3288 mozannotation_free(child_annotations
);
3292 static bool OOPInitialized() { return pidToMinidump
!= nullptr; }
3295 class ProxyToMainThread
: public Runnable
{
3297 ProxyToMainThread() : Runnable("nsExceptionHandler::ProxyToMainThread") {}
3298 NS_IMETHOD
Run() override
{
3303 if (!NS_IsMainThread()) {
3304 // This logic needs to run on the main thread
3305 nsCOMPtr
<nsIThread
> mainThread
= do_GetMainThread();
3306 mozilla::SyncRunnable::DispatchToThread(mainThread
,
3307 new ProxyToMainThread());
3311 if (OOPInitialized()) return;
3313 MOZ_ASSERT(NS_IsMainThread());
3315 MOZ_ASSERT(gExceptionHandler
!= nullptr,
3316 "attempt to initialize OOP crash reporter before in-process "
3320 childCrashNotifyPipe
=
3321 mozilla::Smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
3322 static_cast<int>(::GetCurrentProcessId()))
3325 const std::wstring dumpPath
= gExceptionHandler
->dump_path();
3326 crashServer
= new CrashGenerationServer(
3327 std::wstring(NS_ConvertASCIItoUTF16(childCrashNotifyPipe
).get()),
3328 nullptr, // default security attributes
3329 nullptr, nullptr, // we don't care about process connect here
3330 OnChildProcessDumpRequested
, nullptr, nullptr, nullptr,
3331 nullptr, // we don't care about process exit here
3332 nullptr, nullptr, // we don't care about upload request here
3333 true, // automatically generate dumps
3336 if (sIncludeContextHeap
) {
3337 crashServer
->set_include_context_heap(sIncludeContextHeap
);
3340 #elif defined(XP_LINUX)
3341 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd
,
3343 MOZ_CRASH("can't create crash reporter socketpair()");
3345 const std::string dumpPath
=
3346 gExceptionHandler
->minidump_descriptor().directory();
3348 new CrashGenerationServer(serverSocketFd
, OnChildProcessDumpRequested
,
3349 nullptr, nullptr, nullptr, true, &dumpPath
);
3351 #elif defined(XP_MACOSX)
3352 childCrashNotifyPipe
= mozilla::Smprintf("gecko-crash-server-pipe.%i",
3353 static_cast<int>(getpid()))
3355 const std::string dumpPath
= gExceptionHandler
->dump_path();
3357 crashServer
= new CrashGenerationServer(childCrashNotifyPipe
, nullptr,
3358 nullptr, OnChildProcessDumpRequested
,
3359 nullptr, nullptr, nullptr,
3360 true, // automatically generate dumps
3364 if (!crashServer
->Start()) MOZ_CRASH("can't start crash reporter server()");
3366 pidToMinidump
= new ChildMinidumpMap();
3368 dumpMapLock
= new Mutex("CrashReporter::dumpMapLock");
3371 UpdateCrashEventsDir();
3374 static void OOPDeinit() {
3375 if (!OOPInitialized()) {
3376 NS_WARNING("OOPDeinit() without successful OOPInit()");
3380 #ifdef MOZ_CRASHREPORTER_INJECTOR
3381 if (sInjectorThread
) {
3382 sInjectorThread
->Shutdown();
3383 NS_RELEASE(sInjectorThread
);
3388 crashServer
= nullptr;
3391 dumpMapLock
= nullptr;
3393 delete pidToMinidump
;
3394 pidToMinidump
= nullptr;
3396 #if defined(XP_WIN) || defined(XP_MACOSX)
3397 free(childCrashNotifyPipe
);
3398 childCrashNotifyPipe
= nullptr;
3402 #if defined(XP_WIN) || defined(XP_MACOSX)
3403 // Parent-side API for children
3404 const char* GetChildNotificationPipe() {
3405 if (!GetEnabled()) return kNullNotifyPipe
;
3407 MOZ_ASSERT(OOPInitialized());
3409 return childCrashNotifyPipe
;
3413 #ifdef MOZ_CRASHREPORTER_INJECTOR
3414 void InjectCrashReporterIntoProcess(DWORD processID
,
3415 InjectorCrashCallback
* cb
) {
3416 if (!GetEnabled()) return;
3418 if (!OOPInitialized()) OOPInit();
3420 if (!sInjectorThread
) {
3421 if (NS_FAILED(NS_NewNamedThread("CrashRep Inject", &sInjectorThread
)))
3426 MutexAutoLock
lock(*dumpMapLock
);
3427 ChildProcessData
* pd
= pidToMinidump
->PutEntry(processID
);
3428 MOZ_ASSERT(!pd
->minidump
&& !pd
->callback
);
3430 pd
->minidumpOnly
= true;
3433 nsCOMPtr
<nsIRunnable
> r
= new InjectCrashRunnable(processID
);
3434 sInjectorThread
->Dispatch(r
, nsIEventTarget::DISPATCH_NORMAL
);
3438 ReportInjectedCrash::Run() {
3439 // Crash reporting may have been disabled after this method was dispatched
3440 if (!OOPInitialized()) return NS_OK
;
3442 InjectorCrashCallback
* cb
;
3444 MutexAutoLock
lock(*dumpMapLock
);
3445 ChildProcessData
* pd
= pidToMinidump
->GetEntry(mPID
);
3446 if (!pd
|| !pd
->callback
) return NS_OK
;
3448 MOZ_ASSERT(pd
->minidump
);
3457 void UnregisterInjectorCallback(DWORD processID
) {
3458 if (!OOPInitialized()) return;
3460 MutexAutoLock
lock(*dumpMapLock
);
3461 pidToMinidump
->RemoveEntry(processID
);
3464 #endif // MOZ_CRASHREPORTER_INJECTOR
3466 #if defined(XP_LINUX)
3468 // Parent-side API for children
3469 bool CreateNotificationPipeForChild(int* childCrashFd
, int* childCrashRemapFd
) {
3470 if (!GetEnabled()) {
3472 *childCrashRemapFd
= -1;
3476 MOZ_ASSERT(OOPInitialized());
3478 *childCrashFd
= clientSocketFd
;
3479 *childCrashRemapFd
= gMagicChildCrashReportFd
;
3484 #endif // defined(XP_LINUX)
3486 bool SetRemoteExceptionHandler(const char* aCrashPipe
) {
3487 MOZ_ASSERT(!gExceptionHandler
, "crash client already init'd");
3488 RegisterRuntimeExceptionModule();
3489 InitializeAnnotationFacilities();
3490 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
3492 case Annotation::MozCrashReason
:
3493 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
3494 case Annotation::MainThreadRunnableName
:
3496 case Annotation::OOMAllocationSize
:
3498 case Annotation::PHCBaseAddress
:
3503 mozannotation_register_nscstring(static_cast<uint32_t>(key
),
3504 &crashReporterAPIData_Table
[key
]);
3508 mozannotation_register_cstring(
3509 static_cast<uint32_t>(Annotation::MozCrashReason
), &gMozCrashReason
);
3511 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
3512 mozannotation_register_char_buffer(
3513 static_cast<uint32_t>(Annotation::MainThreadRunnableName
),
3514 &nsThread::sMainThreadRunnableName
[0]);
3517 mozannotation_register_usize(
3518 static_cast<uint32_t>(Annotation::OOMAllocationSize
),
3519 &gOOMAllocationSize
);
3522 // HACK: We're using the PHCBaseAddress annotation to point to the actual
3523 // PHC address information object. This is because we currently have no
3524 // difference between the internal representation of annotations and their
3525 // external representation. Once we remove the old annotation API this will
3526 // be properly addressed.
3527 mozannotation_register_bytebuffer(
3528 static_cast<uint32_t>(Annotation::PHCBaseAddress
),
3529 &mozilla::phc::gAddrInfo
, sizeof(mozilla::phc::gAddrInfo
));
3533 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3534 L
"", ChildFilter
, ChildMinidumpCallback
,
3535 nullptr, // no callback context
3536 google_breakpad::ExceptionHandler::HANDLER_ALL
, GetMinidumpType(),
3537 NS_ConvertASCIItoUTF16(aCrashPipe
).get(), nullptr);
3538 gExceptionHandler
->set_handle_debug_exceptions(true);
3540 # if defined(HAVE_64BIT_BUILD)
3541 SetJitExceptionHandler();
3543 #elif defined(XP_LINUX)
3544 // MinidumpDescriptor requires a non-empty path.
3545 google_breakpad::MinidumpDescriptor
path(".");
3547 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3548 path
, ChildFilter
, ChildMinidumpCallback
,
3549 nullptr, // no callback context
3550 true, // install signal handlers
3551 gMagicChildCrashReportFd
);
3552 #elif defined(XP_MACOSX)
3553 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3554 "", ChildFilter
, ChildMinidumpCallback
,
3555 nullptr, // no callback context
3556 true, // install signal handlers
3560 RecordMainThreadId();
3562 oldTerminateHandler
= std::set_terminate(&TerminateHandler
);
3564 // we either do remote or nothing, no fallback to regular crash reporting
3565 return gExceptionHandler
->IsOutOfProcess();
3568 void GetAnnotation(uint32_t childPid
, Annotation annotation
,
3569 nsACString
& outStr
) {
3570 if (!GetEnabled()) {
3574 MutexAutoLock
lock(*dumpMapLock
);
3576 ChildProcessData
* pd
= pidToMinidump
->GetEntry(childPid
);
3581 outStr
= (*pd
->annotations
)[annotation
];
3584 bool TakeMinidumpForChild(uint32_t childPid
, nsIFile
** dump
,
3585 AnnotationTable
& aAnnotations
, uint32_t* aSequence
) {
3586 if (!GetEnabled()) return false;
3588 MutexAutoLock
lock(*dumpMapLock
);
3590 ChildProcessData
* pd
= pidToMinidump
->GetEntry(childPid
);
3591 if (!pd
) return false;
3593 NS_IF_ADDREF(*dump
= pd
->minidump
);
3594 // Only plugin process minidumps taken using the injector don't have
3596 if (!pd
->minidumpOnly
) {
3597 aAnnotations
= *(pd
->annotations
);
3600 *aSequence
= pd
->sequence
;
3603 pidToMinidump
->RemoveEntry(pd
);
3608 bool FinalizeOrphanedMinidump(uint32_t aChildPid
, GeckoProcessType aType
,
3609 nsString
* aDumpId
) {
3610 AnnotationTable annotations
;
3611 nsCOMPtr
<nsIFile
> minidump
;
3613 if (!TakeMinidumpForChild(aChildPid
, getter_AddRefs(minidump
), annotations
)) {
3618 if (!GetIDFromMinidump(minidump
, id
)) {
3626 annotations
[Annotation::ProcessType
] =
3627 XRE_ChildProcessTypeToAnnotation(aType
);
3629 return WriteExtraFile(id
, annotations
);
3634 // Function invoked by the WER runtime exception handler running in an
3635 // external process. This function isn't used anywhere inside Gecko directly
3636 // but rather invoked via CreateRemoteThread() in the main process.
3637 DWORD WINAPI
WerNotifyProc(LPVOID aParameter
) {
3638 const WindowsErrorReportingData
* werData
=
3639 static_cast<const WindowsErrorReportingData
*>(aParameter
);
3641 // Hold the mutex until the current dump request is complete, to
3642 // prevent UnsetExceptionHandler() from pulling the rug out from
3644 MutexAutoLock
safetyLock(*dumpSafetyLock
);
3645 if (!isSafeToDump
|| !ShouldReport()) {
3649 ProcessId pid
= werData
->mChildPid
;
3650 nsCOMPtr
<nsIFile
> minidump
;
3651 if (!GetPendingDir(getter_AddRefs(minidump
))) {
3654 xpstring
minidump_native_name(werData
->mMinidumpFile
,
3655 werData
->mMinidumpFile
+ 40);
3656 nsString
minidump_name(minidump_native_name
.c_str());
3657 minidump
->Append(minidump_name
);
3660 MutexAutoLock
lock(*dumpMapLock
);
3661 ChildProcessData
* pd
= pidToMinidump
->PutEntry(pid
);
3662 MOZ_ASSERT(!pd
->minidump
);
3663 pd
->minidump
= minidump
;
3664 pd
->sequence
= ++crashSequence
;
3665 pd
->annotations
= MakeUnique
<AnnotationTable
>();
3666 (*pd
->annotations
)[Annotation::WindowsErrorReporting
] = "1"_ns
;
3667 if (werData
->mOOMAllocationSize
> 0) {
3668 char buffer
[32] = {};
3669 XP_STOA(werData
->mOOMAllocationSize
, buffer
);
3670 (*pd
->annotations
)[Annotation::OOMAllocationSize
] = buffer
;
3673 AddSharedAnnotations(*(pd
->annotations
));
3681 //-----------------------------------------------------------------------------
3682 // CreateMinidumpsAndPair() and helpers
3686 * Renames the stand alone dump file aDumpFile to:
3687 * |aOwnerDumpFile-aDumpFileProcessType.dmp|
3688 * and moves it into the same directory as aOwnerDumpFile. Does not
3689 * modify aOwnerDumpFile in any way.
3691 * @param aDumpFile - the dump file to associate with aOwnerDumpFile.
3692 * @param aOwnerDumpFile - the new owner of aDumpFile.
3693 * @param aDumpFileProcessType - process name associated with aDumpFile.
3695 static void RenameAdditionalHangMinidump(nsIFile
* minidump
,
3696 nsIFile
* childMinidump
,
3697 const nsACString
& name
) {
3698 nsCOMPtr
<nsIFile
> directory
;
3699 childMinidump
->GetParent(getter_AddRefs(directory
));
3700 if (!directory
) return;
3702 nsAutoCString leafName
;
3703 childMinidump
->GetNativeLeafName(leafName
);
3705 // turn "<id>.dmp" into "<id>-<name>.dmp
3706 leafName
.Insert("-"_ns
+ name
, leafName
.Length() - 4);
3708 if (NS_FAILED(minidump
->MoveToNative(directory
, leafName
))) {
3709 NS_WARNING("RenameAdditionalHangMinidump failed to move minidump.");
3713 // Stores the minidump in the nsIFile pointed by the |context| parameter.
3714 static bool PairedDumpCallback(
3716 const MinidumpDescriptor
& descriptor
,
3718 const XP_CHAR
* dump_path
, const XP_CHAR
* minidump_id
,
3722 EXCEPTION_POINTERS
* /*unused*/, MDRawAssertionInfo
* /*unused*/,
3724 const phc::AddrInfo
* addrInfo
, bool succeeded
) {
3725 XP_CHAR
* path
= static_cast<XP_CHAR
*>(context
);
3726 size_t size
= XP_PATH_MAX
;
3729 Concat(path
, descriptor
.path(), &size
);
3731 path
= Concat(path
, dump_path
, &size
);
3732 path
= Concat(path
, XP_PATH_SEPARATOR
, &size
);
3733 path
= Concat(path
, minidump_id
, &size
);
3734 Concat(path
, dumpFileExtension
, &size
);
3740 ThreadId
CurrentThreadId() {
3742 return ::GetCurrentThreadId();
3743 #elif defined(XP_LINUX)
3744 return sys_gettid();
3745 #elif defined(XP_MACOSX)
3746 // Just return an index, since Mach ports can't be directly serialized
3747 thread_act_port_array_t threads_for_task
;
3748 mach_msg_type_number_t thread_count
;
3750 if (task_threads(mach_task_self(), &threads_for_task
, &thread_count
))
3753 for (unsigned int i
= 0; i
< thread_count
; ++i
) {
3754 if (threads_for_task
[i
] == mach_thread_self()) return i
;
3758 # error "Unsupported platform"
3763 static mach_port_t
GetChildThread(ProcessHandle childPid
,
3764 ThreadId childBlamedThread
) {
3765 mach_port_t childThread
= MACH_PORT_NULL
;
3766 thread_act_port_array_t threads_for_task
;
3767 mach_msg_type_number_t thread_count
;
3769 if (task_threads(childPid
, &threads_for_task
, &thread_count
) ==
3771 childBlamedThread
< thread_count
) {
3772 childThread
= threads_for_task
[childBlamedThread
];
3779 bool CreateMinidumpsAndPair(ProcessHandle aTargetHandle
,
3780 ThreadId aTargetBlamedThread
,
3781 const nsACString
& aIncomingPairName
,
3782 AnnotationTable
& aTargetAnnotations
,
3783 nsIFile
** aMainDumpOut
) {
3784 if (!GetEnabled()) {
3788 AutoIOInterposerDisable disableIOInterposition
;
3791 mach_port_t targetThread
= GetChildThread(aTargetHandle
, aTargetBlamedThread
);
3793 ThreadId targetThread
= aTargetBlamedThread
;
3798 dump_path
= gExceptionHandler
->dump_path();
3800 dump_path
= gExceptionHandler
->minidump_descriptor().directory();
3803 // Ugly, but due to Breakpad limitations we can't allocate memory in the
3804 // callback when generating a dump of the calling process.
3805 XP_CHAR minidumpPath
[XP_PATH_MAX
] = {};
3808 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
3809 aTargetHandle
, targetThread
, dump_path
, PairedDumpCallback
,
3810 static_cast<void*>(minidumpPath
)
3819 nsCOMPtr
<nsIFile
> targetMinidump
;
3820 CreateFileFromPath(xpstring(minidumpPath
), getter_AddRefs(targetMinidump
));
3822 // Create a dump of this process.
3823 if (!google_breakpad::ExceptionHandler::WriteMinidump(
3828 PairedDumpCallback
, static_cast<void*>(minidumpPath
)
3834 targetMinidump
->Remove(false);
3838 nsCOMPtr
<nsIFile
> incomingDump
;
3839 CreateFileFromPath(xpstring(minidumpPath
), getter_AddRefs(incomingDump
));
3841 RenameAdditionalHangMinidump(incomingDump
, targetMinidump
, aIncomingPairName
);
3843 if (ShouldReport()) {
3844 MoveToPending(targetMinidump
, nullptr, nullptr);
3845 MoveToPending(incomingDump
, nullptr, nullptr);
3847 #if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
3848 DllBlocklist_Shutdown();
3851 AddSharedAnnotations(aTargetAnnotations
);
3853 nsTArray
<CAnnotation
>* child_annotations
=
3854 mozannotation_retrieve(reinterpret_cast<uintptr_t>(aTargetHandle
),
3855 static_cast<size_t>(Annotation::Count
));
3857 nsTArray
<CAnnotation
>* child_annotations
= mozannotation_retrieve(
3858 aTargetHandle
, static_cast<size_t>(Annotation::Count
));
3860 AddChildProcessAnnotations(aTargetAnnotations
, child_annotations
);
3861 if (child_annotations
) {
3862 mozannotation_free(child_annotations
);
3865 targetMinidump
.forget(aMainDumpOut
);
3870 bool UnsetRemoteExceptionHandler(bool wasSet
) {
3871 // On Linux we don't unset breakpad's exception handler if the sandbox is
3872 // enabled because it requires invoking `sigaltstack` and we don't want to
3873 // allow that syscall in the sandbox. See bug 1622452.
3874 #if !defined(XP_LINUX) || !defined(MOZ_SANDBOX)
3876 std::set_terminate(oldTerminateHandler
);
3877 delete gExceptionHandler
;
3878 gExceptionHandler
= nullptr;
3881 TeardownAnnotationFacilities();
3886 #if defined(MOZ_WIDGET_ANDROID)
3887 void SetNotificationPipeForChild(int childCrashFd
) {
3888 gMagicChildCrashReportFd
= childCrashFd
;
3892 } // namespace CrashReporter
3894 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 24)
3896 // Bionic introduced support for getgrgid_r() and getgrnam_r() only in version
3897 // 24 (that is Android Nougat / 7.1.2). Since GeckoView is built by version 16
3898 // (32-bit) or 21 (64-bit), those functions aren't defined, but nix needs them
3899 // and minidump-writer relies on nix. These functions should never be called in
3900 // practice hence we implement them only to satisfy nix linking requirements but
3901 // we crash if we accidentally enter them.
3905 int getgrgid_r(gid_t gid
, struct group
* grp
, char* buf
, size_t buflen
,
3906 struct group
** result
) {
3907 MOZ_CRASH("getgrgid_r() is not available");
3911 int getgrnam_r(const char* name
, struct group
* grp
, char* buf
, size_t buflen
,
3912 struct group
** result
) {
3913 MOZ_CRASH("getgrnam_r() is not available");
3917 int mlockall(int flags
) {
3918 MOZ_CRASH("mlockall() is not available");
3922 int munlockall(void) {
3923 MOZ_CRASH("munlockall() is not available");
3928 #endif // __ANDROID_API__ && (__ANDROID_API__ < 24)