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