Bug 1839489 [wpt PR 40652] - Revert "Add the Sec-CH-UA-Form-Factor header", a=testonly
[gecko.git] / toolkit / crashreporter / nsExceptionHandler.cpp
blobd6df62fa278433c463581b4d1165fba4019712f9
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"
14 #include "nsString.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"
33 #include "nsThread.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"
42 #if defined(XP_WIN)
43 # ifdef WIN32_LEAN_AND_MEAN
44 # undef WIN32_LEAN_AND_MEAN
45 # endif
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"
53 # include <dbghelp.h>
54 # include <string.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"
65 # include <string>
66 # include <Carbon/Carbon.h>
67 # include <CoreFoundation/CoreFoundation.h>
68 # include <crt_externs.h>
69 # include <fcntl.h>
70 # include <mach/mach.h>
71 # include <mach/vm_statistics.h>
72 # include <sys/sysctl.h>
73 # include <sys/types.h>
74 # include <spawn.h>
75 # include <unistd.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"
85 # include <fcntl.h>
86 # include <sys/types.h>
87 # include "sys/sysinfo.h"
88 # include <sys/wait.h>
89 # include <unistd.h>
90 #else
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;
97 #endif
99 #include <stdlib.h>
100 #include <time.h>
101 #include <prenv.h>
102 #include <prio.h>
103 #include "mozilla/Mutex.h"
104 #include "nsDebug.h"
105 #include "nsCRT.h"
106 #include "nsIFile.h"
108 #include "mozilla/IOInterposer.h"
109 #include "mozilla/mozalloc_oom.h"
111 #if defined(XP_MACOSX)
112 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
113 #endif
114 #if defined(MOZ_WIDGET_ANDROID)
115 # include "common/linux/file_id.h"
116 #endif
118 using google_breakpad::ClientInfo;
119 using google_breakpad::CrashGenerationServer;
120 #ifdef XP_LINUX
121 using google_breakpad::MinidumpDescriptor;
122 #elif defined(XP_WIN)
123 using google_breakpad::ExceptionHandler;
124 #endif
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;
130 #endif
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 {
142 #ifdef XP_WIN
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)
158 #else
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
167 # ifdef XP_LINUX
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))
173 # else
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
184 # endif
185 #endif // XP_WIN
187 #if defined(__GNUC__)
188 # define MAYBE_UNUSED __attribute__((unused))
189 #else
190 # define MAYBE_UNUSED
191 #endif // defined(__GNUC__)
193 #ifndef XP_LINUX
194 static const XP_CHAR dumpFileExtension[] = XP_TEXT(".dmp");
195 #endif
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;
233 #endif
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;
244 static
245 #if defined(XP_UNIX)
246 pthread_t
247 #elif defined(XP_WIN) // defined(XP_UNIX)
248 DWORD
249 #endif // defined(XP_WIN)
250 gMainThreadId = 0;
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
276 // details.
277 static FileHandle gMagicChildCrashReportFd =
278 # if defined(MOZ_WIDGET_ANDROID)
279 // On android the fd is set at the time of child creation.
280 kInvalidFileHandle
281 # else
283 # endif // defined(MOZ_WIDGET_ANDROID)
285 #endif
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),
292 sequence(0),
293 annotations(nullptr),
294 minidumpOnly(false)
295 #ifdef MOZ_CRASHREPORTER_INJECTOR
297 callback(nullptr)
298 #endif
302 nsCOMPtr<nsIFile> minidump;
303 // Each crashing process is assigned an increasing sequence number to
304 // indicate which process crashed first.
305 uint32_t sequence;
306 UniquePtr<AnnotationTable> annotations;
307 bool minidumpOnly; // If true then no annotations are present
308 #ifdef MOZ_CRASHREPORTER_INJECTOR
309 InjectorCrashCallback* callback;
310 #endif
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 {
322 public:
323 explicit ReportInjectedCrash(uint32_t pid)
324 : Runnable("ReportInjectedCrash"), mPID(pid) {}
326 NS_IMETHOD Run() override;
328 private:
329 uint32_t mPID;
331 #endif // MOZ_CRASHREPORTER_INJECTOR
333 void RecordMainThreadId() {
334 gMainThreadId =
335 #if defined(XP_UNIX)
336 pthread_self()
337 #elif defined(XP_WIN) // defined(XP_UNIX)
338 GetCurrentThreadId()
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.
350 #if defined(XP_UNIX)
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)
359 #if 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);
376 return current;
379 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI patched_SetUnhandledExceptionFilter(
380 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
381 if (!gBlockUnhandledExceptionFilter) {
382 // don't intercept
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
394 return nullptr;
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,
402 (PCONTEXT)context};
403 return sUnhandledExceptionFilter(&pointers);
406 static void SetJitExceptionHandler() {
407 sUnhandledExceptionFilter = GetUnhandledExceptionFilter();
408 if (sUnhandledExceptionFilter)
409 js::SetJitExceptionHandler(JitExceptionHandler);
411 # endif
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
419 * allocations.
421 static const SIZE_T kReserveSize = 0x5000000; // 80 MB
422 static void* gBreakpadReservedVM;
423 #endif
425 #ifdef XP_LINUX
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));
431 #endif
433 #ifdef XP_WIN
434 static void CreateFileFromPath(const xpstring& path, nsIFile** file) {
435 NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
438 static xpstring* CreatePathFromFile(nsIFile* file) {
439 nsAutoString path;
440 nsresult rv = file->GetPath(path);
441 if (NS_FAILED(rv)) {
442 return nullptr;
444 return new xpstring(static_cast<wchar_t*>(path.get()), path.Length());
446 #else
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) {
452 nsAutoCString path;
453 nsresult rv = file->GetNativePath(path);
454 if (NS_FAILED(rv)) {
455 return nullptr;
457 return new xpstring(path.get(), path.Length());
459 #endif
461 static time_t GetCurrentTimeForCrashTime() {
462 #ifdef XP_LINUX
463 struct kernel_timeval tv;
464 sys_gettimeofday(&tv, nullptr);
465 return tv.tv_sec;
466 #else
467 return time(nullptr);
468 #endif
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));
478 str += appendLen;
479 *str = '\0';
480 *size -= appendLen;
482 return str;
485 void AnnotateOOMAllocationSize(size_t size) { gOOMAllocationSize = size; }
487 static size_t gTexturesSize = 0;
489 void AnnotateTexturesSize(size_t size) { gTexturesSize = size; }
491 #ifndef XP_WIN
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);
500 if (fdfrom < 0) {
501 return false;
504 bool ok = false;
506 int fdto = sys_open(to, O_WRONLY | O_CREAT, 0666);
507 if (fdto < 0) {
508 sys_close(fdfrom);
509 return false;
512 char buf[kBufSize];
513 while (true) {
514 int r = sys_read(fdfrom, buf, kBufSize);
515 if (r == 0) {
516 ok = true;
517 break;
519 if (r < 0) {
520 break;
522 char* wbuf = buf;
523 while (r) {
524 int w = sys_write(fdto, wbuf, r);
525 if (w > 0) {
526 r -= w;
527 wbuf += w;
528 } else if (errno != EINTR) {
529 break;
532 if (r) {
533 break;
537 sys_close(fdfrom);
538 sys_close(fdto);
540 return ok;
542 #endif
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 {
550 public:
551 PlatformWriter() : mBuffer{}, mPos(0), mFD(kInvalidFileHandle) {}
552 explicit PlatformWriter(const XP_CHAR* aPath) : PlatformWriter() {
553 Open(aPath);
556 ~PlatformWriter() {
557 if (Valid()) {
558 Flush();
559 #ifdef XP_WIN
560 CloseHandle(mFD);
561 #elif defined(XP_UNIX)
562 sys_close(mFD);
563 #endif
567 void Open(const XP_CHAR* aPath) {
568 #ifdef XP_WIN
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);
573 #endif
576 void OpenHandle(FileHandle aFD) { mFD = aFD; }
577 bool Valid() { return mFD != kInvalidFileHandle; }
579 void WriteBuffer(const char* aBuffer, size_t aLen) {
580 if (!Valid()) {
581 return;
584 while (aLen-- > 0) {
585 WriteChar(*aBuffer++);
589 void WriteString(const char* aStr) { WriteBuffer(aStr, my_strlen(aStr)); }
591 template <int N>
592 void WriteLiteral(const char (&aStr)[N]) {
593 WriteBuffer(aStr, N - 1);
596 FileHandle FileDesc() { return mFD; }
598 private:
599 PlatformWriter(const PlatformWriter&) = delete;
601 const PlatformWriter& operator=(const PlatformWriter&) = delete;
603 void WriteChar(char aChar) {
604 if (mPos == kBufferSize) {
605 Flush();
608 mBuffer[mPos++] = aChar;
611 void Flush() {
612 if (mPos > 0) {
613 char* buffer = mBuffer;
614 size_t length = mPos;
615 while (length > 0) {
616 #ifdef XP_WIN
617 DWORD written_bytes = 0;
618 if (!WriteFile(mFD, buffer, length, &written_bytes, nullptr)) {
619 break;
621 #elif defined(XP_UNIX)
622 ssize_t written_bytes = sys_write(mFD, buffer, length);
623 if (written_bytes < 0) {
624 if (errno == EAGAIN) {
625 continue;
628 break;
630 #endif
631 buffer += written_bytes;
632 length -= written_bytes;
635 mPos = 0;
639 static const size_t kBufferSize = 512;
641 char mBuffer[kBufferSize];
642 size_t mPos;
643 FileHandle mFD;
646 class JSONAnnotationWriter : public AnnotationWriter {
647 public:
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);
660 WritePrefix();
661 mWriter.WriteBuffer(annotationStr, my_strlen(annotationStr));
662 WriteSeparator();
663 WriteEscapedString(aValue, len);
664 WriteSuffix();
667 void Write(Annotation aAnnotation, uint64_t aValue) override {
668 char buffer[32] = {};
669 XP_STOA(aValue, buffer);
670 Write(aAnnotation, buffer);
673 private:
674 void WritePrefix() {
675 if (mEmpty) {
676 mWriter.WriteBuffer("\"", 1);
677 mEmpty = false;
678 } else {
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++) {
687 uint8_t c = aStr[i];
688 if (c <= 0x1f || c == '\\' || c == '\"') {
689 mWriter.WriteBuffer("\\u00", 4);
690 WriteHexDigitAsAsciiChar((c & 0x00f0) >> 4);
691 WriteHexDigitAsAsciiChar(c & 0x000f);
692 } else {
693 mWriter.WriteBuffer(aStr + i, 1);
698 void WriteHexDigitAsAsciiChar(uint8_t u) {
699 char buf[1];
700 buf[0] = static_cast<unsigned>((u < 10) ? '0' + u : 'a' + (u - 10));
701 mWriter.WriteBuffer(buf, 1);
704 PlatformWriter& mWriter;
705 bool mEmpty;
708 class BinaryAnnotationWriter : public AnnotationWriter {
709 public:
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);
727 private:
728 PlatformWriter& mPlatformWriter;
731 #ifdef MOZ_PHC
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) {
740 char addrString[32];
741 *aBuffer = 0;
742 for (size_t i = 0; i < aStack.mLength; i++) {
743 if (i != 0) {
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()) {
757 return;
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(?!)";
775 break;
776 case phc::AddrInfo::Kind::NeverAllocatedPage:
777 kindString = "NeverAllocatedPage";
778 break;
779 case phc::AddrInfo::Kind::InUsePage:
780 kindString = "InUsePage(?!)";
781 break;
782 case phc::AddrInfo::Kind::FreedPage:
783 kindString = "FreedPage";
784 break;
785 case phc::AddrInfo::Kind::GuardPage:
786 kindString = "GuardPage";
787 break;
788 default:
789 kindString = "Unmatched(?!)";
790 break;
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()) {
806 return;
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(?!)";
822 break;
823 case phc::AddrInfo::Kind::NeverAllocatedPage:
824 kindString = "NeverAllocatedPage";
825 break;
826 case phc::AddrInfo::Kind::InUsePage:
827 kindString = "InUsePage(?!)";
828 break;
829 case phc::AddrInfo::Kind::FreedPage:
830 kindString = "FreedPage";
831 break;
832 case phc::AddrInfo::Kind::GuardPage:
833 kindString = "GuardPage";
834 break;
835 default:
836 kindString = "Unmatched(?!)";
837 break;
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);
851 #endif
854 * If minidump_id is null, we assume that dump_path contains the full
855 * dump file path.
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;
861 XP_CHAR* p;
862 if (minidump_id) {
863 p = Concat(extraDataPath, dump_path, &size);
864 p = Concat(p, XP_PATH_SEPARATOR, &size);
865 p = Concat(p, minidump_id, &size);
866 } else {
867 p = Concat(extraDataPath, dump_path, &size);
868 // Skip back past the .dmp extension, if any.
869 if (*(p - 4) == XP_TEXT('.')) {
870 p -= 4;
871 size += 4;
874 Concat(p, extraFileExtension, &size);
875 aWriter.Open(extraDataPath);
878 #ifdef XP_WIN
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);
897 #elif XP_MACOSX
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) ==
918 KERN_SUCCESS) {
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);
945 #elif XP_LINUX
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
955 // read it.
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);
967 if (fd == -1) {
968 // No /proc/meminfo? Well, fail silently.
969 return;
971 auto Guard = MakeScopeExit([fd]() { mozilla::Unused << sys_close(fd); });
973 ssize_t bytesRead = 0;
974 do {
975 if ((bytesRead = sys_read(fd, buffer + bufferLen,
976 BUFFER_SIZE_BYTES - bufferLen)) < 0) {
977 if ((errno == EAGAIN) || (errno == EINTR)) {
978 continue;
981 // Cannot read for some reason. Let's give up.
982 return;
985 bufferLen += bytesRead;
987 if (bufferLen == BUFFER_SIZE_BYTES) {
988 // The file is too large, bail out
989 return;
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.
1005 struct DataBuffer {
1006 DataBuffer() : data{0}, pos(0) {}
1007 // Clear the buffer.
1008 void reset() {
1009 pos = 0;
1010 data[0] = 0;
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) {
1017 return;
1019 data[pos++] = c;
1020 data[pos] = 0;
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`.
1028 return false;
1031 return true;
1034 // A NUL-terminated string of `pos + 1` chars (the +1 is for the 0).
1035 char data[256];
1037 // Invariant: < 256.
1038 size_t pos;
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) {
1046 int result;
1047 if (!my_strtoui(&result, data)) {
1048 return false;
1050 *number = result;
1051 return true;
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
1057 // `/proc/meminfo`.
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") {
1063 *multiplier = 1024;
1064 return true;
1066 // Other units don't seem to be specified/used.
1067 return false;
1071 // The state of the mini-parser.
1072 enum class State {
1073 // Reading the label, including the trailing ':'.
1074 Label,
1075 // Reading the number, ignoring any whitespace.
1076 Number,
1077 // Reading the unit, ignoring any whitespace.
1078 Unit,
1081 // A single measure being read from /proc/meminfo, e.g.
1082 // the total physical memory available on the system.
1083 struct Measure {
1084 Measure() : state(State::Label) {}
1085 // Reset the measure for a new read.
1086 void reset() {
1087 state = State::Label;
1088 label.reset();
1089 number.reset();
1090 unit.reset();
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)) {
1099 return false;
1101 size_t unitAsMultiplier = 0;
1102 if (!unit.asMultiplier(&unitAsMultiplier)) {
1103 return false;
1105 if (numberAsSize * unitAsMultiplier >= numberAsSize) {
1106 *result = numberAsSize * unitAsMultiplier;
1107 } else {
1108 // Overflow. Unlikely, but just in case, let's return
1109 // the maximal possible value.
1110 *result = size_t(-1);
1112 return true;
1115 // The label being read, e.g. `MemFree`. Does not include the trailing ':'.
1116 DataBuffer label;
1118 // The number being read, e.g. "1024".
1119 NumberBuffer number;
1121 // The unit being read, e.g. "kB".
1122 UnitBuffer unit;
1124 // What we're reading at the moment.
1125 State state;
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`.
1131 struct ValueStore {
1132 ValueStore() : value(0), found(false) {}
1133 size_t value;
1134 bool found;
1136 ValueStore commitLimit;
1137 ValueStore committedAS;
1138 ValueStore memTotal;
1139 ValueStore swapTotal;
1141 // The current measure.
1142 Measure measure;
1144 for (size_t pos = 0; pos < size_t(bufferLen); ++pos) {
1145 const char c = buffer[pos];
1146 switch (measure.state) {
1147 case State::Label:
1148 if (c == ':') {
1149 // We have finished reading the label.
1150 measure.state = State::Number;
1151 } else {
1152 measure.label.append(c);
1154 break;
1155 case State::Number:
1156 if (c == ' ') {
1157 // Ignore whitespace
1158 } else if ('0' <= c && c <= '9') {
1159 // Accumulate numbers.
1160 measure.number.append(c);
1161 } else {
1162 // We have jumped to the unit.
1163 measure.unit.append(c);
1164 measure.state = State::Unit;
1166 break;
1167 case State::Unit:
1168 if (c == ' ') {
1169 // Ignore whitespace
1170 } else if (c == '\n') {
1171 // Flush line.
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".
1178 const char* label;
1179 // If non-nullptr, store the value at this address.
1180 ValueStore* dest;
1181 // If other than Annotation::Count, write the value for this
1182 // annotation.
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) {
1196 size_t value;
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);
1206 break;
1209 // Otherwise, ignore.
1210 } else {
1211 measure.unit.append(c);
1213 break;
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)
1223 : 0;
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);
1232 #else
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
1244 * sole argument.
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) {
1252 # ifdef XP_WIN
1253 XP_CHAR cmdLine[CMDLINE_SIZE];
1254 XP_CHAR* p;
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 = {};
1265 si.cb = sizeof(si);
1267 // If CreateProcess() fails don't do anything.
1268 if (CreateProcess(
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,
1274 &pi)) {
1275 CloseHandle(pi.hProcess);
1276 CloseHandle(pi.hThread);
1278 # elif defined(XP_MACOSX)
1279 pid_t pid = 0;
1280 char* const my_argv[] = {const_cast<char*>(aProgramPath),
1281 const_cast<char*>(aMinidumpPath), nullptr};
1283 char** env = nullptr;
1284 char*** nsEnv = _NSGetEnviron();
1285 if (nsEnv) {
1286 env = *nsEnv;
1289 int rv = posix_spawnp(&pid, my_argv[0], nullptr, nullptr, my_argv, env);
1291 if (rv != 0) {
1292 return false;
1294 # else // !XP_MACOSX
1295 pid_t pid = sys_fork();
1297 if (pid == -1) {
1298 return false;
1299 } else if (pid == 0) {
1300 Unused << execl(aProgramPath, aProgramPath, aMinidumpPath, nullptr);
1301 _exit(1);
1303 # endif // XP_MACOSX
1305 return true;
1308 #else
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();
1327 if (pid == -1)
1328 return false;
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);
1338 } else {
1339 Unused << execlp(
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);
1345 _exit(1);
1347 } else {
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
1350 // handler exits
1351 int status;
1352 Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
1355 return true;
1358 #endif
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()) {
1364 return;
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;
1376 if (len > 0) {
1377 aWriter.Write(Annotation::MainThreadRunnableName, buf, len);
1379 #endif
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,
1410 time_t crashTime) {
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);
1459 #ifdef MOZ_PHC
1460 WritePHCAddrInfo(writer, addrInfo);
1461 #endif
1464 static void WriteCrashEventFile(time_t crashTime, const char* crashTimeString,
1465 const phc::AddrInfo* addrInfo,
1466 #ifdef XP_LINUX
1467 const MinidumpDescriptor& descriptor
1468 #else
1469 const XP_CHAR* minidump_id
1470 #endif
1472 // Minidump IDs are UUIDs (36) + NULL.
1473 static char id_ascii[37] = {};
1474 #ifdef XP_LINUX
1475 const char* index = strrchr(descriptor.path(), '/');
1476 MOZ_ASSERT(index);
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);
1481 #else
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));
1486 #endif
1488 PlatformWriter eventFile;
1490 if (!eventsDirectory.empty()) {
1491 static XP_CHAR crashEventPath[XP_PATH_MAX];
1492 size_t size = XP_PATH_MAX;
1493 XP_CHAR* p;
1494 p = Concat(crashEventPath, eventsDirectory.c_str(), &size);
1495 p = Concat(p, XP_PATH_SEPARATOR, &size);
1496 #ifdef XP_LINUX
1497 Concat(p, id_ascii, &size);
1498 #else
1499 Concat(p, minidump_id, &size);
1500 #endif
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(
1519 #ifdef XP_LINUX
1520 const MinidumpDescriptor& descriptor,
1521 #else
1522 const XP_CHAR* dump_path, const XP_CHAR* minidump_id,
1523 #endif
1524 void* context,
1525 #ifdef XP_WIN
1526 EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion,
1527 #endif
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;
1533 XP_CHAR* p;
1534 #ifndef XP_LINUX
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);
1539 #else
1540 Concat(minidumpPath, descriptor.path(), &size);
1541 #endif
1543 static XP_CHAR memoryReportLocalPath[XP_PATH_MAX];
1544 size = XP_PATH_MAX;
1545 #ifndef XP_LINUX
1546 p = Concat(memoryReportLocalPath, dump_path, &size);
1547 p = Concat(p, XP_PATH_SEPARATOR, &size);
1548 p = Concat(p, minidump_id, &size);
1549 #else
1550 p = Concat(memoryReportLocalPath, descriptor.path(), &size);
1551 // Skip back past the .dmp extension
1552 p -= 4;
1553 #endif
1554 Concat(p, memoryReportExtension, &size);
1556 if (!memoryReportPath.empty()) {
1557 #ifdef XP_WIN
1558 CopyFile(memoryReportPath.c_str(), memoryReportLocalPath, false);
1559 #else
1560 copy_file(memoryReportPath.c_str(), memoryReportLocalPath);
1561 #endif
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,
1575 #ifdef XP_LINUX
1576 descriptor
1577 #else
1578 minidump_id
1579 #endif
1583 PlatformWriter apiData;
1584 #ifdef XP_LINUX
1585 OpenAPIData(apiData, descriptor.path());
1586 #else
1587 OpenAPIData(apiData, dump_path, minidump_id);
1588 #endif
1589 WriteAnnotationsForMainProcessCrash(apiData, addrInfo, crashTime);
1592 if (!doReport) {
1593 #ifdef XP_WIN
1594 TerminateProcess(GetCurrentProcess(), 1);
1595 #endif // XP_WIN
1596 return returnValue;
1599 #if defined(MOZ_WIDGET_ANDROID) // Android
1600 returnValue =
1601 LaunchCrashHandlerService(crashReporterPath.c_str(), minidumpPath);
1602 #else // Windows, Mac, Linux, etc...
1603 returnValue = LaunchProgram(crashReporterPath.c_str(), minidumpPath);
1604 # ifdef XP_WIN
1605 TerminateProcess(GetCurrentProcess(), 1);
1606 # endif
1607 #endif
1609 return returnValue;
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;
1618 ++len;
1619 aBuf[len] = 0;
1621 return len;
1623 #endif
1625 #if defined(XP_WIN)
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) {
1631 return 0;
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) {
1645 return 0;
1648 FSRef fsRef;
1649 OSErr err =
1650 FSFindFolder(kUserDomain, kTemporaryFolderType, kCreateFolder, &fsRef);
1651 if (err != noErr) {
1652 return 0;
1655 OSStatus status = FSRefMakePath(&fsRef, (UInt8*)aBuf, PATH_MAX);
1656 if (status != noErr) {
1657 return 0;
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");
1668 if (!tempenv) {
1669 return false;
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/";
1681 if (!tempenv) {
1682 tempenv = tmpPath;
1684 size_t size = aBufLen;
1685 Concat(aBuf, tempenv, &size);
1686 return EnsureTrailingSlash(aBuf, aBufLen);
1689 #else
1690 # error "Implement this for your platform"
1691 #endif
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);
1703 if (!actualLen) {
1704 return false;
1706 aResult.SetLength(actualLen);
1707 return true;
1710 #ifdef XP_WIN
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) {
1726 if (!exinfo) {
1727 return true;
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
1742 default:
1743 return true;
1747 #endif // XP_WIN
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();
1755 #if defined(XP_WIN)
1756 # if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
1757 DllBlocklist_Shutdown();
1758 # endif
1759 FreeBreakpadVM();
1760 #endif // XP_WIN
1763 #ifdef XP_WIN
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.
1801 minidump_type =
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>(
1808 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);
1813 # endif
1815 const char* e = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1816 if (e && *e) {
1817 minidump_type = MiniDumpWithFullMemory;
1820 return minidump_type;
1823 #else
1825 static bool Filter(void* context) {
1826 PrepareForMinidump();
1827 return true;
1830 static bool ChildFilter(void* context) {
1831 if (gEncounteredChildException.exchange(true)) {
1832 return false;
1835 PrepareForMinidump();
1836 return true;
1839 #endif // !defined(XP_WIN)
1841 static bool ChildMinidumpCallback(
1842 #if defined(XP_WIN)
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,
1848 #endif
1849 void* context,
1850 #if defined(XP_WIN)
1851 EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion,
1852 #endif // defined(XP_WIN)
1853 const mozilla::phc::AddrInfo* addr_info, bool succeeded) {
1854 return 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) {
1862 return false;
1865 envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
1866 if (envvar && *envvar) {
1867 return false;
1870 return true;
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);
1885 # ifdef XP_MACOSX
1886 exePath->SetNativeLeafName("MacOS"_ns);
1887 exePath->Append(u"crashreporter.app"_ns);
1888 exePath->Append(u"Contents"_ns);
1889 exePath->Append(u"MacOS"_ns);
1890 # endif
1892 exePath->Append(aName);
1893 aPath = exePath->NativePath();
1894 return NS_OK;
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;
1915 delete notesField;
1916 notesField = nullptr;
1919 nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force /*=false*/) {
1920 if (gExceptionHandler) return NS_ERROR_ALREADY_INITIALIZED;
1922 #if defined(DEBUG)
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;
1927 #else
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;
1932 #endif
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))) {
1947 return rv;
1950 crashReporterPath = crashReporterPath_temp.get();
1951 #else
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);
1956 } else {
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";
1966 } else {
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;
1978 #ifdef XP_WIN
1979 ReserveBreakpadVM();
1981 // Pre-load psapi.dll to prevent it from being loaded during exception
1982 // handling.
1983 ::LoadLibraryW(L"psapi.dll");
1984 #endif // XP_WIN
1986 #ifdef MOZ_WIDGET_ANDROID
1987 androidUserSerial = getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
1988 #endif
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
2000 #ifdef XP_LINUX
2001 MinidumpDescriptor descriptor(tempPath.get());
2002 #endif
2004 #ifdef XP_WIN
2005 previousUnhandledExceptionFilter = GetUnhandledExceptionFilter();
2006 #endif
2008 gExceptionHandler = new google_breakpad::ExceptionHandler(
2009 #ifdef XP_LINUX
2010 descriptor,
2011 #elif defined(XP_WIN)
2012 std::wstring(tempPath.get()),
2013 #else
2014 tempPath.get(),
2015 #endif
2017 Filter, MinidumpCallback, nullptr,
2018 #ifdef XP_WIN
2019 google_breakpad::ExceptionHandler::HANDLER_ALL, GetMinidumpType(),
2020 (const wchar_t*)nullptr, nullptr);
2021 #else
2022 true
2023 # ifdef XP_MACOSX
2025 nullptr
2026 # endif
2027 # ifdef XP_LINUX
2030 # endif
2032 #endif // XP_WIN
2034 if (!gExceptionHandler) return NS_ERROR_OUT_OF_MEMORY;
2036 #ifdef XP_WIN
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();
2045 # endif
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);
2056 # ifdef DEBUG
2057 if (!ok)
2058 printf_stderr(
2059 "SetUnhandledExceptionFilter hook failed; crash reporter is "
2060 "vulnerable.\n");
2061 # endif
2062 #endif
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;
2079 #endif
2081 oldTerminateHandler = std::set_terminate(&TerminateHandler);
2083 return NS_OK;
2086 bool GetEnabled() { return gExceptionHandler != nullptr; }
2088 bool GetMinidumpPath(nsAString& aPath) {
2089 if (!gExceptionHandler) return false;
2091 #ifndef XP_LINUX
2092 aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
2093 #else
2094 aPath = CONVERT_XP_CHAR_TO_UTF16(
2095 gExceptionHandler->minidump_descriptor().directory().c_str());
2096 #endif
2097 return true;
2100 nsresult SetMinidumpPath(const nsAString& aPath) {
2101 if (!gExceptionHandler) return NS_ERROR_NOT_INITIALIZED;
2103 #ifdef XP_WIN
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()));
2109 #else
2110 gExceptionHandler->set_dump_path(NS_ConvertUTF16toUTF8(aPath).BeginReading());
2111 #endif
2112 return NS_OK;
2115 static nsresult WriteDataToFile(nsIFile* aFile, const nsACString& data) {
2116 PRFileDesc* fd;
2117 nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
2118 NS_ENSURE_SUCCESS(rv, rv);
2120 rv = NS_OK;
2121 if (PR_Write(fd, data.Data(), data.Length()) == -1) {
2122 rv = NS_ERROR_FAILURE;
2124 PR_Close(fd);
2125 return rv;
2128 static nsresult GetFileContents(nsIFile* aFile, nsACString& data) {
2129 PRFileDesc* fd;
2130 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
2131 NS_ENSURE_SUCCESS(rv, rv);
2133 rv = NS_OK;
2134 int32_t filesize = PR_Available(fd);
2135 if (filesize <= 0) {
2136 rv = NS_ERROR_FILE_NOT_FOUND;
2137 } else {
2138 data.SetLength(filesize);
2139 if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
2140 rv = NS_ERROR_FAILURE;
2143 PR_Close(fd);
2144 return rv;
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) {
2156 bool exists;
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);
2168 if (!exists) {
2169 if (aInitFunc) {
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);
2174 } else {
2175 // didn't pass in an init func
2176 rv = NS_ERROR_FAILURE;
2178 } else {
2179 // just get the file's contents
2180 rv = GetFileContents(dataFile, aContents);
2183 return rv;
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));
2192 return NS_OK;
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)) {
2200 return rv;
2203 return NS_OK;
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;
2228 #if defined(XP_WIN)
2229 SetEnvironmentVariableW(aEnvVarName, directoryPath->c_str());
2230 #else
2231 setenv(aEnvVarName, directoryPath->c_str(), /* overwrite */ 1);
2232 #endif
2234 delete directoryPath;
2236 if (aDirectory) {
2237 directory.forget(aDirectory);
2240 return NS_OK;
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
2246 // crash time.
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;
2251 nsresult rv =
2252 SetupCrashReporterDirectory(aAppDataDirectory, "Crash Reports",
2253 XP_TEXT("MOZ_CRASHREPORTER_DATA_DIRECTORY"),
2254 getter_AddRefs(dataDirectory));
2256 if (NS_WARN_IF(NS_FAILED(rv))) {
2257 return rv;
2260 rv = SetupCrashReporterDirectory(aAppDataDirectory, "Pending Pings",
2261 XP_TEXT("MOZ_CRASHREPORTER_PING_DIRECTORY"));
2263 if (NS_WARN_IF(NS_FAILED(rv))) {
2264 return rv;
2267 nsAutoCString data;
2268 if (NS_SUCCEEDED(GetOrInit(dataDirectory, "InstallTime"_ns + aBuildID, data,
2269 InitInstallTime)))
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;
2291 #if defined(XP_WIN)
2292 rv = lastCrashFile->GetPath(filename);
2293 #else
2294 rv = lastCrashFile->GetNativePath(filename);
2295 #endif
2296 NS_ENSURE_SUCCESS(rv, rv);
2298 if (filename.Length() < XP_PATH_MAX) {
2299 #if defined(XP_WIN)
2300 wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
2301 #else
2302 strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
2303 #endif
2306 return NS_OK;
2309 static void OOPDeinit();
2311 nsresult UnsetExceptionHandler() {
2312 if (isSafeToDump) {
2313 MutexAutoLock lock(*dumpSafetyLock);
2314 isSafeToDump = false;
2317 #ifdef XP_WIN
2318 // allow SetUnhandledExceptionFilter
2319 gBlockUnhandledExceptionFilter = false;
2320 #endif
2322 delete gExceptionHandler;
2324 TeardownAnnotationFacilities();
2326 if (!gExceptionHandler) return NS_ERROR_NOT_INITIALIZED;
2328 gExceptionHandler = nullptr;
2330 OOPDeinit();
2332 delete dumpSafetyLock;
2333 dumpSafetyLock = nullptr;
2335 std::set_terminate(oldTerminateHandler);
2337 return NS_OK;
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;
2364 return NS_OK;
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;
2376 return NS_OK;
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)
2394 : mKey(key) {
2395 if (GetEnabled()) {
2396 MutexAutoLock lock(*crashReporterAPILock);
2397 auto& entry = crashReporterAPIData_Table[mKey];
2398 mPrevious = std::move(entry);
2399 entry = data;
2403 AutoAnnotateCrashReport::~AutoAnnotateCrashReport() {
2404 if (GetEnabled()) {
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()) {
2414 aDst[key] = value;
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;
2450 return NS_OK;
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");
2464 PR_SetEnv(env);
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()) {
2483 return false;
2486 data = entry;
2487 return true;
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);
2495 return NS_OK;
2496 #else
2497 return NS_ERROR_NOT_IMPLEMENTED;
2498 #endif
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);
2506 return NS_OK;
2507 #else
2508 return NS_ERROR_NOT_IMPLEMENTED;
2509 #endif
2512 void SetIncludeContextHeap(bool aValue) {
2513 sIncludeContextHeap = aValue;
2515 #ifdef XP_WIN
2516 if (gExceptionHandler) {
2517 gExceptionHandler->set_include_context_heap(sIncludeContextHeap);
2519 #endif
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;
2537 int i;
2538 nsAutoCString envVar;
2539 char* env;
2540 char* argv0 = getenv("MOZ_APP_LAUNCHER");
2541 for (i = 0; i < argc; i++) {
2542 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
2543 envVar.AppendInt(i);
2544 envVar += "=";
2545 if (argv0 && i == 0) {
2546 // Is there a request to suppress default binary launcher?
2547 envVar += argv0;
2548 } else {
2549 envVar += argv[i];
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;
2557 PR_SetEnv(env);
2560 // make sure the arg list is terminated
2561 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
2562 envVar.AppendInt(i);
2563 envVar += "=";
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;
2570 PR_SetEnv(env);
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=";
2576 envVar += appfile;
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);
2581 PR_SetEnv(env);
2584 return NS_OK;
2587 #ifdef XP_WIN
2588 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo) {
2589 if (!gExceptionHandler) return NS_ERROR_NOT_INITIALIZED;
2591 return gExceptionHandler->WriteMinidumpForException(aExceptionInfo)
2592 ? NS_OK
2593 : NS_ERROR_FAILURE;
2595 #endif
2597 #ifdef XP_LINUX
2598 bool WriteMinidumpForSigInfo(int signo, siginfo_t* info, void* uc) {
2599 if (!gExceptionHandler) {
2600 // Crash reporting is disabled.
2601 return false;
2603 return gExceptionHandler->HandleSignal(signo, info, uc);
2605 #endif
2607 #ifdef XP_MACOSX
2608 nsresult AppendObjCExceptionInfoToAppNotes(void* inException) {
2609 nsAutoCString excString;
2610 GetObjCExceptionInfo(inException, excString);
2611 AppendAppNotesToCrashReport(excString);
2612 return NS_OK;
2614 #endif
2617 * Combined code to get/set the crash reporter submission pref on
2618 * different platforms.
2620 static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref) {
2621 nsresult rv;
2622 #if defined(XP_WIN)
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
2671 // and we're done.
2672 if (writePref) {
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);
2680 regKey->Close();
2681 return rv;
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
2687 // to "true".
2688 uint32_t value;
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);
2694 regKey->Close();
2695 if (NS_SUCCEEDED(rv)) {
2696 *aSubmitReports = !!value;
2697 return NS_OK;
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;
2706 return NS_OK;
2709 rv = regKey->ReadIntValue(u"SubmitCrashReport"_ns, &value);
2710 // default to true on failure
2711 if (NS_FAILED(rv)) {
2712 value = 1;
2713 rv = NS_OK;
2715 regKey->Close();
2717 *aSubmitReports = !!value;
2718 return NS_OK;
2719 #elif defined(XP_MACOSX)
2720 rv = NS_OK;
2721 if (writePref) {
2722 CFPropertyListRef cfValue =
2723 (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
2724 ::CFPreferencesSetAppValue(CFSTR("submitReport"), cfValue,
2725 reporterClientAppID);
2726 if (!::CFPreferencesAppSynchronize(reporterClientAppID))
2727 rv = NS_ERROR_FAILURE;
2728 } else {
2729 *aSubmitReports = true;
2730 Boolean keyExistsAndHasValidFormat = false;
2731 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(
2732 CFSTR("submitReport"), reporterClientAppID,
2733 &keyExistsAndHasValidFormat);
2734 if (keyExistsAndHasValidFormat) *aSubmitReports = !!prefValue;
2736 return rv;
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);
2748 bool exists;
2749 rv = reporterINI->Exists(&exists);
2750 NS_ENSURE_SUCCESS(rv, rv);
2751 if (!exists) {
2752 if (!writePref) {
2753 // If reading the pref, default to true if .ini doesn't exist.
2754 *aSubmitReports = true;
2755 return NS_OK;
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.
2771 if (writePref) {
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);
2779 return rv;
2782 nsAutoCString submitReportValue;
2783 rv = iniParser->GetString("Crash Reporter"_ns, "SubmitReport"_ns,
2784 submitReportValue);
2786 // Default to "true" if the pref can't be found.
2787 if (NS_FAILED(rv))
2788 *aSubmitReports = true;
2789 else if (submitReportValue.EqualsASCII("0"))
2790 *aSubmitReports = false;
2791 else
2792 *aSubmitReports = true;
2794 return NS_OK;
2795 #else
2796 return NS_ERROR_NOT_IMPLEMENTED;
2797 #endif
2800 nsresult GetSubmitReports(bool* aSubmitReports) {
2801 return PrefSubmitReports(aSubmitReports, false);
2804 nsresult SetSubmitReports(bool aSubmitReports) {
2805 nsresult rv;
2807 nsCOMPtr<nsIObserverService> obsServ =
2808 mozilla::services::GetObserverService();
2809 if (!obsServ) {
2810 return NS_ERROR_FAILURE;
2813 rv = PrefSubmitReports(&aSubmitReports, true);
2814 if (NS_FAILED(rv)) {
2815 return rv;
2818 obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
2819 return NS_OK;
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");
2829 if (env && *env) {
2830 NS_NewNativeLocalFile(nsDependentCString(env), false,
2831 getter_AddRefs(eventsDir));
2832 EnsureDirectoryExists(eventsDir);
2835 xpstring* path = CreatePathFromFile(eventsDir);
2836 if (!path) {
2837 return; // There's no clean failure from this
2840 eventsDirectory = xpstring(*path);
2841 #ifdef XP_WIN
2842 SetEnvironmentVariableW(eventsDirectoryEnv, path->c_str());
2843 #else
2844 setenv(eventsDirectoryEnv, path->c_str(), /* overwrite */ 1);
2845 #endif
2847 delete path;
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");
2874 if (env && *env) {
2875 SetCrashEventsDir(nullptr);
2878 nsCOMPtr<nsIFile> eventsDir;
2879 nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
2880 if (NS_SUCCEEDED(rv)) {
2881 SetProfileDirectory(eventsDir);
2882 return;
2885 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
2886 if (NS_SUCCEEDED(rv)) {
2887 SetUserAppDataDirectory(eventsDir);
2888 return;
2891 NS_WARNING(
2892 "Couldn't get the user appdata directory. Crash events may not be "
2893 "produced.");
2896 bool GetCrashEventsDir(nsAString& aPath) {
2897 if (eventsDirectory.empty()) {
2898 return false;
2900 aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory.c_str());
2901 return true;
2904 void SetMemoryReportFile(nsIFile* aFile) {
2905 if (!gExceptionHandler) {
2906 return;
2909 PathString path;
2910 #ifdef XP_WIN
2911 aFile->GetPath(path);
2912 #else
2913 aFile->GetNativePath(path);
2914 #endif
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)) {
2924 return rv;
2926 defaultMemoryReportFile->AppendNative("memory-report.json.gz"_ns);
2927 defaultMemoryReportPath = CreatePathFromFile(defaultMemoryReportFile);
2928 if (!defaultMemoryReportPath) {
2929 return NS_ERROR_FAILURE;
2931 } else {
2932 CreateFileFromPath(*defaultMemoryReportPath,
2933 getter_AddRefs(defaultMemoryReportFile));
2934 if (!defaultMemoryReportFile) {
2935 return NS_ERROR_FAILURE;
2938 defaultMemoryReportFile.forget(aFile);
2939 return NS_OK;
2942 static void FindPendingDir() {
2943 if (!pendingDirectory.empty()) {
2944 return;
2946 nsCOMPtr<nsIFile> pendingDir;
2947 nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
2948 if (NS_FAILED(rv)) {
2949 NS_WARNING(
2950 "Couldn't get the user appdata directory, crash dumps will go in an "
2951 "unusual location");
2952 } else {
2953 pendingDir->Append(u"Crash Reports"_ns);
2954 pendingDir->Append(u"pending"_ns);
2956 PathString path;
2957 #ifdef XP_WIN
2958 pendingDir->GetPath(path);
2959 #else
2960 pendingDir->GetNativePath(path);
2961 #endif
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()) {
2972 return false;
2975 nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
2976 if (!pending) {
2977 NS_WARNING("Can't set up pending directory during shutdown.");
2978 return false;
2980 #ifdef XP_WIN
2981 pending->InitWithPath(nsDependentString(pendingDirectory.c_str()));
2982 #else
2983 pending->InitWithNativePath(nsDependentCString(pendingDirectory.c_str()));
2984 #endif
2985 pending.swap(*dir);
2986 return true;
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/
2994 // limbo.
2995 static bool GetMinidumpLimboDir(nsIFile** dir) {
2996 if (ShouldReport()) {
2997 return GetPendingDir(dir);
2998 } else {
2999 #ifndef XP_LINUX
3000 CreateFileFromPath(gExceptionHandler->dump_path(), dir);
3001 #else
3002 CreateFileFromPath(gExceptionHandler->minidump_descriptor().directory(),
3003 dir);
3004 #endif
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)) {
3030 return false;
3033 nsAutoString fileName(id);
3035 if (aAdditionalMinidump) {
3036 fileName.Append('-');
3037 fileName.Append(*aAdditionalMinidump);
3040 fileName.Append(u".dmp"_ns);
3041 (*minidump)->Append(fileName);
3043 bool exists;
3044 if (NS_FAILED((*minidump)->Exists(&exists)) || !exists) {
3045 return false;
3048 return true;
3051 bool GetIDFromMinidump(nsIFile* minidump, nsAString& id) {
3052 if (minidump && NS_SUCCEEDED(minidump->GetLeafName(id))) {
3053 id.ReplaceLiteral(id.Length() - 4, 4, u"");
3054 return true;
3056 return false;
3059 bool GetExtraFileForID(const nsAString& id, nsIFile** extraFile) {
3060 if (!GetMinidumpLimboDir(extraFile)) {
3061 return false;
3064 (*extraFile)->Append(id + u".extra"_ns);
3066 bool exists;
3067 if (NS_FAILED((*extraFile)->Exists(&exists)) || !exists) {
3068 return false;
3071 return true;
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);
3089 return true;
3092 static bool WriteExtraFile(PlatformWriter& pw,
3093 const AnnotationTable& aAnnotations) {
3094 if (!pw.Valid()) {
3095 return false;
3098 JSONAnnotationWriter writer(pw);
3099 WriteAnnotations(writer, aAnnotations);
3100 WriteSynthesizedAnnotations(writer);
3102 return true;
3105 bool WriteExtraFile(const nsAString& id, const AnnotationTable& annotations) {
3106 nsCOMPtr<nsIFile> extra;
3107 if (!GetMinidumpLimboDir(getter_AddRefs(extra))) {
3108 return false;
3111 extra->Append(id + u".extra"_ns);
3112 PathString path;
3113 #ifdef XP_WIN
3114 NS_ENSURE_SUCCESS(extra->GetPath(path), false);
3115 #elif defined(XP_UNIX)
3116 NS_ENSURE_SUCCESS(extra->GetNativePath(path), false);
3117 #endif
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,
3126 // uptime, etc...
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";
3138 return;
3141 for (const auto& annotation : *aChildAnnotations) {
3142 switch (annotation.data.tag) {
3143 case AnnotationData::Tag::Empty:
3144 break;
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
3152 // follow-ups.
3153 if (annotation.data.usize_data._0 != 0) {
3154 aAnnotations[static_cast<Annotation>(annotation.id)] =
3155 nsPrintfCString("%zu", annotation.data.usize_data._0);
3157 } else {
3158 aAnnotations[static_cast<Annotation>(annotation.id)] =
3159 nsPrintfCString("%zu", annotation.data.usize_data._0);
3161 break;
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;
3169 } break;
3171 case AnnotationData::Tag::ByteBuffer: {
3172 if (annotation.id ==
3173 static_cast<uint32_t>(Annotation::PHCBaseAddress)) {
3174 #ifdef MOZ_PHC
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);
3179 #endif
3181 } break;
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))) {
3196 return false;
3199 if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, u""_ns))) {
3200 return false;
3203 if (memoryReport) {
3204 nsAutoString leafName;
3205 nsresult rv = dumpFile->GetLeafName(leafName);
3206 if (NS_FAILED(rv)) {
3207 return false;
3209 // Generate the correct memory report filename from the dumpFile's name
3210 leafName.Replace(
3211 leafName.Length() - 4, 4,
3212 static_cast<nsString>(CONVERT_XP_CHAR_TO_UTF16(memoryReportExtension)));
3213 if (NS_FAILED(memoryReport->MoveTo(pendingDir, leafName))) {
3214 return false;
3218 return true;
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();
3227 #endif
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
3237 // under us.
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);
3253 #if XP_WIN
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());
3259 #else
3260 nsTArray<CAnnotation>* child_annotations = mozannotation_retrieve(pid);
3261 #endif
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
3268 bool runCallback;
3269 #endif
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;
3285 #endif
3287 #ifdef MOZ_CRASHREPORTER_INJECTOR
3288 if (runCallback) NS_DispatchToMainThread(new ReportInjectedCrash(pid));
3289 #endif
3292 if (child_annotations) {
3293 mozannotation_free(child_annotations);
3297 static bool OOPInitialized() { return pidToMinidump != nullptr; }
3299 void OOPInit() {
3300 class ProxyToMainThread : public Runnable {
3301 public:
3302 ProxyToMainThread() : Runnable("nsExceptionHandler::ProxyToMainThread") {}
3303 NS_IMETHOD Run() override {
3304 OOPInit();
3305 return NS_OK;
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());
3313 return;
3316 if (OOPInitialized()) return;
3318 MOZ_ASSERT(NS_IsMainThread());
3320 MOZ_ASSERT(gExceptionHandler != nullptr,
3321 "attempt to initialize OOP crash reporter before in-process "
3322 "crashreporter!");
3324 #if defined(XP_WIN)
3325 childCrashNotifyPipe =
3326 mozilla::Smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
3327 static_cast<int>(::GetCurrentProcessId()))
3328 .release();
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
3339 &dumpPath);
3341 if (sIncludeContextHeap) {
3342 crashServer->set_include_context_heap(sIncludeContextHeap);
3345 #elif defined(XP_LINUX)
3346 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
3347 &clientSocketFd))
3348 MOZ_CRASH("can't create crash reporter socketpair()");
3350 const std::string dumpPath =
3351 gExceptionHandler->minidump_descriptor().directory();
3352 crashServer =
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()))
3359 .release();
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
3366 dumpPath);
3367 #endif
3369 if (!crashServer->Start()) MOZ_CRASH("can't start crash reporter server()");
3371 pidToMinidump = new ChildMinidumpMap();
3373 dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
3375 FindPendingDir();
3376 UpdateCrashEventsDir();
3379 static void OOPDeinit() {
3380 if (!OOPInitialized()) {
3381 NS_WARNING("OOPDeinit() without successful OOPInit()");
3382 return;
3385 #ifdef MOZ_CRASHREPORTER_INJECTOR
3386 if (sInjectorThread) {
3387 sInjectorThread->Shutdown();
3388 NS_RELEASE(sInjectorThread);
3390 #endif
3392 delete crashServer;
3393 crashServer = nullptr;
3395 delete dumpMapLock;
3396 dumpMapLock = nullptr;
3398 delete pidToMinidump;
3399 pidToMinidump = nullptr;
3401 #if defined(XP_WIN) || defined(XP_MACOSX)
3402 free(childCrashNotifyPipe);
3403 childCrashNotifyPipe = nullptr;
3404 #endif
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;
3416 #endif
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)))
3427 return;
3431 MutexAutoLock lock(*dumpMapLock);
3432 ChildProcessData* pd = pidToMinidump->PutEntry(processID);
3433 MOZ_ASSERT(!pd->minidump && !pd->callback);
3434 pd->callback = cb;
3435 pd->minidumpOnly = true;
3438 nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
3439 sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
3442 NS_IMETHODIMP
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);
3455 cb = pd->callback;
3458 cb->OnCrash(mPID);
3459 return NS_OK;
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()) {
3476 *childCrashFd = -1;
3477 *childCrashRemapFd = -1;
3478 return true;
3481 MOZ_ASSERT(OOPInitialized());
3483 *childCrashFd = clientSocketFd;
3484 *childCrashRemapFd = gMagicChildCrashReportFd;
3486 return true;
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)) {
3496 switch (key) {
3497 case Annotation::MozCrashReason:
3498 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
3499 case Annotation::MainThreadRunnableName:
3500 #endif
3501 case Annotation::OOMAllocationSize:
3502 #ifdef MOZ_PHC
3503 case Annotation::PHCBaseAddress:
3504 #endif
3505 break;
3507 default:
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]);
3520 #endif
3522 mozannotation_register_usize(
3523 static_cast<uint32_t>(Annotation::OOMAllocationSize),
3524 &gOOMAllocationSize);
3526 #ifdef MOZ_PHC
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));
3535 #endif
3537 #if defined(XP_WIN)
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();
3547 # endif
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
3562 aCrashPipe);
3563 #endif
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()) {
3576 return;
3579 MutexAutoLock lock(*dumpMapLock);
3581 ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
3582 if (!pd) {
3583 return;
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
3600 // annotations.
3601 if (!pd->minidumpOnly) {
3602 aAnnotations = *(pd->annotations);
3604 if (aSequence) {
3605 *aSequence = pd->sequence;
3608 pidToMinidump->RemoveEntry(pd);
3610 return !!*dump;
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)) {
3619 return false;
3622 nsAutoString id;
3623 if (!GetIDFromMinidump(minidump, id)) {
3624 return false;
3627 if (aDumpId) {
3628 *aDumpId = id;
3631 annotations[Annotation::ProcessType] =
3632 XRE_ChildProcessTypeToAnnotation(aType);
3634 return WriteExtraFile(id, annotations);
3637 #ifdef XP_WIN
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
3648 // under us.
3649 MutexAutoLock safetyLock(*dumpSafetyLock);
3650 if (!isSafeToDump || !ShouldReport()) {
3651 return S_OK;
3654 ProcessId pid = werData->mChildPid;
3655 nsCOMPtr<nsIFile> minidump;
3656 if (!GetPendingDir(getter_AddRefs(minidump))) {
3657 return S_OK;
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));
3681 return S_OK;
3684 #endif // XP_WIN
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(
3720 #ifdef XP_LINUX
3721 const MinidumpDescriptor& descriptor,
3722 #else
3723 const XP_CHAR* dump_path, const XP_CHAR* minidump_id,
3724 #endif
3725 void* context,
3726 #ifdef XP_WIN
3727 EXCEPTION_POINTERS* /*unused*/, MDRawAssertionInfo* /*unused*/,
3728 #endif
3729 const phc::AddrInfo* addrInfo, bool succeeded) {
3730 XP_CHAR* path = static_cast<XP_CHAR*>(context);
3731 size_t size = XP_PATH_MAX;
3733 #ifdef XP_LINUX
3734 Concat(path, descriptor.path(), &size);
3735 #else
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);
3740 #endif
3742 return true;
3745 ThreadId CurrentThreadId() {
3746 #if defined(XP_WIN)
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))
3756 return -1;
3758 for (unsigned int i = 0; i < thread_count; ++i) {
3759 if (threads_for_task[i] == mach_thread_self()) return i;
3761 abort();
3762 #else
3763 # error "Unsupported platform"
3764 #endif
3767 #ifdef XP_MACOSX
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) ==
3775 KERN_SUCCESS &&
3776 childBlamedThread < thread_count) {
3777 childThread = threads_for_task[childBlamedThread];
3780 return childThread;
3782 #endif
3784 bool CreateMinidumpsAndPair(ProcessHandle aTargetHandle,
3785 ThreadId aTargetBlamedThread,
3786 const nsACString& aIncomingPairName,
3787 AnnotationTable& aTargetAnnotations,
3788 nsIFile** aMainDumpOut) {
3789 if (!GetEnabled()) {
3790 return false;
3793 AutoIOInterposerDisable disableIOInterposition;
3795 #ifdef XP_MACOSX
3796 mach_port_t targetThread = GetChildThread(aTargetHandle, aTargetBlamedThread);
3797 #else
3798 ThreadId targetThread = aTargetBlamedThread;
3799 #endif
3801 xpstring dump_path;
3802 #ifndef XP_LINUX
3803 dump_path = gExceptionHandler->dump_path();
3804 #else
3805 dump_path = gExceptionHandler->minidump_descriptor().directory();
3806 #endif
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] = {};
3812 // dump the target
3813 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
3814 aTargetHandle, targetThread, dump_path, PairedDumpCallback,
3815 static_cast<void*>(minidumpPath)
3816 #ifdef XP_WIN
3818 GetMinidumpType()
3819 #endif
3820 )) {
3821 return false;
3824 nsCOMPtr<nsIFile> targetMinidump;
3825 CreateFileFromPath(xpstring(minidumpPath), getter_AddRefs(targetMinidump));
3827 // Create a dump of this process.
3828 if (!google_breakpad::ExceptionHandler::WriteMinidump(
3829 dump_path,
3830 #ifdef XP_MACOSX
3831 true,
3832 #endif
3833 PairedDumpCallback, static_cast<void*>(minidumpPath)
3834 #ifdef XP_WIN
3836 GetMinidumpType()
3837 #endif
3838 )) {
3839 targetMinidump->Remove(false);
3840 return 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();
3854 #endif
3856 AddSharedAnnotations(aTargetAnnotations);
3857 #if XP_WIN
3858 nsTArray<CAnnotation>* child_annotations =
3859 mozannotation_retrieve(reinterpret_cast<uintptr_t>(aTargetHandle));
3860 #else
3861 nsTArray<CAnnotation>* child_annotations =
3862 mozannotation_retrieve(aTargetHandle);
3863 #endif
3864 AddChildProcessAnnotations(aTargetAnnotations, child_annotations);
3865 if (child_annotations) {
3866 mozannotation_free(child_annotations);
3869 targetMinidump.forget(aMainDumpOut);
3871 return true;
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)
3879 if (wasSet) {
3880 std::set_terminate(oldTerminateHandler);
3881 delete gExceptionHandler;
3882 gExceptionHandler = nullptr;
3884 #endif
3885 TeardownAnnotationFacilities();
3887 return true;
3890 #if defined(MOZ_WIDGET_ANDROID)
3891 void SetNotificationPipeForChild(int childCrashFd) {
3892 gMagicChildCrashReportFd = childCrashFd;
3894 #endif
3896 } // namespace CrashReporter