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 "mozilla/WindowsVersion.h"
60 # include "psapi.h" // For PERFORMANCE_INFORMATION and K32GetPerformanceInfo()
61 #elif defined(XP_MACOSX)
62 # include "breakpad-client/mac/crash_generation/client_info.h"
63 # include "breakpad-client/mac/crash_generation/crash_generation_server.h"
64 # include "breakpad-client/mac/handler/exception_handler.h"
66 # include <Carbon/Carbon.h>
67 # include <CoreFoundation/CoreFoundation.h>
68 # include <crt_externs.h>
70 # include <mach/mach.h>
71 # include <mach/vm_statistics.h>
72 # include <sys/sysctl.h>
73 # include <sys/types.h>
76 # include "mac_utils.h"
77 #elif defined(XP_LINUX)
78 # include "nsIINIParser.h"
79 # include "common/linux/linux_libc_support.h"
80 # include "third_party/lss/linux_syscall_support.h"
81 # include "breakpad-client/linux/crash_generation/client_info.h"
82 # include "breakpad-client/linux/crash_generation/crash_generation_server.h"
83 # include "breakpad-client/linux/handler/exception_handler.h"
84 # include "common/linux/eintr_wrapper.h"
86 # include <sys/types.h>
87 # include "sys/sysinfo.h"
88 # include <sys/wait.h>
91 # error "Not yet implemented for this platform"
92 #endif // defined(XP_WIN)
94 #ifdef MOZ_CRASHREPORTER_INJECTOR
95 # include "InjectCrashReporter.h"
96 using mozilla::InjectCrashRunnable
;
103 #include "mozilla/Mutex.h"
108 #include "mozilla/IOInterposer.h"
109 #include "mozilla/mozalloc_oom.h"
111 #if defined(XP_MACOSX)
112 CFStringRef reporterClientAppID
= CFSTR("org.mozilla.crashreporter");
114 #if defined(MOZ_WIDGET_ANDROID)
115 # include "common/linux/file_id.h"
118 using google_breakpad::ClientInfo
;
119 using google_breakpad::CrashGenerationServer
;
121 using google_breakpad::MinidumpDescriptor
;
122 #elif defined(XP_WIN)
123 using google_breakpad::ExceptionHandler
;
125 #if defined(MOZ_WIDGET_ANDROID)
126 using google_breakpad::auto_wasteful_vector
;
127 using google_breakpad::FileID
;
128 using google_breakpad::kDefaultBuildIdSize
;
129 using google_breakpad::PageAllocator
;
131 using namespace mozilla
;
133 namespace mozilla::phc
{
135 // Global instance that is retrieved by the process generating the crash report
136 mozilla::phc::AddrInfo gAddrInfo
;
138 } // namespace mozilla::phc
140 namespace CrashReporter
{
143 typedef wchar_t XP_CHAR
;
144 typedef std::wstring xpstring
;
145 # define XP_TEXT(x) L##x
146 # define CONVERT_XP_CHAR_TO_UTF16(x) x
147 # define XP_STRLEN(x) wcslen(x)
148 # define my_strlen strlen
149 # define my_memchr memchr
150 # define CRASH_REPORTER_FILENAME u"crashreporter.exe"_ns
151 # define XP_PATH_SEPARATOR L"\\"
152 # define XP_PATH_SEPARATOR_CHAR L'\\'
153 # define XP_PATH_MAX (MAX_PATH + 1)
154 // "<reporter path>" "<minidump path>"
155 # define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
156 # define XP_TTOA(time, buffer) _i64toa((time), (buffer), 10)
157 # define XP_STOA(size, buffer) _ui64toa((size), (buffer), 10)
159 typedef char XP_CHAR
;
160 typedef std::string xpstring
;
161 # define XP_TEXT(x) x
162 # define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
163 # define CRASH_REPORTER_FILENAME u"crashreporter"_ns
164 # define XP_PATH_SEPARATOR "/"
165 # define XP_PATH_SEPARATOR_CHAR '/'
166 # define XP_PATH_MAX PATH_MAX
168 # define XP_STRLEN(x) my_strlen(x)
169 # define XP_TTOA(time, buffer) \
170 my_u64tostring(uint64_t(time), (buffer), sizeof(buffer))
171 # define XP_STOA(size, buffer) \
172 my_u64tostring((size), (buffer), sizeof(buffer))
174 # define XP_STRLEN(x) strlen(x)
175 # define XP_TTOA(time, buffer) sprintf(buffer, "%" PRIu64, uint64_t(time))
176 # define XP_STOA(size, buffer) sprintf(buffer, "%zu", size_t(size))
177 # define my_strlen strlen
178 # define my_memchr memchr
179 # define sys_close close
180 # define sys_fork fork
181 # define sys_open open
182 # define sys_read read
183 # define sys_write write
187 #if defined(__GNUC__)
188 # define MAYBE_UNUSED __attribute__((unused))
190 # define MAYBE_UNUSED
191 #endif // defined(__GNUC__)
194 static const XP_CHAR dumpFileExtension
[] = XP_TEXT(".dmp");
197 static const XP_CHAR extraFileExtension
[] = XP_TEXT(".extra");
198 static const XP_CHAR memoryReportExtension
[] = XP_TEXT(".memory.json.gz");
199 static xpstring
* defaultMemoryReportPath
= nullptr;
201 static const char kCrashMainID
[] = "crash.main.3\n";
203 static google_breakpad::ExceptionHandler
* gExceptionHandler
= nullptr;
204 static mozilla::Atomic
<bool> gEncounteredChildException(false);
206 static xpstring pendingDirectory
;
207 static xpstring crashReporterPath
;
208 static xpstring memoryReportPath
;
210 // Where crash events should go.
211 static xpstring eventsDirectory
;
213 // If this is false, we don't launch the crash reporter
214 static bool doReport
= true;
216 // if this is true, we pass the exception on to the OS crash reporter
217 static bool showOSCrashReporter
= false;
219 // The time of the last recorded crash, as a time_t value.
220 static time_t lastCrashTime
= 0;
221 // The pathname of a file to store the crash time in
222 static XP_CHAR lastCrashTimeFilename
[XP_PATH_MAX
] = {0};
224 #if defined(MOZ_WIDGET_ANDROID)
225 // on Android 4.2 and above there is a user serial number associated
226 // with the current process that gets lost when we fork so we need to
227 // explicitly pass it to am
228 static char* androidUserSerial
= nullptr;
230 // Before Android 8 we needed to use "startservice" to start the crash reporting
231 // service. After Android 8 we need to use "start-foreground-service"
232 static const char* androidStartServiceCommand
= nullptr;
235 // this holds additional data sent via the API
236 static Mutex
* crashReporterAPILock
;
237 static Mutex
* notesFieldLock
;
238 static AnnotationTable crashReporterAPIData_Table
;
239 static nsCString
* notesField
= nullptr;
240 static bool isGarbageCollecting
;
241 static uint32_t eventloopNestingLevel
= 0;
242 static time_t inactiveStateStart
= 0;
247 #elif defined(XP_WIN) // defined(XP_UNIX)
249 #endif // defined(XP_WIN)
252 // Avoid a race during application termination.
253 static Mutex
* dumpSafetyLock
;
254 static bool isSafeToDump
= false;
256 // Whether to include heap regions of the crash context.
257 static bool sIncludeContextHeap
= false;
259 // OOP crash reporting
260 static CrashGenerationServer
* crashServer
; // chrome process has this
262 static std::terminate_handler oldTerminateHandler
= nullptr;
264 #if defined(XP_WIN) || defined(XP_MACOSX)
265 // If crash reporting is disabled, we hand out this "null" pipe to the
266 // child process and don't attempt to connect to a parent server.
267 static const char kNullNotifyPipe
[] = "-";
268 static char* childCrashNotifyPipe
;
270 #elif defined(XP_LINUX)
271 static int serverSocketFd
= -1;
272 static int clientSocketFd
= -1;
274 // On Linux these file descriptors are created in the parent process and
275 // remapped in the child ones. See PosixProcessLauncher::DoSetup() for more
277 static FileHandle gMagicChildCrashReportFd
=
278 # if defined(MOZ_WIDGET_ANDROID)
279 // On android the fd is set at the time of child creation.
283 # endif // defined(MOZ_WIDGET_ANDROID)
287 // |dumpMapLock| must protect all access to |pidToMinidump|.
288 static Mutex
* dumpMapLock
;
289 struct ChildProcessData
: public nsUint32HashKey
{
290 explicit ChildProcessData(KeyTypePointer aKey
)
291 : nsUint32HashKey(aKey
),
293 annotations(nullptr),
295 #ifdef MOZ_CRASHREPORTER_INJECTOR
302 nsCOMPtr
<nsIFile
> minidump
;
303 // Each crashing process is assigned an increasing sequence number to
304 // indicate which process crashed first.
306 UniquePtr
<AnnotationTable
> annotations
;
307 bool minidumpOnly
; // If true then no annotations are present
308 #ifdef MOZ_CRASHREPORTER_INJECTOR
309 InjectorCrashCallback
* callback
;
313 typedef nsTHashtable
<ChildProcessData
> ChildMinidumpMap
;
314 static ChildMinidumpMap
* pidToMinidump
;
315 static uint32_t crashSequence
;
316 static bool OOPInitialized();
318 #ifdef MOZ_CRASHREPORTER_INJECTOR
319 static nsIThread
* sInjectorThread
;
321 class ReportInjectedCrash
: public Runnable
{
323 explicit ReportInjectedCrash(uint32_t pid
)
324 : Runnable("ReportInjectedCrash"), mPID(pid
) {}
326 NS_IMETHOD
Run() override
;
331 #endif // MOZ_CRASHREPORTER_INJECTOR
333 void RecordMainThreadId() {
337 #elif defined(XP_WIN) // defined(XP_UNIX)
339 #endif // defined(XP_WIN)
343 bool SignalSafeIsMainThread() {
344 // We can't rely on NS_IsMainThread() because we are in a signal handler, and
345 // sTLSIsMainThread is a thread local variable and it can be lazy allocated
346 // i.e., we could hit code path where this variable has not been accessed
347 // before and needs to be allocated right now, which will lead to spinlock
348 // deadlock effectively hanging the process, as in bug 1756407.
351 pthread_t th
= pthread_self();
352 return pthread_equal(th
, gMainThreadId
);
353 #elif defined(XP_WIN) // defined(XP_UNIX)
354 DWORD th
= GetCurrentThreadId();
355 return th
== gMainThreadId
;
356 #endif // defined(XP_WIN)
360 // the following are used to prevent other DLLs reverting the last chance
361 // exception handler to the windows default. Any attempt to change the
362 // unhandled exception filter or to reset it is ignored and our crash
363 // reporter is loaded instead (in case it became unloaded somehow)
364 typedef LPTOP_LEVEL_EXCEPTION_FILTER(WINAPI
* SetUnhandledExceptionFilter_func
)(
365 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
366 static WindowsDllInterceptor::FuncHookType
<SetUnhandledExceptionFilter_func
>
367 stub_SetUnhandledExceptionFilter
;
368 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter
= nullptr;
369 static WindowsDllInterceptor gKernel32Intercept
;
370 static bool gBlockUnhandledExceptionFilter
= true;
372 static LPTOP_LEVEL_EXCEPTION_FILTER
GetUnhandledExceptionFilter() {
373 // Set a dummy value to get the current filter, then restore
374 LPTOP_LEVEL_EXCEPTION_FILTER current
= SetUnhandledExceptionFilter(nullptr);
375 SetUnhandledExceptionFilter(current
);
379 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
patched_SetUnhandledExceptionFilter(
380 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
) {
381 if (!gBlockUnhandledExceptionFilter
) {
383 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter
);
386 if (lpTopLevelExceptionFilter
== previousUnhandledExceptionFilter
) {
387 // OK to swap back and forth between the previous filter
388 previousUnhandledExceptionFilter
=
389 stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter
);
390 return previousUnhandledExceptionFilter
;
393 // intercept attempts to change the filter
397 # if defined(HAVE_64BIT_BUILD)
398 static LPTOP_LEVEL_EXCEPTION_FILTER sUnhandledExceptionFilter
= nullptr;
400 static long JitExceptionHandler(void* exceptionRecord
, void* context
) {
401 EXCEPTION_POINTERS pointers
= {(PEXCEPTION_RECORD
)exceptionRecord
,
403 return sUnhandledExceptionFilter(&pointers
);
406 static void SetJitExceptionHandler() {
407 sUnhandledExceptionFilter
= GetUnhandledExceptionFilter();
408 if (sUnhandledExceptionFilter
)
409 js::SetJitExceptionHandler(JitExceptionHandler
);
414 * Reserve some VM space. In the event that we crash because VM space is
415 * being leaked without leaking memory, freeing this space before taking
416 * the minidump will allow us to collect a minidump.
418 * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
421 static const SIZE_T kReserveSize
= 0x5000000; // 80 MB
422 static void* gBreakpadReservedVM
;
426 static inline void my_u64tostring(uint64_t aValue
, char* aBuffer
,
427 size_t aBufferLength
) {
428 my_memset(aBuffer
, 0, aBufferLength
);
429 my_uitos(aBuffer
, aValue
, my_uint_len(aValue
));
434 static void CreateFileFromPath(const xpstring
& path
, nsIFile
** file
) {
435 NS_NewLocalFile(nsDependentString(path
.c_str()), false, file
);
438 static xpstring
* CreatePathFromFile(nsIFile
* file
) {
440 nsresult rv
= file
->GetPath(path
);
444 return new xpstring(static_cast<wchar_t*>(path
.get()), path
.Length());
447 static void CreateFileFromPath(const xpstring
& path
, nsIFile
** file
) {
448 NS_NewNativeLocalFile(nsDependentCString(path
.c_str()), false, file
);
451 MAYBE_UNUSED
static xpstring
* CreatePathFromFile(nsIFile
* file
) {
453 nsresult rv
= file
->GetNativePath(path
);
457 return new xpstring(path
.get(), path
.Length());
461 static time_t GetCurrentTimeForCrashTime() {
463 struct kernel_timeval tv
;
464 sys_gettimeofday(&tv
, nullptr);
467 return time(nullptr);
471 static XP_CHAR
* Concat(XP_CHAR
* str
, const XP_CHAR
* toAppend
, size_t* size
) {
472 size_t appendLen
= XP_STRLEN(toAppend
);
473 if (appendLen
>= *size
) {
474 appendLen
= *size
- 1;
477 memcpy(str
, toAppend
, appendLen
* sizeof(XP_CHAR
));
485 void AnnotateOOMAllocationSize(size_t size
) { gOOMAllocationSize
= size
; }
487 static size_t gTexturesSize
= 0;
489 void AnnotateTexturesSize(size_t size
) { gTexturesSize
= size
; }
492 // Like Windows CopyFile for *nix
494 // This function is not declared static even though it's not used outside of
495 // this file because of an issue in Fennec which prevents breakpad's exception
496 // handler from invoking the MinidumpCallback function. See bug 1424304.
497 bool copy_file(const char* from
, const char* to
) {
498 const int kBufSize
= 4096;
499 int fdfrom
= sys_open(from
, O_RDONLY
, 0);
506 int fdto
= sys_open(to
, O_WRONLY
| O_CREAT
, 0666);
514 int r
= sys_read(fdfrom
, buf
, kBufSize
);
524 int w
= sys_write(fdto
, wbuf
, r
);
528 } else if (errno
!= EINTR
) {
545 * The PlatformWriter class provides a tool to create and write to a file that
546 * is safe to call from within an exception handler. To use it this way the
547 * file path needs to be provided as a bare C string.
549 class PlatformWriter
{
551 PlatformWriter() : mBuffer
{}, mPos(0), mFD(kInvalidFileHandle
) {}
552 explicit PlatformWriter(const XP_CHAR
* aPath
) : PlatformWriter() {
561 #elif defined(XP_UNIX)
567 void Open(const XP_CHAR
* aPath
) {
569 mFD
= CreateFile(aPath
, GENERIC_WRITE
, 0, nullptr, CREATE_ALWAYS
,
570 FILE_ATTRIBUTE_NORMAL
, nullptr);
571 #elif defined(XP_UNIX)
572 mFD
= sys_open(aPath
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
576 void OpenHandle(FileHandle aFD
) { mFD
= aFD
; }
577 bool Valid() { return mFD
!= kInvalidFileHandle
; }
579 void WriteBuffer(const char* aBuffer
, size_t aLen
) {
585 WriteChar(*aBuffer
++);
589 void WriteString(const char* aStr
) { WriteBuffer(aStr
, my_strlen(aStr
)); }
592 void WriteLiteral(const char (&aStr
)[N
]) {
593 WriteBuffer(aStr
, N
- 1);
596 FileHandle
FileDesc() { return mFD
; }
599 PlatformWriter(const PlatformWriter
&) = delete;
601 const PlatformWriter
& operator=(const PlatformWriter
&) = delete;
603 void WriteChar(char aChar
) {
604 if (mPos
== kBufferSize
) {
608 mBuffer
[mPos
++] = aChar
;
613 char* buffer
= mBuffer
;
614 size_t length
= mPos
;
617 DWORD written_bytes
= 0;
618 if (!WriteFile(mFD
, buffer
, length
, &written_bytes
, nullptr)) {
621 #elif defined(XP_UNIX)
622 ssize_t written_bytes
= sys_write(mFD
, buffer
, length
);
623 if (written_bytes
< 0) {
624 if (errno
== EAGAIN
) {
631 buffer
+= written_bytes
;
632 length
-= written_bytes
;
639 static const size_t kBufferSize
= 512;
641 char mBuffer
[kBufferSize
];
646 class JSONAnnotationWriter
: public AnnotationWriter
{
648 explicit JSONAnnotationWriter(PlatformWriter
& aPlatformWriter
)
649 : mWriter(aPlatformWriter
), mEmpty(true) {
650 mWriter
.WriteBuffer("{", 1);
653 ~JSONAnnotationWriter() { mWriter
.WriteBuffer("}", 1); }
655 void Write(Annotation aAnnotation
, const char* aValue
,
656 size_t aLen
= 0) override
{
657 size_t len
= aLen
? aLen
: my_strlen(aValue
);
658 const char* annotationStr
= AnnotationToString(aAnnotation
);
661 mWriter
.WriteBuffer(annotationStr
, my_strlen(annotationStr
));
663 WriteEscapedString(aValue
, len
);
667 void Write(Annotation aAnnotation
, uint64_t aValue
) override
{
668 char buffer
[32] = {};
669 XP_STOA(aValue
, buffer
);
670 Write(aAnnotation
, buffer
);
676 mWriter
.WriteBuffer("\"", 1);
679 mWriter
.WriteBuffer(",\"", 2);
683 void WriteSeparator() { mWriter
.WriteBuffer("\":\"", 3); }
684 void WriteSuffix() { mWriter
.WriteBuffer("\"", 1); }
685 void WriteEscapedString(const char* aStr
, size_t aLen
) {
686 for (size_t i
= 0; i
< aLen
; i
++) {
688 if (c
<= 0x1f || c
== '\\' || c
== '\"') {
689 mWriter
.WriteBuffer("\\u00", 4);
690 WriteHexDigitAsAsciiChar((c
& 0x00f0) >> 4);
691 WriteHexDigitAsAsciiChar(c
& 0x000f);
693 mWriter
.WriteBuffer(aStr
+ i
, 1);
698 void WriteHexDigitAsAsciiChar(uint8_t u
) {
700 buf
[0] = static_cast<unsigned>((u
< 10) ? '0' + u
: 'a' + (u
- 10));
701 mWriter
.WriteBuffer(buf
, 1);
704 PlatformWriter
& mWriter
;
708 class BinaryAnnotationWriter
: public AnnotationWriter
{
710 explicit BinaryAnnotationWriter(PlatformWriter
& aPlatformWriter
)
711 : mPlatformWriter(aPlatformWriter
) {}
713 void Write(Annotation aAnnotation
, const char* aValue
,
714 size_t aLen
= 0) override
{
715 uint64_t len
= aLen
? aLen
: my_strlen(aValue
);
716 mPlatformWriter
.WriteBuffer((const char*)&aAnnotation
, sizeof(aAnnotation
));
717 mPlatformWriter
.WriteBuffer((const char*)&len
, sizeof(len
));
718 mPlatformWriter
.WriteBuffer(aValue
, len
);
721 void Write(Annotation aAnnotation
, uint64_t aValue
) override
{
722 char buffer
[32] = {};
723 XP_STOA(aValue
, buffer
);
724 Write(aAnnotation
, buffer
);
728 PlatformWriter
& mPlatformWriter
;
733 // 21 is the max length of a 64-bit decimal address entry, including the
734 // trailing comma or '\0'. And then we add another 32 just to be safe.
735 const size_t phcStringifiedAnnotationSize
=
736 (mozilla::phc::StackTrace::kMaxFrames
* 21) + 32;
738 static void PHCStackTraceToString(char* aBuffer
, size_t aBufferLen
,
739 const phc::StackTrace
& aStack
) {
742 for (size_t i
= 0; i
< aStack
.mLength
; i
++) {
744 strcat(aBuffer
, ",");
746 XP_STOA(uintptr_t(aStack
.mPcs
[i
]), addrString
);
747 strncat(aBuffer
, addrString
, aBufferLen
);
751 // The stack traces are encoded as a comma-separated list of decimal
752 // (not hexadecimal!) addresses, e.g. "12345678,12345679,12345680".
753 static void WritePHCStackTrace(AnnotationWriter
& aWriter
,
754 const Annotation aName
,
755 const Maybe
<phc::StackTrace
>& aStack
) {
756 if (aStack
.isNothing()) {
760 // 21 is the max length of a 64-bit decimal address entry, including the
761 // trailing comma or '\0'. And then we add another 32 just to be safe.
762 char addrsString
[phcStringifiedAnnotationSize
];
763 PHCStackTraceToString(addrsString
, sizeof(addrsString
), *aStack
);
764 aWriter
.Write(aName
, addrsString
);
767 static void WritePHCAddrInfo(AnnotationWriter
& writer
,
768 const phc::AddrInfo
* aAddrInfo
) {
769 // Is this a PHC allocation needing special treatment?
770 if (aAddrInfo
&& aAddrInfo
->mKind
!= phc::AddrInfo::Kind::Unknown
) {
771 const char* kindString
;
772 switch (aAddrInfo
->mKind
) {
773 case phc::AddrInfo::Kind::Unknown
:
774 kindString
= "Unknown(?!)";
776 case phc::AddrInfo::Kind::NeverAllocatedPage
:
777 kindString
= "NeverAllocatedPage";
779 case phc::AddrInfo::Kind::InUsePage
:
780 kindString
= "InUsePage(?!)";
782 case phc::AddrInfo::Kind::FreedPage
:
783 kindString
= "FreedPage";
785 case phc::AddrInfo::Kind::GuardPage
:
786 kindString
= "GuardPage";
789 kindString
= "Unmatched(?!)";
792 writer
.Write(Annotation::PHCKind
, kindString
);
793 writer
.Write(Annotation::PHCBaseAddress
, uintptr_t(aAddrInfo
->mBaseAddr
));
794 writer
.Write(Annotation::PHCUsableSize
, aAddrInfo
->mUsableSize
);
796 WritePHCStackTrace(writer
, Annotation::PHCAllocStack
,
797 aAddrInfo
->mAllocStack
);
798 WritePHCStackTrace(writer
, Annotation::PHCFreeStack
, aAddrInfo
->mFreeStack
);
802 static void PopulatePHCStackTraceAnnotation(
803 AnnotationTable
& aAnnotations
, const Annotation aName
,
804 const Maybe
<phc::StackTrace
>& aStack
) {
805 if (aStack
.isNothing()) {
809 char addrsString
[phcStringifiedAnnotationSize
];
810 PHCStackTraceToString(addrsString
, sizeof(addrsString
), *aStack
);
811 aAnnotations
[aName
] = addrsString
;
814 static void PopulatePHCAnnotations(AnnotationTable
& aAnnotations
,
815 const phc::AddrInfo
* aAddrInfo
) {
816 // Is this a PHC allocation needing special treatment?
817 if (aAddrInfo
&& aAddrInfo
->mKind
!= phc::AddrInfo::Kind::Unknown
) {
818 const char* kindString
;
819 switch (aAddrInfo
->mKind
) {
820 case phc::AddrInfo::Kind::Unknown
:
821 kindString
= "Unknown(?!)";
823 case phc::AddrInfo::Kind::NeverAllocatedPage
:
824 kindString
= "NeverAllocatedPage";
826 case phc::AddrInfo::Kind::InUsePage
:
827 kindString
= "InUsePage(?!)";
829 case phc::AddrInfo::Kind::FreedPage
:
830 kindString
= "FreedPage";
832 case phc::AddrInfo::Kind::GuardPage
:
833 kindString
= "GuardPage";
836 kindString
= "Unmatched(?!)";
840 aAnnotations
[Annotation::PHCKind
] = kindString
;
841 aAnnotations
[Annotation::PHCBaseAddress
] =
842 nsPrintfCString("%zu", uintptr_t(aAddrInfo
->mBaseAddr
));
843 aAnnotations
[Annotation::PHCUsableSize
] =
844 nsPrintfCString("%zu", aAddrInfo
->mUsableSize
);
845 PopulatePHCStackTraceAnnotation(aAnnotations
, Annotation::PHCAllocStack
,
846 aAddrInfo
->mAllocStack
);
847 PopulatePHCStackTraceAnnotation(aAnnotations
, Annotation::PHCFreeStack
,
848 aAddrInfo
->mFreeStack
);
854 * If minidump_id is null, we assume that dump_path contains the full
857 static void OpenAPIData(PlatformWriter
& aWriter
, const XP_CHAR
* dump_path
,
858 const XP_CHAR
* minidump_id
= nullptr) {
859 static XP_CHAR extraDataPath
[XP_PATH_MAX
];
860 size_t size
= XP_PATH_MAX
;
863 p
= Concat(extraDataPath
, dump_path
, &size
);
864 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
865 p
= Concat(p
, minidump_id
, &size
);
867 p
= Concat(extraDataPath
, dump_path
, &size
);
868 // Skip back past the .dmp extension, if any.
869 if (*(p
- 4) == XP_TEXT('.')) {
874 Concat(p
, extraFileExtension
, &size
);
875 aWriter
.Open(extraDataPath
);
879 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
880 MEMORYSTATUSEX statex
;
881 statex
.dwLength
= sizeof(statex
);
882 if (GlobalMemoryStatusEx(&statex
)) {
883 aWriter
.Write(Annotation::SystemMemoryUsePercentage
, statex
.dwMemoryLoad
);
884 aWriter
.Write(Annotation::TotalVirtualMemory
, statex
.ullTotalVirtual
);
885 aWriter
.Write(Annotation::AvailableVirtualMemory
, statex
.ullAvailVirtual
);
886 aWriter
.Write(Annotation::TotalPhysicalMemory
, statex
.ullTotalPhys
);
887 aWriter
.Write(Annotation::AvailablePhysicalMemory
, statex
.ullAvailPhys
);
890 PERFORMANCE_INFORMATION info
;
891 if (K32GetPerformanceInfo(&info
, sizeof(info
))) {
892 aWriter
.Write(Annotation::TotalPageFile
, info
.CommitLimit
* info
.PageSize
);
893 aWriter
.Write(Annotation::AvailablePageFile
,
894 (info
.CommitLimit
- info
.CommitTotal
) * info
.PageSize
);
898 // Extract the total physical memory of the system.
899 static void WritePhysicalMemoryStatus(AnnotationWriter
& aWriter
) {
900 uint64_t physicalMemoryByteSize
= 0;
901 const size_t NAME_LEN
= 2;
902 int name
[NAME_LEN
] = {/* Hardware */ CTL_HW
,
903 /* 64-bit physical memory size */ HW_MEMSIZE
};
904 size_t infoByteSize
= sizeof(physicalMemoryByteSize
);
905 if (sysctl(name
, NAME_LEN
, &physicalMemoryByteSize
, &infoByteSize
,
906 /* We do not replace data */ nullptr,
907 /* We do not replace data */ 0) != -1) {
908 aWriter
.Write(Annotation::TotalPhysicalMemory
, physicalMemoryByteSize
);
912 // Extract available and purgeable physical memory.
913 static void WriteAvailableMemoryStatus(AnnotationWriter
& aWriter
) {
914 auto host
= mach_host_self();
915 vm_statistics64_data_t stats
;
916 unsigned int count
= HOST_VM_INFO64_COUNT
;
917 if (host_statistics64(host
, HOST_VM_INFO64
, (host_info64_t
)&stats
, &count
) ==
919 aWriter
.Write(Annotation::AvailablePhysicalMemory
,
920 stats
.free_count
* vm_page_size
);
921 aWriter
.Write(Annotation::PurgeablePhysicalMemory
,
922 stats
.purgeable_count
* vm_page_size
);
926 // Extract the status of the swap.
927 static void WriteSwapFileStatus(AnnotationWriter
& aWriter
) {
928 const size_t NAME_LEN
= 2;
929 int name
[] = {/* Hardware */ CTL_VM
,
930 /* 64-bit physical memory size */ VM_SWAPUSAGE
};
931 struct xsw_usage swapUsage
;
932 size_t infoByteSize
= sizeof(swapUsage
);
933 if (sysctl(name
, NAME_LEN
, &swapUsage
, &infoByteSize
,
934 /* We do not replace data */ nullptr,
935 /* We do not replace data */ 0) != -1) {
936 aWriter
.Write(Annotation::AvailableSwapMemory
, swapUsage
.xsu_avail
);
939 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
940 WritePhysicalMemoryStatus(aWriter
);
941 WriteAvailableMemoryStatus(aWriter
);
942 WriteSwapFileStatus(aWriter
);
947 static void AnnotateMemoryStatus(AnnotationWriter
& aWriter
) {
948 // We can't simply call `sysinfo` as this requires libc.
949 // So we need to parse /proc/meminfo.
951 // We read the entire file to memory prior to parsing
952 // as it makes the parser code a little bit simpler.
953 // As /proc/meminfo is synchronized via `proc_create_single`,
954 // there's no risk of race condition regardless of how we
957 // The buffer in which we're going to load the entire file.
958 // A typical size for /proc/meminfo is 1KiB, so 4KiB should
959 // be large enough until further notice.
960 const size_t BUFFER_SIZE_BYTES
= 4096;
961 char buffer
[BUFFER_SIZE_BYTES
];
963 size_t bufferLen
= 0;
965 // Read and load into memory.
966 int fd
= sys_open("/proc/meminfo", O_RDONLY
, /* chmod */ 0);
968 // No /proc/meminfo? Well, fail silently.
971 auto Guard
= MakeScopeExit([fd
]() { mozilla::Unused
<< sys_close(fd
); });
973 ssize_t bytesRead
= 0;
975 if ((bytesRead
= sys_read(fd
, buffer
+ bufferLen
,
976 BUFFER_SIZE_BYTES
- bufferLen
)) < 0) {
977 if ((errno
== EAGAIN
) || (errno
== EINTR
)) {
981 // Cannot read for some reason. Let's give up.
985 bufferLen
+= bytesRead
;
987 if (bufferLen
== BUFFER_SIZE_BYTES
) {
988 // The file is too large, bail out
991 } while (bytesRead
!= 0);
994 // Each line of /proc/meminfo looks like
995 // SomeLabel: number unit
996 // The last line is empty.
997 // Let's write a parser.
998 // Note that we don't care about writing a normative parser, so
999 // we happily skip whitespaces without checking that it's necessary.
1001 // A stack-allocated structure containing a 0-terminated string.
1002 // We could avoid the memory copies and make it a slice at the cost
1003 // of a slightly more complicated parser. Since we're not in a
1004 // performance-critical section, we didn't.
1006 DataBuffer() : data
{0}, pos(0) {}
1007 // Clear the buffer.
1012 // Append a character.
1014 // In case of error (if c is '\0' or the buffer is full), does nothing.
1015 void append(char c
) {
1016 if (c
== 0 || pos
>= sizeof(data
) - 1) {
1022 // Compare the buffer against a nul-terminated string.
1023 bool operator==(const char* s
) const {
1024 for (size_t i
= 0; i
< pos
; ++i
) {
1025 if (s
[i
] != data
[i
]) {
1026 // Note: Since `data` never contains a '0' in positions [0,pos)
1027 // this will bailout once we have reached the end of `s`.
1034 // A NUL-terminated string of `pos + 1` chars (the +1 is for the 0).
1037 // Invariant: < 256.
1041 // A DataBuffer holding the string representation of a non-negative number.
1042 struct NumberBuffer
: DataBuffer
{
1043 // If possible, convert the string into a number.
1044 // Returns `true` in case of success, `false` in case of failure.
1045 bool asNumber(size_t* number
) {
1047 if (!my_strtoui(&result
, data
)) {
1055 // A DataBuffer holding the string representation of a unit. As of this
1056 // writing, we only support unit `kB`, which seems to be the only unit used in
1058 struct UnitBuffer
: DataBuffer
{
1059 // If possible, convert the string into a multiplier, e.g. `kB => 1024`.
1060 // Return `true` in case of success, `false` in case of failure.
1061 bool asMultiplier(size_t* multiplier
) {
1062 if (*this == "kB") {
1066 // Other units don't seem to be specified/used.
1071 // The state of the mini-parser.
1073 // Reading the label, including the trailing ':'.
1075 // Reading the number, ignoring any whitespace.
1077 // Reading the unit, ignoring any whitespace.
1081 // A single measure being read from /proc/meminfo, e.g.
1082 // the total physical memory available on the system.
1084 Measure() : state(State::Label
) {}
1085 // Reset the measure for a new read.
1087 state
= State::Label
;
1092 // Attempt to convert the measure into a number.
1093 // Return `true` if both the number and the multiplier could be
1094 // converted, `false` otherwise.
1095 // In case of overflow, produces the maximal possible `size_t`.
1096 bool asValue(size_t* result
) {
1097 size_t numberAsSize
= 0;
1098 if (!number
.asNumber(&numberAsSize
)) {
1101 size_t unitAsMultiplier
= 0;
1102 if (!unit
.asMultiplier(&unitAsMultiplier
)) {
1105 if (numberAsSize
* unitAsMultiplier
>= numberAsSize
) {
1106 *result
= numberAsSize
* unitAsMultiplier
;
1108 // Overflow. Unlikely, but just in case, let's return
1109 // the maximal possible value.
1110 *result
= size_t(-1);
1115 // The label being read, e.g. `MemFree`. Does not include the trailing ':'.
1118 // The number being read, e.g. "1024".
1119 NumberBuffer number
;
1121 // The unit being read, e.g. "kB".
1124 // What we're reading at the moment.
1128 // A value we wish to store for later processing.
1129 // e.g. to compute `AvailablePageFile`, we need to
1130 // store `CommitLimit` and `Committed_AS`.
1132 ValueStore() : value(0), found(false) {}
1136 ValueStore commitLimit
;
1137 ValueStore committedAS
;
1138 ValueStore memTotal
;
1139 ValueStore swapTotal
;
1141 // The current measure.
1144 for (size_t pos
= 0; pos
< size_t(bufferLen
); ++pos
) {
1145 const char c
= buffer
[pos
];
1146 switch (measure
.state
) {
1149 // We have finished reading the label.
1150 measure
.state
= State::Number
;
1152 measure
.label
.append(c
);
1157 // Ignore whitespace
1158 } else if ('0' <= c
&& c
<= '9') {
1159 // Accumulate numbers.
1160 measure
.number
.append(c
);
1162 // We have jumped to the unit.
1163 measure
.unit
.append(c
);
1164 measure
.state
= State::Unit
;
1169 // Ignore whitespace
1170 } else if (c
== '\n') {
1172 // - If this one of the measures we're interested in, write it.
1173 // - Once we're done, reset the parser.
1174 auto Guard
= MakeScopeExit([&measure
]() { measure
.reset(); });
1176 struct PointOfInterest
{
1177 // The label we're looking for, e.g. "MemTotal".
1179 // If non-nullptr, store the value at this address.
1181 // If other than Annotation::Count, write the value for this
1183 Annotation annotation
;
1185 const PointOfInterest POINTS_OF_INTEREST
[] = {
1186 {"MemTotal", &memTotal
, Annotation::TotalPhysicalMemory
},
1187 {"MemFree", nullptr, Annotation::AvailablePhysicalMemory
},
1188 {"MemAvailable", nullptr, Annotation::AvailableVirtualMemory
},
1189 {"SwapFree", nullptr, Annotation::AvailableSwapMemory
},
1190 {"SwapTotal", &swapTotal
, Annotation::Count
},
1191 {"CommitLimit", &commitLimit
, Annotation::Count
},
1192 {"Committed_AS", &committedAS
, Annotation::Count
},
1194 for (const auto& pointOfInterest
: POINTS_OF_INTEREST
) {
1195 if (measure
.label
== pointOfInterest
.label
) {
1197 if (measure
.asValue(&value
)) {
1198 if (pointOfInterest
.dest
!= nullptr) {
1199 pointOfInterest
.dest
->found
= true;
1200 pointOfInterest
.dest
->value
= value
;
1202 if (pointOfInterest
.annotation
!= Annotation::Count
) {
1203 aWriter
.Write(pointOfInterest
.annotation
, value
);
1209 // Otherwise, ignore.
1211 measure
.unit
.append(c
);
1217 if (commitLimit
.found
&& committedAS
.found
) {
1218 // If available, attempt to determine the available virtual memory.
1219 // As `commitLimit` is not guaranteed to be larger than `committedAS`,
1220 // we return `0` in case the commit limit has already been exceeded.
1221 uint64_t availablePageFile
= (committedAS
.value
<= commitLimit
.value
)
1222 ? (commitLimit
.value
- committedAS
.value
)
1224 aWriter
.Write(Annotation::AvailablePageFile
, availablePageFile
);
1226 if (memTotal
.found
&& swapTotal
.found
) {
1227 // If available, attempt to determine the available virtual memory.
1228 aWriter
.Write(Annotation::TotalPageFile
, memTotal
.value
+ swapTotal
.value
);
1234 static void AnnotateMemoryStatus(AnnotationTable
&) {
1235 // No memory data for other platforms yet.
1238 #endif // XP_WIN || XP_MACOSX || XP_LINUX || else
1240 #if !defined(MOZ_WIDGET_ANDROID)
1243 * Launches the program specified in aProgramPath with aMinidumpPath as its
1246 * @param aProgramPath The path of the program to be launched
1247 * @param aMinidumpPath The path of the minidump file, passed as an argument
1248 * to the launched program
1250 static bool LaunchProgram(const XP_CHAR
* aProgramPath
,
1251 const XP_CHAR
* aMinidumpPath
) {
1253 XP_CHAR cmdLine
[CMDLINE_SIZE
];
1256 size_t size
= CMDLINE_SIZE
;
1257 p
= Concat(cmdLine
, L
"\"", &size
);
1258 p
= Concat(p
, aProgramPath
, &size
);
1259 p
= Concat(p
, L
"\" \"", &size
);
1260 p
= Concat(p
, aMinidumpPath
, &size
);
1261 Concat(p
, L
"\"", &size
);
1263 PROCESS_INFORMATION pi
= {};
1264 STARTUPINFO si
= {};
1267 // If CreateProcess() fails don't do anything.
1269 /* lpApplicationName */ nullptr, (LPWSTR
)cmdLine
,
1270 /* lpProcessAttributes */ nullptr, /* lpThreadAttributes */ nullptr,
1271 /* bInheritHandles */ FALSE
,
1272 NORMAL_PRIORITY_CLASS
| CREATE_NO_WINDOW
| CREATE_BREAKAWAY_FROM_JOB
,
1273 /* lpEnvironment */ nullptr, /* lpCurrentDirectory */ nullptr, &si
,
1275 CloseHandle(pi
.hProcess
);
1276 CloseHandle(pi
.hThread
);
1278 # elif defined(XP_MACOSX)
1280 char* const my_argv
[] = {const_cast<char*>(aProgramPath
),
1281 const_cast<char*>(aMinidumpPath
), nullptr};
1283 char** env
= nullptr;
1284 char*** nsEnv
= _NSGetEnviron();
1289 int rv
= posix_spawnp(&pid
, my_argv
[0], nullptr, nullptr, my_argv
, env
);
1294 # else // !XP_MACOSX
1295 pid_t pid
= sys_fork();
1299 } else if (pid
== 0) {
1300 Unused
<< execl(aProgramPath
, aProgramPath
, aMinidumpPath
, nullptr);
1303 # endif // XP_MACOSX
1311 * Launch the crash reporter activity on Android
1313 * @param aProgramPath The path of the program to be launched
1314 * @param aMinidumpPath The path to the crash minidump file
1317 static bool LaunchCrashHandlerService(const XP_CHAR
* aProgramPath
,
1318 const XP_CHAR
* aMinidumpPath
) {
1319 static XP_CHAR extrasPath
[XP_PATH_MAX
];
1320 size_t size
= XP_PATH_MAX
;
1322 XP_CHAR
* p
= Concat(extrasPath
, aMinidumpPath
, &size
);
1323 p
= Concat(p
- 3, "extra", &size
);
1325 pid_t pid
= sys_fork();
1329 else if (pid
== 0) {
1330 // Invoke the crash handler service using am
1331 if (androidUserSerial
) {
1332 Unused
<< execlp("/system/bin/am", "/system/bin/am",
1333 androidStartServiceCommand
, "--user", androidUserSerial
,
1334 "-a", "org.mozilla.gecko.ACTION_CRASHED", "-n",
1335 aProgramPath
, "--es", "minidumpPath", aMinidumpPath
,
1336 "--es", "extrasPath", extrasPath
, "--ez", "fatal",
1337 "true", "--es", "processType", "MAIN", (char*)0);
1340 "/system/bin/am", "/system/bin/am", androidStartServiceCommand
, "-a",
1341 "org.mozilla.gecko.ACTION_CRASHED", "-n", aProgramPath
, "--es",
1342 "minidumpPath", aMinidumpPath
, "--es", "extrasPath", extrasPath
,
1343 "--ez", "fatal", "true", "--es", "processType", "MAIN", (char*)0);
1348 // We need to wait on the 'am start' command above to finish, otherwise
1349 // everything will be killed by the ActivityManager as soon as the signal
1352 Unused
<< HANDLE_EINTR(sys_waitpid(pid
, &status
, __WALL
));
1360 static void WriteMainThreadRunnableName(AnnotationWriter
& aWriter
) {
1361 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1362 // Only try to collect this information if the main thread is crashing.
1363 if (!SignalSafeIsMainThread()) {
1367 // NOTE: Use `my_memchr` over `strlen` to ensure we don't run off the end of
1368 // the buffer if it contains no null bytes. This is used instead of `strnlen`,
1369 // as breakpad's linux support library doesn't export a `my_strnlen` function.
1370 const char* buf
= nsThread::sMainThreadRunnableName
.begin();
1371 size_t len
= nsThread::kRunnableNameBufSize
;
1372 if (const void* end
= my_memchr(buf
, '\0', len
)) {
1373 len
= static_cast<const char*>(end
) - buf
;
1377 aWriter
.Write(Annotation::MainThreadRunnableName
, buf
, len
);
1382 static void WriteOOMAllocationSize(AnnotationWriter
& aWriter
) {
1383 if (gOOMAllocationSize
) {
1384 aWriter
.Write(Annotation::OOMAllocationSize
, gOOMAllocationSize
);
1388 static void WriteMozCrashReason(AnnotationWriter
& aWriter
) {
1389 if (gMozCrashReason
!= nullptr) {
1390 aWriter
.Write(Annotation::MozCrashReason
, gMozCrashReason
);
1394 static void WriteAnnotations(AnnotationWriter
& aWriter
,
1395 const AnnotationTable
& aAnnotations
) {
1396 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
1397 const nsCString
& value
= aAnnotations
[key
];
1398 if (!value
.IsEmpty()) {
1399 aWriter
.Write(key
, value
.get(), value
.Length());
1404 static void WriteSynthesizedAnnotations(AnnotationWriter
& aWriter
) {
1405 AnnotateMemoryStatus(aWriter
);
1408 static void WriteAnnotationsForMainProcessCrash(PlatformWriter
& pw
,
1409 const phc::AddrInfo
* addrInfo
,
1411 JSONAnnotationWriter
writer(pw
);
1412 WriteAnnotations(writer
, crashReporterAPIData_Table
);
1413 WriteSynthesizedAnnotations(writer
);
1414 writer
.Write(Annotation::CrashTime
, uint64_t(crashTime
));
1416 if (inactiveStateStart
) {
1417 writer
.Write(Annotation::LastInteractionDuration
,
1418 crashTime
- inactiveStateStart
);
1421 double uptimeTS
= (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation())
1422 .ToSecondsSigDigits();
1423 char uptimeTSString
[64] = {};
1424 SimpleNoCLibDtoA(uptimeTS
, uptimeTSString
, sizeof(uptimeTSString
));
1425 writer
.Write(Annotation::UptimeTS
, uptimeTSString
);
1427 // calculate time since last crash (if possible).
1428 if (lastCrashTime
!= 0) {
1429 uint64_t timeSinceLastCrash
= crashTime
- lastCrashTime
;
1431 if (timeSinceLastCrash
!= 0) {
1432 writer
.Write(Annotation::SecondsSinceLastCrash
, timeSinceLastCrash
);
1436 if (isGarbageCollecting
) {
1437 writer
.Write(Annotation::IsGarbageCollecting
, "1");
1440 if (eventloopNestingLevel
> 0) {
1441 writer
.Write(Annotation::EventLoopNestingLevel
, eventloopNestingLevel
);
1444 #if defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST)
1445 // HACK: The DLL blocklist code will manually write its annotations as JSON
1446 DllBlocklist_WriteNotes(writer
);
1447 #endif // defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST)
1449 WriteMozCrashReason(writer
);
1451 WriteMainThreadRunnableName(writer
);
1453 WriteOOMAllocationSize(writer
);
1455 if (gTexturesSize
) {
1456 writer
.Write(Annotation::TextureUsage
, gTexturesSize
);
1460 WritePHCAddrInfo(writer
, addrInfo
);
1464 static void WriteCrashEventFile(time_t crashTime
, const char* crashTimeString
,
1465 const phc::AddrInfo
* addrInfo
,
1467 const MinidumpDescriptor
& descriptor
1469 const XP_CHAR
* minidump_id
1472 // Minidump IDs are UUIDs (36) + NULL.
1473 static char id_ascii
[37] = {};
1475 const char* index
= strrchr(descriptor
.path(), '/');
1477 MOZ_ASSERT(strlen(index
) == 1 + 36 + 4); // "/" + UUID + ".dmp"
1478 for (uint32_t i
= 0; i
< 36; i
++) {
1479 id_ascii
[i
] = *(index
+ 1 + i
);
1482 MOZ_ASSERT(XP_STRLEN(minidump_id
) == 36);
1483 for (uint32_t i
= 0; i
< 36; i
++) {
1484 id_ascii
[i
] = *((char*)(minidump_id
+ i
));
1488 PlatformWriter eventFile
;
1490 if (!eventsDirectory
.empty()) {
1491 static XP_CHAR crashEventPath
[XP_PATH_MAX
];
1492 size_t size
= XP_PATH_MAX
;
1494 p
= Concat(crashEventPath
, eventsDirectory
.c_str(), &size
);
1495 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1497 Concat(p
, id_ascii
, &size
);
1499 Concat(p
, minidump_id
, &size
);
1502 eventFile
.Open(crashEventPath
);
1503 eventFile
.WriteLiteral(kCrashMainID
);
1504 eventFile
.WriteString(crashTimeString
);
1505 eventFile
.WriteLiteral("\n");
1506 eventFile
.WriteString(id_ascii
);
1507 eventFile
.WriteLiteral("\n");
1508 WriteAnnotationsForMainProcessCrash(eventFile
, addrInfo
, crashTime
);
1512 // Callback invoked from breakpad's exception handler, this writes out the
1513 // last annotations after a crash occurs and launches the crash reporter client.
1515 // This function is not declared static even though it's not used outside of
1516 // this file because of an issue in Fennec which prevents breakpad's exception
1517 // handler from invoking it. See bug 1424304.
1518 bool MinidumpCallback(
1520 const MinidumpDescriptor
& descriptor
,
1522 const XP_CHAR
* dump_path
, const XP_CHAR
* minidump_id
,
1526 EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
,
1528 const phc::AddrInfo
* addrInfo
, bool succeeded
) {
1529 bool returnValue
= showOSCrashReporter
? false : succeeded
;
1531 static XP_CHAR minidumpPath
[XP_PATH_MAX
];
1532 size_t size
= XP_PATH_MAX
;
1535 p
= Concat(minidumpPath
, dump_path
, &size
);
1536 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1537 p
= Concat(p
, minidump_id
, &size
);
1538 Concat(p
, dumpFileExtension
, &size
);
1540 Concat(minidumpPath
, descriptor
.path(), &size
);
1543 static XP_CHAR memoryReportLocalPath
[XP_PATH_MAX
];
1546 p
= Concat(memoryReportLocalPath
, dump_path
, &size
);
1547 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
1548 p
= Concat(p
, minidump_id
, &size
);
1550 p
= Concat(memoryReportLocalPath
, descriptor
.path(), &size
);
1551 // Skip back past the .dmp extension
1554 Concat(p
, memoryReportExtension
, &size
);
1556 if (!memoryReportPath
.empty()) {
1558 CopyFile(memoryReportPath
.c_str(), memoryReportLocalPath
, false);
1560 copy_file(memoryReportPath
.c_str(), memoryReportLocalPath
);
1564 time_t crashTime
= GetCurrentTimeForCrashTime();
1565 char crashTimeString
[32];
1566 XP_TTOA(crashTime
, crashTimeString
);
1568 // write crash time to file
1569 if (lastCrashTimeFilename
[0] != 0) {
1570 PlatformWriter
lastCrashFile(lastCrashTimeFilename
);
1571 lastCrashFile
.WriteString(crashTimeString
);
1574 WriteCrashEventFile(crashTime
, crashTimeString
, addrInfo
,
1583 PlatformWriter apiData
;
1585 OpenAPIData(apiData
, descriptor
.path());
1587 OpenAPIData(apiData
, dump_path
, minidump_id
);
1589 WriteAnnotationsForMainProcessCrash(apiData
, addrInfo
, crashTime
);
1594 TerminateProcess(GetCurrentProcess(), 1);
1599 #if defined(MOZ_WIDGET_ANDROID) // Android
1601 LaunchCrashHandlerService(crashReporterPath
.c_str(), minidumpPath
);
1602 #else // Windows, Mac, Linux, etc...
1603 returnValue
= LaunchProgram(crashReporterPath
.c_str(), minidumpPath
);
1605 TerminateProcess(GetCurrentProcess(), 1);
1612 #if defined(XP_MACOSX) || defined(__ANDROID__) || defined(XP_LINUX)
1613 static size_t EnsureTrailingSlash(XP_CHAR
* aBuf
, size_t aBufLen
) {
1614 size_t len
= XP_STRLEN(aBuf
);
1615 if ((len
+ 1) < aBufLen
&& len
> 0 &&
1616 aBuf
[len
- 1] != XP_PATH_SEPARATOR_CHAR
) {
1617 aBuf
[len
] = XP_PATH_SEPARATOR_CHAR
;
1627 static size_t BuildTempPath(wchar_t* aBuf
, size_t aBufLen
) {
1628 // first figure out buffer size
1629 DWORD pathLen
= GetTempPath(0, nullptr);
1630 if (pathLen
== 0 || pathLen
>= aBufLen
) {
1634 return GetTempPath(pathLen
, aBuf
);
1637 static size_t BuildTempPath(char16_t
* aBuf
, size_t aBufLen
) {
1638 return BuildTempPath(reinterpret_cast<wchar_t*>(aBuf
), aBufLen
);
1641 #elif defined(XP_MACOSX)
1643 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1644 if (aBufLen
< PATH_MAX
) {
1650 FSFindFolder(kUserDomain
, kTemporaryFolderType
, kCreateFolder
, &fsRef
);
1655 OSStatus status
= FSRefMakePath(&fsRef
, (UInt8
*)aBuf
, PATH_MAX
);
1656 if (status
!= noErr
) {
1660 return EnsureTrailingSlash(aBuf
, aBufLen
);
1663 #elif defined(__ANDROID__)
1665 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1666 // GeckoAppShell sets this in the environment
1667 const char* tempenv
= PR_GetEnv("TMPDIR");
1671 size_t size
= aBufLen
;
1672 Concat(aBuf
, tempenv
, &size
);
1673 return EnsureTrailingSlash(aBuf
, aBufLen
);
1676 #elif defined(XP_UNIX)
1678 static size_t BuildTempPath(char* aBuf
, size_t aBufLen
) {
1679 const char* tempenv
= PR_GetEnv("TMPDIR");
1680 const char* tmpPath
= "/tmp/";
1684 size_t size
= aBufLen
;
1685 Concat(aBuf
, tempenv
, &size
);
1686 return EnsureTrailingSlash(aBuf
, aBufLen
);
1690 # error "Implement this for your platform"
1693 template <typename CharT
, size_t N
>
1694 static size_t BuildTempPath(CharT (&aBuf
)[N
]) {
1695 static_assert(N
>= XP_PATH_MAX
, "char array length is too small");
1696 return BuildTempPath(&aBuf
[0], N
);
1699 template <typename PathStringT
>
1700 static bool BuildTempPath(PathStringT
& aResult
) {
1701 aResult
.SetLength(XP_PATH_MAX
);
1702 size_t actualLen
= BuildTempPath(aResult
.BeginWriting(), XP_PATH_MAX
);
1706 aResult
.SetLength(actualLen
);
1712 static void ReserveBreakpadVM() {
1713 if (!gBreakpadReservedVM
) {
1714 gBreakpadReservedVM
=
1715 VirtualAlloc(nullptr, kReserveSize
, MEM_RESERVE
, PAGE_NOACCESS
);
1719 static void FreeBreakpadVM() {
1720 if (gBreakpadReservedVM
) {
1721 VirtualFree(gBreakpadReservedVM
, 0, MEM_RELEASE
);
1725 static bool IsCrashingException(EXCEPTION_POINTERS
* exinfo
) {
1730 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)exinfo
->ExceptionRecord
;
1731 switch (e
->ExceptionCode
) {
1732 case STATUS_FLOAT_DENORMAL_OPERAND
:
1733 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
1734 case STATUS_FLOAT_INEXACT_RESULT
:
1735 case STATUS_FLOAT_INVALID_OPERATION
:
1736 case STATUS_FLOAT_OVERFLOW
:
1737 case STATUS_FLOAT_STACK_CHECK
:
1738 case STATUS_FLOAT_UNDERFLOW
:
1739 case STATUS_FLOAT_MULTIPLE_FAULTS
:
1740 case STATUS_FLOAT_MULTIPLE_TRAPS
:
1741 return false; // Don't write minidump, continue exception search
1749 // Do various actions to prepare the child process for minidump generation.
1750 // This includes disabling the I/O interposer and DLL blocklist which both
1751 // would get in the way. We also free the address space we had reserved in
1752 // 32-bit builds to free room for the minidump generation to do its work.
1753 static void PrepareForMinidump() {
1754 mozilla::IOInterposer::Disable();
1756 # if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
1757 DllBlocklist_Shutdown();
1766 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
1767 * and should not be handled as crashes.
1769 static ExceptionHandler::FilterResult
Filter(void* context
,
1770 EXCEPTION_POINTERS
* exinfo
,
1771 MDRawAssertionInfo
* assertion
) {
1772 if (!IsCrashingException(exinfo
)) {
1773 return ExceptionHandler::FilterResult::ContinueSearch
;
1776 PrepareForMinidump();
1777 return ExceptionHandler::FilterResult::HandleException
;
1780 static ExceptionHandler::FilterResult
ChildFilter(
1781 void* context
, EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
) {
1782 if (!IsCrashingException(exinfo
)) {
1783 return ExceptionHandler::FilterResult::ContinueSearch
;
1786 if (gEncounteredChildException
.exchange(true)) {
1787 return ExceptionHandler::FilterResult::AbortWithoutMinidump
;
1790 PrepareForMinidump();
1791 return ExceptionHandler::FilterResult::HandleException
;
1794 static MINIDUMP_TYPE
GetMinidumpType() {
1795 MINIDUMP_TYPE minidump_type
= static_cast<MINIDUMP_TYPE
>(
1796 MiniDumpWithFullMemoryInfo
| MiniDumpWithUnloadedModules
);
1798 # ifdef NIGHTLY_BUILD
1799 // This is Nightly only because this doubles the size of minidumps based
1800 // on the experimental data.
1802 static_cast<MINIDUMP_TYPE
>(minidump_type
| MiniDumpWithProcessThreadData
);
1804 // dbghelp.dll on Win7 can't handle overlapping memory regions so we only
1805 // enable this feature on Win8 or later.
1806 if (IsWin8OrLater()) {
1807 minidump_type
= static_cast<MINIDUMP_TYPE
>(
1809 // This allows us to examine heap objects referenced from stack objects
1810 // at the cost of further doubling the size of minidumps.
1811 MiniDumpWithIndirectlyReferencedMemory
);
1815 const char* e
= PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1817 minidump_type
= MiniDumpWithFullMemory
;
1820 return minidump_type
;
1825 static bool Filter(void* context
) {
1826 PrepareForMinidump();
1830 static bool ChildFilter(void* context
) {
1831 if (gEncounteredChildException
.exchange(true)) {
1835 PrepareForMinidump();
1839 #endif // !defined(XP_WIN)
1841 static bool ChildMinidumpCallback(
1843 const wchar_t* dump_path
, const wchar_t* minidump_id
,
1844 #elif defined(XP_LINUX)
1845 const MinidumpDescriptor
& descriptor
,
1846 #else // defined(XP_MACOSX)
1847 const char* dump_dir
, const char* minidump_id
,
1851 EXCEPTION_POINTERS
* exinfo
, MDRawAssertionInfo
* assertion
,
1852 #endif // defined(XP_WIN)
1853 const mozilla::phc::AddrInfo
* addr_info
, bool succeeded
) {
1857 static bool ShouldReport() {
1858 // this environment variable prevents us from launching
1859 // the crash reporter client
1860 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
1861 if (envvar
&& *envvar
) {
1865 envvar
= PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1866 if (envvar
&& *envvar
) {
1873 static void TerminateHandler() { MOZ_CRASH("Unhandled exception"); }
1875 #if !defined(MOZ_WIDGET_ANDROID)
1877 // Locate the specified executable and store its path as a native string in
1878 // the |aPath| so we can later invoke it from within the exception handler.
1879 static nsresult
LocateExecutable(nsIFile
* aXREDirectory
, const nsAString
& aName
,
1880 PathString
& aPath
) {
1881 nsCOMPtr
<nsIFile
> exePath
;
1882 nsresult rv
= aXREDirectory
->Clone(getter_AddRefs(exePath
));
1883 NS_ENSURE_SUCCESS(rv
, rv
);
1886 exePath
->SetNativeLeafName("MacOS"_ns
);
1887 exePath
->Append(u
"crashreporter.app"_ns
);
1888 exePath
->Append(u
"Contents"_ns
);
1889 exePath
->Append(u
"MacOS"_ns
);
1892 exePath
->Append(aName
);
1893 aPath
= exePath
->NativePath();
1897 #endif // !defined(MOZ_WIDGET_ANDROID)
1899 static void InitializeAnnotationFacilities() {
1900 crashReporterAPILock
= new Mutex("crashReporterAPILock");
1901 notesFieldLock
= new Mutex("notesFieldLock");
1902 notesField
= new nsCString();
1905 static void TeardownAnnotationFacilities() {
1906 std::fill(crashReporterAPIData_Table
.begin(),
1907 crashReporterAPIData_Table
.end(), ""_ns
);
1909 delete crashReporterAPILock
;
1910 crashReporterAPILock
= nullptr;
1912 delete notesFieldLock
;
1913 notesFieldLock
= nullptr;
1916 notesField
= nullptr;
1919 nsresult
SetExceptionHandler(nsIFile
* aXREDirectory
, bool force
/*=false*/) {
1920 if (gExceptionHandler
) return NS_ERROR_ALREADY_INITIALIZED
;
1923 // In debug builds, disable the crash reporter by default, and allow to
1924 // enable it with the MOZ_CRASHREPORTER environment variable.
1925 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER");
1926 if ((!envvar
|| !*envvar
) && !force
) return NS_OK
;
1928 // In other builds, enable the crash reporter by default, and allow
1929 // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
1930 const char* envvar
= PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
1931 if (envvar
&& *envvar
&& !force
) return NS_OK
;
1934 // this environment variable prevents us from launching
1935 // the crash reporter client
1936 doReport
= ShouldReport();
1938 RegisterRuntimeExceptionModule();
1939 InitializeAnnotationFacilities();
1941 #if !defined(MOZ_WIDGET_ANDROID)
1942 // Locate the crash reporter executable
1943 PathString crashReporterPath_temp
;
1944 nsresult rv
= LocateExecutable(aXREDirectory
, CRASH_REPORTER_FILENAME
,
1945 crashReporterPath_temp
);
1946 if (NS_WARN_IF(NS_FAILED(rv
))) {
1950 crashReporterPath
= crashReporterPath_temp
.get();
1952 // On Android, we launch a service defined via MOZ_ANDROID_CRASH_HANDLER
1953 const char* androidCrashHandler
= PR_GetEnv("MOZ_ANDROID_CRASH_HANDLER");
1954 if (androidCrashHandler
) {
1955 crashReporterPath
= xpstring(androidCrashHandler
);
1957 NS_WARNING("No Android crash handler set");
1960 const char* deviceAndroidVersion
=
1961 PR_GetEnv("MOZ_ANDROID_DEVICE_SDK_VERSION");
1962 if (deviceAndroidVersion
!= nullptr) {
1963 const int deviceSdkVersion
= atol(deviceAndroidVersion
);
1964 if (deviceSdkVersion
>= 26) {
1965 androidStartServiceCommand
= (char*)"start-foreground-service";
1967 androidStartServiceCommand
= (char*)"startservice";
1970 #endif // !defined(MOZ_WIDGET_ANDROID)
1972 // get temp path to use for minidump path
1973 PathString tempPath
;
1974 if (!BuildTempPath(tempPath
)) {
1975 return NS_ERROR_FAILURE
;
1979 ReserveBreakpadVM();
1981 // Pre-load psapi.dll to prevent it from being loaded during exception
1983 ::LoadLibraryW(L
"psapi.dll");
1986 #ifdef MOZ_WIDGET_ANDROID
1987 androidUserSerial
= getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
1990 // Initialize the flag and mutex used to avoid dump processing
1991 // once browser termination has begun.
1992 NS_ASSERTION(!dumpSafetyLock
, "Shouldn't have a lock yet");
1993 // Do not deallocate this lock while it is still possible for
1994 // isSafeToDump to be tested on another thread.
1995 dumpSafetyLock
= new Mutex("dumpSafetyLock");
1996 MutexAutoLock
lock(*dumpSafetyLock
);
1997 isSafeToDump
= true;
1999 // now set the exception handler
2001 MinidumpDescriptor
descriptor(tempPath
.get());
2005 previousUnhandledExceptionFilter
= GetUnhandledExceptionFilter();
2008 gExceptionHandler
= new google_breakpad::ExceptionHandler(
2011 #elif defined(XP_WIN)
2012 std::wstring(tempPath
.get()),
2017 Filter
, MinidumpCallback
, nullptr,
2019 google_breakpad::ExceptionHandler::HANDLER_ALL
, GetMinidumpType(),
2020 (const wchar_t*)nullptr, nullptr);
2034 if (!gExceptionHandler
) return NS_ERROR_OUT_OF_MEMORY
;
2037 gExceptionHandler
->set_handle_debug_exceptions(true);
2039 // Initially set sIncludeContextHeap to true for debugging startup crashes
2040 // even if the controlling pref value is false.
2041 SetIncludeContextHeap(true);
2042 # if defined(HAVE_64BIT_BUILD)
2043 // Tell JS about the new filter before we disable SetUnhandledExceptionFilter
2044 SetJitExceptionHandler();
2047 RecordMainThreadId();
2049 // protect the crash reporter from being unloaded
2050 gBlockUnhandledExceptionFilter
= true;
2051 gKernel32Intercept
.Init("kernel32.dll");
2052 DebugOnly
<bool> ok
= stub_SetUnhandledExceptionFilter
.Set(
2053 gKernel32Intercept
, "SetUnhandledExceptionFilter",
2054 &patched_SetUnhandledExceptionFilter
);
2059 "SetUnhandledExceptionFilter hook failed; crash reporter is "
2064 // store application start time
2065 char timeString
[32];
2066 time_t startupTime
= time(nullptr);
2067 XP_TTOA(startupTime
, timeString
);
2068 AnnotateCrashReport(Annotation::StartupTime
, nsDependentCString(timeString
));
2070 #if defined(XP_MACOSX)
2071 // On OS X, many testers like to see the OS crash reporting dialog
2072 // since it offers immediate stack traces. We allow them to set
2073 // a default to pass exceptions to the OS handler.
2074 Boolean keyExistsAndHasValidFormat
= false;
2075 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(
2076 CFSTR("OSCrashReporter"), kCFPreferencesCurrentApplication
,
2077 &keyExistsAndHasValidFormat
);
2078 if (keyExistsAndHasValidFormat
) showOSCrashReporter
= prefValue
;
2081 oldTerminateHandler
= std::set_terminate(&TerminateHandler
);
2086 bool GetEnabled() { return gExceptionHandler
!= nullptr; }
2088 bool GetMinidumpPath(nsAString
& aPath
) {
2089 if (!gExceptionHandler
) return false;
2092 aPath
= CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler
->dump_path().c_str());
2094 aPath
= CONVERT_XP_CHAR_TO_UTF16(
2095 gExceptionHandler
->minidump_descriptor().directory().c_str());
2100 nsresult
SetMinidumpPath(const nsAString
& aPath
) {
2101 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2104 gExceptionHandler
->set_dump_path(
2105 std::wstring(char16ptr_t(aPath
.BeginReading())));
2106 #elif defined(XP_LINUX)
2107 gExceptionHandler
->set_minidump_descriptor(
2108 MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath
).BeginReading()));
2110 gExceptionHandler
->set_dump_path(NS_ConvertUTF16toUTF8(aPath
).BeginReading());
2115 static nsresult
WriteDataToFile(nsIFile
* aFile
, const nsACString
& data
) {
2117 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
, 00600, &fd
);
2118 NS_ENSURE_SUCCESS(rv
, rv
);
2121 if (PR_Write(fd
, data
.Data(), data
.Length()) == -1) {
2122 rv
= NS_ERROR_FAILURE
;
2128 static nsresult
GetFileContents(nsIFile
* aFile
, nsACString
& data
) {
2130 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
2131 NS_ENSURE_SUCCESS(rv
, rv
);
2134 int32_t filesize
= PR_Available(fd
);
2135 if (filesize
<= 0) {
2136 rv
= NS_ERROR_FILE_NOT_FOUND
;
2138 data
.SetLength(filesize
);
2139 if (PR_Read(fd
, data
.BeginWriting(), filesize
) == -1) {
2140 rv
= NS_ERROR_FAILURE
;
2147 // Function typedef for initializing a piece of data that we
2148 // don't already have.
2149 typedef nsresult (*InitDataFunc
)(nsACString
&);
2151 // Attempt to read aFile's contents into aContents, if aFile
2152 // does not exist, create it and initialize its contents
2153 // by calling aInitFunc for the data.
2154 static nsresult
GetOrInit(nsIFile
* aDir
, const nsACString
& filename
,
2155 nsACString
& aContents
, InitDataFunc aInitFunc
) {
2158 nsCOMPtr
<nsIFile
> dataFile
;
2159 nsresult rv
= aDir
->Clone(getter_AddRefs(dataFile
));
2160 NS_ENSURE_SUCCESS(rv
, rv
);
2162 rv
= dataFile
->AppendNative(filename
);
2163 NS_ENSURE_SUCCESS(rv
, rv
);
2165 rv
= dataFile
->Exists(&exists
);
2166 NS_ENSURE_SUCCESS(rv
, rv
);
2170 // get the initial value and write it to the file
2171 rv
= aInitFunc(aContents
);
2172 NS_ENSURE_SUCCESS(rv
, rv
);
2173 rv
= WriteDataToFile(dataFile
, aContents
);
2175 // didn't pass in an init func
2176 rv
= NS_ERROR_FAILURE
;
2179 // just get the file's contents
2180 rv
= GetFileContents(dataFile
, aContents
);
2186 // Init the "install time" data. We're taking an easy way out here
2187 // and just setting this to "the time when this version was first run".
2188 static nsresult
InitInstallTime(nsACString
& aInstallTime
) {
2189 time_t t
= time(nullptr);
2190 aInstallTime
= nsPrintfCString("%" PRIu64
, static_cast<uint64_t>(t
));
2195 // Ensure a directory exists and create it if missing.
2196 static nsresult
EnsureDirectoryExists(nsIFile
* dir
) {
2197 nsresult rv
= dir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
2199 if (NS_WARN_IF(NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
2206 // Creates a directory that will be accessible by the crash reporter. The
2207 // directory will live under Firefox default data directory and will use the
2208 // specified name. The directory path will be passed to the crashreporter via
2209 // the specified environment variable.
2210 static nsresult
SetupCrashReporterDirectory(nsIFile
* aAppDataDirectory
,
2211 const char* aDirName
,
2212 const XP_CHAR
* aEnvVarName
,
2213 nsIFile
** aDirectory
= nullptr) {
2214 nsCOMPtr
<nsIFile
> directory
;
2215 nsresult rv
= aAppDataDirectory
->Clone(getter_AddRefs(directory
));
2216 NS_ENSURE_SUCCESS(rv
, rv
);
2218 rv
= directory
->AppendNative(nsDependentCString(aDirName
));
2219 NS_ENSURE_SUCCESS(rv
, rv
);
2221 EnsureDirectoryExists(directory
);
2222 xpstring
* directoryPath
= CreatePathFromFile(directory
);
2224 if (!directoryPath
) {
2225 return NS_ERROR_FAILURE
;
2229 SetEnvironmentVariableW(aEnvVarName
, directoryPath
->c_str());
2231 setenv(aEnvVarName
, directoryPath
->c_str(), /* overwrite */ 1);
2234 delete directoryPath
;
2237 directory
.forget(aDirectory
);
2243 // Annotate the crash report with a Unique User ID and time
2244 // since install. Also do some prep work for recording
2245 // time since last crash, which must be calculated at
2247 // If any piece of data doesn't exist, initialize it first.
2248 nsresult
SetupExtraData(nsIFile
* aAppDataDirectory
,
2249 const nsACString
& aBuildID
) {
2250 nsCOMPtr
<nsIFile
> dataDirectory
;
2252 SetupCrashReporterDirectory(aAppDataDirectory
, "Crash Reports",
2253 XP_TEXT("MOZ_CRASHREPORTER_DATA_DIRECTORY"),
2254 getter_AddRefs(dataDirectory
));
2256 if (NS_WARN_IF(NS_FAILED(rv
))) {
2260 rv
= SetupCrashReporterDirectory(aAppDataDirectory
, "Pending Pings",
2261 XP_TEXT("MOZ_CRASHREPORTER_PING_DIRECTORY"));
2263 if (NS_WARN_IF(NS_FAILED(rv
))) {
2268 if (NS_SUCCEEDED(GetOrInit(dataDirectory
, "InstallTime"_ns
+ aBuildID
, data
,
2270 AnnotateCrashReport(Annotation::InstallTime
, data
);
2272 // this is a little different, since we can't init it with anything,
2273 // since it's stored at crash time, and we can't annotate the
2274 // crash report with the stored value, since we really want
2275 // (now - LastCrash), so we just get a value if it exists,
2276 // and store it in a time_t value.
2277 if (NS_SUCCEEDED(GetOrInit(dataDirectory
, "LastCrash"_ns
, data
, nullptr))) {
2278 lastCrashTime
= (time_t)atol(data
.get());
2281 // not really the best place to init this, but I have the path I need here
2282 nsCOMPtr
<nsIFile
> lastCrashFile
;
2283 rv
= dataDirectory
->Clone(getter_AddRefs(lastCrashFile
));
2284 NS_ENSURE_SUCCESS(rv
, rv
);
2286 rv
= lastCrashFile
->AppendNative("LastCrash"_ns
);
2287 NS_ENSURE_SUCCESS(rv
, rv
);
2288 memset(lastCrashTimeFilename
, 0, sizeof(lastCrashTimeFilename
));
2290 PathString filename
;
2292 rv
= lastCrashFile
->GetPath(filename
);
2294 rv
= lastCrashFile
->GetNativePath(filename
);
2296 NS_ENSURE_SUCCESS(rv
, rv
);
2298 if (filename
.Length() < XP_PATH_MAX
) {
2300 wcsncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
2302 strncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
2309 static void OOPDeinit();
2311 nsresult
UnsetExceptionHandler() {
2313 MutexAutoLock
lock(*dumpSafetyLock
);
2314 isSafeToDump
= false;
2318 // allow SetUnhandledExceptionFilter
2319 gBlockUnhandledExceptionFilter
= false;
2322 delete gExceptionHandler
;
2324 TeardownAnnotationFacilities();
2326 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2328 gExceptionHandler
= nullptr;
2332 delete dumpSafetyLock
;
2333 dumpSafetyLock
= nullptr;
2335 std::set_terminate(oldTerminateHandler
);
2340 nsresult
AnnotateCrashReport(Annotation key
, bool data
) {
2341 return AnnotateCrashReport(key
, data
? "1"_ns
: "0"_ns
);
2344 nsresult
AnnotateCrashReport(Annotation key
, int data
) {
2345 nsAutoCString dataString
;
2346 dataString
.AppendInt(data
);
2348 return AnnotateCrashReport(key
, dataString
);
2351 nsresult
AnnotateCrashReport(Annotation key
, unsigned int data
) {
2352 nsAutoCString dataString
;
2353 dataString
.AppendInt(data
);
2355 return AnnotateCrashReport(key
, dataString
);
2358 nsresult
AnnotateCrashReport(Annotation key
, const nsACString
& data
) {
2359 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2361 MutexAutoLock
lock(*crashReporterAPILock
);
2362 crashReporterAPIData_Table
[key
] = data
;
2367 nsresult
AppendToCrashReportAnnotation(Annotation key
, const nsACString
& data
) {
2368 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2370 MutexAutoLock
lock(*crashReporterAPILock
);
2371 nsAutoCString
newString(crashReporterAPIData_Table
[key
]);
2372 newString
.Append(" - "_ns
);
2373 newString
.Append(data
);
2374 crashReporterAPIData_Table
[key
] = newString
;
2379 nsresult
RemoveCrashReportAnnotation(Annotation key
) {
2380 return AnnotateCrashReport(key
, ""_ns
);
2383 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, bool data
)
2384 : AutoAnnotateCrashReport(key
, data
? "1"_ns
: "0"_ns
) {}
2386 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, int data
)
2387 : AutoAnnotateCrashReport(key
, nsPrintfCString("%d", data
)) {}
2389 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
, unsigned data
)
2390 : AutoAnnotateCrashReport(key
, nsPrintfCString("%u", data
)) {}
2392 AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key
,
2393 const nsACString
& data
)
2396 MutexAutoLock
lock(*crashReporterAPILock
);
2397 auto& entry
= crashReporterAPIData_Table
[mKey
];
2398 mPrevious
= std::move(entry
);
2403 AutoAnnotateCrashReport::~AutoAnnotateCrashReport() {
2405 MutexAutoLock
lock(*crashReporterAPILock
);
2406 crashReporterAPIData_Table
[mKey
] = std::move(mPrevious
);
2410 void MergeCrashAnnotations(AnnotationTable
& aDst
, const AnnotationTable
& aSrc
) {
2411 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
2412 const nsCString
& value
= aSrc
[key
];
2413 if (!value
.IsEmpty()) {
2419 static void MergeContentCrashAnnotations(AnnotationTable
& aDst
) {
2420 MutexAutoLock
lock(*crashReporterAPILock
);
2421 MergeCrashAnnotations(aDst
, crashReporterAPIData_Table
);
2424 // Adds crash time, uptime and memory report annotations
2425 static void AddCommonAnnotations(AnnotationTable
& aAnnotations
) {
2426 const time_t crashTime
= time(nullptr);
2427 nsAutoCString crashTimeStr
;
2428 crashTimeStr
.AppendInt(static_cast<uint64_t>(crashTime
));
2429 aAnnotations
[Annotation::CrashTime
] = crashTimeStr
;
2431 if (inactiveStateStart
) {
2432 nsAutoCString inactiveDuration
;
2433 inactiveDuration
.AppendInt(
2434 static_cast<uint64_t>(crashTime
- inactiveStateStart
));
2435 aAnnotations
[Annotation::LastInteractionDuration
] = inactiveDuration
;
2438 double uptimeTS
= (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation())
2439 .ToSecondsSigDigits();
2440 nsAutoCString uptimeStr
;
2441 uptimeStr
.AppendFloat(uptimeTS
);
2442 aAnnotations
[Annotation::UptimeTS
] = uptimeStr
;
2445 nsresult
SetGarbageCollecting(bool collecting
) {
2446 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2448 isGarbageCollecting
= collecting
;
2453 void SetEventloopNestingLevel(uint32_t level
) { eventloopNestingLevel
= level
; }
2455 void ClearInactiveStateStart() { inactiveStateStart
= 0; }
2456 void SetInactiveStateStart() {
2457 if (!inactiveStateStart
) {
2458 inactiveStateStart
= GetCurrentTimeForCrashTime();
2462 void SetMinidumpAnalysisAllThreads() {
2463 char* env
= strdup("MOZ_CRASHREPORTER_DUMP_ALL_THREADS=1");
2467 nsresult
AppendAppNotesToCrashReport(const nsACString
& data
) {
2468 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2470 MutexAutoLock
lock(*notesFieldLock
);
2472 notesField
->Append(data
);
2473 return AnnotateCrashReport(Annotation::Notes
, *notesField
);
2476 // Returns true if found, false if not found.
2477 static bool GetAnnotation(CrashReporter::Annotation key
, nsACString
& data
) {
2478 if (!gExceptionHandler
) return false;
2480 MutexAutoLock
lock(*crashReporterAPILock
);
2481 const nsCString
& entry
= crashReporterAPIData_Table
[key
];
2482 if (entry
.IsEmpty()) {
2490 nsresult
RegisterAppMemory(void* ptr
, size_t length
) {
2491 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2493 #if defined(XP_LINUX) || defined(XP_WIN)
2494 gExceptionHandler
->RegisterAppMemory(ptr
, length
);
2497 return NS_ERROR_NOT_IMPLEMENTED
;
2501 nsresult
UnregisterAppMemory(void* ptr
) {
2502 if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED
;
2504 #if defined(XP_LINUX) || defined(XP_WIN)
2505 gExceptionHandler
->UnregisterAppMemory(ptr
);
2508 return NS_ERROR_NOT_IMPLEMENTED
;
2512 void SetIncludeContextHeap(bool aValue
) {
2513 sIncludeContextHeap
= aValue
;
2516 if (gExceptionHandler
) {
2517 gExceptionHandler
->set_include_context_heap(sIncludeContextHeap
);
2522 bool GetServerURL(nsACString
& aServerURL
) {
2523 if (!gExceptionHandler
) return false;
2525 return GetAnnotation(CrashReporter::Annotation::ServerURL
, aServerURL
);
2528 nsresult
SetServerURL(const nsACString
& aServerURL
) {
2529 // store server URL with the API data
2530 // the client knows to handle this specially
2531 return AnnotateCrashReport(Annotation::ServerURL
, aServerURL
);
2534 nsresult
SetRestartArgs(int argc
, char** argv
) {
2535 if (!gExceptionHandler
) return NS_OK
;
2538 nsAutoCString envVar
;
2540 char* argv0
= getenv("MOZ_APP_LAUNCHER");
2541 for (i
= 0; i
< argc
; i
++) {
2542 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
2543 envVar
.AppendInt(i
);
2545 if (argv0
&& i
== 0) {
2546 // Is there a request to suppress default binary launcher?
2552 // PR_SetEnv() wants the string to be available for the lifetime
2553 // of the app, so dup it here. This conversion is not lossy.
2554 env
= ToNewCString(envVar
, mozilla::fallible
);
2555 if (!env
) return NS_ERROR_OUT_OF_MEMORY
;
2560 // make sure the arg list is terminated
2561 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
2562 envVar
.AppendInt(i
);
2565 // PR_SetEnv() wants the string to be available for the lifetime
2566 // of the app, so dup it here. This conversion is not lossy.
2567 env
= ToNewCString(envVar
, mozilla::fallible
);
2568 if (!env
) return NS_ERROR_OUT_OF_MEMORY
;
2572 // make sure we save the info in XUL_APP_FILE for the reporter
2573 const char* appfile
= PR_GetEnv("XUL_APP_FILE");
2574 if (appfile
&& *appfile
) {
2575 envVar
= "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
2578 // PR_SetEnv() wants the string to be available for the lifetime
2579 // of the app, so dup it here. This conversion is not lossy.
2580 env
= ToNewCString(envVar
);
2588 nsresult
WriteMinidumpForException(EXCEPTION_POINTERS
* aExceptionInfo
) {
2589 if (!gExceptionHandler
) return NS_ERROR_NOT_INITIALIZED
;
2591 return gExceptionHandler
->WriteMinidumpForException(aExceptionInfo
)
2598 bool WriteMinidumpForSigInfo(int signo
, siginfo_t
* info
, void* uc
) {
2599 if (!gExceptionHandler
) {
2600 // Crash reporting is disabled.
2603 return gExceptionHandler
->HandleSignal(signo
, info
, uc
);
2608 nsresult
AppendObjCExceptionInfoToAppNotes(void* inException
) {
2609 nsAutoCString excString
;
2610 GetObjCExceptionInfo(inException
, excString
);
2611 AppendAppNotesToCrashReport(excString
);
2617 * Combined code to get/set the crash reporter submission pref on
2618 * different platforms.
2620 static nsresult
PrefSubmitReports(bool* aSubmitReports
, bool writePref
) {
2624 * NOTE! This needs to stay in sync with the preference checking code
2625 * in toolkit/crashreporter/client/crashreporter_win.cpp
2627 nsCOMPtr
<nsIXULAppInfo
> appinfo
=
2628 do_GetService("@mozilla.org/xre/app-info;1", &rv
);
2629 NS_ENSURE_SUCCESS(rv
, rv
);
2631 nsAutoCString appVendor
, appName
;
2632 rv
= appinfo
->GetVendor(appVendor
);
2633 NS_ENSURE_SUCCESS(rv
, rv
);
2634 rv
= appinfo
->GetName(appName
);
2635 NS_ENSURE_SUCCESS(rv
, rv
);
2637 nsCOMPtr
<nsIWindowsRegKey
> regKey(
2638 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv
));
2639 NS_ENSURE_SUCCESS(rv
, rv
);
2641 nsAutoCString regPath
;
2643 regPath
.AppendLiteral("Software\\");
2645 // We need to ensure the registry keys are created so we can properly
2646 // write values to it
2648 // Create appVendor key
2649 if (!appVendor
.IsEmpty()) {
2650 regPath
.Append(appVendor
);
2651 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2652 NS_ConvertUTF8toUTF16(regPath
),
2653 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2654 regPath
.Append('\\');
2657 // Create appName key
2658 regPath
.Append(appName
);
2659 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2660 NS_ConvertUTF8toUTF16(regPath
),
2661 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2662 regPath
.Append('\\');
2664 // Create Crash Reporter key
2665 regPath
.AppendLiteral("Crash Reporter");
2666 regKey
->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2667 NS_ConvertUTF8toUTF16(regPath
),
2668 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2670 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
2673 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2674 NS_ConvertUTF8toUTF16(regPath
),
2675 nsIWindowsRegKey::ACCESS_SET_VALUE
);
2676 NS_ENSURE_SUCCESS(rv
, rv
);
2678 uint32_t value
= *aSubmitReports
? 1 : 0;
2679 rv
= regKey
->WriteIntValue(u
"SubmitCrashReport"_ns
, value
);
2684 // We're reading the pref value, so we need to first look under
2685 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
2686 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
2689 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
2690 NS_ConvertUTF8toUTF16(regPath
),
2691 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
2692 if (NS_SUCCEEDED(rv
)) {
2693 rv
= regKey
->ReadIntValue(u
"SubmitCrashReport"_ns
, &value
);
2695 if (NS_SUCCEEDED(rv
)) {
2696 *aSubmitReports
= !!value
;
2701 rv
= regKey
->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
2702 NS_ConvertUTF8toUTF16(regPath
),
2703 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
2704 if (NS_FAILED(rv
)) {
2705 *aSubmitReports
= true;
2709 rv
= regKey
->ReadIntValue(u
"SubmitCrashReport"_ns
, &value
);
2710 // default to true on failure
2711 if (NS_FAILED(rv
)) {
2717 *aSubmitReports
= !!value
;
2719 #elif defined(XP_MACOSX)
2722 CFPropertyListRef cfValue
=
2723 (CFPropertyListRef
)(*aSubmitReports
? kCFBooleanTrue
: kCFBooleanFalse
);
2724 ::CFPreferencesSetAppValue(CFSTR("submitReport"), cfValue
,
2725 reporterClientAppID
);
2726 if (!::CFPreferencesAppSynchronize(reporterClientAppID
))
2727 rv
= NS_ERROR_FAILURE
;
2729 *aSubmitReports
= true;
2730 Boolean keyExistsAndHasValidFormat
= false;
2731 Boolean prefValue
= ::CFPreferencesGetAppBooleanValue(
2732 CFSTR("submitReport"), reporterClientAppID
,
2733 &keyExistsAndHasValidFormat
);
2734 if (keyExistsAndHasValidFormat
) *aSubmitReports
= !!prefValue
;
2737 #elif defined(XP_UNIX)
2739 * NOTE! This needs to stay in sync with the preference checking code
2740 * in toolkit/crashreporter/client/crashreporter_linux.cpp
2742 nsCOMPtr
<nsIFile
> reporterINI
;
2743 rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI
));
2744 NS_ENSURE_SUCCESS(rv
, rv
);
2745 reporterINI
->AppendNative("Crash Reports"_ns
);
2746 reporterINI
->AppendNative("crashreporter.ini"_ns
);
2749 rv
= reporterINI
->Exists(&exists
);
2750 NS_ENSURE_SUCCESS(rv
, rv
);
2753 // If reading the pref, default to true if .ini doesn't exist.
2754 *aSubmitReports
= true;
2757 // Create the file so the INI processor can write to it.
2758 rv
= reporterINI
->Create(nsIFile::NORMAL_FILE_TYPE
, 0600);
2759 NS_ENSURE_SUCCESS(rv
, rv
);
2762 nsCOMPtr
<nsIINIParserFactory
> iniFactory
=
2763 do_GetService("@mozilla.org/xpcom/ini-parser-factory;1", &rv
);
2764 NS_ENSURE_SUCCESS(rv
, rv
);
2766 nsCOMPtr
<nsIINIParser
> iniParser
;
2767 rv
= iniFactory
->CreateINIParser(reporterINI
, getter_AddRefs(iniParser
));
2768 NS_ENSURE_SUCCESS(rv
, rv
);
2770 // If we're writing the pref, just set and we're done.
2772 nsCOMPtr
<nsIINIParserWriter
> iniWriter
= do_QueryInterface(iniParser
);
2773 NS_ENSURE_TRUE(iniWriter
, NS_ERROR_FAILURE
);
2775 rv
= iniWriter
->SetString("Crash Reporter"_ns
, "SubmitReport"_ns
,
2776 *aSubmitReports
? "1"_ns
: "0"_ns
);
2777 NS_ENSURE_SUCCESS(rv
, rv
);
2778 rv
= iniWriter
->WriteFile(reporterINI
);
2782 nsAutoCString submitReportValue
;
2783 rv
= iniParser
->GetString("Crash Reporter"_ns
, "SubmitReport"_ns
,
2786 // Default to "true" if the pref can't be found.
2788 *aSubmitReports
= true;
2789 else if (submitReportValue
.EqualsASCII("0"))
2790 *aSubmitReports
= false;
2792 *aSubmitReports
= true;
2796 return NS_ERROR_NOT_IMPLEMENTED
;
2800 nsresult
GetSubmitReports(bool* aSubmitReports
) {
2801 return PrefSubmitReports(aSubmitReports
, false);
2804 nsresult
SetSubmitReports(bool aSubmitReports
) {
2807 nsCOMPtr
<nsIObserverService
> obsServ
=
2808 mozilla::services::GetObserverService();
2810 return NS_ERROR_FAILURE
;
2813 rv
= PrefSubmitReports(&aSubmitReports
, true);
2814 if (NS_FAILED(rv
)) {
2818 obsServ
->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
2822 static void SetCrashEventsDir(nsIFile
* aDir
) {
2823 static const XP_CHAR eventsDirectoryEnv
[] =
2824 XP_TEXT("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
2826 nsCOMPtr
<nsIFile
> eventsDir
= aDir
;
2828 const char* env
= PR_GetEnv("CRASHES_EVENTS_DIR");
2830 NS_NewNativeLocalFile(nsDependentCString(env
), false,
2831 getter_AddRefs(eventsDir
));
2832 EnsureDirectoryExists(eventsDir
);
2835 xpstring
* path
= CreatePathFromFile(eventsDir
);
2837 return; // There's no clean failure from this
2840 eventsDirectory
= xpstring(*path
);
2842 SetEnvironmentVariableW(eventsDirectoryEnv
, path
->c_str());
2844 setenv(eventsDirectoryEnv
, path
->c_str(), /* overwrite */ 1);
2850 void SetProfileDirectory(nsIFile
* aDir
) {
2851 nsCOMPtr
<nsIFile
> dir
;
2852 aDir
->Clone(getter_AddRefs(dir
));
2854 dir
->Append(u
"crashes"_ns
);
2855 EnsureDirectoryExists(dir
);
2856 dir
->Append(u
"events"_ns
);
2857 EnsureDirectoryExists(dir
);
2858 SetCrashEventsDir(dir
);
2861 void SetUserAppDataDirectory(nsIFile
* aDir
) {
2862 nsCOMPtr
<nsIFile
> dir
;
2863 aDir
->Clone(getter_AddRefs(dir
));
2865 dir
->Append(u
"Crash Reports"_ns
);
2866 EnsureDirectoryExists(dir
);
2867 dir
->Append(u
"events"_ns
);
2868 EnsureDirectoryExists(dir
);
2869 SetCrashEventsDir(dir
);
2872 void UpdateCrashEventsDir() {
2873 const char* env
= PR_GetEnv("CRASHES_EVENTS_DIR");
2875 SetCrashEventsDir(nullptr);
2878 nsCOMPtr
<nsIFile
> eventsDir
;
2879 nsresult rv
= NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir
));
2880 if (NS_SUCCEEDED(rv
)) {
2881 SetProfileDirectory(eventsDir
);
2885 rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir
));
2886 if (NS_SUCCEEDED(rv
)) {
2887 SetUserAppDataDirectory(eventsDir
);
2892 "Couldn't get the user appdata directory. Crash events may not be "
2896 bool GetCrashEventsDir(nsAString
& aPath
) {
2897 if (eventsDirectory
.empty()) {
2900 aPath
= CONVERT_XP_CHAR_TO_UTF16(eventsDirectory
.c_str());
2904 void SetMemoryReportFile(nsIFile
* aFile
) {
2905 if (!gExceptionHandler
) {
2911 aFile
->GetPath(path
);
2913 aFile
->GetNativePath(path
);
2915 memoryReportPath
= xpstring(path
.get());
2918 nsresult
GetDefaultMemoryReportFile(nsIFile
** aFile
) {
2919 nsCOMPtr
<nsIFile
> defaultMemoryReportFile
;
2920 if (!defaultMemoryReportPath
) {
2921 nsresult rv
= NS_GetSpecialDirectory(
2922 NS_APP_PROFILE_DIR_STARTUP
, getter_AddRefs(defaultMemoryReportFile
));
2923 if (NS_FAILED(rv
)) {
2926 defaultMemoryReportFile
->AppendNative("memory-report.json.gz"_ns
);
2927 defaultMemoryReportPath
= CreatePathFromFile(defaultMemoryReportFile
);
2928 if (!defaultMemoryReportPath
) {
2929 return NS_ERROR_FAILURE
;
2932 CreateFileFromPath(*defaultMemoryReportPath
,
2933 getter_AddRefs(defaultMemoryReportFile
));
2934 if (!defaultMemoryReportFile
) {
2935 return NS_ERROR_FAILURE
;
2938 defaultMemoryReportFile
.forget(aFile
);
2942 static void FindPendingDir() {
2943 if (!pendingDirectory
.empty()) {
2946 nsCOMPtr
<nsIFile
> pendingDir
;
2947 nsresult rv
= NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir
));
2948 if (NS_FAILED(rv
)) {
2950 "Couldn't get the user appdata directory, crash dumps will go in an "
2951 "unusual location");
2953 pendingDir
->Append(u
"Crash Reports"_ns
);
2954 pendingDir
->Append(u
"pending"_ns
);
2958 pendingDir
->GetPath(path
);
2960 pendingDir
->GetNativePath(path
);
2962 pendingDirectory
= xpstring(path
.get());
2966 // The "pending" dir is Crash Reports/pending, from which minidumps
2967 // can be submitted. Because this method may be called off the main thread,
2968 // we store the pending directory as a path.
2969 static bool GetPendingDir(nsIFile
** dir
) {
2970 // MOZ_ASSERT(OOPInitialized());
2971 if (pendingDirectory
.empty()) {
2975 nsCOMPtr
<nsIFile
> pending
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
2977 NS_WARNING("Can't set up pending directory during shutdown.");
2981 pending
->InitWithPath(nsDependentString(pendingDirectory
.c_str()));
2983 pending
->InitWithNativePath(nsDependentCString(pendingDirectory
.c_str()));
2989 // The "limbo" dir is where minidumps go to wait for something else to
2990 // use them. If we're |ShouldReport()|, then the "something else" is
2991 // a minidump submitter, and they're coming from the
2992 // Crash Reports/pending/ dir. Otherwise, we don't know what the
2993 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
2995 static bool GetMinidumpLimboDir(nsIFile
** dir
) {
2996 if (ShouldReport()) {
2997 return GetPendingDir(dir
);
3000 CreateFileFromPath(gExceptionHandler
->dump_path(), dir
);
3002 CreateFileFromPath(gExceptionHandler
->minidump_descriptor().directory(),
3005 return nullptr != *dir
;
3009 void DeleteMinidumpFilesForID(const nsAString
& aId
,
3010 const Maybe
<nsString
>& aAdditionalMinidump
) {
3011 nsCOMPtr
<nsIFile
> minidumpFile
;
3012 if (GetMinidumpForID(aId
, getter_AddRefs(minidumpFile
))) {
3013 minidumpFile
->Remove(false);
3016 nsCOMPtr
<nsIFile
> extraFile
;
3017 if (GetExtraFileForID(aId
, getter_AddRefs(extraFile
))) {
3018 extraFile
->Remove(false);
3021 if (aAdditionalMinidump
&& GetMinidumpForID(aId
, getter_AddRefs(minidumpFile
),
3022 aAdditionalMinidump
)) {
3023 minidumpFile
->Remove(false);
3027 bool GetMinidumpForID(const nsAString
& id
, nsIFile
** minidump
,
3028 const Maybe
<nsString
>& aAdditionalMinidump
) {
3029 if (!GetMinidumpLimboDir(minidump
)) {
3033 nsAutoString
fileName(id
);
3035 if (aAdditionalMinidump
) {
3036 fileName
.Append('-');
3037 fileName
.Append(*aAdditionalMinidump
);
3040 fileName
.Append(u
".dmp"_ns
);
3041 (*minidump
)->Append(fileName
);
3044 if (NS_FAILED((*minidump
)->Exists(&exists
)) || !exists
) {
3051 bool GetIDFromMinidump(nsIFile
* minidump
, nsAString
& id
) {
3052 if (minidump
&& NS_SUCCEEDED(minidump
->GetLeafName(id
))) {
3053 id
.ReplaceLiteral(id
.Length() - 4, 4, u
"");
3059 bool GetExtraFileForID(const nsAString
& id
, nsIFile
** extraFile
) {
3060 if (!GetMinidumpLimboDir(extraFile
)) {
3064 (*extraFile
)->Append(id
+ u
".extra"_ns
);
3067 if (NS_FAILED((*extraFile
)->Exists(&exists
)) || !exists
) {
3074 bool GetExtraFileForMinidump(nsIFile
* minidump
, nsIFile
** extraFile
) {
3075 nsAutoString leafName
;
3076 nsresult rv
= minidump
->GetLeafName(leafName
);
3077 if (NS_FAILED(rv
)) return false;
3079 nsCOMPtr
<nsIFile
> extraF
;
3080 rv
= minidump
->Clone(getter_AddRefs(extraF
));
3081 if (NS_FAILED(rv
)) return false;
3083 leafName
.Replace(leafName
.Length() - 3, 3, u
"extra"_ns
);
3084 rv
= extraF
->SetLeafName(leafName
);
3085 if (NS_FAILED(rv
)) return false;
3087 *extraFile
= nullptr;
3088 extraF
.swap(*extraFile
);
3092 static bool WriteExtraFile(PlatformWriter
& pw
,
3093 const AnnotationTable
& aAnnotations
) {
3098 JSONAnnotationWriter
writer(pw
);
3099 WriteAnnotations(writer
, aAnnotations
);
3100 WriteSynthesizedAnnotations(writer
);
3105 bool WriteExtraFile(const nsAString
& id
, const AnnotationTable
& annotations
) {
3106 nsCOMPtr
<nsIFile
> extra
;
3107 if (!GetMinidumpLimboDir(getter_AddRefs(extra
))) {
3111 extra
->Append(id
+ u
".extra"_ns
);
3114 NS_ENSURE_SUCCESS(extra
->GetPath(path
), false);
3115 #elif defined(XP_UNIX)
3116 NS_ENSURE_SUCCESS(extra
->GetNativePath(path
), false);
3119 PlatformWriter
pw(path
.get());
3120 return WriteExtraFile(pw
, annotations
);
3123 // This adds annotations that were populated in the main process but are not
3124 // present among the ones that were passed in. Additionally common annotations
3125 // which are present in every crash report are added, including crash time,
3127 static void AddSharedAnnotations(AnnotationTable
& aAnnotations
) {
3128 MergeContentCrashAnnotations(aAnnotations
);
3129 AddCommonAnnotations(aAnnotations
);
3132 static void AddChildProcessAnnotations(
3133 AnnotationTable
& aAnnotations
, nsTArray
<CAnnotation
>* aChildAnnotations
) {
3134 if (!aChildAnnotations
) {
3135 // TODO: We should probably make a list of errors that occurred when
3136 // generating a crash report as more than one can occurr.
3137 aAnnotations
[Annotation::DumperError
] = "MissingAnnotations";
3141 for (const auto& annotation
: *aChildAnnotations
) {
3142 switch (annotation
.data
.tag
) {
3143 case AnnotationData::Tag::Empty
:
3146 case AnnotationData::Tag::UsizeData
:
3147 if (annotation
.id
==
3148 static_cast<uint32_t>(Annotation::OOMAllocationSize
)) {
3149 // We need to special-case OOMAllocationSize here because it should
3150 // not be added if its value is 0. We'll come up with a more general
3151 // method of skipping ignored values for crash annotations in the
3153 if (annotation
.data
.usize_data
._0
!= 0) {
3154 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3155 nsPrintfCString("%zu", annotation
.data
.usize_data
._0
);
3158 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3159 nsPrintfCString("%zu", annotation
.data
.usize_data
._0
);
3163 case AnnotationData::Tag::NSCStringData
: {
3164 const auto& string
= annotation
.data
.nsc_string_data
._0
;
3165 if (!string
.IsEmpty()) {
3166 aAnnotations
[static_cast<Annotation
>(annotation
.id
)] =
3167 annotation
.data
.nsc_string_data
._0
;
3171 case AnnotationData::Tag::ByteBuffer
: {
3172 if (annotation
.id
==
3173 static_cast<uint32_t>(Annotation::PHCBaseAddress
)) {
3175 const auto& buffer
= annotation
.data
.byte_buffer
._0
;
3176 mozilla::phc::AddrInfo addr_info
;
3177 memcpy(&addr_info
, buffer
.Elements(), sizeof(addr_info
));
3178 PopulatePHCAnnotations(aAnnotations
, &addr_info
);
3186 // It really only makes sense to call this function when
3187 // ShouldReport() is true.
3188 // Uses dumpFile's filename to generate memoryReport's filename (same name with
3189 // a different extension)
3190 static bool MoveToPending(nsIFile
* dumpFile
, nsIFile
* extraFile
,
3191 nsIFile
* memoryReport
) {
3192 nsCOMPtr
<nsIFile
> pendingDir
;
3193 if (!GetPendingDir(getter_AddRefs(pendingDir
))) return false;
3195 if (NS_FAILED(dumpFile
->MoveTo(pendingDir
, u
""_ns
))) {
3199 if (extraFile
&& NS_FAILED(extraFile
->MoveTo(pendingDir
, u
""_ns
))) {
3204 nsAutoString leafName
;
3205 nsresult rv
= dumpFile
->GetLeafName(leafName
);
3206 if (NS_FAILED(rv
)) {
3209 // Generate the correct memory report filename from the dumpFile's name
3211 leafName
.Length() - 4, 4,
3212 static_cast<nsString
>(CONVERT_XP_CHAR_TO_UTF16(memoryReportExtension
)));
3213 if (NS_FAILED(memoryReport
->MoveTo(pendingDir
, leafName
))) {
3221 static void MaybeAnnotateDumperError(const ClientInfo
& aClientInfo
,
3222 AnnotationTable
& aAnnotations
) {
3223 #if defined(MOZ_OXIDIZED_BREAKPAD)
3224 if (aClientInfo
.had_error()) {
3225 aAnnotations
[Annotation::DumperError
] = *aClientInfo
.error_msg();
3230 static void OnChildProcessDumpRequested(
3231 void* aContext
, const ClientInfo
& aClientInfo
,
3232 const xpstring
& aFilePath
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
3233 nsCOMPtr
<nsIFile
> minidump
;
3235 // Hold the mutex until the current dump request is complete, to
3236 // prevent UnsetExceptionHandler() from pulling the rug out from
3238 MutexAutoLock
lock(*dumpSafetyLock
);
3239 if (!isSafeToDump
) return;
3241 CreateFileFromPath(aFilePath
, getter_AddRefs(minidump
));
3243 ProcessId pid
= aClientInfo
.pid();
3244 if (ShouldReport()) {
3245 nsCOMPtr
<nsIFile
> memoryReport
;
3246 if (!memoryReportPath
.empty()) {
3247 CreateFileFromPath(memoryReportPath
, getter_AddRefs(memoryReport
));
3248 MOZ_ASSERT(memoryReport
);
3250 MoveToPending(minidump
, nullptr, memoryReport
);
3254 nsTArray
<CAnnotation
>* child_annotations
= mozannotation_retrieve(
3255 reinterpret_cast<uintptr_t>(aClientInfo
.process_handle()));
3256 #elif defined(XP_MACOSX)
3257 nsTArray
<CAnnotation
>* child_annotations
=
3258 mozannotation_retrieve(aClientInfo
.task());
3260 nsTArray
<CAnnotation
>* child_annotations
= mozannotation_retrieve(pid
);
3263 // TODO: Write a minimal set of annotations if we fail to read them, and
3264 // add an error to the minidump to highlight this fact.
3267 #ifdef MOZ_CRASHREPORTER_INJECTOR
3271 MutexAutoLock
lock(*dumpMapLock
);
3272 ChildProcessData
* pd
= pidToMinidump
->PutEntry(pid
);
3273 MOZ_ASSERT(!pd
->minidump
);
3274 pd
->minidump
= minidump
;
3275 pd
->sequence
= ++crashSequence
;
3276 pd
->annotations
= MakeUnique
<AnnotationTable
>();
3277 AnnotationTable
& annotations
= *(pd
->annotations
);
3278 AddSharedAnnotations(annotations
);
3279 AddChildProcessAnnotations(annotations
, child_annotations
);
3281 MaybeAnnotateDumperError(aClientInfo
, annotations
);
3283 #ifdef MOZ_CRASHREPORTER_INJECTOR
3284 runCallback
= nullptr != pd
->callback
;
3287 #ifdef MOZ_CRASHREPORTER_INJECTOR
3288 if (runCallback
) NS_DispatchToMainThread(new ReportInjectedCrash(pid
));
3292 if (child_annotations
) {
3293 mozannotation_free(child_annotations
);
3297 static bool OOPInitialized() { return pidToMinidump
!= nullptr; }
3300 class ProxyToMainThread
: public Runnable
{
3302 ProxyToMainThread() : Runnable("nsExceptionHandler::ProxyToMainThread") {}
3303 NS_IMETHOD
Run() override
{
3308 if (!NS_IsMainThread()) {
3309 // This logic needs to run on the main thread
3310 nsCOMPtr
<nsIThread
> mainThread
= do_GetMainThread();
3311 mozilla::SyncRunnable::DispatchToThread(mainThread
,
3312 new ProxyToMainThread());
3316 if (OOPInitialized()) return;
3318 MOZ_ASSERT(NS_IsMainThread());
3320 MOZ_ASSERT(gExceptionHandler
!= nullptr,
3321 "attempt to initialize OOP crash reporter before in-process "
3325 childCrashNotifyPipe
=
3326 mozilla::Smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
3327 static_cast<int>(::GetCurrentProcessId()))
3330 const std::wstring dumpPath
= gExceptionHandler
->dump_path();
3331 crashServer
= new CrashGenerationServer(
3332 std::wstring(NS_ConvertASCIItoUTF16(childCrashNotifyPipe
).get()),
3333 nullptr, // default security attributes
3334 nullptr, nullptr, // we don't care about process connect here
3335 OnChildProcessDumpRequested
, nullptr, nullptr, nullptr,
3336 nullptr, // we don't care about process exit here
3337 nullptr, nullptr, // we don't care about upload request here
3338 true, // automatically generate dumps
3341 if (sIncludeContextHeap
) {
3342 crashServer
->set_include_context_heap(sIncludeContextHeap
);
3345 #elif defined(XP_LINUX)
3346 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd
,
3348 MOZ_CRASH("can't create crash reporter socketpair()");
3350 const std::string dumpPath
=
3351 gExceptionHandler
->minidump_descriptor().directory();
3353 new CrashGenerationServer(serverSocketFd
, OnChildProcessDumpRequested
,
3354 nullptr, nullptr, nullptr, true, &dumpPath
);
3356 #elif defined(XP_MACOSX)
3357 childCrashNotifyPipe
= mozilla::Smprintf("gecko-crash-server-pipe.%i",
3358 static_cast<int>(getpid()))
3360 const std::string dumpPath
= gExceptionHandler
->dump_path();
3362 crashServer
= new CrashGenerationServer(childCrashNotifyPipe
, nullptr,
3363 nullptr, OnChildProcessDumpRequested
,
3364 nullptr, nullptr, nullptr,
3365 true, // automatically generate dumps
3369 if (!crashServer
->Start()) MOZ_CRASH("can't start crash reporter server()");
3371 pidToMinidump
= new ChildMinidumpMap();
3373 dumpMapLock
= new Mutex("CrashReporter::dumpMapLock");
3376 UpdateCrashEventsDir();
3379 static void OOPDeinit() {
3380 if (!OOPInitialized()) {
3381 NS_WARNING("OOPDeinit() without successful OOPInit()");
3385 #ifdef MOZ_CRASHREPORTER_INJECTOR
3386 if (sInjectorThread
) {
3387 sInjectorThread
->Shutdown();
3388 NS_RELEASE(sInjectorThread
);
3393 crashServer
= nullptr;
3396 dumpMapLock
= nullptr;
3398 delete pidToMinidump
;
3399 pidToMinidump
= nullptr;
3401 #if defined(XP_WIN) || defined(XP_MACOSX)
3402 free(childCrashNotifyPipe
);
3403 childCrashNotifyPipe
= nullptr;
3407 #if defined(XP_WIN) || defined(XP_MACOSX)
3408 // Parent-side API for children
3409 const char* GetChildNotificationPipe() {
3410 if (!GetEnabled()) return kNullNotifyPipe
;
3412 MOZ_ASSERT(OOPInitialized());
3414 return childCrashNotifyPipe
;
3418 #ifdef MOZ_CRASHREPORTER_INJECTOR
3419 void InjectCrashReporterIntoProcess(DWORD processID
,
3420 InjectorCrashCallback
* cb
) {
3421 if (!GetEnabled()) return;
3423 if (!OOPInitialized()) OOPInit();
3425 if (!sInjectorThread
) {
3426 if (NS_FAILED(NS_NewNamedThread("CrashRep Inject", &sInjectorThread
)))
3431 MutexAutoLock
lock(*dumpMapLock
);
3432 ChildProcessData
* pd
= pidToMinidump
->PutEntry(processID
);
3433 MOZ_ASSERT(!pd
->minidump
&& !pd
->callback
);
3435 pd
->minidumpOnly
= true;
3438 nsCOMPtr
<nsIRunnable
> r
= new InjectCrashRunnable(processID
);
3439 sInjectorThread
->Dispatch(r
, nsIEventTarget::DISPATCH_NORMAL
);
3443 ReportInjectedCrash::Run() {
3444 // Crash reporting may have been disabled after this method was dispatched
3445 if (!OOPInitialized()) return NS_OK
;
3447 InjectorCrashCallback
* cb
;
3449 MutexAutoLock
lock(*dumpMapLock
);
3450 ChildProcessData
* pd
= pidToMinidump
->GetEntry(mPID
);
3451 if (!pd
|| !pd
->callback
) return NS_OK
;
3453 MOZ_ASSERT(pd
->minidump
);
3462 void UnregisterInjectorCallback(DWORD processID
) {
3463 if (!OOPInitialized()) return;
3465 MutexAutoLock
lock(*dumpMapLock
);
3466 pidToMinidump
->RemoveEntry(processID
);
3469 #endif // MOZ_CRASHREPORTER_INJECTOR
3471 #if defined(XP_LINUX)
3473 // Parent-side API for children
3474 bool CreateNotificationPipeForChild(int* childCrashFd
, int* childCrashRemapFd
) {
3475 if (!GetEnabled()) {
3477 *childCrashRemapFd
= -1;
3481 MOZ_ASSERT(OOPInitialized());
3483 *childCrashFd
= clientSocketFd
;
3484 *childCrashRemapFd
= gMagicChildCrashReportFd
;
3489 #endif // defined(XP_LINUX)
3491 bool SetRemoteExceptionHandler(const char* aCrashPipe
) {
3492 MOZ_ASSERT(!gExceptionHandler
, "crash client already init'd");
3493 RegisterRuntimeExceptionModule();
3494 InitializeAnnotationFacilities();
3495 for (auto key
: MakeEnumeratedRange(Annotation::Count
)) {
3497 case Annotation::MozCrashReason
:
3498 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
3499 case Annotation::MainThreadRunnableName
:
3501 case Annotation::OOMAllocationSize
:
3503 case Annotation::PHCBaseAddress
:
3508 mozannotation_register_nscstring(static_cast<uint32_t>(key
),
3509 &crashReporterAPIData_Table
[key
]);
3513 mozannotation_register_cstring(
3514 static_cast<uint32_t>(Annotation::MozCrashReason
), &gMozCrashReason
);
3516 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
3517 mozannotation_register_char_buffer(
3518 static_cast<uint32_t>(Annotation::MainThreadRunnableName
),
3519 &nsThread::sMainThreadRunnableName
[0]);
3522 mozannotation_register_usize(
3523 static_cast<uint32_t>(Annotation::OOMAllocationSize
),
3524 &gOOMAllocationSize
);
3527 // HACK: We're using the PHCBaseAddress annotation to point to the actual
3528 // PHC address information object. This is because we currently have no
3529 // difference between the internal representation of annotations and their
3530 // external representation. Once we remove the old annotation API this will
3531 // be properly addressed.
3532 mozannotation_register_bytebuffer(
3533 static_cast<uint32_t>(Annotation::PHCBaseAddress
),
3534 &mozilla::phc::gAddrInfo
, sizeof(mozilla::phc::gAddrInfo
));
3538 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3539 L
"", ChildFilter
, ChildMinidumpCallback
,
3540 nullptr, // no callback context
3541 google_breakpad::ExceptionHandler::HANDLER_ALL
, GetMinidumpType(),
3542 NS_ConvertASCIItoUTF16(aCrashPipe
).get(), nullptr);
3543 gExceptionHandler
->set_handle_debug_exceptions(true);
3545 # if defined(HAVE_64BIT_BUILD)
3546 SetJitExceptionHandler();
3548 #elif defined(XP_LINUX)
3549 // MinidumpDescriptor requires a non-empty path.
3550 google_breakpad::MinidumpDescriptor
path(".");
3552 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3553 path
, ChildFilter
, ChildMinidumpCallback
,
3554 nullptr, // no callback context
3555 true, // install signal handlers
3556 gMagicChildCrashReportFd
);
3557 #elif defined(XP_MACOSX)
3558 gExceptionHandler
= new google_breakpad::ExceptionHandler(
3559 "", ChildFilter
, ChildMinidumpCallback
,
3560 nullptr, // no callback context
3561 true, // install signal handlers
3565 RecordMainThreadId();
3567 oldTerminateHandler
= std::set_terminate(&TerminateHandler
);
3569 // we either do remote or nothing, no fallback to regular crash reporting
3570 return gExceptionHandler
->IsOutOfProcess();
3573 void GetAnnotation(uint32_t childPid
, Annotation annotation
,
3574 nsACString
& outStr
) {
3575 if (!GetEnabled()) {
3579 MutexAutoLock
lock(*dumpMapLock
);
3581 ChildProcessData
* pd
= pidToMinidump
->GetEntry(childPid
);
3586 outStr
= (*pd
->annotations
)[annotation
];
3589 bool TakeMinidumpForChild(uint32_t childPid
, nsIFile
** dump
,
3590 AnnotationTable
& aAnnotations
, uint32_t* aSequence
) {
3591 if (!GetEnabled()) return false;
3593 MutexAutoLock
lock(*dumpMapLock
);
3595 ChildProcessData
* pd
= pidToMinidump
->GetEntry(childPid
);
3596 if (!pd
) return false;
3598 NS_IF_ADDREF(*dump
= pd
->minidump
);
3599 // Only plugin process minidumps taken using the injector don't have
3601 if (!pd
->minidumpOnly
) {
3602 aAnnotations
= *(pd
->annotations
);
3605 *aSequence
= pd
->sequence
;
3608 pidToMinidump
->RemoveEntry(pd
);
3613 bool FinalizeOrphanedMinidump(uint32_t aChildPid
, GeckoProcessType aType
,
3614 nsString
* aDumpId
) {
3615 AnnotationTable annotations
;
3616 nsCOMPtr
<nsIFile
> minidump
;
3618 if (!TakeMinidumpForChild(aChildPid
, getter_AddRefs(minidump
), annotations
)) {
3623 if (!GetIDFromMinidump(minidump
, id
)) {
3631 annotations
[Annotation::ProcessType
] =
3632 XRE_ChildProcessTypeToAnnotation(aType
);
3634 return WriteExtraFile(id
, annotations
);
3639 // Function invoked by the WER runtime exception handler running in an
3640 // external process. This function isn't used anywhere inside Gecko directly
3641 // but rather invoked via CreateRemoteThread() in the main process.
3642 DWORD WINAPI
WerNotifyProc(LPVOID aParameter
) {
3643 const WindowsErrorReportingData
* werData
=
3644 static_cast<const WindowsErrorReportingData
*>(aParameter
);
3646 // Hold the mutex until the current dump request is complete, to
3647 // prevent UnsetExceptionHandler() from pulling the rug out from
3649 MutexAutoLock
safetyLock(*dumpSafetyLock
);
3650 if (!isSafeToDump
|| !ShouldReport()) {
3654 ProcessId pid
= werData
->mChildPid
;
3655 nsCOMPtr
<nsIFile
> minidump
;
3656 if (!GetPendingDir(getter_AddRefs(minidump
))) {
3659 xpstring
minidump_native_name(werData
->mMinidumpFile
,
3660 werData
->mMinidumpFile
+ 40);
3661 nsString
minidump_name(minidump_native_name
.c_str());
3662 minidump
->Append(minidump_name
);
3665 MutexAutoLock
lock(*dumpMapLock
);
3666 ChildProcessData
* pd
= pidToMinidump
->PutEntry(pid
);
3667 MOZ_ASSERT(!pd
->minidump
);
3668 pd
->minidump
= minidump
;
3669 pd
->sequence
= ++crashSequence
;
3670 pd
->annotations
= MakeUnique
<AnnotationTable
>();
3671 (*pd
->annotations
)[Annotation::WindowsErrorReporting
] = "1"_ns
;
3672 if (werData
->mOOMAllocationSize
> 0) {
3673 char buffer
[32] = {};
3674 XP_STOA(werData
->mOOMAllocationSize
, buffer
);
3675 (*pd
->annotations
)[Annotation::OOMAllocationSize
] = buffer
;
3678 AddSharedAnnotations(*(pd
->annotations
));
3686 //-----------------------------------------------------------------------------
3687 // CreateMinidumpsAndPair() and helpers
3691 * Renames the stand alone dump file aDumpFile to:
3692 * |aOwnerDumpFile-aDumpFileProcessType.dmp|
3693 * and moves it into the same directory as aOwnerDumpFile. Does not
3694 * modify aOwnerDumpFile in any way.
3696 * @param aDumpFile - the dump file to associate with aOwnerDumpFile.
3697 * @param aOwnerDumpFile - the new owner of aDumpFile.
3698 * @param aDumpFileProcessType - process name associated with aDumpFile.
3700 static void RenameAdditionalHangMinidump(nsIFile
* minidump
,
3701 nsIFile
* childMinidump
,
3702 const nsACString
& name
) {
3703 nsCOMPtr
<nsIFile
> directory
;
3704 childMinidump
->GetParent(getter_AddRefs(directory
));
3705 if (!directory
) return;
3707 nsAutoCString leafName
;
3708 childMinidump
->GetNativeLeafName(leafName
);
3710 // turn "<id>.dmp" into "<id>-<name>.dmp
3711 leafName
.Insert("-"_ns
+ name
, leafName
.Length() - 4);
3713 if (NS_FAILED(minidump
->MoveToNative(directory
, leafName
))) {
3714 NS_WARNING("RenameAdditionalHangMinidump failed to move minidump.");
3718 // Stores the minidump in the nsIFile pointed by the |context| parameter.
3719 static bool PairedDumpCallback(
3721 const MinidumpDescriptor
& descriptor
,
3723 const XP_CHAR
* dump_path
, const XP_CHAR
* minidump_id
,
3727 EXCEPTION_POINTERS
* /*unused*/, MDRawAssertionInfo
* /*unused*/,
3729 const phc::AddrInfo
* addrInfo
, bool succeeded
) {
3730 XP_CHAR
* path
= static_cast<XP_CHAR
*>(context
);
3731 size_t size
= XP_PATH_MAX
;
3734 Concat(path
, descriptor
.path(), &size
);
3736 path
= Concat(path
, dump_path
, &size
);
3737 path
= Concat(path
, XP_PATH_SEPARATOR
, &size
);
3738 path
= Concat(path
, minidump_id
, &size
);
3739 Concat(path
, dumpFileExtension
, &size
);
3745 ThreadId
CurrentThreadId() {
3747 return ::GetCurrentThreadId();
3748 #elif defined(XP_LINUX)
3749 return sys_gettid();
3750 #elif defined(XP_MACOSX)
3751 // Just return an index, since Mach ports can't be directly serialized
3752 thread_act_port_array_t threads_for_task
;
3753 mach_msg_type_number_t thread_count
;
3755 if (task_threads(mach_task_self(), &threads_for_task
, &thread_count
))
3758 for (unsigned int i
= 0; i
< thread_count
; ++i
) {
3759 if (threads_for_task
[i
] == mach_thread_self()) return i
;
3763 # error "Unsupported platform"
3768 static mach_port_t
GetChildThread(ProcessHandle childPid
,
3769 ThreadId childBlamedThread
) {
3770 mach_port_t childThread
= MACH_PORT_NULL
;
3771 thread_act_port_array_t threads_for_task
;
3772 mach_msg_type_number_t thread_count
;
3774 if (task_threads(childPid
, &threads_for_task
, &thread_count
) ==
3776 childBlamedThread
< thread_count
) {
3777 childThread
= threads_for_task
[childBlamedThread
];
3784 bool CreateMinidumpsAndPair(ProcessHandle aTargetHandle
,
3785 ThreadId aTargetBlamedThread
,
3786 const nsACString
& aIncomingPairName
,
3787 AnnotationTable
& aTargetAnnotations
,
3788 nsIFile
** aMainDumpOut
) {
3789 if (!GetEnabled()) {
3793 AutoIOInterposerDisable disableIOInterposition
;
3796 mach_port_t targetThread
= GetChildThread(aTargetHandle
, aTargetBlamedThread
);
3798 ThreadId targetThread
= aTargetBlamedThread
;
3803 dump_path
= gExceptionHandler
->dump_path();
3805 dump_path
= gExceptionHandler
->minidump_descriptor().directory();
3808 // Ugly, but due to Breakpad limitations we can't allocate memory in the
3809 // callback when generating a dump of the calling process.
3810 XP_CHAR minidumpPath
[XP_PATH_MAX
] = {};
3813 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
3814 aTargetHandle
, targetThread
, dump_path
, PairedDumpCallback
,
3815 static_cast<void*>(minidumpPath
)
3824 nsCOMPtr
<nsIFile
> targetMinidump
;
3825 CreateFileFromPath(xpstring(minidumpPath
), getter_AddRefs(targetMinidump
));
3827 // Create a dump of this process.
3828 if (!google_breakpad::ExceptionHandler::WriteMinidump(
3833 PairedDumpCallback
, static_cast<void*>(minidumpPath
)
3839 targetMinidump
->Remove(false);
3843 nsCOMPtr
<nsIFile
> incomingDump
;
3844 CreateFileFromPath(xpstring(minidumpPath
), getter_AddRefs(incomingDump
));
3846 RenameAdditionalHangMinidump(incomingDump
, targetMinidump
, aIncomingPairName
);
3848 if (ShouldReport()) {
3849 MoveToPending(targetMinidump
, nullptr, nullptr);
3850 MoveToPending(incomingDump
, nullptr, nullptr);
3852 #if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
3853 DllBlocklist_Shutdown();
3856 AddSharedAnnotations(aTargetAnnotations
);
3858 nsTArray
<CAnnotation
>* child_annotations
=
3859 mozannotation_retrieve(reinterpret_cast<uintptr_t>(aTargetHandle
));
3861 nsTArray
<CAnnotation
>* child_annotations
=
3862 mozannotation_retrieve(aTargetHandle
);
3864 AddChildProcessAnnotations(aTargetAnnotations
, child_annotations
);
3865 if (child_annotations
) {
3866 mozannotation_free(child_annotations
);
3869 targetMinidump
.forget(aMainDumpOut
);
3874 bool UnsetRemoteExceptionHandler(bool wasSet
) {
3875 // On Linux we don't unset breakpad's exception handler if the sandbox is
3876 // enabled because it requires invoking `sigaltstack` and we don't want to
3877 // allow that syscall in the sandbox. See bug 1622452.
3878 #if !defined(XP_LINUX) || !defined(MOZ_SANDBOX)
3880 std::set_terminate(oldTerminateHandler
);
3881 delete gExceptionHandler
;
3882 gExceptionHandler
= nullptr;
3885 TeardownAnnotationFacilities();
3890 #if defined(MOZ_WIDGET_ANDROID)
3891 void SetNotificationPipeForChild(int childCrashFd
) {
3892 gMagicChildCrashReportFd
= childCrashFd
;
3896 } // namespace CrashReporter