summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/nsExceptionHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/nsExceptionHandler.cpp')
-rw-r--r--toolkit/crashreporter/nsExceptionHandler.cpp4147
1 files changed, 4147 insertions, 0 deletions
diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp
new file mode 100644
index 000000000..1e0743192
--- /dev/null
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -0,0 +1,4147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsExceptionHandler.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDataHashtable.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/dom/CrashReporterChild.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/TimeStamp.h"
+
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "jsfriendapi.h"
+
+#if defined(XP_WIN32)
+#ifdef WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include "nsXULAppAPI.h"
+#include "nsIXULAppInfo.h"
+#include "nsIWindowsRegKey.h"
+#include "client/windows/crash_generation/client_info.h"
+#include "client/windows/crash_generation/crash_generation_server.h"
+#include "client/windows/handler/exception_handler.h"
+#include <dbghelp.h>
+#include <string.h>
+#include "nsDirectoryServiceUtils.h"
+
+#include "nsWindowsDllInterceptor.h"
+#elif defined(XP_MACOSX)
+#include "client/mac/crash_generation/client_info.h"
+#include "client/mac/crash_generation/crash_generation_server.h"
+#include "client/mac/handler/exception_handler.h"
+#include <string>
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <crt_externs.h>
+#include <fcntl.h>
+#include <mach/mach.h>
+#include <sys/types.h>
+#include <spawn.h>
+#include <unistd.h>
+#include "mac_utils.h"
+#elif defined(XP_LINUX)
+#include "nsIINIParser.h"
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+#include "client/linux/crash_generation/client_info.h"
+#include "client/linux/crash_generation/crash_generation_server.h"
+#include "client/linux/handler/exception_handler.h"
+#include "common/linux/eintr_wrapper.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#elif defined(XP_SOLARIS)
+#include "client/solaris/handler/exception_handler.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#else
+#error "Not yet implemented for this platform"
+#endif // defined(XP_WIN32)
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+#include "InjectCrashReporter.h"
+using mozilla::InjectCrashRunnable;
+#endif
+
+#include <stdlib.h>
+#include <time.h>
+#include <prenv.h>
+#include <prio.h>
+#include <prmem.h>
+#include "mozilla/Mutex.h"
+#include "nsDebug.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "prprf.h"
+#include <map>
+#include <vector>
+
+#include "mozilla/double-conversion.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/mozalloc_oom.h"
+#include "mozilla/WindowsDllBlocklist.h"
+
+#if defined(XP_MACOSX)
+CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+#include "common/linux/file_id.h"
+#endif
+
+using google_breakpad::CrashGenerationServer;
+using google_breakpad::ClientInfo;
+#ifdef XP_LINUX
+using google_breakpad::MinidumpDescriptor;
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+using google_breakpad::auto_wasteful_vector;
+using google_breakpad::FileID;
+using google_breakpad::PageAllocator;
+#endif
+using namespace mozilla;
+using mozilla::dom::CrashReporterChild;
+using mozilla::dom::PCrashReporterChild;
+using mozilla::ipc::CrashReporterClient;
+
+namespace CrashReporter {
+
+#ifdef XP_WIN32
+typedef wchar_t XP_CHAR;
+typedef std::wstring xpstring;
+#define XP_TEXT(x) L##x
+#define CONVERT_XP_CHAR_TO_UTF16(x) x
+#define XP_STRLEN(x) wcslen(x)
+#define my_strlen strlen
+#define CRASH_REPORTER_FILENAME "crashreporter.exe"
+#define PATH_SEPARATOR "\\"
+#define XP_PATH_SEPARATOR L"\\"
+#define XP_PATH_SEPARATOR_CHAR L'\\'
+#define XP_PATH_MAX (MAX_PATH + 1)
+// "<reporter path>" "<minidump path>"
+#define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
+#ifdef _USE_32BIT_TIME_T
+#define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
+#else
+#define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
+#endif
+#define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
+#else
+typedef char XP_CHAR;
+typedef std::string xpstring;
+#define XP_TEXT(x) x
+#define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
+#define CRASH_REPORTER_FILENAME "crashreporter"
+#define PATH_SEPARATOR "/"
+#define XP_PATH_SEPARATOR "/"
+#define XP_PATH_SEPARATOR_CHAR '/'
+#define XP_PATH_MAX PATH_MAX
+#ifdef XP_LINUX
+#define XP_STRLEN(x) my_strlen(x)
+#define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
+#define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
+#else
+#define XP_STRLEN(x) strlen(x)
+#define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
+#define XP_STOA(size, buffer, base) sprintf(buffer, "%zu", (size_t) size)
+#define my_strlen strlen
+#define sys_close close
+#define sys_fork fork
+#define sys_open open
+#define sys_read read
+#define sys_write write
+#endif
+#endif // XP_WIN32
+
+#if defined(__GNUC__)
+#define MAYBE_UNUSED __attribute__((unused))
+#else
+#define MAYBE_UNUSED
+#endif // defined(__GNUC__)
+
+#ifndef XP_LINUX
+static const XP_CHAR dumpFileExtension[] = XP_TEXT(".dmp");
+#endif
+
+static const XP_CHAR childCrashAnnotationBaseName[] = XP_TEXT("GeckoChildCrash");
+static const XP_CHAR extraFileExtension[] = XP_TEXT(".extra");
+static const XP_CHAR memoryReportExtension[] = XP_TEXT(".memory.json.gz");
+static xpstring *defaultMemoryReportPath = nullptr;
+
+// A whitelist of crash annotations which do not contain sensitive data
+// and are saved in the crash record and sent with Firefox Health Report.
+static char const * const kCrashEventAnnotations[] = {
+ "AsyncShutdownTimeout",
+ "BuildID",
+ "ProductID",
+ "ProductName",
+ "ReleaseChannel",
+ "SecondsSinceLastCrash",
+ "ShutdownProgress",
+ "StartupCrash",
+ "TelemetryEnvironment",
+ "Version",
+ // The following entries are not normal annotations but are included
+ // in the crash record/FHR:
+ // "ContainsMemoryReport"
+ // "EventLoopNestingLevel"
+ // "IsGarbageCollecting"
+ // "AvailablePageFile"
+ // "AvailableVirtualMemory"
+ // "SystemMemoryUsePercentage"
+ // "OOMAllocationSize"
+ // "TotalPageFile"
+ // "TotalPhysicalMemory"
+ // "TotalVirtualMemory"
+ // "MozCrashReason"
+};
+
+static const char kCrashMainID[] = "crash.main.2\n";
+
+static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
+
+static XP_CHAR* pendingDirectory;
+static XP_CHAR* crashReporterPath;
+static XP_CHAR* memoryReportPath;
+
+// Where crash events should go.
+static XP_CHAR* eventsDirectory;
+static char* eventsEnv = nullptr;
+
+// The current telemetry session ID to write to the event file
+static char* currentSessionId = nullptr;
+
+// If this is false, we don't launch the crash reporter
+static bool doReport = true;
+
+// If this is true, we don't have a crash reporter
+static bool headlessClient = false;
+
+// if this is true, we pass the exception on to the OS crash reporter
+static bool showOSCrashReporter = false;
+
+// The time of the last recorded crash, as a time_t value.
+static time_t lastCrashTime = 0;
+// The pathname of a file to store the crash time in
+static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
+
+// A marker file to hold the path to the last dump written, which
+// will be checked on startup.
+static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
+
+// Whether we've already looked for the marker file.
+static bool lastRunCrashID_checked = false;
+// The minidump ID contained in the marker file.
+static nsString* lastRunCrashID = nullptr;
+
+#if defined(MOZ_WIDGET_ANDROID)
+// on Android 4.2 and above there is a user serial number associated
+// with the current process that gets lost when we fork so we need to
+// explicitly pass it to am
+static char* androidUserSerial = nullptr;
+#endif
+
+// these are just here for readability
+static const char kTimeSinceLastCrashParameter[] = "SecondsSinceLastCrash=";
+static const int kTimeSinceLastCrashParameterLen =
+ sizeof(kTimeSinceLastCrashParameter)-1;
+
+// this holds additional data sent via the API
+static Mutex* crashReporterAPILock;
+static Mutex* notesFieldLock;
+static AnnotationTable* crashReporterAPIData_Hash;
+static nsCString* crashReporterAPIData = nullptr;
+static nsCString* crashEventAPIData = nullptr;
+static nsCString* notesField = nullptr;
+static bool isGarbageCollecting;
+static uint32_t eventloopNestingLevel = 0;
+
+// Avoid a race during application termination.
+static Mutex* dumpSafetyLock;
+static bool isSafeToDump = false;
+
+// OOP crash reporting
+static CrashGenerationServer* crashServer; // chrome process has this
+
+static std::terminate_handler oldTerminateHandler = nullptr;
+
+#if (defined(XP_MACOSX) || defined(XP_WIN))
+// This field is valid in both chrome and content processes.
+static xpstring* childProcessTmpDir = nullptr;
+#endif
+
+# if defined(XP_WIN) || defined(XP_MACOSX)
+// If crash reporting is disabled, we hand out this "null" pipe to the
+// child process and don't attempt to connect to a parent server.
+static const char kNullNotifyPipe[] = "-";
+static char* childCrashNotifyPipe;
+
+# elif defined(XP_LINUX)
+static int serverSocketFd = -1;
+static int clientSocketFd = -1;
+static const int kMagicChildCrashReportFd = 4;
+
+# endif
+
+// |dumpMapLock| must protect all access to |pidToMinidump|.
+static Mutex* dumpMapLock;
+struct ChildProcessData : public nsUint32HashKey
+{
+ explicit ChildProcessData(KeyTypePointer aKey)
+ : nsUint32HashKey(aKey)
+ , sequence(0)
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ , callback(nullptr)
+#endif
+ { }
+
+ nsCOMPtr<nsIFile> minidump;
+ // Each crashing process is assigned an increasing sequence number to
+ // indicate which process crashed first.
+ uint32_t sequence;
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ InjectorCrashCallback* callback;
+#endif
+};
+
+typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
+static ChildMinidumpMap* pidToMinidump;
+static uint32_t crashSequence;
+static bool OOPInitialized();
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+static nsIThread* sInjectorThread;
+
+class ReportInjectedCrash : public Runnable
+{
+public:
+ explicit ReportInjectedCrash(uint32_t pid) : mPID(pid) { }
+
+ NS_IMETHOD Run();
+
+private:
+ uint32_t mPID;
+};
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+// Crashreporter annotations that we don't send along in subprocess reports.
+static const char* kSubprocessBlacklist[] = {
+ "FramePoisonBase",
+ "FramePoisonSize",
+ "StartupCrash",
+ "StartupTime",
+ "URL"
+};
+
+// If annotations are attempted before the crash reporter is enabled,
+// they queue up here.
+class DelayedNote;
+nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
+
+#if defined(XP_WIN)
+// the following are used to prevent other DLLs reverting the last chance
+// exception handler to the windows default. Any attempt to change the
+// unhandled exception filter or to reset it is ignored and our crash
+// reporter is loaded instead (in case it became unloaded somehow)
+typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
+ (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
+static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
+static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = nullptr;
+static WindowsDllInterceptor gKernel32Intercept;
+static bool gBlockUnhandledExceptionFilter = true;
+
+static LPTOP_LEVEL_EXCEPTION_FILTER GetUnhandledExceptionFilter()
+{
+ // Set a dummy value to get the current filter, then restore
+ LPTOP_LEVEL_EXCEPTION_FILTER current = SetUnhandledExceptionFilter(nullptr);
+ SetUnhandledExceptionFilter(current);
+ return current;
+}
+
+static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
+patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
+{
+ if (!gBlockUnhandledExceptionFilter) {
+ // don't intercept
+ return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
+ }
+
+ if (lpTopLevelExceptionFilter == previousUnhandledExceptionFilter) {
+ // OK to swap back and forth between the previous filter
+ previousUnhandledExceptionFilter =
+ stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
+ return previousUnhandledExceptionFilter;
+ }
+
+ // intercept attempts to change the filter
+ return nullptr;
+}
+
+static LPTOP_LEVEL_EXCEPTION_FILTER sUnhandledExceptionFilter = nullptr;
+
+static long
+JitExceptionHandler(void *exceptionRecord, void *context)
+{
+ EXCEPTION_POINTERS pointers = {
+ (PEXCEPTION_RECORD)exceptionRecord,
+ (PCONTEXT)context
+ };
+ return sUnhandledExceptionFilter(&pointers);
+}
+
+/**
+ * Reserve some VM space. In the event that we crash because VM space is
+ * being leaked without leaking memory, freeing this space before taking
+ * the minidump will allow us to collect a minidump.
+ *
+ * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
+ * allocations.
+ */
+static const SIZE_T kReserveSize = 0x4000000; // 64 MB
+static void* gBreakpadReservedVM;
+#endif
+
+#ifdef XP_MACOSX
+static cpu_type_t pref_cpu_types[2] = {
+#if defined(__i386__)
+ CPU_TYPE_X86,
+#elif defined(__x86_64__)
+ CPU_TYPE_X86_64,
+#elif defined(__ppc__)
+ CPU_TYPE_POWERPC,
+#endif
+ CPU_TYPE_ANY };
+
+static posix_spawnattr_t spawnattr;
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID)
+// Android builds use a custom library loader,
+// so the embedding will provide a list of shared
+// libraries that are mapped into anonymous mappings.
+typedef struct {
+ std::string name;
+ uintptr_t start_address;
+ size_t length;
+ size_t file_offset;
+} mapping_info;
+static std::vector<mapping_info> library_mappings;
+typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
+#endif
+}
+
+// Format a non-negative double to a string, without using C-library functions,
+// which need to be avoided (.e.g. bug 1240160, comment 10). Leave the utility
+// non-file static so that we can gtest it. Return false if we failed to
+// get the formatting done correctly.
+bool SimpleNoCLibDtoA(double aValue, char* aBuffer, int aBufferLength)
+{
+ // aBufferLength is the size of the buffer. Be paranoid.
+ aBuffer[aBufferLength-1] = '\0';
+
+ if (aValue < 0) {
+ return false;
+ }
+
+ int length, point, i;
+ bool sign;
+ bool ok = true;
+ double_conversion::DoubleToStringConverter::DoubleToAscii(
+ aValue,
+ double_conversion::DoubleToStringConverter::SHORTEST,
+ 8,
+ aBuffer,
+ aBufferLength,
+ &sign,
+ &length,
+ &point);
+
+ // length does not account for the 0 terminator.
+ if (length > point && (length+1) < (aBufferLength-1)) {
+ // We have to insert a decimal point. Not worried about adding a leading zero
+ // in the < 1 (point == 0) case.
+ aBuffer[length+1] = '\0';
+ for (i=length; i>point; i-=1) {
+ aBuffer[i] = aBuffer[i-1];
+ }
+ aBuffer[i] = '.'; // Not worried about locales
+ } else if (length < point) {
+ // Trailing zeros scenario
+ for (i=length; i<point; i+=1) {
+ if (i >= aBufferLength-2) {
+ ok = false;
+ }
+ aBuffer[i] = '0';
+ }
+ aBuffer[i] = '\0';
+ }
+ return ok;
+}
+
+namespace CrashReporter {
+
+#ifdef XP_LINUX
+inline void
+my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
+{
+ my_memset(buffer, 0, buffer_length);
+ my_uitos(buffer, t, my_uint_len(t));
+}
+#endif
+
+#ifdef XP_WIN
+static void
+CreateFileFromPath(const xpstring& path, nsIFile** file)
+{
+ NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
+}
+
+static void
+CreateFileFromPath(const wchar_t* path, nsIFile** file)
+{
+ CreateFileFromPath(std::wstring(path), file);
+}
+
+static xpstring*
+CreatePathFromFile(nsIFile* file)
+{
+ nsAutoString path;
+ nsresult rv = file->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ return new xpstring(static_cast<wchar_t*>(path.get()), path.Length());
+}
+#else
+static void
+CreateFileFromPath(const xpstring& path, nsIFile** file)
+{
+ NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
+}
+
+MAYBE_UNUSED static xpstring*
+CreatePathFromFile(nsIFile* file)
+{
+ nsAutoCString path;
+ nsresult rv = file->GetNativePath(path);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ return new xpstring(path.get(), path.Length());
+}
+#endif
+
+static XP_CHAR*
+Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
+{
+ int appendLen = XP_STRLEN(toAppend);
+ if (appendLen >= *size) appendLen = *size - 1;
+
+ memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
+ str += appendLen;
+ *str = '\0';
+ *size -= appendLen;
+
+ return str;
+}
+
+static size_t gOOMAllocationSize = 0;
+
+void AnnotateOOMAllocationSize(size_t size)
+{
+ gOOMAllocationSize = size;
+}
+
+static size_t gTexturesSize = 0;
+
+void AnnotateTexturesSize(size_t size)
+{
+ gTexturesSize = size;
+}
+
+static size_t gNumOfPendingIPC = 0;
+static uint32_t gTopPendingIPCCount = 0;
+static const char* gTopPendingIPCName = nullptr;
+static uint32_t gTopPendingIPCType = 0;
+
+void AnnotatePendingIPC(size_t aNumOfPendingIPC,
+ uint32_t aTopPendingIPCCount,
+ const char* aTopPendingIPCName,
+ uint32_t aTopPendingIPCType)
+{
+ gNumOfPendingIPC = aNumOfPendingIPC;
+ gTopPendingIPCCount = aTopPendingIPCCount;
+ gTopPendingIPCName = aTopPendingIPCName;
+ gTopPendingIPCType = aTopPendingIPCType;
+}
+
+#ifndef XP_WIN
+// Like Windows CopyFile for *nix
+bool copy_file(const char* from, const char* to)
+{
+ const int kBufSize = 4096;
+ int fdfrom = sys_open(from, O_RDONLY, 0);
+ if (fdfrom < 0) {
+ return false;
+ }
+
+ bool ok = false;
+
+ int fdto = sys_open(to, O_WRONLY | O_CREAT, 0666);
+ if (fdto < 0) {
+ sys_close(fdfrom);
+ return false;
+ }
+
+ char buf[kBufSize];
+ while (true) {
+ int r = sys_read(fdfrom, buf, kBufSize);
+ if (r == 0) {
+ ok = true;
+ break;
+ }
+ if (r < 0) {
+ break;
+ }
+ char* wbuf = buf;
+ while (r) {
+ int w = sys_write(fdto, wbuf, r);
+ if (w > 0) {
+ r -= w;
+ wbuf += w;
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
+ if (r) {
+ break;
+ }
+ }
+
+ sys_close(fdfrom);
+ sys_close(fdto);
+
+ return ok;
+}
+#endif
+
+#ifdef XP_WIN
+
+class PlatformWriter
+{
+public:
+ PlatformWriter()
+ : mHandle(INVALID_HANDLE_VALUE)
+ { }
+
+ explicit PlatformWriter(const wchar_t* path)
+ : PlatformWriter()
+ {
+ Open(path);
+ }
+
+ ~PlatformWriter() {
+ if (Valid()) {
+ CloseHandle(mHandle);
+ }
+ }
+
+ void Open(const wchar_t* path) {
+ mHandle = CreateFile(path, GENERIC_WRITE, 0,
+ nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ }
+
+ bool Valid() {
+ return mHandle != INVALID_HANDLE_VALUE;
+ }
+
+ void WriteBuffer(const char* buffer, size_t len)
+ {
+ if (!Valid()) {
+ return;
+ }
+ DWORD nBytes;
+ WriteFile(mHandle, buffer, len, &nBytes, nullptr);
+ }
+
+ HANDLE Handle() {
+ return mHandle;
+ }
+
+private:
+ HANDLE mHandle;
+};
+
+#elif defined(XP_UNIX)
+
+class PlatformWriter
+{
+public:
+ PlatformWriter()
+ : mFD(-1)
+ { }
+
+ explicit PlatformWriter(const char* path)
+ : PlatformWriter()
+ {
+ Open(path);
+ }
+
+ ~PlatformWriter() {
+ if (Valid()) {
+ sys_close(mFD);
+ }
+ }
+
+ void Open(const char* path) {
+ mFD = sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ }
+
+ bool Valid() {
+ return mFD != -1;
+ }
+
+ void WriteBuffer(const char* buffer, size_t len) {
+ if (!Valid()) {
+ return;
+ }
+ Unused << sys_write(mFD, buffer, len);
+ }
+
+private:
+ int mFD;
+};
+
+#else
+#error "Need implementation of PlatformWrite for this platform"
+#endif
+
+template<int N>
+void
+WriteLiteral(PlatformWriter& pw, const char (&str)[N])
+{
+ pw.WriteBuffer(str, N - 1);
+}
+
+static void
+WriteString(PlatformWriter& pw, const char* str) {
+#ifdef XP_LINUX
+ size_t len = my_strlen(str);
+#else
+ size_t len = strlen(str);
+#endif
+
+ pw.WriteBuffer(str, len);
+}
+
+template<int N>
+static void
+WriteAnnotation(PlatformWriter& pw, const char (&name)[N],
+ const char* value) {
+ WriteLiteral(pw, name);
+ WriteLiteral(pw, "=");
+ WriteString(pw, value);
+ WriteLiteral(pw, "\n");
+};
+
+/**
+ * If minidump_id is null, we assume that dump_path contains the full
+ * dump file path.
+ */
+static void
+OpenAPIData(PlatformWriter& aWriter,
+ const XP_CHAR* dump_path, const XP_CHAR* minidump_id = nullptr
+ )
+{
+ static XP_CHAR extraDataPath[XP_PATH_MAX];
+ int size = XP_PATH_MAX;
+ XP_CHAR* p;
+ if (minidump_id) {
+ p = Concat(extraDataPath, dump_path, &size);
+ p = Concat(p, XP_PATH_SEPARATOR, &size);
+ p = Concat(p, minidump_id, &size);
+ } else {
+ p = Concat(extraDataPath, dump_path, &size);
+ // Skip back past the .dmp extension, if any.
+ if (*(p - 4) == XP_TEXT('.')) {
+ p -= 4;
+ size += 4;
+ }
+ }
+ Concat(p, extraFileExtension, &size);
+ aWriter.Open(extraDataPath);
+}
+
+#ifdef XP_WIN
+void
+WriteGlobalMemoryStatus(PlatformWriter* apiData, PlatformWriter* eventFile)
+{
+ char buffer[128];
+
+ // Try to get some information about memory.
+ MEMORYSTATUSEX statex;
+ statex.dwLength = sizeof(statex);
+ if (GlobalMemoryStatusEx(&statex)) {
+
+#define WRITE_STATEX_FIELD(field, name, conversionFunc) \
+ conversionFunc(statex.field, buffer, 10); \
+ if (apiData) { \
+ WriteAnnotation(*apiData, name, buffer); \
+ } \
+ if (eventFile) { \
+ WriteAnnotation(*eventFile, name, buffer); \
+ }
+
+ WRITE_STATEX_FIELD(dwMemoryLoad, "SystemMemoryUsePercentage", ltoa);
+ WRITE_STATEX_FIELD(ullTotalVirtual, "TotalVirtualMemory", _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailVirtual, "AvailableVirtualMemory", _ui64toa);
+ WRITE_STATEX_FIELD(ullTotalPageFile, "TotalPageFile", _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailPageFile, "AvailablePageFile", _ui64toa);
+ WRITE_STATEX_FIELD(ullTotalPhys, "TotalPhysicalMemory", _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailPhys, "AvailablePhysicalMemory", _ui64toa);
+
+#undef WRITE_STATEX_FIELD
+ }
+}
+#endif
+
+bool MinidumpCallback(
+#ifdef XP_LINUX
+ const MinidumpDescriptor& descriptor,
+#else
+ const XP_CHAR* dump_path,
+ const XP_CHAR* minidump_id,
+#endif
+ void* context,
+#ifdef XP_WIN32
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+#endif
+ bool succeeded)
+{
+ bool returnValue = showOSCrashReporter ? false : succeeded;
+
+ static XP_CHAR minidumpPath[XP_PATH_MAX];
+ int size = XP_PATH_MAX;
+ XP_CHAR* p;
+#ifndef XP_LINUX
+ p = Concat(minidumpPath, dump_path, &size);
+ p = Concat(p, XP_PATH_SEPARATOR, &size);
+ p = Concat(p, minidump_id, &size);
+ Concat(p, dumpFileExtension, &size);
+#else
+ Concat(minidumpPath, descriptor.path(), &size);
+#endif
+
+ static XP_CHAR memoryReportLocalPath[XP_PATH_MAX];
+ size = XP_PATH_MAX;
+#ifndef XP_LINUX
+ p = Concat(memoryReportLocalPath, dump_path, &size);
+ p = Concat(p, XP_PATH_SEPARATOR, &size);
+ p = Concat(p, minidump_id, &size);
+#else
+ p = Concat(memoryReportLocalPath, descriptor.path(), &size);
+ // Skip back past the .dmp extension
+ p -= 4;
+#endif
+ Concat(p, memoryReportExtension, &size);
+
+ if (memoryReportPath) {
+#ifdef XP_WIN
+ CopyFile(memoryReportPath, memoryReportLocalPath, false);
+#else
+ copy_file(memoryReportPath, memoryReportLocalPath);
+#endif
+ }
+
+ if (headlessClient) {
+ // Leave a marker indicating that there was a crash.
+ PlatformWriter markerFile(crashMarkerFilename);
+#if defined(XP_WIN)
+ markerFile.WriteBuffer(reinterpret_cast<const char*>(minidumpPath),
+ 2*wcslen(minidumpPath));
+#elif defined(XP_UNIX)
+ markerFile.WriteBuffer(minidumpPath, my_strlen(minidumpPath));
+#endif
+ }
+
+ char oomAllocationSizeBuffer[32] = "";
+ if (gOOMAllocationSize) {
+ XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
+ }
+
+ char texturesSizeBuffer[32] = "";
+ if (gTexturesSize) {
+ XP_STOA(gTexturesSize, texturesSizeBuffer, 10);
+ }
+
+ char numOfPendingIPCBuffer[32] = "";
+ char topPendingIPCCountBuffer[32] = "";
+ char topPendingIPCTypeBuffer[11] = "0x";
+ if (gNumOfPendingIPC) {
+ XP_STOA(gNumOfPendingIPC, numOfPendingIPCBuffer, 10);
+ if (gTopPendingIPCCount) {
+ XP_STOA(gTopPendingIPCCount, topPendingIPCCountBuffer, 10);
+ }
+ if (gTopPendingIPCType) {
+ XP_STOA(gTopPendingIPCType, &topPendingIPCTypeBuffer[2], 16);
+ }
+ }
+
+ // calculate time since last crash (if possible), and store
+ // the time of this crash.
+ time_t crashTime;
+#ifdef XP_LINUX
+ struct kernel_timeval tv;
+ sys_gettimeofday(&tv, nullptr);
+ crashTime = tv.tv_sec;
+#else
+ crashTime = time(nullptr);
+#endif
+ time_t timeSinceLastCrash = 0;
+ // stringified versions of the above
+ char crashTimeString[32];
+ char timeSinceLastCrashString[32];
+
+ XP_TTOA(crashTime, crashTimeString, 10);
+ if (lastCrashTime != 0) {
+ timeSinceLastCrash = crashTime - lastCrashTime;
+ XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
+ }
+ // write crash time to file
+ if (lastCrashTimeFilename[0] != 0) {
+ PlatformWriter lastCrashFile(lastCrashTimeFilename);
+ WriteString(lastCrashFile, crashTimeString);
+ }
+
+ bool ignored = false;
+ double uptimeTS = (TimeStamp::NowLoRes()-
+ TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+ char uptimeTSString[64];
+ SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
+
+ // Write crash event file.
+
+ // Minidump IDs are UUIDs (36) + NULL.
+ static char id_ascii[37];
+#ifdef XP_LINUX
+ const char * index = strrchr(descriptor.path(), '/');
+ MOZ_ASSERT(index);
+ MOZ_ASSERT(strlen(index) == 1 + 36 + 4); // "/" + UUID + ".dmp"
+ for (uint32_t i = 0; i < 36; i++) {
+ id_ascii[i] = *(index + 1 + i);
+ }
+#else
+ MOZ_ASSERT(XP_STRLEN(minidump_id) == 36);
+ for (uint32_t i = 0; i < 36; i++) {
+ id_ascii[i] = *((char *)(minidump_id + i));
+ }
+#endif
+
+ {
+ PlatformWriter apiData;
+ PlatformWriter eventFile;
+
+ if (eventsDirectory) {
+ static XP_CHAR crashEventPath[XP_PATH_MAX];
+ int size = XP_PATH_MAX;
+ XP_CHAR* p;
+ p = Concat(crashEventPath, eventsDirectory, &size);
+ p = Concat(p, XP_PATH_SEPARATOR, &size);
+#ifdef XP_LINUX
+ p = Concat(p, id_ascii, &size);
+#else
+ p = Concat(p, minidump_id, &size);
+#endif
+
+ eventFile.Open(crashEventPath);
+ WriteLiteral(eventFile, kCrashMainID);
+ WriteString(eventFile, crashTimeString);
+ WriteLiteral(eventFile, "\n");
+ WriteString(eventFile, id_ascii);
+ WriteLiteral(eventFile, "\n");
+ if (currentSessionId) {
+ WriteAnnotation(eventFile, "TelemetrySessionId", currentSessionId);
+ }
+ if (crashEventAPIData) {
+ eventFile.WriteBuffer(crashEventAPIData->get(), crashEventAPIData->Length());
+ }
+ }
+
+ if (!crashReporterAPIData->IsEmpty()) {
+ // write out API data
+#ifdef XP_LINUX
+ OpenAPIData(apiData, descriptor.path());
+#else
+ OpenAPIData(apiData, dump_path, minidump_id);
+#endif
+ apiData.WriteBuffer(crashReporterAPIData->get(), crashReporterAPIData->Length());
+ }
+ WriteAnnotation(apiData, "CrashTime", crashTimeString);
+ WriteAnnotation(eventFile, "CrashTime", crashTimeString);
+
+ WriteAnnotation(apiData, "UptimeTS", uptimeTSString);
+ WriteAnnotation(eventFile, "UptimeTS", uptimeTSString);
+
+ if (timeSinceLastCrash != 0) {
+ WriteAnnotation(apiData, "SecondsSinceLastCrash",
+ timeSinceLastCrashString);
+ WriteAnnotation(eventFile, "SecondsSinceLastCrash",
+ timeSinceLastCrashString);
+ }
+ if (isGarbageCollecting) {
+ WriteAnnotation(apiData, "IsGarbageCollecting",
+ isGarbageCollecting ? "1" : "0");
+ WriteAnnotation(eventFile, "IsGarbageCollecting",
+ isGarbageCollecting ? "1" : "0");
+ }
+
+ char buffer[128];
+
+ if (eventloopNestingLevel > 0) {
+ XP_STOA(eventloopNestingLevel, buffer, 10);
+ WriteAnnotation(apiData, "EventLoopNestingLevel", buffer);
+ WriteAnnotation(eventFile, "EventLoopNestingLevel", buffer);
+ }
+
+#ifdef XP_WIN
+ if (gBreakpadReservedVM) {
+ _ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
+ WriteAnnotation(apiData, "BreakpadReserveAddress", buffer);
+ _ui64toa(kReserveSize, buffer, 10);
+ WriteAnnotation(apiData, "BreakpadReserveSize", buffer);
+ }
+
+#ifdef HAS_DLL_BLOCKLIST
+ if (apiData.Valid()) {
+ DllBlocklist_WriteNotes(apiData.Handle());
+ DllBlocklist_WriteNotes(eventFile.Handle());
+ }
+#endif
+ WriteGlobalMemoryStatus(&apiData, &eventFile);
+#endif // XP_WIN
+
+ if (gMozCrashReason) {
+ WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
+ WriteAnnotation(eventFile, "MozCrashReason", gMozCrashReason);
+ }
+
+ if (oomAllocationSizeBuffer[0]) {
+ WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
+ WriteAnnotation(eventFile, "OOMAllocationSize", oomAllocationSizeBuffer);
+ }
+
+ if (texturesSizeBuffer[0]) {
+ WriteAnnotation(apiData, "TextureUsage", texturesSizeBuffer);
+ WriteAnnotation(eventFile, "TextureUsage", texturesSizeBuffer);
+ }
+
+ if (numOfPendingIPCBuffer[0]) {
+ WriteAnnotation(apiData, "NumberOfPendingIPC", numOfPendingIPCBuffer);
+ WriteAnnotation(eventFile, "NumberOfPendingIPC", numOfPendingIPCBuffer);
+ if (topPendingIPCCountBuffer[0]) {
+ WriteAnnotation(apiData, "TopPendingIPCCount", topPendingIPCCountBuffer);
+ WriteAnnotation(eventFile, "TopPendingIPCCount", topPendingIPCCountBuffer);
+ }
+ if (gTopPendingIPCName) {
+ WriteAnnotation(apiData, "TopPendingIPCName", gTopPendingIPCName);
+ WriteAnnotation(eventFile, "TopPendingIPCName", gTopPendingIPCName);
+ }
+ if (topPendingIPCTypeBuffer[2]) {
+ WriteAnnotation(apiData, "TopPendingIPCType", topPendingIPCTypeBuffer);
+ WriteAnnotation(eventFile, "TopPendingIPCType", topPendingIPCTypeBuffer);
+ }
+ }
+
+ if (memoryReportPath) {
+ WriteLiteral(apiData, "ContainsMemoryReport=1\n");
+ WriteLiteral(eventFile, "ContainsMemoryReport=1\n");
+ }
+ }
+
+#ifdef XP_WIN
+ if (!doReport) {
+ TerminateProcess(GetCurrentProcess(), 1);
+ return returnValue;
+ }
+
+ XP_CHAR cmdLine[CMDLINE_SIZE];
+ size = CMDLINE_SIZE;
+ p = Concat(cmdLine, L"\"", &size);
+ p = Concat(p, crashReporterPath, &size);
+ p = Concat(p, L"\" \"", &size);
+ p = Concat(p, minidumpPath, &size);
+ Concat(p, L"\"", &size);
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWNORMAL;
+ ZeroMemory(&pi, sizeof(pi));
+
+ if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
+ nullptr, nullptr, &si, &pi)) {
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+ }
+ // we're not really in a position to do anything if the CreateProcess fails
+ TerminateProcess(GetCurrentProcess(), 1);
+#elif defined(XP_UNIX)
+ if (!doReport) {
+ return returnValue;
+ }
+
+#ifdef XP_MACOSX
+ char* const my_argv[] = {
+ crashReporterPath,
+ minidumpPath,
+ nullptr
+ };
+
+ char **env = nullptr;
+ char ***nsEnv = _NSGetEnviron();
+ if (nsEnv)
+ env = *nsEnv;
+ int result = posix_spawnp(nullptr,
+ my_argv[0],
+ nullptr,
+ &spawnattr,
+ my_argv,
+ env);
+
+ if (result != 0)
+ return false;
+
+#else // !XP_MACOSX
+ pid_t pid = sys_fork();
+
+ if (pid == -1)
+ return false;
+ else if (pid == 0) {
+#if !defined(MOZ_WIDGET_ANDROID)
+ // need to clobber this, as libcurl might load NSS,
+ // and we want it to load the system NSS.
+ unsetenv("LD_LIBRARY_PATH");
+ Unused << execl(crashReporterPath,
+ crashReporterPath, minidumpPath, (char*)0);
+#else
+ // Invoke the reportCrash activity using am
+ if (androidUserSerial) {
+ Unused << execlp("/system/bin/am",
+ "/system/bin/am",
+ "start",
+ "--user", androidUserSerial,
+ "-a", "org.mozilla.gecko.reportCrash",
+ "-n", crashReporterPath,
+ "--es", "minidumpPath", minidumpPath,
+ "--ez", "minidumpSuccess", succeeded ? "true" : "false",
+ (char*)0);
+ } else {
+ Unused << execlp("/system/bin/am",
+ "/system/bin/am",
+ "start",
+ "-a", "org.mozilla.gecko.reportCrash",
+ "-n", crashReporterPath,
+ "--es", "minidumpPath", minidumpPath,
+ "--ez", "minidumpSuccess", succeeded ? "true" : "false",
+ (char*)0);
+ }
+#endif
+ _exit(1);
+#ifdef MOZ_WIDGET_ANDROID
+ } else {
+ // We need to wait on the 'am start' command above to finish, otherwise everything will
+ // be killed by the ActivityManager as soon as the signal handler exits
+ int status;
+ Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
+#endif
+ }
+#endif // XP_MACOSX
+#endif // XP_UNIX
+
+ return returnValue;
+}
+
+#if defined(XP_MACOSX) || defined(__ANDROID__) || defined(XP_LINUX)
+static size_t
+EnsureTrailingSlash(XP_CHAR* aBuf, size_t aBufLen)
+{
+ size_t len = XP_STRLEN(aBuf);
+ if ((len + 1) < aBufLen
+ && len > 0
+ && aBuf[len - 1] != XP_PATH_SEPARATOR_CHAR) {
+ aBuf[len] = XP_PATH_SEPARATOR_CHAR;
+ ++len;
+ aBuf[len] = 0;
+ }
+ return len;
+}
+#endif
+
+#if defined(XP_WIN32)
+
+static size_t
+BuildTempPath(wchar_t* aBuf, size_t aBufLen)
+{
+ // first figure out buffer size
+ DWORD pathLen = GetTempPath(0, nullptr);
+ if (pathLen == 0 || pathLen >= aBufLen) {
+ return 0;
+ }
+
+ return GetTempPath(pathLen, aBuf);
+}
+
+static size_t
+BuildTempPath(char16_t* aBuf, size_t aBufLen)
+{
+ return BuildTempPath(reinterpret_cast<wchar_t*>(aBuf), aBufLen);
+}
+
+#elif defined(XP_MACOSX)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+ if (aBufLen < PATH_MAX) {
+ return 0;
+ }
+
+ FSRef fsRef;
+ OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
+ kCreateFolder, &fsRef);
+ if (err != noErr) {
+ return 0;
+ }
+
+ OSStatus status = FSRefMakePath(&fsRef, (UInt8*)aBuf, PATH_MAX);
+ if (status != noErr) {
+ return 0;
+ }
+
+ return EnsureTrailingSlash(aBuf, aBufLen);
+}
+
+#elif defined(__ANDROID__)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+ // GeckoAppShell or Gonk's init.rc sets this in the environment
+ const char *tempenv = PR_GetEnv("TMPDIR");
+ if (!tempenv) {
+ return false;
+ }
+ int size = (int)aBufLen;
+ Concat(aBuf, tempenv, &size);
+ return EnsureTrailingSlash(aBuf, aBufLen);
+}
+
+#elif defined(XP_UNIX)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+ const char *tempenv = PR_GetEnv("TMPDIR");
+ const char *tmpPath = "/tmp/";
+ if (!tempenv) {
+ tempenv = tmpPath;
+ }
+ int size = (int)aBufLen;
+ Concat(aBuf, tempenv, &size);
+ return EnsureTrailingSlash(aBuf, aBufLen);
+}
+
+#else
+#error "Implement this for your platform"
+#endif
+
+template <typename CharT, size_t N>
+static size_t
+BuildTempPath(CharT (&aBuf)[N])
+{
+ static_assert(N >= XP_PATH_MAX, "char array length is too small");
+ return BuildTempPath(&aBuf[0], N);
+}
+
+template <typename PathStringT>
+static bool
+BuildTempPath(PathStringT& aResult)
+{
+ aResult.SetLength(XP_PATH_MAX);
+ size_t actualLen = BuildTempPath(aResult.BeginWriting(), XP_PATH_MAX);
+ if (!actualLen) {
+ return false;
+ }
+ aResult.SetLength(actualLen);
+ return true;
+}
+
+static void
+PrepareChildExceptionTimeAnnotations()
+{
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ static XP_CHAR tempPath[XP_PATH_MAX] = {0};
+
+ // Get the temp path
+ int charsAvailable = XP_PATH_MAX;
+ XP_CHAR* p = tempPath;
+#if (defined(XP_MACOSX) || defined(XP_WIN))
+ if (!childProcessTmpDir || childProcessTmpDir->empty()) {
+ return;
+ }
+ p = Concat(p, childProcessTmpDir->c_str(), &charsAvailable);
+ // Ensure that this path ends with a path separator
+ if (p > tempPath && *(p - 1) != XP_PATH_SEPARATOR_CHAR) {
+ p = Concat(p, XP_PATH_SEPARATOR, &charsAvailable);
+ }
+#else
+ size_t tempPathLen = BuildTempPath(tempPath);
+ if (!tempPathLen) {
+ return;
+ }
+ p += tempPathLen;
+ charsAvailable -= tempPathLen;
+#endif
+
+ // Generate and append the file name
+ p = Concat(p, childCrashAnnotationBaseName, &charsAvailable);
+ XP_CHAR pidBuffer[32] = XP_TEXT("");
+#if defined(XP_WIN32)
+ _ui64tow(GetCurrentProcessId(), pidBuffer, 10);
+#else
+ XP_STOA(getpid(), pidBuffer, 10);
+#endif
+ p = Concat(p, pidBuffer, &charsAvailable);
+
+ // Now open the file...
+ PlatformWriter apiData;
+ OpenAPIData(apiData, tempPath);
+
+ // ...and write out any annotations. These must be escaped if necessary
+ // (but don't call EscapeAnnotation here, because it touches the heap).
+#ifdef XP_WIN
+ WriteGlobalMemoryStatus(&apiData, nullptr);
+#endif
+
+ char oomAllocationSizeBuffer[32] = "";
+ if (gOOMAllocationSize) {
+ XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
+ }
+
+ if (oomAllocationSizeBuffer[0]) {
+ WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
+ }
+
+ if (gMozCrashReason) {
+ WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
+ }
+
+ char numOfPendingIPCBuffer[32] = "";
+ char topPendingIPCCountBuffer[32] = "";
+ char topPendingIPCTypeBuffer[11] = "0x";
+ if (gNumOfPendingIPC) {
+ XP_STOA(gNumOfPendingIPC, numOfPendingIPCBuffer, 10);
+ if (gTopPendingIPCCount) {
+ XP_STOA(gTopPendingIPCCount, topPendingIPCCountBuffer, 10);
+ }
+ if (gTopPendingIPCType) {
+ XP_STOA(gTopPendingIPCType, &topPendingIPCTypeBuffer[2], 16);
+ }
+ }
+
+ if (numOfPendingIPCBuffer[0]) {
+ WriteAnnotation(apiData, "NumberOfPendingIPC", numOfPendingIPCBuffer);
+ if (topPendingIPCCountBuffer[0]) {
+ WriteAnnotation(apiData, "TopPendingIPCCount", topPendingIPCCountBuffer);
+ }
+ if (gTopPendingIPCName) {
+ WriteAnnotation(apiData, "TopPendingIPCName", gTopPendingIPCName);
+ }
+ if (topPendingIPCTypeBuffer[2]) {
+ WriteAnnotation(apiData, "TopPendingIPCType", topPendingIPCTypeBuffer);
+ }
+ }
+}
+
+#ifdef XP_WIN
+static void
+ReserveBreakpadVM()
+{
+ if (!gBreakpadReservedVM) {
+ gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
+ PAGE_NOACCESS);
+ }
+}
+
+static void
+FreeBreakpadVM()
+{
+ if (gBreakpadReservedVM) {
+ VirtualFree(gBreakpadReservedVM, 0, MEM_RELEASE);
+ }
+}
+
+/**
+ * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
+ * and should not be handled as crashes.
+ *
+ * Also calls FreeBreakpadVM if appropriate.
+ */
+static bool FPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion)
+{
+ if (!exinfo) {
+ mozilla::IOInterposer::Disable();
+ FreeBreakpadVM();
+ return true;
+ }
+
+ PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)exinfo->ExceptionRecord;
+ switch (e->ExceptionCode) {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INEXACT_RESULT:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_OVERFLOW:
+ case STATUS_FLOAT_STACK_CHECK:
+ case STATUS_FLOAT_UNDERFLOW:
+ case STATUS_FLOAT_MULTIPLE_FAULTS:
+ case STATUS_FLOAT_MULTIPLE_TRAPS:
+ return false; // Don't write minidump, continue exception search
+ }
+ mozilla::IOInterposer::Disable();
+ FreeBreakpadVM();
+ return true;
+}
+
+static bool
+ChildFPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion)
+{
+ bool result = FPEFilter(context, exinfo, assertion);
+ if (result) {
+ PrepareChildExceptionTimeAnnotations();
+ }
+ return result;
+}
+
+MINIDUMP_TYPE GetMinidumpType()
+{
+ MINIDUMP_TYPE minidump_type = MiniDumpNormal;
+
+ // Try to determine what version of dbghelp.dll we're using.
+ // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
+
+ DWORD version_size = GetFileVersionInfoSizeW(L"dbghelp.dll", nullptr);
+ if (version_size > 0) {
+ std::vector<BYTE> buffer(version_size);
+ if (GetFileVersionInfoW(L"dbghelp.dll",
+ 0,
+ version_size,
+ &buffer[0])) {
+ UINT len;
+ VS_FIXEDFILEINFO* file_info;
+ VerQueryValue(&buffer[0], L"\\", (void**)&file_info, &len);
+ WORD major = HIWORD(file_info->dwFileVersionMS),
+ minor = LOWORD(file_info->dwFileVersionMS),
+ revision = HIWORD(file_info->dwFileVersionLS);
+ if (major > 6 || (major == 6 && minor > 1) ||
+ (major == 6 && minor == 1 && revision >= 7600)) {
+ minidump_type = MiniDumpWithFullMemoryInfo;
+ }
+ }
+ }
+
+ const char* e = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
+ if (e && *e) {
+ minidump_type = MiniDumpWithFullMemory;
+ }
+
+ return minidump_type;
+}
+
+#endif // XP_WIN
+
+static bool ShouldReport()
+{
+ // this environment variable prevents us from launching
+ // the crash reporter client
+ const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
+ if (envvar && *envvar) {
+ return false;
+ }
+
+ envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
+ if (envvar && *envvar) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+Filter(void* context)
+{
+ mozilla::IOInterposer::Disable();
+ return true;
+}
+
+static bool
+ChildFilter(void* context)
+{
+ bool result = Filter(context);
+ if (result) {
+ PrepareChildExceptionTimeAnnotations();
+ }
+ return result;
+}
+
+void TerminateHandler()
+{
+ MOZ_CRASH("Unhandled exception");
+}
+
+nsresult SetExceptionHandler(nsIFile* aXREDirectory,
+ bool force/*=false*/)
+{
+ if (gExceptionHandler)
+ return NS_ERROR_ALREADY_INITIALIZED;
+
+#if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
+ // In non-debug builds, enable the crash reporter by default, and allow
+ // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
+ // Also enable it by default in debug gonk builds as it is difficult to
+ // set environment on startup.
+ const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
+ if (envvar && *envvar && !force)
+ return NS_OK;
+#else
+ // In debug builds, disable the crash reporter by default, and allow to
+ // enable it with the MOZ_CRASHREPORTER environment variable.
+ const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER");
+ if ((!envvar || !*envvar) && !force)
+ return NS_OK;
+#endif
+
+#if defined(MOZ_WIDGET_GONK)
+ doReport = false;
+ headlessClient = true;
+#elif defined(XP_WIN)
+ doReport = ShouldReport();
+#else
+ // this environment variable prevents us from launching
+ // the crash reporter client
+ doReport = ShouldReport();
+#endif
+
+ // allocate our strings
+ crashReporterAPIData = new nsCString();
+ crashEventAPIData = new nsCString();
+
+ NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
+ crashReporterAPILock = new Mutex("crashReporterAPILock");
+ NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
+ notesFieldLock = new Mutex("notesFieldLock");
+
+ crashReporterAPIData_Hash =
+ new nsDataHashtable<nsCStringHashKey,nsCString>();
+ NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
+
+ notesField = new nsCString();
+ NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
+
+ if (!headlessClient) {
+ // locate crashreporter executable
+ nsCOMPtr<nsIFile> exePath;
+ nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#if defined(XP_MACOSX)
+ exePath->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
+ exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
+ exePath->Append(NS_LITERAL_STRING("Contents"));
+ exePath->Append(NS_LITERAL_STRING("MacOS"));
+#endif
+
+ exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
+
+#ifdef XP_WIN32
+ nsString crashReporterPath_temp;
+
+ exePath->GetPath(crashReporterPath_temp);
+ crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
+#elif !defined(__ANDROID__)
+ nsCString crashReporterPath_temp;
+
+ exePath->GetNativePath(crashReporterPath_temp);
+ crashReporterPath = ToNewCString(crashReporterPath_temp);
+#else
+ // On Android, we launch using the application package name instead of a
+ // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
+ // back to the static ANDROID_PACKAGE_NAME.
+ const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
+ if (androidPackageName != nullptr) {
+ nsCString package(androidPackageName);
+ package.Append("/org.mozilla.gecko.CrashReporter");
+ crashReporterPath = ToNewCString(package);
+ } else {
+ nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
+ crashReporterPath = ToNewCString(package);
+ }
+#endif
+ }
+
+ // get temp path to use for minidump path
+#if defined(XP_WIN32)
+ nsString tempPath;
+#else
+ nsCString tempPath;
+#endif
+ if (!BuildTempPath(tempPath)) {
+ return NS_ERROR_FAILURE;
+ }
+
+#ifdef XP_MACOSX
+ // Initialize spawn attributes, since this calls malloc.
+ if (posix_spawnattr_init(&spawnattr) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set spawn attributes.
+ size_t attr_count = ArrayLength(pref_cpu_types);
+ size_t attr_ocount = 0;
+ if (posix_spawnattr_setbinpref_np(&spawnattr,
+ attr_count,
+ pref_cpu_types,
+ &attr_ocount) != 0 ||
+ attr_ocount != attr_count) {
+ posix_spawnattr_destroy(&spawnattr);
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#ifdef XP_WIN32
+ ReserveBreakpadVM();
+#endif // XP_WIN32
+
+#ifdef MOZ_WIDGET_ANDROID
+ androidUserSerial = getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
+#endif
+
+ // Initialize the flag and mutex used to avoid dump processing
+ // once browser termination has begun.
+ NS_ASSERTION(!dumpSafetyLock, "Shouldn't have a lock yet");
+ // Do not deallocate this lock while it is still possible for
+ // isSafeToDump to be tested on another thread.
+ dumpSafetyLock = new Mutex("dumpSafetyLock");
+ MutexAutoLock lock(*dumpSafetyLock);
+ isSafeToDump = true;
+
+ // now set the exception handler
+#ifdef XP_LINUX
+ MinidumpDescriptor descriptor(tempPath.get());
+#endif
+
+#ifdef XP_WIN
+ previousUnhandledExceptionFilter = GetUnhandledExceptionFilter();
+#endif
+
+ gExceptionHandler = new google_breakpad::
+ ExceptionHandler(
+#ifdef XP_LINUX
+ descriptor,
+#elif defined(XP_WIN)
+ std::wstring(tempPath.get()),
+#else
+ tempPath.get(),
+#endif
+
+#ifdef XP_WIN
+ FPEFilter,
+#else
+ Filter,
+#endif
+ MinidumpCallback,
+ nullptr,
+#ifdef XP_WIN32
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ GetMinidumpType(),
+ (const wchar_t*) nullptr,
+ nullptr);
+#else
+ true
+#ifdef XP_MACOSX
+ , nullptr
+#endif
+#ifdef XP_LINUX
+ , -1
+#endif
+ );
+#endif // XP_WIN32
+
+ if (!gExceptionHandler)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+#ifdef XP_WIN
+ gExceptionHandler->set_handle_debug_exceptions(true);
+
+#ifdef _WIN64
+ // Tell JS about the new filter before we disable SetUnhandledExceptionFilter
+ sUnhandledExceptionFilter = GetUnhandledExceptionFilter();
+ if (sUnhandledExceptionFilter)
+ js::SetJitExceptionHandler(JitExceptionHandler);
+#endif
+
+ // protect the crash reporter from being unloaded
+ gBlockUnhandledExceptionFilter = true;
+ gKernel32Intercept.Init("kernel32.dll");
+ bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
+ reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
+ (void**) &stub_SetUnhandledExceptionFilter);
+
+#ifdef DEBUG
+ if (!ok)
+ printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
+#endif
+#endif
+
+ // store application start time
+ char timeString[32];
+ time_t startupTime = time(nullptr);
+ XP_TTOA(startupTime, timeString, 10);
+ AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
+ nsDependentCString(timeString));
+
+#if defined(XP_MACOSX)
+ // On OS X, many testers like to see the OS crash reporting dialog
+ // since it offers immediate stack traces. We allow them to set
+ // a default to pass exceptions to the OS handler.
+ Boolean keyExistsAndHasValidFormat = false;
+ Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
+ kCFPreferencesCurrentApplication,
+ &keyExistsAndHasValidFormat);
+ if (keyExistsAndHasValidFormat)
+ showOSCrashReporter = prefValue;
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID)
+ for (unsigned int i = 0; i < library_mappings.size(); i++) {
+ PageAllocator allocator;
+ auto_wasteful_vector<uint8_t, sizeof(MDGUID)> guid(&allocator);
+ FileID::ElfFileIdentifierFromMappedFile(
+ (void const *)library_mappings[i].start_address, guid);
+ gExceptionHandler->AddMappingInfo(library_mappings[i].name,
+ guid.data(),
+ library_mappings[i].start_address,
+ library_mappings[i].length,
+ library_mappings[i].file_offset);
+ }
+#endif
+
+ mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
+ oldTerminateHandler = std::set_terminate(&TerminateHandler);
+
+ return NS_OK;
+}
+
+bool GetEnabled()
+{
+ return gExceptionHandler != nullptr;
+}
+
+bool GetMinidumpPath(nsAString& aPath)
+{
+ if (!gExceptionHandler)
+ return false;
+
+#ifndef XP_LINUX
+ aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
+#else
+ aPath = CONVERT_XP_CHAR_TO_UTF16(
+ gExceptionHandler->minidump_descriptor().directory().c_str());
+#endif
+ return true;
+}
+
+nsresult SetMinidumpPath(const nsAString& aPath)
+{
+ if (!gExceptionHandler)
+ return NS_ERROR_NOT_INITIALIZED;
+
+#ifdef XP_WIN32
+ gExceptionHandler->set_dump_path(std::wstring(char16ptr_t(aPath.BeginReading())));
+#elif defined(XP_LINUX)
+ gExceptionHandler->set_minidump_descriptor(
+ MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath).BeginReading()));
+#else
+ gExceptionHandler->set_dump_path(NS_ConvertUTF16toUTF8(aPath).BeginReading());
+#endif
+ return NS_OK;
+}
+
+static nsresult
+WriteDataToFile(nsIFile* aFile, const nsACString& data)
+{
+ PRFileDesc* fd;
+ nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_OK;
+ if (PR_Write(fd, data.Data(), data.Length()) == -1) {
+ rv = NS_ERROR_FAILURE;
+ }
+ PR_Close(fd);
+ return rv;
+}
+
+static nsresult
+GetFileContents(nsIFile* aFile, nsACString& data)
+{
+ PRFileDesc* fd;
+ nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_OK;
+ int32_t filesize = PR_Available(fd);
+ if (filesize <= 0) {
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ }
+ else {
+ data.SetLength(filesize);
+ if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+ PR_Close(fd);
+ return rv;
+}
+
+// Function typedef for initializing a piece of data that we
+// don't already have.
+typedef nsresult (*InitDataFunc)(nsACString&);
+
+// Attempt to read aFile's contents into aContents, if aFile
+// does not exist, create it and initialize its contents
+// by calling aInitFunc for the data.
+static nsresult
+GetOrInit(nsIFile* aDir, const nsACString& filename,
+ nsACString& aContents, InitDataFunc aInitFunc)
+{
+ bool exists;
+
+ nsCOMPtr<nsIFile> dataFile;
+ nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = dataFile->AppendNative(filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = dataFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exists) {
+ if (aInitFunc) {
+ // get the initial value and write it to the file
+ rv = aInitFunc(aContents);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = WriteDataToFile(dataFile, aContents);
+ }
+ else {
+ // didn't pass in an init func
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+ else {
+ // just get the file's contents
+ rv = GetFileContents(dataFile, aContents);
+ }
+
+ return rv;
+}
+
+// Init the "install time" data. We're taking an easy way out here
+// and just setting this to "the time when this version was first run".
+static nsresult
+InitInstallTime(nsACString& aInstallTime)
+{
+ time_t t = time(nullptr);
+ char buf[16];
+ SprintfLiteral(buf, "%ld", t);
+ aInstallTime = buf;
+
+ return NS_OK;
+}
+
+// Ensure a directory exists and create it if missing.
+static nsresult
+EnsureDirectoryExists(nsIFile* dir)
+{
+ nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+
+ if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+// Annotate the crash report with a Unique User ID and time
+// since install. Also do some prep work for recording
+// time since last crash, which must be calculated at
+// crash time.
+// If any piece of data doesn't exist, initialize it first.
+nsresult SetupExtraData(nsIFile* aAppDataDirectory,
+ const nsACString& aBuildID)
+{
+ nsCOMPtr<nsIFile> dataDirectory;
+ nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ EnsureDirectoryExists(dataDirectory);
+
+#if defined(XP_WIN32)
+ nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
+
+ nsAutoString dataDirectoryPath;
+ rv = dataDirectory->GetPath(dataDirectoryPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dataDirEnv.Append(dataDirectoryPath);
+
+ _wputenv(dataDirEnv.get());
+#else
+ // Save this path in the environment for the crash reporter application.
+ nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
+
+ nsAutoCString dataDirectoryPath;
+ rv = dataDirectory->GetNativePath(dataDirectoryPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dataDirEnv.Append(dataDirectoryPath);
+
+ char* env = ToNewCString(dataDirEnv);
+ NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
+
+ PR_SetEnv(env);
+#endif
+
+ nsAutoCString data;
+ if(NS_SUCCEEDED(GetOrInit(dataDirectory,
+ NS_LITERAL_CSTRING("InstallTime") + aBuildID,
+ data, InitInstallTime)))
+ AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
+
+ // this is a little different, since we can't init it with anything,
+ // since it's stored at crash time, and we can't annotate the
+ // crash report with the stored value, since we really want
+ // (now - LastCrash), so we just get a value if it exists,
+ // and store it in a time_t value.
+ if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
+ data, nullptr))) {
+ lastCrashTime = (time_t)atol(data.get());
+ }
+
+ // not really the best place to init this, but I have the path I need here
+ nsCOMPtr<nsIFile> lastCrashFile;
+ rv = dataDirectory->Clone(getter_AddRefs(lastCrashFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = lastCrashFile->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ memset(lastCrashTimeFilename, 0, sizeof(lastCrashTimeFilename));
+
+#if defined(XP_WIN32)
+ nsAutoString filename;
+ rv = lastCrashFile->GetPath(filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filename.Length() < XP_PATH_MAX)
+ wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
+#else
+ nsAutoCString filename;
+ rv = lastCrashFile->GetNativePath(filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filename.Length() < XP_PATH_MAX)
+ strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
+#endif
+
+ if (headlessClient) {
+ nsCOMPtr<nsIFile> markerFile;
+ rv = dataDirectory->Clone(getter_AddRefs(markerFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
+
+#if defined(XP_WIN32)
+ nsAutoString markerFilename;
+ rv = markerFile->GetPath(markerFilename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (markerFilename.Length() < XP_PATH_MAX)
+ wcsncpy(crashMarkerFilename, markerFilename.get(),
+ markerFilename.Length());
+#else
+ nsAutoCString markerFilename;
+ rv = markerFile->GetNativePath(markerFilename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (markerFilename.Length() < XP_PATH_MAX)
+ strncpy(crashMarkerFilename, markerFilename.get(),
+ markerFilename.Length());
+#endif
+ }
+
+ return NS_OK;
+}
+
+static void OOPDeinit();
+
+nsresult UnsetExceptionHandler()
+{
+ if (isSafeToDump) {
+ MutexAutoLock lock(*dumpSafetyLock);
+ isSafeToDump = false;
+ }
+
+#ifdef XP_WIN
+ // allow SetUnhandledExceptionFilter
+ gBlockUnhandledExceptionFilter = false;
+#endif
+
+ delete gExceptionHandler;
+
+ // do this here in the unlikely case that we succeeded in allocating
+ // our strings but failed to allocate gExceptionHandler.
+ delete crashReporterAPIData_Hash;
+ crashReporterAPIData_Hash = nullptr;
+
+ delete crashReporterAPILock;
+ crashReporterAPILock = nullptr;
+
+ delete notesFieldLock;
+ notesFieldLock = nullptr;
+
+ delete crashReporterAPIData;
+ crashReporterAPIData = nullptr;
+
+ delete crashEventAPIData;
+ crashEventAPIData = nullptr;
+
+ delete notesField;
+ notesField = nullptr;
+
+ delete lastRunCrashID;
+ lastRunCrashID = nullptr;
+
+ if (pendingDirectory) {
+ free(pendingDirectory);
+ pendingDirectory = nullptr;
+ }
+
+ if (crashReporterPath) {
+ free(crashReporterPath);
+ crashReporterPath = nullptr;
+ }
+
+ if (eventsDirectory) {
+ free(eventsDirectory);
+ eventsDirectory = nullptr;
+ }
+
+ if (currentSessionId) {
+ free(currentSessionId);
+ currentSessionId = nullptr;
+ }
+
+ if (memoryReportPath) {
+ free(memoryReportPath);
+ memoryReportPath = nullptr;
+ }
+
+#ifdef XP_MACOSX
+ posix_spawnattr_destroy(&spawnattr);
+#endif
+
+ if (!gExceptionHandler)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ gExceptionHandler = nullptr;
+
+ OOPDeinit();
+
+ delete dumpSafetyLock;
+ dumpSafetyLock = nullptr;
+
+ std::set_terminate(oldTerminateHandler);
+
+ return NS_OK;
+}
+
+static void ReplaceChar(nsCString& str, const nsACString& character,
+ const nsACString& replacement)
+{
+ nsCString::const_iterator iter, end;
+
+ str.BeginReading(iter);
+ str.EndReading(end);
+
+ while (FindInReadable(character, iter, end)) {
+ nsCString::const_iterator start;
+ str.BeginReading(start);
+ int32_t pos = end - start;
+ str.Replace(pos - 1, 1, replacement);
+
+ str.BeginReading(iter);
+ iter.advance(pos + replacement.Length() - 1);
+ str.EndReading(end);
+ }
+}
+
+static bool DoFindInReadable(const nsACString& str, const nsACString& value)
+{
+ nsACString::const_iterator start, end;
+ str.BeginReading(start);
+ str.EndReading(end);
+
+ return FindInReadable(value, start, end);
+}
+
+static bool
+IsInWhitelist(const nsACString& key)
+{
+ for (size_t i = 0; i < ArrayLength(kCrashEventAnnotations); ++i) {
+ if (key.EqualsASCII(kCrashEventAnnotations[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// This function is miscompiled with MSVC 2005/2008 when PGO is on.
+#ifdef _MSC_VER
+#pragma optimize("", off)
+#endif
+static nsresult
+EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
+{
+ if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
+ DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
+ return NS_ERROR_INVALID_ARG;
+
+ if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
+ return NS_ERROR_INVALID_ARG;
+
+ escapedData = data;
+
+ // escape backslashes
+ ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
+ NS_LITERAL_CSTRING("\\\\"));
+ // escape newlines
+ ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
+ NS_LITERAL_CSTRING("\\n"));
+ return NS_OK;
+}
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif
+
+class DelayedNote
+{
+ public:
+ DelayedNote(const nsACString& aKey, const nsACString& aData)
+ : mKey(aKey), mData(aData), mType(Annotation) {}
+
+ explicit DelayedNote(const nsACString& aData)
+ : mData(aData), mType(AppNote) {}
+
+ void Run()
+ {
+ if (mType == Annotation) {
+ AnnotateCrashReport(mKey, mData);
+ } else {
+ AppendAppNotesToCrashReport(mData);
+ }
+ }
+
+ private:
+ nsCString mKey;
+ nsCString mData;
+ enum AnnotationType { Annotation, AppNote } mType;
+};
+
+static void
+EnqueueDelayedNote(DelayedNote* aNote)
+{
+ if (!gDelayedAnnotations) {
+ gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
+ }
+ gDelayedAnnotations->AppendElement(aNote);
+}
+
+class CrashReporterHelperRunnable : public Runnable {
+public:
+ explicit CrashReporterHelperRunnable(const nsACString& aKey,
+ const nsACString& aData)
+ : mKey(aKey)
+ , mData(aData)
+ , mAppendAppNotes(false)
+ {}
+ explicit CrashReporterHelperRunnable(const nsACString& aData)
+ : mKey()
+ , mData(aData)
+ , mAppendAppNotes(true)
+ {}
+
+ NS_IMETHOD Run() override;
+
+private:
+ nsCString mKey;
+ nsCString mData;
+ bool mAppendAppNotes;
+};
+
+nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCString escapedData;
+ nsresult rv = EscapeAnnotation(key, data, escapedData);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!XRE_IsParentProcess()) {
+ // The newer CrashReporterClient can be used from any thread.
+ if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
+ client->AnnotateCrashReport(nsCString(key), escapedData);
+ return NS_OK;
+ }
+
+ // Otherwise, we have to handle this on the main thread since we will go
+ // through IPDL.
+ if (!NS_IsMainThread()) {
+ // Child process needs to handle this in the main thread:
+ nsCOMPtr<nsIRunnable> r = new CrashReporterHelperRunnable(key, data);
+ NS_DispatchToMainThread(r);
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+ PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
+ if (!reporter) {
+ EnqueueDelayedNote(new DelayedNote(key, data));
+ return NS_OK;
+ }
+ if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+ }
+
+ MutexAutoLock lock(*crashReporterAPILock);
+
+ crashReporterAPIData_Hash->Put(key, escapedData);
+
+ // now rebuild the file contents
+ crashReporterAPIData->Truncate(0);
+ crashEventAPIData->Truncate(0);
+ for (auto it = crashReporterAPIData_Hash->Iter(); !it.Done(); it.Next()) {
+ const nsACString& key = it.Key();
+ nsCString entry = it.Data();
+ if (!entry.IsEmpty()) {
+ NS_NAMED_LITERAL_CSTRING(kEquals, "=");
+ NS_NAMED_LITERAL_CSTRING(kNewline, "\n");
+ nsAutoCString line = key + kEquals + entry + kNewline;
+
+ crashReporterAPIData->Append(line);
+ if (IsInWhitelist(key)) {
+ crashEventAPIData->Append(line);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult RemoveCrashReportAnnotation(const nsACString& key)
+{
+ return AnnotateCrashReport(key, NS_LITERAL_CSTRING(""));
+}
+
+nsresult SetGarbageCollecting(bool collecting)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ isGarbageCollecting = collecting;
+
+ return NS_OK;
+}
+
+void SetEventloopNestingLevel(uint32_t level)
+{
+ eventloopNestingLevel = level;
+}
+
+nsresult AppendAppNotesToCrashReport(const nsACString& data)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
+ return NS_ERROR_INVALID_ARG;
+
+ if (!XRE_IsParentProcess()) {
+ // Since we don't go through AnnotateCrashReport in the parent process,
+ // we must ensure that the data is escaped and valid before the parent
+ // sees it.
+ nsCString escapedData;
+ nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
+ client->AppendAppNotes(escapedData);
+ return NS_OK;
+ }
+
+ if (!NS_IsMainThread()) {
+ // Child process needs to handle this in the main thread:
+ nsCOMPtr<nsIRunnable> r = new CrashReporterHelperRunnable(data);
+ NS_DispatchToMainThread(r);
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+ PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
+ if (!reporter) {
+ EnqueueDelayedNote(new DelayedNote(data));
+ return NS_OK;
+ }
+
+ if (!reporter->SendAppendAppNotes(escapedData))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+ }
+
+ MutexAutoLock lock(*notesFieldLock);
+
+ notesField->Append(data);
+ return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
+}
+
+nsresult CrashReporterHelperRunnable::Run()
+{
+ // We expect this to be in the child process' main thread. If it isn't,
+ // something is happening we didn't design for.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Don't just leave the assert, paranoid about infinite recursion
+ if (NS_IsMainThread()) {
+ if (mAppendAppNotes) {
+ return AppendAppNotesToCrashReport(mData);
+ } else {
+ return AnnotateCrashReport(mKey, mData);
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+// Returns true if found, false if not found.
+bool GetAnnotation(const nsACString& key, nsACString& data)
+{
+ if (!gExceptionHandler)
+ return false;
+
+ nsAutoCString entry;
+ if (!crashReporterAPIData_Hash->Get(key, &entry))
+ return false;
+
+ data = entry;
+ return true;
+}
+
+nsresult RegisterAppMemory(void* ptr, size_t length)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+#if defined(XP_LINUX) || defined(XP_WIN32)
+ gExceptionHandler->RegisterAppMemory(ptr, length);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult UnregisterAppMemory(void* ptr)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+#if defined(XP_LINUX) || defined(XP_WIN32)
+ gExceptionHandler->UnregisterAppMemory(ptr);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+bool GetServerURL(nsACString& aServerURL)
+{
+ if (!gExceptionHandler)
+ return false;
+
+ return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
+}
+
+nsresult SetServerURL(const nsACString& aServerURL)
+{
+ // store server URL with the API data
+ // the client knows to handle this specially
+ return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
+ aServerURL);
+}
+
+nsresult
+SetRestartArgs(int argc, char** argv)
+{
+ if (!gExceptionHandler)
+ return NS_OK;
+
+ int i;
+ nsAutoCString envVar;
+ char *env;
+ char *argv0 = getenv("MOZ_APP_LAUNCHER");
+ for (i = 0; i < argc; i++) {
+ envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
+ envVar.AppendInt(i);
+ envVar += "=";
+ if (argv0 && i == 0) {
+ // Is there a request to suppress default binary launcher?
+ envVar += argv0;
+ } else {
+ envVar += argv[i];
+ }
+
+ // PR_SetEnv() wants the string to be available for the lifetime
+ // of the app, so dup it here
+ env = ToNewCString(envVar);
+ if (!env)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PR_SetEnv(env);
+ }
+
+ // make sure the arg list is terminated
+ envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
+ envVar.AppendInt(i);
+ envVar += "=";
+
+ // PR_SetEnv() wants the string to be available for the lifetime
+ // of the app, so dup it here
+ env = ToNewCString(envVar);
+ if (!env)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PR_SetEnv(env);
+
+ // make sure we save the info in XUL_APP_FILE for the reporter
+ const char *appfile = PR_GetEnv("XUL_APP_FILE");
+ if (appfile && *appfile) {
+ envVar = "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
+ envVar += appfile;
+ env = ToNewCString(envVar);
+ PR_SetEnv(env);
+ }
+
+ return NS_OK;
+}
+
+#ifdef XP_WIN32
+nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
+{
+ if (!gExceptionHandler)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
+}
+#endif
+
+#ifdef XP_LINUX
+bool WriteMinidumpForSigInfo(int signo, siginfo_t* info, void* uc)
+{
+ if (!gExceptionHandler) {
+ // Crash reporting is disabled.
+ return false;
+ }
+ return gExceptionHandler->HandleSignal(signo, info, uc);
+}
+#endif
+
+#ifdef XP_MACOSX
+nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
+{
+ nsAutoCString excString;
+ GetObjCExceptionInfo(inException, excString);
+ AppendAppNotesToCrashReport(excString);
+ return NS_OK;
+}
+#endif
+
+/*
+ * Combined code to get/set the crash reporter submission pref on
+ * different platforms.
+ */
+static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
+{
+ nsresult rv;
+#if defined(XP_WIN32)
+ /*
+ * NOTE! This needs to stay in sync with the preference checking code
+ * in toolkit/crashreporter/client/crashreporter_win.cpp
+ */
+ nsCOMPtr<nsIXULAppInfo> appinfo =
+ do_GetService("@mozilla.org/xre/app-info;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString appVendor, appName;
+ rv = appinfo->GetVendor(appVendor);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = appinfo->GetName(appName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIWindowsRegKey> regKey
+ (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString regPath;
+
+ regPath.AppendLiteral("Software\\");
+
+ // We need to ensure the registry keys are created so we can properly
+ // write values to it
+
+ // Create appVendor key
+ if(!appVendor.IsEmpty()) {
+ regPath.Append(appVendor);
+ regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ regPath.Append('\\');
+ }
+
+ // Create appName key
+ regPath.Append(appName);
+ regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ regPath.Append('\\');
+
+ // Create Crash Reporter key
+ regPath.AppendLiteral("Crash Reporter");
+ regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+
+ // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
+ // and we're done.
+ if (writePref) {
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t value = *aSubmitReports ? 1 : 0;
+ rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
+ regKey->Close();
+ return rv;
+ }
+
+ // We're reading the pref value, so we need to first look under
+ // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
+ // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
+ // to "true".
+ uint32_t value;
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+ if (NS_SUCCEEDED(rv)) {
+ rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
+ regKey->Close();
+ if (NS_SUCCEEDED(rv)) {
+ *aSubmitReports = !!value;
+ return NS_OK;
+ }
+ }
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_ConvertUTF8toUTF16(regPath),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+ if (NS_FAILED(rv)) {
+ *aSubmitReports = true;
+ return NS_OK;
+ }
+
+ rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
+ // default to true on failure
+ if (NS_FAILED(rv)) {
+ value = 1;
+ rv = NS_OK;
+ }
+ regKey->Close();
+
+ *aSubmitReports = !!value;
+ return NS_OK;
+#elif defined(XP_MACOSX)
+ rv = NS_OK;
+ if (writePref) {
+ CFPropertyListRef cfValue = (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
+ ::CFPreferencesSetAppValue(CFSTR("submitReport"),
+ cfValue,
+ reporterClientAppID);
+ if (!::CFPreferencesAppSynchronize(reporterClientAppID))
+ rv = NS_ERROR_FAILURE;
+ }
+ else {
+ *aSubmitReports = true;
+ Boolean keyExistsAndHasValidFormat = false;
+ Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
+ reporterClientAppID,
+ &keyExistsAndHasValidFormat);
+ if (keyExistsAndHasValidFormat)
+ *aSubmitReports = !!prefValue;
+ }
+ return rv;
+#elif defined(XP_UNIX)
+ /*
+ * NOTE! This needs to stay in sync with the preference checking code
+ * in toolkit/crashreporter/client/crashreporter_linux.cpp
+ */
+ nsCOMPtr<nsIFile> reporterINI;
+ rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
+ reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
+
+ bool exists;
+ rv = reporterINI->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists) {
+ if (!writePref) {
+ // If reading the pref, default to true if .ini doesn't exist.
+ *aSubmitReports = true;
+ return NS_OK;
+ }
+ // Create the file so the INI processor can write to it.
+ rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIINIParserFactory> iniFactory =
+ do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIINIParser> iniParser;
+ rv = iniFactory->CreateINIParser(reporterINI,
+ getter_AddRefs(iniParser));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we're writing the pref, just set and we're done.
+ if (writePref) {
+ nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
+ NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
+
+ rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
+ NS_LITERAL_CSTRING("SubmitReport"),
+ *aSubmitReports ? NS_LITERAL_CSTRING("1") :
+ NS_LITERAL_CSTRING("0"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = iniWriter->WriteFile(nullptr, 0);
+ return rv;
+ }
+
+ nsAutoCString submitReportValue;
+ rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
+ NS_LITERAL_CSTRING("SubmitReport"),
+ submitReportValue);
+
+ // Default to "true" if the pref can't be found.
+ if (NS_FAILED(rv))
+ *aSubmitReports = true;
+ else if (submitReportValue.EqualsASCII("0"))
+ *aSubmitReports = false;
+ else
+ *aSubmitReports = true;
+
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult GetSubmitReports(bool* aSubmitReports)
+{
+ return PrefSubmitReports(aSubmitReports, false);
+}
+
+nsresult SetSubmitReports(bool aSubmitReports)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ if (!obsServ) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = PrefSubmitReports(&aSubmitReports, true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
+ return NS_OK;
+}
+
+static void
+SetCrashEventsDir(nsIFile* aDir)
+{
+ nsCOMPtr<nsIFile> eventsDir = aDir;
+
+ const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
+ if (env && *env) {
+ NS_NewNativeLocalFile(nsDependentCString(env),
+ false, getter_AddRefs(eventsDir));
+ EnsureDirectoryExists(eventsDir);
+ }
+
+ if (eventsDirectory) {
+ free(eventsDirectory);
+ }
+
+#ifdef XP_WIN
+ nsString path;
+ eventsDir->GetPath(path);
+ eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
+
+ // Save the path in the environment for the crash reporter application.
+ nsAutoString eventsDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_EVENTS_DIRECTORY="));
+ eventsDirEnv.Append(path);
+ _wputenv(eventsDirEnv.get());
+#else
+ nsCString path;
+ eventsDir->GetNativePath(path);
+ eventsDirectory = ToNewCString(path);
+
+ // Save the path in the environment for the crash reporter application.
+ nsAutoCString eventsDirEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY=");
+ eventsDirEnv.Append(path);
+
+ // PR_SetEnv() wants the string to be available for the lifetime
+ // of the app, so dup it here.
+ char* oldEventsEnv = eventsEnv;
+ eventsEnv = ToNewCString(eventsDirEnv);
+ PR_SetEnv(eventsEnv);
+
+ if (oldEventsEnv) {
+ free(oldEventsEnv);
+ }
+#endif
+}
+
+void
+SetProfileDirectory(nsIFile* aDir)
+{
+ nsCOMPtr<nsIFile> dir;
+ aDir->Clone(getter_AddRefs(dir));
+
+ dir->Append(NS_LITERAL_STRING("crashes"));
+ EnsureDirectoryExists(dir);
+ dir->Append(NS_LITERAL_STRING("events"));
+ EnsureDirectoryExists(dir);
+ SetCrashEventsDir(dir);
+}
+
+void
+SetUserAppDataDirectory(nsIFile* aDir)
+{
+ nsCOMPtr<nsIFile> dir;
+ aDir->Clone(getter_AddRefs(dir));
+
+ dir->Append(NS_LITERAL_STRING("Crash Reports"));
+ EnsureDirectoryExists(dir);
+ dir->Append(NS_LITERAL_STRING("events"));
+ EnsureDirectoryExists(dir);
+ SetCrashEventsDir(dir);
+}
+
+void
+UpdateCrashEventsDir()
+{
+ const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
+ if (env && *env) {
+ SetCrashEventsDir(nullptr);
+ }
+
+ nsCOMPtr<nsIFile> eventsDir;
+ nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
+ if (NS_SUCCEEDED(rv)) {
+ SetProfileDirectory(eventsDir);
+ return;
+ }
+
+ rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
+ if (NS_SUCCEEDED(rv)) {
+ SetUserAppDataDirectory(eventsDir);
+ return;
+ }
+
+ NS_WARNING("Couldn't get the user appdata directory. Crash events may not be produced.");
+}
+
+bool GetCrashEventsDir(nsAString& aPath)
+{
+ if (!eventsDirectory) {
+ return false;
+ }
+
+ aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory);
+ return true;
+}
+
+void
+SetMemoryReportFile(nsIFile* aFile)
+{
+ if (!gExceptionHandler) {
+ return;
+ }
+#ifdef XP_WIN
+ nsString path;
+ aFile->GetPath(path);
+ memoryReportPath = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
+#else
+ nsCString path;
+ aFile->GetNativePath(path);
+ memoryReportPath = ToNewCString(path);
+#endif
+}
+
+nsresult
+GetDefaultMemoryReportFile(nsIFile** aFile)
+{
+ nsCOMPtr<nsIFile> defaultMemoryReportFile;
+ if (!defaultMemoryReportPath) {
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP,
+ getter_AddRefs(defaultMemoryReportFile));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ defaultMemoryReportFile->AppendNative(NS_LITERAL_CSTRING("memory-report.json.gz"));
+ defaultMemoryReportPath = CreatePathFromFile(defaultMemoryReportFile);
+ if (!defaultMemoryReportPath) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ CreateFileFromPath(*defaultMemoryReportPath,
+ getter_AddRefs(defaultMemoryReportFile));
+ if (!defaultMemoryReportFile) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ defaultMemoryReportFile.forget(aFile);
+ return NS_OK;
+}
+
+void
+SetTelemetrySessionId(const nsACString& id)
+{
+ if (!gExceptionHandler) {
+ return;
+ }
+ if (currentSessionId) {
+ free(currentSessionId);
+ }
+ currentSessionId = ToNewCString(id);
+}
+
+static void
+FindPendingDir()
+{
+ if (pendingDirectory)
+ return;
+
+ nsCOMPtr<nsIFile> pendingDir;
+ nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
+ }
+ else {
+ pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
+ pendingDir->Append(NS_LITERAL_STRING("pending"));
+
+#ifdef XP_WIN
+ nsString path;
+ pendingDir->GetPath(path);
+ pendingDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
+#else
+ nsCString path;
+ pendingDir->GetNativePath(path);
+ pendingDirectory = ToNewCString(path);
+#endif
+ }
+}
+
+// The "pending" dir is Crash Reports/pending, from which minidumps
+// can be submitted. Because this method may be called off the main thread,
+// we store the pending directory as a path.
+static bool
+GetPendingDir(nsIFile** dir)
+{
+ // MOZ_ASSERT(OOPInitialized());
+ if (!pendingDirectory) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ if (!pending) {
+ NS_WARNING("Can't set up pending directory during shutdown.");
+ return false;
+ }
+#ifdef XP_WIN
+ pending->InitWithPath(nsDependentString(pendingDirectory));
+#else
+ pending->InitWithNativePath(nsDependentCString(pendingDirectory));
+#endif
+ pending.swap(*dir);
+ return true;
+}
+
+// The "limbo" dir is where minidumps go to wait for something else to
+// use them. If we're |ShouldReport()|, then the "something else" is
+// a minidump submitter, and they're coming from the
+// Crash Reports/pending/ dir. Otherwise, we don't know what the
+// "somthing else" is, but the minidumps stay in [profile]/minidumps/
+// limbo.
+static bool
+GetMinidumpLimboDir(nsIFile** dir)
+{
+ if (ShouldReport()) {
+ return GetPendingDir(dir);
+ }
+ else {
+#ifndef XP_LINUX
+ CreateFileFromPath(gExceptionHandler->dump_path(), dir);
+#else
+ CreateFileFromPath(gExceptionHandler->minidump_descriptor().directory(),
+ dir);
+#endif
+ return nullptr != *dir;
+ }
+}
+
+void
+DeleteMinidumpFilesForID(const nsAString& id)
+{
+ nsCOMPtr<nsIFile> minidumpFile;
+ GetMinidumpForID(id, getter_AddRefs(minidumpFile));
+ bool exists = false;
+ if (minidumpFile && NS_SUCCEEDED(minidumpFile->Exists(&exists)) && exists) {
+ nsCOMPtr<nsIFile> childExtraFile;
+ GetExtraFileForMinidump(minidumpFile, getter_AddRefs(childExtraFile));
+ if (childExtraFile) {
+ childExtraFile->Remove(false);
+ }
+ minidumpFile->Remove(false);
+ }
+}
+
+bool
+GetMinidumpForID(const nsAString& id, nsIFile** minidump)
+{
+ if (!GetMinidumpLimboDir(minidump))
+ return false;
+ (*minidump)->Append(id + NS_LITERAL_STRING(".dmp"));
+ return true;
+}
+
+bool
+GetIDFromMinidump(nsIFile* minidump, nsAString& id)
+{
+ if (minidump && NS_SUCCEEDED(minidump->GetLeafName(id))) {
+ id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING(""));
+ return true;
+ }
+ return false;
+}
+
+bool
+GetExtraFileForID(const nsAString& id, nsIFile** extraFile)
+{
+ if (!GetMinidumpLimboDir(extraFile))
+ return false;
+ (*extraFile)->Append(id + NS_LITERAL_STRING(".extra"));
+ return true;
+}
+
+bool
+GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile)
+{
+ nsAutoString leafName;
+ nsresult rv = minidump->GetLeafName(leafName);
+ if (NS_FAILED(rv))
+ return false;
+
+ nsCOMPtr<nsIFile> extraF;
+ rv = minidump->Clone(getter_AddRefs(extraF));
+ if (NS_FAILED(rv))
+ return false;
+
+ leafName.Replace(leafName.Length() - 3, 3,
+ NS_LITERAL_STRING("extra"));
+ rv = extraF->SetLeafName(leafName);
+ if (NS_FAILED(rv))
+ return false;
+
+ *extraFile = nullptr;
+ extraF.swap(*extraFile);
+ return true;
+}
+
+bool
+AppendExtraData(const nsAString& id, const AnnotationTable& data)
+{
+ nsCOMPtr<nsIFile> extraFile;
+ if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
+ return false;
+ return AppendExtraData(extraFile, data);
+}
+
+//-----------------------------------------------------------------------------
+// Helpers for AppendExtraData()
+//
+struct Blacklist {
+ Blacklist() : mItems(nullptr), mLen(0) { }
+ Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
+
+ bool Contains(const nsACString& key) const {
+ for (int i = 0; i < mLen; ++i)
+ if (key.EqualsASCII(mItems[i]))
+ return true;
+ return false;
+ }
+
+ const char** mItems;
+ const int mLen;
+};
+
+static void
+WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
+{
+ PR_Write(fd, key.BeginReading(), key.Length());
+ PR_Write(fd, "=", 1);
+ PR_Write(fd, value.BeginReading(), value.Length());
+ PR_Write(fd, "\n", 1);
+}
+
+template<int N>
+void
+WriteLiteral(PRFileDesc* fd, const char (&str)[N])
+{
+ PR_Write(fd, str, N - 1);
+}
+
+static bool
+WriteExtraData(nsIFile* extraFile,
+ const AnnotationTable& data,
+ const Blacklist& blacklist,
+ bool writeCrashTime=false,
+ bool truncate=false)
+{
+ PRFileDesc* fd;
+ int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
+ nsresult rv =
+ extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
+ 0600, &fd);
+ if (NS_FAILED(rv))
+ return false;
+
+ for (auto iter = data.ConstIter(); !iter.Done(); iter.Next()) {
+ // Skip entries in the blacklist.
+ const nsACString& key = iter.Key();
+ if (blacklist.Contains(key)) {
+ continue;
+ }
+ WriteAnnotation(fd, key, iter.Data());
+ }
+
+ if (writeCrashTime) {
+ time_t crashTime = time(nullptr);
+ char crashTimeString[32];
+ XP_TTOA(crashTime, crashTimeString, 10);
+
+ WriteAnnotation(fd,
+ nsDependentCString("CrashTime"),
+ nsDependentCString(crashTimeString));
+
+ bool ignored = false;
+ double uptimeTS = (TimeStamp::NowLoRes()-
+ TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+ char uptimeTSString[64];
+ SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
+
+ WriteAnnotation(fd,
+ nsDependentCString("UptimeTS"),
+ nsDependentCString(uptimeTSString));
+ }
+
+ if (memoryReportPath) {
+ WriteLiteral(fd, "ContainsMemoryReport=1\n");
+ }
+
+ PR_Close(fd);
+ return true;
+}
+
+bool
+AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
+{
+ return WriteExtraData(extraFile, data, Blacklist());
+}
+
+static bool
+GetExtraFileForChildPid(uint32_t aPid, nsIFile** aExtraFile)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ nsCOMPtr<nsIFile> extraFile;
+ nsresult rv;
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ if (!childProcessTmpDir) {
+ return false;
+ }
+ CreateFileFromPath(*childProcessTmpDir, getter_AddRefs(extraFile));
+ if (!extraFile) {
+ return false;
+ }
+#elif defined(XP_UNIX)
+ rv = NS_NewLocalFile(NS_LITERAL_STRING("/tmp"), false,
+ getter_AddRefs(extraFile));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+#else
+#error "Implement this for your platform"
+#endif
+
+ nsAutoString leafName;
+#if defined(XP_WIN)
+ leafName.AppendPrintf("%S%u%S", childCrashAnnotationBaseName, aPid,
+ extraFileExtension);
+#else
+ leafName.AppendPrintf("%s%u%s", childCrashAnnotationBaseName, aPid,
+ extraFileExtension);
+#endif
+
+ rv = extraFile->Append(leafName);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ extraFile.forget(aExtraFile);
+ return true;
+}
+
+static bool
+IsDataEscaped(char* aData)
+{
+ if (strchr(aData, '\n')) {
+ // There should not be any newlines
+ return false;
+ }
+ char* pos = aData;
+ while ((pos = strchr(pos, '\\'))) {
+ if (*(pos + 1) != '\\') {
+ return false;
+ }
+ // Add 2 to account for the second pos
+ pos += 2;
+ }
+ return true;
+}
+
+static void
+ReadAndValidateExceptionTimeAnnotations(FILE*& aFd,
+ AnnotationTable& aAnnotations)
+{
+ char line[0x1000];
+ while (fgets(line, sizeof(line), aFd)) {
+ char* data = strchr(line, '=');
+ if (!data) {
+ // bad data? Abort!
+ break;
+ }
+ // Move past the '='
+ *data = 0;
+ ++data;
+ size_t dataLen = strlen(data);
+ // Chop off any trailing newline
+ if (dataLen > 0 && data[dataLen - 1] == '\n') {
+ data[dataLen - 1] = 0;
+ --dataLen;
+ }
+ // There should not be any newlines in the key
+ if (strchr(line, '\n')) {
+ break;
+ }
+ // Data should have been escaped by the child
+ if (!IsDataEscaped(data)) {
+ break;
+ }
+ // Looks good, save the (line,data) pair
+ aAnnotations.Put(nsDependentCString(line),
+ nsDependentCString(data, dataLen));
+ }
+}
+
+/**
+ * NOTE: One side effect of this function is that it deletes the
+ * GeckoChildCrash<pid>.extra file if it exists, once processed.
+ */
+static bool
+WriteExtraForMinidump(nsIFile* minidump,
+ uint32_t pid,
+ const Blacklist& blacklist,
+ nsIFile** extraFile)
+{
+ nsCOMPtr<nsIFile> extra;
+ if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra))) {
+ return false;
+ }
+
+ if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
+ blacklist,
+ true /*write crash time*/,
+ true /*truncate*/)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> exceptionTimeExtra;
+ FILE* fd;
+ if (pid && GetExtraFileForChildPid(pid, getter_AddRefs(exceptionTimeExtra)) &&
+ NS_SUCCEEDED(exceptionTimeExtra->OpenANSIFileDesc("r", &fd))) {
+ AnnotationTable exceptionTimeAnnotations;
+ ReadAndValidateExceptionTimeAnnotations(fd, exceptionTimeAnnotations);
+ fclose(fd);
+ if (!AppendExtraData(extra, exceptionTimeAnnotations)) {
+ return false;
+ }
+ }
+ if (exceptionTimeExtra) {
+ exceptionTimeExtra->Remove(false);
+ }
+
+ extra.forget(extraFile);
+
+ return true;
+}
+
+// It really only makes sense to call this function when
+// ShouldReport() is true.
+// Uses dumpFile's filename to generate memoryReport's filename (same name with
+// a different extension)
+static bool
+MoveToPending(nsIFile* dumpFile, nsIFile* extraFile, nsIFile* memoryReport)
+{
+ nsCOMPtr<nsIFile> pendingDir;
+ if (!GetPendingDir(getter_AddRefs(pendingDir)))
+ return false;
+
+ if (NS_FAILED(dumpFile->MoveTo(pendingDir, EmptyString()))) {
+ return false;
+ }
+
+ if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, EmptyString()))) {
+ return false;
+ }
+
+ if (memoryReport) {
+ nsAutoString leafName;
+ nsresult rv = dumpFile->GetLeafName(leafName);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ // Generate the correct memory report filename from the dumpFile's name
+ leafName.Replace(leafName.Length() - 4, 4,
+ static_cast<nsString>(CONVERT_XP_CHAR_TO_UTF16(memoryReportExtension)));
+ if (NS_FAILED(memoryReport->MoveTo(pendingDir, leafName))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+OnChildProcessDumpRequested(void* aContext,
+#ifdef XP_MACOSX
+ const ClientInfo& aClientInfo,
+ const xpstring& aFilePath
+#else
+ const ClientInfo* aClientInfo,
+ const xpstring* aFilePath
+#endif
+ )
+{
+ nsCOMPtr<nsIFile> minidump;
+ nsCOMPtr<nsIFile> extraFile;
+
+ // Hold the mutex until the current dump request is complete, to
+ // prevent UnsetExceptionHandler() from pulling the rug out from
+ // under us.
+ MutexAutoLock lock(*dumpSafetyLock);
+ if (!isSafeToDump)
+ return;
+
+ CreateFileFromPath(
+#ifdef XP_MACOSX
+ aFilePath,
+#else
+ *aFilePath,
+#endif
+ getter_AddRefs(minidump));
+
+ uint32_t pid =
+#ifdef XP_MACOSX
+ aClientInfo.pid();
+#else
+ aClientInfo->pid();
+#endif
+
+ if (!WriteExtraForMinidump(minidump, pid,
+ Blacklist(kSubprocessBlacklist,
+ ArrayLength(kSubprocessBlacklist)),
+ getter_AddRefs(extraFile)))
+ return;
+
+ if (ShouldReport()) {
+ nsCOMPtr<nsIFile> memoryReport;
+ if (memoryReportPath) {
+ CreateFileFromPath(memoryReportPath, getter_AddRefs(memoryReport));
+ MOZ_ASSERT(memoryReport);
+ }
+ MoveToPending(minidump, extraFile, memoryReport);
+ }
+
+ {
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ bool runCallback;
+#endif
+ {
+ MutexAutoLock lock(*dumpMapLock);
+ ChildProcessData* pd = pidToMinidump->PutEntry(pid);
+ MOZ_ASSERT(!pd->minidump);
+ pd->minidump = minidump;
+ pd->sequence = ++crashSequence;
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ runCallback = nullptr != pd->callback;
+#endif
+ }
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ if (runCallback)
+ NS_DispatchToMainThread(new ReportInjectedCrash(pid));
+#endif
+ }
+}
+
+static bool
+OOPInitialized()
+{
+ return pidToMinidump != nullptr;
+}
+
+void
+OOPInit()
+{
+ class ProxyToMainThread : public Runnable
+ {
+ public:
+ NS_IMETHOD Run() override {
+ OOPInit();
+ return NS_OK;
+ }
+ };
+ if (!NS_IsMainThread()) {
+ // This logic needs to run on the main thread
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ mozilla::SyncRunnable::DispatchToThread(mainThread, new ProxyToMainThread());
+ return;
+ }
+
+ if (OOPInitialized())
+ return;
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(gExceptionHandler != nullptr,
+ "attempt to initialize OOP crash reporter before in-process crashreporter!");
+
+#if (defined(XP_WIN) || defined(XP_MACOSX))
+ nsCOMPtr<nsIFile> tmpDir;
+# if defined(MOZ_CONTENT_SANDBOX)
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(tmpDir));
+ if (NS_FAILED(rv) && PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
+ // Temporary hack for xpcshell, will be fixed in bug 1257098
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+ }
+ if (NS_SUCCEEDED(rv)) {
+ childProcessTmpDir = CreatePathFromFile(tmpDir);
+ }
+# else
+ if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(tmpDir)))) {
+ childProcessTmpDir = CreatePathFromFile(tmpDir);
+ }
+# endif // defined(MOZ_CONTENT_SANDBOX)
+#endif // (defined(XP_WIN) || defined(XP_MACOSX))
+
+#if defined(XP_WIN)
+ childCrashNotifyPipe =
+ PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
+ static_cast<int>(::GetCurrentProcessId()));
+
+ const std::wstring dumpPath = gExceptionHandler->dump_path();
+ crashServer = new CrashGenerationServer(
+ std::wstring(NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get()),
+ nullptr, // default security attributes
+ nullptr, nullptr, // we don't care about process connect here
+ OnChildProcessDumpRequested, nullptr,
+ nullptr, nullptr, // we don't care about process exit here
+ nullptr, nullptr, // we don't care about upload request here
+ true, // automatically generate dumps
+ &dumpPath);
+
+#elif defined(XP_LINUX)
+ if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
+ &clientSocketFd))
+ NS_RUNTIMEABORT("can't create crash reporter socketpair()");
+
+ const std::string dumpPath =
+ gExceptionHandler->minidump_descriptor().directory();
+ crashServer = new CrashGenerationServer(
+ serverSocketFd,
+ OnChildProcessDumpRequested, nullptr,
+ nullptr, nullptr, // we don't care about process exit here
+ true,
+ &dumpPath);
+
+#elif defined(XP_MACOSX)
+ childCrashNotifyPipe =
+ PR_smprintf("gecko-crash-server-pipe.%i",
+ static_cast<int>(getpid()));
+ const std::string dumpPath = gExceptionHandler->dump_path();
+
+ crashServer = new CrashGenerationServer(
+ childCrashNotifyPipe,
+ nullptr,
+ nullptr,
+ OnChildProcessDumpRequested, nullptr,
+ nullptr, nullptr,
+ true, // automatically generate dumps
+ dumpPath);
+#endif
+
+ if (!crashServer->Start())
+ NS_RUNTIMEABORT("can't start crash reporter server()");
+
+ pidToMinidump = new ChildMinidumpMap();
+
+ dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
+
+ FindPendingDir();
+ UpdateCrashEventsDir();
+}
+
+static void
+OOPDeinit()
+{
+ if (!OOPInitialized()) {
+ NS_WARNING("OOPDeinit() without successful OOPInit()");
+ return;
+ }
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ if (sInjectorThread) {
+ sInjectorThread->Shutdown();
+ NS_RELEASE(sInjectorThread);
+ }
+#endif
+
+ delete crashServer;
+ crashServer = nullptr;
+
+ delete dumpMapLock;
+ dumpMapLock = nullptr;
+
+ delete pidToMinidump;
+ pidToMinidump = nullptr;
+
+#if defined(XP_WIN)
+ PR_Free(childCrashNotifyPipe);
+ childCrashNotifyPipe = nullptr;
+#endif
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+// Parent-side API for children
+const char*
+GetChildNotificationPipe()
+{
+ if (!GetEnabled())
+ return kNullNotifyPipe;
+
+ MOZ_ASSERT(OOPInitialized());
+
+ return childCrashNotifyPipe;
+}
+#endif
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+void
+InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
+{
+ if (!GetEnabled())
+ return;
+
+ if (!OOPInitialized())
+ OOPInit();
+
+ if (!sInjectorThread) {
+ if (NS_FAILED(NS_NewThread(&sInjectorThread)))
+ return;
+ }
+
+ {
+ MutexAutoLock lock(*dumpMapLock);
+ ChildProcessData* pd = pidToMinidump->PutEntry(processID);
+ MOZ_ASSERT(!pd->minidump && !pd->callback);
+ pd->callback = cb;
+ }
+
+ nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
+ sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+ReportInjectedCrash::Run()
+{
+ // Crash reporting may have been disabled after this method was dispatched
+ if (!OOPInitialized())
+ return NS_OK;
+
+ InjectorCrashCallback* cb;
+ {
+ MutexAutoLock lock(*dumpMapLock);
+ ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
+ if (!pd || !pd->callback)
+ return NS_OK;
+
+ MOZ_ASSERT(pd->minidump);
+
+ cb = pd->callback;
+ }
+
+ cb->OnCrash(mPID);
+ return NS_OK;
+}
+
+void
+UnregisterInjectorCallback(DWORD processID)
+{
+ if (!OOPInitialized())
+ return;
+
+ MutexAutoLock lock(*dumpMapLock);
+ pidToMinidump->RemoveEntry(processID);
+}
+
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+bool
+CheckForLastRunCrash()
+{
+ if (lastRunCrashID)
+ return true;
+
+ // The exception handler callback leaves the filename of the
+ // last minidump in a known file.
+ nsCOMPtr<nsIFile> lastCrashFile;
+ CreateFileFromPath(crashMarkerFilename,
+ getter_AddRefs(lastCrashFile));
+
+ bool exists;
+ if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
+ return false;
+ }
+
+ nsAutoCString lastMinidump_contents;
+ if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
+ return false;
+ }
+ lastCrashFile->Remove(false);
+
+#ifdef XP_WIN
+ // Ugly but effective.
+ nsDependentString lastMinidump(
+ reinterpret_cast<const char16_t*>(lastMinidump_contents.get()));
+#else
+ nsAutoCString lastMinidump = lastMinidump_contents;
+#endif
+ nsCOMPtr<nsIFile> lastMinidumpFile;
+ CreateFileFromPath(lastMinidump.get(),
+ getter_AddRefs(lastMinidumpFile));
+
+ if (!lastMinidumpFile || NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> lastExtraFile;
+ if (!GetExtraFileForMinidump(lastMinidumpFile,
+ getter_AddRefs(lastExtraFile))) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> memoryReportFile;
+ nsresult rv = GetDefaultMemoryReportFile(getter_AddRefs(memoryReportFile));
+ if (NS_FAILED(rv) || NS_FAILED(memoryReportFile->Exists(&exists)) || !exists) {
+ memoryReportFile = nullptr;
+ }
+
+ FindPendingDir();
+
+ // Move {dump,extra,memory} to pending folder
+ if (!MoveToPending(lastMinidumpFile, lastExtraFile, memoryReportFile)) {
+ return false;
+ }
+
+ lastRunCrashID = new nsString();
+ return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
+}
+
+bool
+GetLastRunCrashID(nsAString& id)
+{
+ if (!lastRunCrashID_checked) {
+ CheckForLastRunCrash();
+ lastRunCrashID_checked = true;
+ }
+
+ if (!lastRunCrashID) {
+ return false;
+ }
+
+ id = *lastRunCrashID;
+ return true;
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+void
+InitChildProcessTmpDir()
+{
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ // When retrieved by the child process, this will always resolve to the
+ // correct directory regardless of sandbox level.
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+ if (NS_SUCCEEDED(rv)) {
+ childProcessTmpDir = CreatePathFromFile(tmpDir);
+ }
+}
+#endif // defined(XP_WIN) || defined(XP_MACOSX)
+
+#if defined(XP_WIN)
+// Child-side API
+bool
+SetRemoteExceptionHandler(const nsACString& crashPipe)
+{
+ // crash reporting is disabled
+ if (crashPipe.Equals(kNullNotifyPipe))
+ return true;
+
+ MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
+
+ gExceptionHandler = new google_breakpad::
+ ExceptionHandler(L"",
+ ChildFPEFilter,
+ nullptr, // no minidump callback
+ nullptr, // no callback context
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ GetMinidumpType(),
+ NS_ConvertASCIItoUTF16(crashPipe).get(),
+ nullptr);
+ gExceptionHandler->set_handle_debug_exceptions(true);
+
+ mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
+ oldTerminateHandler = std::set_terminate(&TerminateHandler);
+
+ // we either do remote or nothing, no fallback to regular crash reporting
+ return gExceptionHandler->IsOutOfProcess();
+}
+
+//--------------------------------------------------
+#elif defined(XP_LINUX)
+
+// Parent-side API for children
+bool
+CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
+{
+ if (!GetEnabled()) {
+ *childCrashFd = -1;
+ *childCrashRemapFd = -1;
+ return true;
+ }
+
+ MOZ_ASSERT(OOPInitialized());
+
+ *childCrashFd = clientSocketFd;
+ *childCrashRemapFd = kMagicChildCrashReportFd;
+
+ return true;
+}
+
+// Child-side API
+bool
+SetRemoteExceptionHandler()
+{
+ MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
+
+ // MinidumpDescriptor requires a non-empty path.
+ google_breakpad::MinidumpDescriptor path(".");
+
+ gExceptionHandler = new google_breakpad::
+ ExceptionHandler(path,
+ ChildFilter,
+ nullptr, // no minidump callback
+ nullptr, // no callback context
+ true, // install signal handlers
+ kMagicChildCrashReportFd);
+
+ if (gDelayedAnnotations) {
+ for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
+ gDelayedAnnotations->ElementAt(i)->Run();
+ }
+ delete gDelayedAnnotations;
+ }
+
+ mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
+ oldTerminateHandler = std::set_terminate(&TerminateHandler);
+
+ // we either do remote or nothing, no fallback to regular crash reporting
+ return gExceptionHandler->IsOutOfProcess();
+}
+
+//--------------------------------------------------
+#elif defined(XP_MACOSX)
+// Child-side API
+bool
+SetRemoteExceptionHandler(const nsACString& crashPipe)
+{
+ // crash reporting is disabled
+ if (crashPipe.Equals(kNullNotifyPipe))
+ return true;
+
+ MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
+
+ gExceptionHandler = new google_breakpad::
+ ExceptionHandler("",
+ ChildFilter,
+ nullptr, // no minidump callback
+ nullptr, // no callback context
+ true, // install signal handlers
+ crashPipe.BeginReading());
+
+ mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
+ oldTerminateHandler = std::set_terminate(&TerminateHandler);
+
+ // we either do remote or nothing, no fallback to regular crash reporting
+ return gExceptionHandler->IsOutOfProcess();
+}
+#endif // XP_WIN
+
+
+bool
+TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
+{
+ if (!GetEnabled())
+ return false;
+
+ MutexAutoLock lock(*dumpMapLock);
+
+ ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
+ if (!pd)
+ return false;
+
+ NS_IF_ADDREF(*dump = pd->minidump);
+ if (aSequence) {
+ *aSequence = pd->sequence;
+ }
+
+ pidToMinidump->RemoveEntry(pd);
+
+ return !!*dump;
+}
+
+//-----------------------------------------------------------------------------
+// CreatePairedMinidumps() and helpers
+//
+
+void
+RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
+ const nsACString& name)
+{
+ nsCOMPtr<nsIFile> directory;
+ childMinidump->GetParent(getter_AddRefs(directory));
+ if (!directory)
+ return;
+
+ nsAutoCString leafName;
+ childMinidump->GetNativeLeafName(leafName);
+
+ // turn "<id>.dmp" into "<id>-<name>.dmp
+ leafName.Insert(NS_LITERAL_CSTRING("-") + name, leafName.Length() - 4);
+
+ if (NS_FAILED(minidump->MoveToNative(directory, leafName))) {
+ NS_WARNING("RenameAdditionalHangMinidump failed to move minidump.");
+ }
+}
+
+static bool
+PairedDumpCallback(
+#ifdef XP_LINUX
+ const MinidumpDescriptor& descriptor,
+#else
+ const XP_CHAR* dump_path,
+ const XP_CHAR* minidump_id,
+#endif
+ void* context,
+#ifdef XP_WIN32
+ EXCEPTION_POINTERS* /*unused*/,
+ MDRawAssertionInfo* /*unused*/,
+#endif
+ bool succeeded)
+{
+ nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
+
+ xpstring dump;
+#ifdef XP_LINUX
+ dump = descriptor.path();
+#else
+ dump = dump_path;
+ dump += XP_PATH_SEPARATOR;
+ dump += minidump_id;
+ dump += dumpFileExtension;
+#endif
+
+ CreateFileFromPath(dump, getter_AddRefs(minidump));
+ return true;
+}
+
+static bool
+PairedDumpCallbackExtra(
+#ifdef XP_LINUX
+ const MinidumpDescriptor& descriptor,
+#else
+ const XP_CHAR* dump_path,
+ const XP_CHAR* minidump_id,
+#endif
+ void* context,
+#ifdef XP_WIN32
+ EXCEPTION_POINTERS* /*unused*/,
+ MDRawAssertionInfo* /*unused*/,
+#endif
+ bool succeeded)
+{
+ PairedDumpCallback(
+#ifdef XP_LINUX
+ descriptor,
+#else
+ dump_path, minidump_id,
+#endif
+ context,
+#ifdef XP_WIN32
+ nullptr, nullptr,
+#endif
+ succeeded);
+
+ nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
+
+ nsCOMPtr<nsIFile> extra;
+ return WriteExtraForMinidump(minidump, 0, Blacklist(), getter_AddRefs(extra));
+}
+
+ThreadId
+CurrentThreadId()
+{
+#if defined(XP_WIN)
+ return ::GetCurrentThreadId();
+#elif defined(XP_LINUX)
+ return sys_gettid();
+#elif defined(XP_MACOSX)
+ // Just return an index, since Mach ports can't be directly serialized
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
+ return -1;
+
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ if (threads_for_task[i] == mach_thread_self())
+ return i;
+ }
+ abort();
+#else
+# error "Unsupported platform"
+#endif
+}
+
+#ifdef XP_MACOSX
+static mach_port_t
+GetChildThread(ProcessHandle childPid, ThreadId childBlamedThread)
+{
+ mach_port_t childThread = MACH_PORT_NULL;
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(childPid, &threads_for_task, &thread_count)
+ == KERN_SUCCESS && childBlamedThread < thread_count) {
+ childThread = threads_for_task[childBlamedThread];
+ }
+
+ return childThread;
+}
+#endif
+
+bool TakeMinidump(nsIFile** aResult, bool aMoveToPending)
+{
+ if (!GetEnabled())
+ return false;
+
+ AutoIOInterposerDisable disableIOInterposition;
+
+ xpstring dump_path;
+#ifndef XP_LINUX
+ dump_path = gExceptionHandler->dump_path();
+#else
+ dump_path = gExceptionHandler->minidump_descriptor().directory();
+#endif
+
+ // capture the dump
+ if (!google_breakpad::ExceptionHandler::WriteMinidump(
+ dump_path,
+#ifdef XP_MACOSX
+ true,
+#endif
+ PairedDumpCallback,
+ static_cast<void*>(aResult)
+#ifdef XP_WIN32
+ , GetMinidumpType()
+#endif
+ )) {
+ return false;
+ }
+
+ if (aMoveToPending) {
+ MoveToPending(*aResult, nullptr, nullptr);
+ }
+ return true;
+}
+
+bool
+CreateMinidumpsAndPair(ProcessHandle aTargetPid,
+ ThreadId aTargetBlamedThread,
+ const nsACString& aIncomingPairName,
+ nsIFile* aIncomingDumpToPair,
+ nsIFile** aMainDumpOut)
+{
+ if (!GetEnabled()) {
+ return false;
+ }
+
+ AutoIOInterposerDisable disableIOInterposition;
+
+#ifdef XP_MACOSX
+ mach_port_t targetThread = GetChildThread(aTargetPid, aTargetBlamedThread);
+#else
+ ThreadId targetThread = aTargetBlamedThread;
+#endif
+
+ xpstring dump_path;
+#ifndef XP_LINUX
+ dump_path = gExceptionHandler->dump_path();
+#else
+ dump_path = gExceptionHandler->minidump_descriptor().directory();
+#endif
+
+ // dump the target
+ nsCOMPtr<nsIFile> targetMinidump;
+ if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
+ aTargetPid,
+ targetThread,
+ dump_path,
+ PairedDumpCallbackExtra,
+ static_cast<void*>(&targetMinidump)
+#ifdef XP_WIN32
+ , GetMinidumpType()
+#endif
+ ))
+ return false;
+
+ nsCOMPtr<nsIFile> targetExtra;
+ GetExtraFileForMinidump(targetMinidump, getter_AddRefs(targetExtra));
+
+ // If aIncomingDumpToPair isn't valid, create a dump of this process.
+ nsCOMPtr<nsIFile> incomingDump;
+ if (aIncomingDumpToPair == nullptr) {
+ if (!google_breakpad::ExceptionHandler::WriteMinidump(
+ dump_path,
+#ifdef XP_MACOSX
+ true,
+#endif
+ PairedDumpCallback,
+ static_cast<void*>(&incomingDump)
+#ifdef XP_WIN32
+ , GetMinidumpType()
+#endif
+ )) {
+ targetMinidump->Remove(false);
+ targetExtra->Remove(false);
+ return false;
+ }
+ } else {
+ incomingDump = aIncomingDumpToPair;
+ }
+
+ RenameAdditionalHangMinidump(incomingDump, targetMinidump, aIncomingPairName);
+
+ if (ShouldReport()) {
+ MoveToPending(targetMinidump, targetExtra, nullptr);
+ MoveToPending(incomingDump, nullptr, nullptr);
+ }
+
+ targetMinidump.forget(aMainDumpOut);
+
+ return true;
+}
+
+bool
+CreateAdditionalChildMinidump(ProcessHandle childPid,
+ ThreadId childBlamedThread,
+ nsIFile* parentMinidump,
+ const nsACString& name)
+{
+ if (!GetEnabled())
+ return false;
+
+#ifdef XP_MACOSX
+ mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
+#else
+ ThreadId childThread = childBlamedThread;
+#endif
+
+ xpstring dump_path;
+#ifndef XP_LINUX
+ dump_path = gExceptionHandler->dump_path();
+#else
+ dump_path = gExceptionHandler->minidump_descriptor().directory();
+#endif
+
+ // dump the child
+ nsCOMPtr<nsIFile> childMinidump;
+ if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
+ childPid,
+ childThread,
+ dump_path,
+ PairedDumpCallback,
+ static_cast<void*>(&childMinidump)
+#ifdef XP_WIN32
+ , GetMinidumpType()
+#endif
+ )) {
+ return false;
+ }
+
+ RenameAdditionalHangMinidump(childMinidump, parentMinidump, name);
+
+ return true;
+}
+
+bool
+UnsetRemoteExceptionHandler()
+{
+ std::set_terminate(oldTerminateHandler);
+ delete gExceptionHandler;
+ gExceptionHandler = nullptr;
+ return true;
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void AddLibraryMapping(const char* library_name,
+ uintptr_t start_address,
+ size_t mapping_length,
+ size_t file_offset)
+{
+ if (!gExceptionHandler) {
+ mapping_info info;
+ info.name = library_name;
+ info.start_address = start_address;
+ info.length = mapping_length;
+ info.file_offset = file_offset;
+ library_mappings.push_back(info);
+ }
+ else {
+ PageAllocator allocator;
+ auto_wasteful_vector<uint8_t, sizeof(MDGUID)> guid(&allocator);
+ FileID::ElfFileIdentifierFromMappedFile((void const *)start_address, guid);
+ gExceptionHandler->AddMappingInfo(library_name,
+ guid.data(),
+ start_address,
+ mapping_length,
+ file_offset);
+ }
+}
+#endif
+
+} // namespace CrashReporter