summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/nsExceptionHandler.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-04-01 13:55:00 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-04-01 13:55:00 -0400
commitce3979c721ba378a448bfbe3671c99d993cbc801 (patch)
treee200d5225bcecef5f974b946a58277fddd24e89c /toolkit/crashreporter/nsExceptionHandler.cpp
parentf6c16cff36048c583ca0e1d019b622336ca861a0 (diff)
parentff2f287f82630ab3887d7d5c1e64e5b888ea0beb (diff)
downloadUXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.gz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.lz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.xz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.zip
Merge branch 'master' into Sync-weave
Diffstat (limited to 'toolkit/crashreporter/nsExceptionHandler.cpp')
-rw-r--r--toolkit/crashreporter/nsExceptionHandler.cpp4101
1 files changed, 0 insertions, 4101 deletions
diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp
deleted file mode 100644
index 4d0b8cdfe..000000000
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ /dev/null
@@ -1,4101 +0,0 @@
-/* -*- 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/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;
-
-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)
- // In non-debug builds, enable the crash reporter by default, and allow
- // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
- 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(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()) {
- // 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());
- EnqueueDelayedNote(new DelayedNote(key, data));
- 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 (!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());
- EnqueueDelayedNote(new DelayedNote(data));
- 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 (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
- getter_AddRefs(tmpDir)))) {
- childProcessTmpDir = CreatePathFromFile(tmpDir);
- }
-#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