toolkit/crashreporter/nsExceptionHandler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsExceptionHandler.h"
     7 #include "nsDataHashtable.h"
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/dom/CrashReporterChild.h"
    10 #include "mozilla/Services.h"
    11 #include "nsIObserverService.h"
    12 #include "mozilla/unused.h"
    14 #include "nsThreadUtils.h"
    15 #include "nsXULAppAPI.h"
    17 #if defined(XP_WIN32)
    18 #ifdef WIN32_LEAN_AND_MEAN
    19 #undef WIN32_LEAN_AND_MEAN
    20 #endif
    22 #include "nsXULAppAPI.h"
    23 #include "nsIXULAppInfo.h"
    24 #include "nsIWindowsRegKey.h"
    25 #include "client/windows/crash_generation/client_info.h"
    26 #include "client/windows/crash_generation/crash_generation_server.h"
    27 #include "client/windows/handler/exception_handler.h"
    28 #include <dbghelp.h>
    29 #include <string.h>
    30 #include "nsDirectoryServiceUtils.h"
    32 #include "nsWindowsDllInterceptor.h"
    33 #elif defined(XP_MACOSX)
    34 #include "client/mac/crash_generation/client_info.h"
    35 #include "client/mac/crash_generation/crash_generation_server.h"
    36 #include "client/mac/handler/exception_handler.h"
    37 #include <string>
    38 #include <Carbon/Carbon.h>
    39 #include <CoreFoundation/CoreFoundation.h>
    40 #include <crt_externs.h>
    41 #include <fcntl.h>
    42 #include <mach/mach.h>
    43 #include <sys/types.h>
    44 #include <spawn.h>
    45 #include <unistd.h>
    46 #include "mac_utils.h"
    47 #elif defined(XP_LINUX)
    48 #include "nsIINIParser.h"
    49 #include "common/linux/linux_libc_support.h"
    50 #include "third_party/lss/linux_syscall_support.h"
    51 #include "client/linux/crash_generation/client_info.h"
    52 #include "client/linux/crash_generation/crash_generation_server.h"
    53 #include "client/linux/handler/exception_handler.h"
    54 #include <fcntl.h>
    55 #include <sys/types.h>
    56 #include <unistd.h>
    57 #elif defined(XP_SOLARIS)
    58 #include "client/solaris/handler/exception_handler.h"
    59 #include <fcntl.h>
    60 #include <sys/types.h>
    61 #include <unistd.h>
    62 #else
    63 #error "Not yet implemented for this platform"
    64 #endif // defined(XP_WIN32)
    66 #ifdef MOZ_CRASHREPORTER_INJECTOR
    67 #include "InjectCrashReporter.h"
    68 using mozilla::InjectCrashRunnable;
    69 #endif
    71 #include <stdlib.h>
    72 #include <time.h>
    73 #include <prenv.h>
    74 #include <prio.h>
    75 #include <prmem.h>
    76 #include "mozilla/Mutex.h"
    77 #include "nsDebug.h"
    78 #include "nsCRT.h"
    79 #include "nsIFile.h"
    80 #include "prprf.h"
    81 #include <map>
    82 #include <vector>
    84 #include "mozilla/IOInterposer.h"
    85 #include "mozilla/mozalloc_oom.h"
    86 #include "mozilla/WindowsDllBlocklist.h"
    88 #if defined(XP_MACOSX)
    89 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
    90 #endif
    91 #if defined(MOZ_WIDGET_ANDROID)
    92 #include "common/linux/file_id.h"
    93 #endif
    95 using google_breakpad::CrashGenerationServer;
    96 using google_breakpad::ClientInfo;
    97 #ifdef XP_LINUX
    98 using google_breakpad::MinidumpDescriptor;
    99 #endif
   100 using namespace mozilla;
   101 using mozilla::dom::CrashReporterChild;
   102 using mozilla::dom::PCrashReporterChild;
   104 namespace CrashReporter {
   106 #ifdef XP_WIN32
   107 typedef wchar_t XP_CHAR;
   108 typedef std::wstring xpstring;
   109 #define CONVERT_XP_CHAR_TO_UTF16(x) x
   110 #define XP_STRLEN(x) wcslen(x)
   111 #define my_strlen strlen
   112 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
   113 #define PATH_SEPARATOR "\\"
   114 #define XP_PATH_SEPARATOR L"\\"
   115 // sort of arbitrary, but MAX_PATH is kinda small
   116 #define XP_PATH_MAX 4096
   117 // "<reporter path>" "<minidump path>"
   118 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
   119 #ifdef _USE_32BIT_TIME_T
   120 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
   121 #else
   122 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
   123 #endif
   124 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
   125 #else
   126 typedef char XP_CHAR;
   127 typedef std::string xpstring;
   128 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
   129 #define CRASH_REPORTER_FILENAME "crashreporter"
   130 #define PATH_SEPARATOR "/"
   131 #define XP_PATH_SEPARATOR "/"
   132 #define XP_PATH_MAX PATH_MAX
   133 #ifdef XP_LINUX
   134 #define XP_STRLEN(x) my_strlen(x)
   135 #define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
   136 #define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
   137 #else
   138 #define XP_STRLEN(x) strlen(x)
   139 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
   140 #define XP_STOA(size, buffer, base) sprintf(buffer, "%zu", size)
   141 #define my_strlen strlen
   142 #define sys_close close
   143 #define sys_fork fork
   144 #define sys_open open
   145 #define sys_write write
   146 #endif
   147 #endif // XP_WIN32
   149 static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
   150                                             '\0'}; // .dmp
   151 static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
   152                                              'r', 'a', '\0'}; // .extra
   154 static const char kCrashMainID[] = "crash.main.1\n";
   156 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
   158 static XP_CHAR* pendingDirectory;
   159 static XP_CHAR* crashReporterPath;
   161 // Where crash events should go.
   162 static XP_CHAR* eventsDirectory;
   164 // If this is false, we don't launch the crash reporter
   165 static bool doReport = true;
   167 // If this is true, we don't have a crash reporter
   168 static bool headlessClient = false;
   170 // if this is true, we pass the exception on to the OS crash reporter
   171 static bool showOSCrashReporter = false;
   173 // The time of the last recorded crash, as a time_t value.
   174 static time_t lastCrashTime = 0;
   175 // The pathname of a file to store the crash time in
   176 static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
   178 // A marker file to hold the path to the last dump written, which
   179 // will be checked on startup.
   180 static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
   182 // Whether we've already looked for the marker file.
   183 static bool lastRunCrashID_checked = false;
   184 // The minidump ID contained in the marker file.
   185 static nsString* lastRunCrashID = nullptr;
   187 #if defined(MOZ_WIDGET_ANDROID)
   188 // on Android 4.2 and above there is a user serial number associated
   189 // with the current process that gets lost when we fork so we need to
   190 // explicitly pass it to am
   191 static char* androidUserSerial = nullptr;
   192 #endif
   194 // these are just here for readability
   195 static const char kCrashTimeParameter[] = "CrashTime=";
   196 static const int kCrashTimeParameterLen = sizeof(kCrashTimeParameter)-1;
   198 static const char kTimeSinceLastCrashParameter[] = "SecondsSinceLastCrash=";
   199 static const int kTimeSinceLastCrashParameterLen =
   200                                      sizeof(kTimeSinceLastCrashParameter)-1;
   202 static const char kSysMemoryParameter[] = "SystemMemoryUsePercentage=";
   203 static const int kSysMemoryParameterLen = sizeof(kSysMemoryParameter)-1;
   205 static const char kTotalVirtualMemoryParameter[] = "TotalVirtualMemory=";
   206 static const int kTotalVirtualMemoryParameterLen =
   207   sizeof(kTotalVirtualMemoryParameter)-1;
   209 static const char kAvailableVirtualMemoryParameter[] = "AvailableVirtualMemory=";
   210 static const int kAvailableVirtualMemoryParameterLen =
   211   sizeof(kAvailableVirtualMemoryParameter)-1;
   213 static const char kOOMAllocationSizeParameter[] = "OOMAllocationSize=";
   214 static const int kOOMAllocationSizeParameterLen =
   215   sizeof(kOOMAllocationSizeParameter)-1;
   217 static const char kTotalPageFileParameter[] = "TotalPageFile=";
   218 static const int kTotalPageFileParameterLen =
   219   sizeof(kTotalPageFileParameter)-1;
   221 static const char kAvailablePageFileParameter[] = "AvailablePageFile=";
   222 static const int kAvailablePageFileParameterLen =
   223   sizeof(kAvailablePageFileParameter)-1;
   225 static const char kTotalPhysicalMemoryParameter[] = "TotalPhysicalMemory=";
   226 static const int kTotalPhysicalMemoryParameterLen =
   227   sizeof(kTotalPhysicalMemoryParameter)-1;
   229 static const char kAvailablePhysicalMemoryParameter[] = "AvailablePhysicalMemory=";
   230 static const int kAvailablePhysicalMemoryParameterLen =
   231   sizeof(kAvailablePhysicalMemoryParameter)-1;
   233 static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
   234 static const int kIsGarbageCollectingParameterLen =
   235   sizeof(kIsGarbageCollectingParameter)-1;
   237 static const char kEventLoopNestingLevelParameter[] = "EventLoopNestingLevel=";
   238 static const int kEventLoopNestingLevelParameterLen =
   239   sizeof(kEventLoopNestingLevelParameter)-1;
   241 #ifdef XP_WIN
   242 static const char kBreakpadReserveAddressParameter[] = "BreakpadReserveAddress=";
   243 static const int kBreakpadReserveAddressParameterLen =
   244   sizeof(kBreakpadReserveAddressParameter)-1;
   246 static const char kBreakpadReserveSizeParameter[] = "BreakpadReserveSize=";
   247 static const int kBreakpadReserveSizeParameterLen =
   248   sizeof(kBreakpadReserveSizeParameter)-1;
   249 #endif
   251 // this holds additional data sent via the API
   252 static Mutex* crashReporterAPILock;
   253 static Mutex* notesFieldLock;
   254 static AnnotationTable* crashReporterAPIData_Hash;
   255 static nsCString* crashReporterAPIData = nullptr;
   256 static nsCString* notesField = nullptr;
   257 static bool isGarbageCollecting;
   258 static uint32_t eventloopNestingLevel = 0;
   260 // Avoid a race during application termination.
   261 static Mutex* dumpSafetyLock;
   262 static bool isSafeToDump = false;
   264 // OOP crash reporting
   265 static CrashGenerationServer* crashServer; // chrome process has this
   267 #  if defined(XP_WIN) || defined(XP_MACOSX)
   268 // If crash reporting is disabled, we hand out this "null" pipe to the
   269 // child process and don't attempt to connect to a parent server.
   270 static const char kNullNotifyPipe[] = "-";
   271 static char* childCrashNotifyPipe;
   273 #  elif defined(XP_LINUX)
   274 static int serverSocketFd = -1;
   275 static int clientSocketFd = -1;
   276 static const int kMagicChildCrashReportFd = 4;
   278 #  endif
   280 // |dumpMapLock| must protect all access to |pidToMinidump|.
   281 static Mutex* dumpMapLock;
   282 struct ChildProcessData : public nsUint32HashKey
   283 {
   284   ChildProcessData(KeyTypePointer aKey)
   285     : nsUint32HashKey(aKey)
   286     , sequence(0)
   287 #ifdef MOZ_CRASHREPORTER_INJECTOR
   288     , callback(nullptr)
   289 #endif
   290   { }
   292   nsCOMPtr<nsIFile> minidump;
   293   // Each crashing process is assigned an increasing sequence number to
   294   // indicate which process crashed first.
   295   uint32_t sequence;
   296 #ifdef MOZ_CRASHREPORTER_INJECTOR
   297   InjectorCrashCallback* callback;
   298 #endif
   299 };
   301 typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
   302 static ChildMinidumpMap* pidToMinidump;
   303 static uint32_t crashSequence;
   304 static bool OOPInitialized();
   306 #ifdef MOZ_CRASHREPORTER_INJECTOR
   307 static nsIThread* sInjectorThread;
   309 class ReportInjectedCrash : public nsRunnable
   310 {
   311 public:
   312   ReportInjectedCrash(uint32_t pid) : mPID(pid) { }
   314   NS_IMETHOD Run();
   316 private:
   317   uint32_t mPID;
   318 };
   319 #endif // MOZ_CRASHREPORTER_INJECTOR
   321 // Crashreporter annotations that we don't send along in subprocess
   322 // reports
   323 static const char* kSubprocessBlacklist[] = {
   324   "FramePoisonBase",
   325   "FramePoisonSize",
   326   "StartupTime",
   327   "URL"
   328 };
   330 // If annotations are attempted before the crash reporter is enabled,
   331 // they queue up here.
   332 class DelayedNote;
   333 nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
   335 #if defined(XP_WIN)
   336 // the following are used to prevent other DLLs reverting the last chance
   337 // exception handler to the windows default. Any attempt to change the 
   338 // unhandled exception filter or to reset it is ignored and our crash
   339 // reporter is loaded instead (in case it became unloaded somehow)
   340 typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
   341   (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
   342 static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
   343 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = nullptr;
   344 static WindowsDllInterceptor gKernel32Intercept;
   345 static bool gBlockUnhandledExceptionFilter = true;
   347 static void NotePreviousUnhandledExceptionFilter()
   348 {
   349   // Set a dummy value to get the previous filter, then restore
   350   previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(nullptr);
   351   SetUnhandledExceptionFilter(previousUnhandledExceptionFilter);
   352 }
   354 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
   355 patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
   356 {
   357   if (!gBlockUnhandledExceptionFilter) {
   358     // don't intercept
   359     return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
   360   }
   362   if (lpTopLevelExceptionFilter == previousUnhandledExceptionFilter) {
   363     // OK to swap back and forth between the previous filter
   364     previousUnhandledExceptionFilter =
   365       stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
   366     return previousUnhandledExceptionFilter;
   367   }
   369   // intercept attempts to change the filter
   370   return nullptr;
   371 }
   373 /**
   374  * Reserve some VM space. In the event that we crash because VM space is
   375  * being leaked without leaking memory, freeing this space before taking
   376  * the minidump will allow us to collect a minidump.
   377  *
   378  * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
   379  * allocations.
   380  */
   381 static const SIZE_T kReserveSize = 0x2800000; // 40 MB
   382 static void* gBreakpadReservedVM;
   383 #endif
   385 #ifdef XP_MACOSX
   386 static cpu_type_t pref_cpu_types[2] = {
   387 #if defined(__i386__)
   388                                  CPU_TYPE_X86,
   389 #elif defined(__x86_64__)
   390                                  CPU_TYPE_X86_64,
   391 #elif defined(__ppc__)
   392                                  CPU_TYPE_POWERPC,
   393 #endif
   394                                  CPU_TYPE_ANY };
   396 static posix_spawnattr_t spawnattr;
   397 #endif
   399 #if defined(MOZ_WIDGET_ANDROID)
   400 // Android builds use a custom library loader,
   401 // so the embedding will provide a list of shared
   402 // libraries that are mapped into anonymous mappings.
   403 typedef struct {
   404   std::string name;
   405   uintptr_t   start_address;
   406   size_t      length;
   407   size_t      file_offset;
   408 } mapping_info;
   409 static std::vector<mapping_info> library_mappings;
   410 typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
   411 #endif
   413 #ifdef XP_LINUX
   414 inline void
   415 my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
   416 {
   417   my_memset(buffer, 0, buffer_length);
   418   my_uitos(buffer, t, my_uint_len(t));
   419 }
   420 #endif
   422 #ifdef XP_WIN
   423 static void
   424 CreateFileFromPath(const xpstring& path, nsIFile** file)
   425 {
   426   NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
   427 }
   428 #else
   429 static void
   430 CreateFileFromPath(const xpstring& path, nsIFile** file)
   431 {
   432   NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
   433 }
   434 #endif
   436 static XP_CHAR*
   437 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
   438 {
   439   int appendLen = XP_STRLEN(toAppend);
   440   if (appendLen >= *size) appendLen = *size - 1;
   442   memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
   443   str += appendLen;
   444   *str = '\0';
   445   *size -= appendLen;
   447   return str;
   448 }
   450 static size_t gOOMAllocationSize = 0;
   452 void AnnotateOOMAllocationSize(size_t size)
   453 {
   454   gOOMAllocationSize = size;
   455 }
   457 bool MinidumpCallback(
   458 #ifdef XP_LINUX
   459                       const MinidumpDescriptor& descriptor,
   460 #else
   461                       const XP_CHAR* dump_path,
   462                       const XP_CHAR* minidump_id,
   463 #endif
   464                       void* context,
   465 #ifdef XP_WIN32
   466                       EXCEPTION_POINTERS* exinfo,
   467                       MDRawAssertionInfo* assertion,
   468 #endif
   469                       bool succeeded)
   470 {
   471   bool returnValue = showOSCrashReporter ? false : succeeded;
   473   static XP_CHAR minidumpPath[XP_PATH_MAX];
   474   int size = XP_PATH_MAX;
   475   XP_CHAR* p;
   476 #ifndef XP_LINUX
   477   p = Concat(minidumpPath, dump_path, &size);
   478   p = Concat(p, XP_PATH_SEPARATOR, &size);
   479   p = Concat(p, minidump_id, &size);
   480   Concat(p, dumpFileExtension, &size);
   481 #else
   482   Concat(minidumpPath, descriptor.path(), &size);
   483 #endif
   485   static XP_CHAR extraDataPath[XP_PATH_MAX];
   486   size = XP_PATH_MAX;
   487 #ifndef XP_LINUX
   488   p = Concat(extraDataPath, dump_path, &size);
   489   p = Concat(p, XP_PATH_SEPARATOR, &size);
   490   p = Concat(p, minidump_id, &size);
   491 #else
   492   p = Concat(extraDataPath, descriptor.path(), &size);
   493   // Skip back past the .dmp extension.
   494   p -= 4;
   495 #endif
   496   Concat(p, extraFileExtension, &size);
   498   if (headlessClient) {
   499     // Leave a marker indicating that there was a crash.
   500 #if defined(XP_WIN32)
   501     HANDLE hFile = CreateFile(crashMarkerFilename, GENERIC_WRITE, 0,
   502                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
   503                               nullptr);
   504     if(hFile != INVALID_HANDLE_VALUE) {
   505       DWORD nBytes;
   506       WriteFile(hFile, minidumpPath, 2*wcslen(minidumpPath), &nBytes, nullptr);
   507       CloseHandle(hFile);
   508     }
   509 #elif defined(XP_UNIX)
   510     int fd = sys_open(crashMarkerFilename,
   511                       O_WRONLY | O_CREAT | O_TRUNC,
   512                       0600);
   513     if (fd != -1) {
   514       unused << sys_write(fd, minidumpPath, my_strlen(minidumpPath));
   515       sys_close(fd);
   516     }
   517 #endif
   518   }
   520   char oomAllocationSizeBuffer[32];
   521   int oomAllocationSizeBufferLen = 0;
   522   if (gOOMAllocationSize) {
   523     XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
   524     oomAllocationSizeBufferLen = my_strlen(oomAllocationSizeBuffer);
   525   }
   527   // calculate time since last crash (if possible), and store
   528   // the time of this crash.
   529   time_t crashTime;
   530 #ifdef XP_LINUX
   531   struct kernel_timeval tv;
   532   sys_gettimeofday(&tv, nullptr);
   533   crashTime = tv.tv_sec;
   534 #else
   535   crashTime = time(nullptr);
   536 #endif
   537   time_t timeSinceLastCrash = 0;
   538   // stringified versions of the above
   539   char crashTimeString[32];
   540   int crashTimeStringLen = 0;
   541   char timeSinceLastCrashString[32];
   542   int timeSinceLastCrashStringLen = 0;
   544   XP_TTOA(crashTime, crashTimeString, 10);
   545   crashTimeStringLen = my_strlen(crashTimeString);
   546   if (lastCrashTime != 0) {
   547     timeSinceLastCrash = crashTime - lastCrashTime;
   548     XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
   549     timeSinceLastCrashStringLen = my_strlen(timeSinceLastCrashString);
   550   }
   551   // write crash time to file
   552   if (lastCrashTimeFilename[0] != 0) {
   553 #if defined(XP_WIN32)
   554     HANDLE hFile = CreateFile(lastCrashTimeFilename, GENERIC_WRITE, 0,
   555                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
   556                               nullptr);
   557     if(hFile != INVALID_HANDLE_VALUE) {
   558       DWORD nBytes;
   559       WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
   560       CloseHandle(hFile);
   561     }
   562 #elif defined(XP_UNIX)
   563     int fd = sys_open(lastCrashTimeFilename,
   564                       O_WRONLY | O_CREAT | O_TRUNC,
   565                       0600);
   566     if (fd != -1) {
   567       unused << sys_write(fd, crashTimeString, crashTimeStringLen);
   568       sys_close(fd);
   569     }
   570 #endif
   571   }
   573   // Write crash event file.
   575   // Minidump IDs are UUIDs (36) + NULL.
   576   static char id_ascii[37];
   577 #ifdef XP_LINUX
   578   const char * index = strrchr(descriptor.path(), '/');
   579   MOZ_ASSERT(index);
   580   MOZ_ASSERT(strlen(index) == 1 + 36 + 4); // "/" + UUID + ".dmp"
   581   for (uint32_t i = 0; i < 36; i++) {
   582     id_ascii[i] = *(index + 1 + i);
   583   }
   584 #else
   585   MOZ_ASSERT(XP_STRLEN(minidump_id) == 36);
   586   for (uint32_t i = 0; i < 36; i++) {
   587     id_ascii[i] = *((char *)(minidump_id + i));
   588   }
   589 #endif
   591   if (eventsDirectory) {
   592     static XP_CHAR crashEventPath[XP_PATH_MAX];
   593     int size = XP_PATH_MAX;
   594     XP_CHAR* p;
   595     p = Concat(crashEventPath, eventsDirectory, &size);
   596     p = Concat(p, XP_PATH_SEPARATOR, &size);
   597 #ifdef XP_LINUX
   598     p = Concat(p, id_ascii, &size);
   599 #else
   600     p = Concat(p, minidump_id, &size);
   601 #endif
   603 #if defined(XP_WIN32)
   604     HANDLE hFile = CreateFile(crashEventPath, GENERIC_WRITE, 0,
   605                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
   606                               nullptr);
   607     if (hFile != INVALID_HANDLE_VALUE) {
   608       DWORD nBytes;
   609       WriteFile(hFile, kCrashMainID, sizeof(kCrashMainID) - 1, &nBytes,
   610                 nullptr);
   611       WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
   612       WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   613       WriteFile(hFile, id_ascii, strlen(id_ascii), &nBytes, nullptr);
   614       CloseHandle(hFile);
   615     }
   616 #elif defined(XP_UNIX)
   617     int fd = sys_open(crashEventPath,
   618                       O_WRONLY | O_CREAT | O_TRUNC,
   619                       0600);
   620     if (fd != -1) {
   621       unused << sys_write(fd, kCrashMainID, sizeof(kCrashMainID) - 1);
   622       unused << sys_write(fd, crashTimeString, crashTimeStringLen);
   623       unused << sys_write(fd, "\n", 1);
   624       unused << sys_write(fd, id_ascii, strlen(id_ascii));
   625       sys_close(fd);
   626     }
   627 #endif
   628   }
   630 #if defined(XP_WIN32)
   631   if (!crashReporterAPIData->IsEmpty()) {
   632     // write out API data
   633     HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
   634                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
   635                               nullptr);
   636     if(hFile != INVALID_HANDLE_VALUE) {
   637       DWORD nBytes;
   638       WriteFile(hFile, crashReporterAPIData->get(),
   639                 crashReporterAPIData->Length(), &nBytes, nullptr);
   640       WriteFile(hFile, kCrashTimeParameter, kCrashTimeParameterLen,
   641                 &nBytes, nullptr);
   642       WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
   643       WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   644       if (timeSinceLastCrash != 0) {
   645         WriteFile(hFile, kTimeSinceLastCrashParameter,
   646                   kTimeSinceLastCrashParameterLen, &nBytes, nullptr);
   647         WriteFile(hFile, timeSinceLastCrashString, timeSinceLastCrashStringLen,
   648                   &nBytes, nullptr);
   649         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   650       }
   651       if (isGarbageCollecting) {
   652         WriteFile(hFile, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen,
   653                   &nBytes, nullptr);
   654         WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, nullptr);
   655         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   656       }
   658       char buffer[128];
   659       int bufferLen;
   661       if (eventloopNestingLevel > 0) {
   662         WriteFile(hFile, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen,
   663                   &nBytes, nullptr);
   664         _ultoa(eventloopNestingLevel, buffer, 10);
   665         WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
   666         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   667       }
   669       if (gBreakpadReservedVM) {
   670         WriteFile(hFile, kBreakpadReserveAddressParameter, kBreakpadReserveAddressParameterLen, &nBytes, nullptr);
   671         _ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
   672         WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
   673         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   674         WriteFile(hFile, kBreakpadReserveSizeParameter, kBreakpadReserveSizeParameterLen, &nBytes, nullptr);
   675         _ui64toa(kReserveSize, buffer, 10);
   676         WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
   677         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   678       }
   680 #ifdef HAS_DLL_BLOCKLIST
   681       DllBlocklist_WriteNotes(hFile);
   682 #endif
   684       // Try to get some information about memory.
   685       MEMORYSTATUSEX statex;
   686       statex.dwLength = sizeof(statex);
   687       if (GlobalMemoryStatusEx(&statex)) {
   689 #define WRITE_STATEX_FIELD(field, paramName, conversionFunc)     \
   690         WriteFile(hFile, k##paramName##Parameter,                \
   691                   k##paramName##ParameterLen, &nBytes, nullptr); \
   692         conversionFunc(statex.field, buffer, 10);                \
   693         bufferLen = strlen(buffer);                              \
   694         WriteFile(hFile, buffer, bufferLen, &nBytes, nullptr);   \
   695         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   697         WRITE_STATEX_FIELD(dwMemoryLoad, SysMemory, ltoa);
   698         WRITE_STATEX_FIELD(ullTotalVirtual, TotalVirtualMemory, _ui64toa);
   699         WRITE_STATEX_FIELD(ullAvailVirtual, AvailableVirtualMemory, _ui64toa);
   700         WRITE_STATEX_FIELD(ullTotalPageFile, TotalPageFile, _ui64toa);
   701         WRITE_STATEX_FIELD(ullAvailPageFile, AvailablePageFile, _ui64toa);
   702         WRITE_STATEX_FIELD(ullTotalPhys, TotalPhysicalMemory, _ui64toa);
   703         WRITE_STATEX_FIELD(ullAvailPhys, AvailablePhysicalMemory, _ui64toa);
   705 #undef WRITE_STATEX_FIELD
   706       }
   708       if (oomAllocationSizeBufferLen) {
   709         WriteFile(hFile, kOOMAllocationSizeParameter,
   710                   kOOMAllocationSizeParameterLen, &nBytes, nullptr);
   711         WriteFile(hFile, oomAllocationSizeBuffer, oomAllocationSizeBufferLen,
   712                   &nBytes, nullptr);
   713         WriteFile(hFile, "\n", 1, &nBytes, nullptr);
   714       }
   715       CloseHandle(hFile);
   716     }
   717   }
   719   if (!doReport) {
   720     return returnValue;
   721   }
   723   XP_CHAR cmdLine[CMDLINE_SIZE];
   724   size = CMDLINE_SIZE;
   725   p = Concat(cmdLine, L"\"", &size);
   726   p = Concat(p, crashReporterPath, &size);
   727   p = Concat(p, L"\" \"", &size);
   728   p = Concat(p, minidumpPath, &size);
   729   Concat(p, L"\"", &size);
   731   STARTUPINFO si;
   732   PROCESS_INFORMATION pi;
   734   ZeroMemory(&si, sizeof(si));
   735   si.cb = sizeof(si);
   736   si.dwFlags = STARTF_USESHOWWINDOW;
   737   si.wShowWindow = SW_SHOWNORMAL;
   738   ZeroMemory(&pi, sizeof(pi));
   740   if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
   741                     nullptr, nullptr, &si, &pi)) {
   742     CloseHandle( pi.hProcess );
   743     CloseHandle( pi.hThread );
   744   }
   745   // we're not really in a position to do anything if the CreateProcess fails
   746   TerminateProcess(GetCurrentProcess(), 1);
   747 #elif defined(XP_UNIX)
   748   if (!crashReporterAPIData->IsEmpty()) {
   749     // write out API data
   750     int fd = sys_open(extraDataPath,
   751                       O_WRONLY | O_CREAT | O_TRUNC,
   752                       0666);
   754     if (fd != -1) {
   755       // not much we can do in case of error
   756       unused << sys_write(fd, crashReporterAPIData->get(),
   757                                   crashReporterAPIData->Length());
   758       unused << sys_write(fd, kCrashTimeParameter, kCrashTimeParameterLen);
   759       unused << sys_write(fd, crashTimeString, crashTimeStringLen);
   760       unused << sys_write(fd, "\n", 1);
   761       if (timeSinceLastCrash != 0) {
   762         unused << sys_write(fd, kTimeSinceLastCrashParameter,
   763                         kTimeSinceLastCrashParameterLen);
   764         unused << sys_write(fd, timeSinceLastCrashString,
   765                         timeSinceLastCrashStringLen);
   766         unused << sys_write(fd, "\n", 1);
   767       }
   768       if (isGarbageCollecting) {
   769         unused << sys_write(fd, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen);
   770         unused << sys_write(fd, isGarbageCollecting ? "1" : "0", 1);
   771         unused << sys_write(fd, "\n", 1);
   772       }
   773       if (eventloopNestingLevel > 0) {
   774         unused << sys_write(fd, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen);
   775         char buffer[16];
   776         XP_TTOA(eventloopNestingLevel, buffer, 10);
   777         unused << sys_write(fd, buffer, my_strlen(buffer));
   778         unused << sys_write(fd, "\n", 1);
   779       }
   780       if (oomAllocationSizeBufferLen) {
   781         unused << sys_write(fd, kOOMAllocationSizeParameter,
   782                             kOOMAllocationSizeParameterLen);
   783         unused << sys_write(fd, oomAllocationSizeBuffer,
   784                             oomAllocationSizeBufferLen);
   785         unused << sys_write(fd, "\n", 1);
   786       }
   787       sys_close(fd);
   788     }
   789   }
   791   if (!doReport) {
   792     return returnValue;
   793   }
   795 #ifdef XP_MACOSX
   796   char* const my_argv[] = {
   797     crashReporterPath,
   798     minidumpPath,
   799     nullptr
   800   };
   802   char **env = nullptr;
   803   char ***nsEnv = _NSGetEnviron();
   804   if (nsEnv)
   805     env = *nsEnv;
   806   int result = posix_spawnp(nullptr,
   807                             my_argv[0],
   808                             nullptr,
   809                             &spawnattr,
   810                             my_argv,
   811                             env);
   813   if (result != 0)
   814     return false;
   816 #else // !XP_MACOSX
   817   pid_t pid = sys_fork();
   819   if (pid == -1)
   820     return false;
   821   else if (pid == 0) {
   822 #if !defined(MOZ_WIDGET_ANDROID)
   823     // need to clobber this, as libcurl might load NSS,
   824     // and we want it to load the system NSS.
   825     unsetenv("LD_LIBRARY_PATH");
   826     unused << execl(crashReporterPath,
   827                     crashReporterPath, minidumpPath, (char*)0);
   828 #else
   829     // Invoke the reportCrash activity using am
   830     if (androidUserSerial) {
   831       unused << execlp("/system/bin/am",
   832                        "/system/bin/am",
   833                        "start",
   834                        "--user", androidUserSerial,
   835                        "-a", "org.mozilla.gecko.reportCrash",
   836                        "-n", crashReporterPath,
   837                        "--es", "minidumpPath", minidumpPath,
   838                        (char*)0);
   839     } else {
   840       unused << execlp("/system/bin/am",
   841                        "/system/bin/am",
   842                        "start",
   843                        "-a", "org.mozilla.gecko.reportCrash",
   844                        "-n", crashReporterPath,
   845                        "--es", "minidumpPath", minidumpPath,
   846                        (char*)0);
   847     }
   848 #endif
   849     _exit(1);
   850   }
   851 #endif // XP_MACOSX
   852 #endif // XP_UNIX
   854   return returnValue;
   855 }
   857 #ifdef XP_WIN
   858 static void
   859 ReserveBreakpadVM()
   860 {
   861   if (!gBreakpadReservedVM) {
   862     gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
   863                                        PAGE_NOACCESS);
   864   }
   865 }
   867 static void
   868 FreeBreakpadVM()
   869 {
   870   if (gBreakpadReservedVM) {
   871     VirtualFree(gBreakpadReservedVM, 0, MEM_RELEASE);
   872   }
   873 }
   875 /**
   876  * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
   877  * and should not be handled as crashes.
   878  *
   879  * Also calls FreeBreakpadVM if appropriate.
   880  */
   881 static bool FPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
   882                       MDRawAssertionInfo* assertion)
   883 {
   884   if (!exinfo) {
   885     mozilla::IOInterposer::Disable();
   886     FreeBreakpadVM();
   887     return true;
   888   }
   890   PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)exinfo->ExceptionRecord;
   891   switch (e->ExceptionCode) {
   892     case STATUS_FLOAT_DENORMAL_OPERAND:
   893     case STATUS_FLOAT_DIVIDE_BY_ZERO:
   894     case STATUS_FLOAT_INEXACT_RESULT:
   895     case STATUS_FLOAT_INVALID_OPERATION:
   896     case STATUS_FLOAT_OVERFLOW:
   897     case STATUS_FLOAT_STACK_CHECK:
   898     case STATUS_FLOAT_UNDERFLOW:
   899     case STATUS_FLOAT_MULTIPLE_FAULTS:
   900     case STATUS_FLOAT_MULTIPLE_TRAPS:
   901       return false; // Don't write minidump, continue exception search
   902   }
   903   mozilla::IOInterposer::Disable();
   904   FreeBreakpadVM();
   905   return true;
   906 }
   907 #endif // XP_WIN
   909 static bool ShouldReport()
   910 {
   911   // this environment variable prevents us from launching
   912   // the crash reporter client
   913   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
   914   if (envvar && *envvar) {
   915     return false;
   916   }
   918   envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
   919   if (envvar && *envvar) {
   920     return false;
   921   }
   923   return true;
   924 }
   926 namespace {
   927   bool Filter(void* context) {
   928     mozilla::IOInterposer::Disable();
   929     return true;
   930   }
   931 }
   934 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
   935                              bool force/*=false*/)
   936 {
   937   if (gExceptionHandler)
   938     return NS_ERROR_ALREADY_INITIALIZED;
   940 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
   941   // In non-debug builds, enable the crash reporter by default, and allow
   942   // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
   943   // Also enable it by default in debug gonk builds as it is difficult to
   944   // set environment on startup.
   945   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
   946   if (envvar && *envvar && !force)
   947     return NS_OK;
   948 #else
   949   // In debug builds, disable the crash reporter by default, and allow to
   950   // enable it with the MOZ_CRASHREPORTER environment variable.
   951   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER");
   952   if ((!envvar || !*envvar) && !force)
   953     return NS_OK;
   954 #endif
   956 #if defined(MOZ_WIDGET_GONK)
   957   doReport = false;
   958   headlessClient = true;
   959 #elif defined(XP_WIN)
   960   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
   961     doReport = ShouldReport();
   962   } else {
   963     doReport = false;
   964     headlessClient = true;
   965   }
   966 #else
   967   // this environment variable prevents us from launching
   968   // the crash reporter client
   969   doReport = ShouldReport();
   970 #endif
   972   // allocate our strings
   973   crashReporterAPIData = new nsCString();
   974   NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY);
   976   NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
   977   crashReporterAPILock = new Mutex("crashReporterAPILock");
   978   NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
   979   notesFieldLock = new Mutex("notesFieldLock");
   981   crashReporterAPIData_Hash =
   982     new nsDataHashtable<nsCStringHashKey,nsCString>();
   983   NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
   985   notesField = new nsCString();
   986   NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
   988   if (!headlessClient) {
   989     // locate crashreporter executable
   990     nsCOMPtr<nsIFile> exePath;
   991     nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
   992     NS_ENSURE_SUCCESS(rv, rv);
   994 #if defined(XP_MACOSX)
   995     exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
   996     exePath->Append(NS_LITERAL_STRING("Contents"));
   997     exePath->Append(NS_LITERAL_STRING("MacOS"));
   998 #endif
  1000     exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
  1002 #ifdef XP_WIN32
  1003     nsString crashReporterPath_temp;
  1005     exePath->GetPath(crashReporterPath_temp);
  1006     crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
  1007 #elif !defined(__ANDROID__)
  1008     nsCString crashReporterPath_temp;
  1010     exePath->GetNativePath(crashReporterPath_temp);
  1011     crashReporterPath = ToNewCString(crashReporterPath_temp);
  1012 #else
  1013     // On Android, we launch using the application package name
  1014     // instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
  1015     nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
  1016     crashReporterPath = ToNewCString(package);
  1017 #endif
  1020   // get temp path to use for minidump path
  1021 #if defined(XP_WIN32)
  1022   nsString tempPath;
  1024   // first figure out buffer size
  1025   int pathLen = GetTempPath(0, nullptr);
  1026   if (pathLen == 0)
  1027     return NS_ERROR_FAILURE;
  1029   tempPath.SetLength(pathLen);
  1030   GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
  1031 #elif defined(XP_MACOSX)
  1032   nsCString tempPath;
  1033   FSRef fsRef;
  1034   OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
  1035                            kCreateFolder, &fsRef);
  1036   if (err != noErr)
  1037     return NS_ERROR_FAILURE;
  1039   char path[PATH_MAX];
  1040   OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
  1041   if (status != noErr)
  1042     return NS_ERROR_FAILURE;
  1044   tempPath = path;
  1046 #elif defined(__ANDROID__)
  1047   // GeckoAppShell or Gonk's init.rc sets this in the environment
  1048   const char *tempenv = PR_GetEnv("TMPDIR");
  1049   if (!tempenv)
  1050     return NS_ERROR_FAILURE;
  1051   nsCString tempPath(tempenv);
  1052 #elif defined(XP_UNIX)
  1053   // we assume it's always /tmp on unix systems
  1054   nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
  1055 #else
  1056 #error "Implement this for your platform"
  1057 #endif
  1059 #ifdef XP_MACOSX
  1060   // Initialize spawn attributes, since this calls malloc.
  1061   if (posix_spawnattr_init(&spawnattr) != 0) {
  1062     return NS_ERROR_FAILURE;
  1065   // Set spawn attributes.
  1066   size_t attr_count = ArrayLength(pref_cpu_types);
  1067   size_t attr_ocount = 0;
  1068   if (posix_spawnattr_setbinpref_np(&spawnattr,
  1069                                     attr_count,
  1070                                     pref_cpu_types,
  1071                                     &attr_ocount) != 0 ||
  1072       attr_ocount != attr_count) {
  1073     posix_spawnattr_destroy(&spawnattr);
  1074     return NS_ERROR_FAILURE;
  1076 #endif
  1078 #ifdef XP_WIN32
  1079   ReserveBreakpadVM();
  1081   MINIDUMP_TYPE minidump_type = MiniDumpNormal;
  1083   // Try to determine what version of dbghelp.dll we're using.
  1084   // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
  1086   DWORD version_size = GetFileVersionInfoSizeW(L"dbghelp.dll", nullptr);
  1087   if (version_size > 0) {
  1088     std::vector<BYTE> buffer(version_size);
  1089     if (GetFileVersionInfoW(L"dbghelp.dll",
  1090                             0,
  1091                             version_size,
  1092                             &buffer[0])) {
  1093       UINT len;
  1094       VS_FIXEDFILEINFO* file_info;
  1095       VerQueryValue(&buffer[0], L"\\", (void**)&file_info, &len);
  1096       WORD major = HIWORD(file_info->dwFileVersionMS),
  1097            minor = LOWORD(file_info->dwFileVersionMS),
  1098            revision = HIWORD(file_info->dwFileVersionLS);
  1099       if (major > 6 || (major == 6 && minor > 1) ||
  1100           (major == 6 && minor == 1 && revision >= 7600)) {
  1101         minidump_type = MiniDumpWithFullMemoryInfo;
  1106   const char* e = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
  1107   if (e && *e) {
  1108     minidump_type = MiniDumpWithFullMemory;
  1110 #endif // XP_WIN32
  1112 #ifdef MOZ_WIDGET_ANDROID
  1113   androidUserSerial = getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
  1114 #endif
  1116   // Initialize the flag and mutex used to avoid dump processing
  1117   // once browser termination has begun.
  1118   NS_ASSERTION(!dumpSafetyLock, "Shouldn't have a lock yet");
  1119   // Do not deallocate this lock while it is still possible for
  1120   // isSafeToDump to be tested on another thread.
  1121   dumpSafetyLock = new Mutex("dumpSafetyLock");
  1122   MutexAutoLock lock(*dumpSafetyLock);
  1123   isSafeToDump = true;
  1125   // now set the exception handler
  1126 #ifdef XP_LINUX
  1127   MinidumpDescriptor descriptor(tempPath.get());
  1128 #endif
  1130 #ifdef XP_WIN
  1131   NotePreviousUnhandledExceptionFilter();
  1132 #endif
  1134   gExceptionHandler = new google_breakpad::
  1135     ExceptionHandler(
  1136 #ifdef XP_LINUX
  1137                      descriptor,
  1138 #else
  1139                      tempPath.get(),
  1140 #endif
  1142 #ifdef XP_WIN
  1143                      FPEFilter,
  1144 #else
  1145                      Filter,
  1146 #endif
  1147                      MinidumpCallback,
  1148                      nullptr,
  1149 #ifdef XP_WIN32
  1150                      google_breakpad::ExceptionHandler::HANDLER_ALL,
  1151                      minidump_type,
  1152                      (const wchar_t*) nullptr,
  1153                      nullptr);
  1154 #else
  1155                      true
  1156 #ifdef XP_MACOSX
  1157                        , nullptr
  1158 #endif
  1159 #ifdef XP_LINUX
  1160                        , -1
  1161 #endif
  1162                       );
  1163 #endif // XP_WIN32
  1165   if (!gExceptionHandler)
  1166     return NS_ERROR_OUT_OF_MEMORY;
  1168 #ifdef XP_WIN
  1169   gExceptionHandler->set_handle_debug_exceptions(true);
  1171   // protect the crash reporter from being unloaded
  1172   gBlockUnhandledExceptionFilter = true;
  1173   gKernel32Intercept.Init("kernel32.dll");
  1174   bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
  1175           reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
  1176           (void**) &stub_SetUnhandledExceptionFilter);
  1178 #ifdef DEBUG
  1179   if (!ok)
  1180     printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
  1181 #endif
  1182 #endif
  1184   // store application start time
  1185   char timeString[32];
  1186   time_t startupTime = time(nullptr);
  1187   XP_TTOA(startupTime, timeString, 10);
  1188   AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
  1189                       nsDependentCString(timeString));
  1191 #if defined(XP_MACOSX)
  1192   // On OS X, many testers like to see the OS crash reporting dialog
  1193   // since it offers immediate stack traces.  We allow them to set
  1194   // a default to pass exceptions to the OS handler.
  1195   Boolean keyExistsAndHasValidFormat = false;
  1196   Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
  1197                                                         kCFPreferencesCurrentApplication,
  1198                                                         &keyExistsAndHasValidFormat);
  1199   if (keyExistsAndHasValidFormat)
  1200     showOSCrashReporter = prefValue;
  1201 #endif
  1203 #if defined(MOZ_WIDGET_ANDROID)
  1204   for (unsigned int i = 0; i < library_mappings.size(); i++) {
  1205     u_int8_t guid[sizeof(MDGUID)];
  1206     google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)library_mappings[i].start_address, guid);
  1207     gExceptionHandler->AddMappingInfo(library_mappings[i].name,
  1208                                       guid,
  1209                                       library_mappings[i].start_address,
  1210                                       library_mappings[i].length,
  1211                                       library_mappings[i].file_offset);
  1213 #endif
  1215   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
  1217   return NS_OK;
  1220 bool GetEnabled()
  1222   return gExceptionHandler != nullptr;
  1225 bool GetMinidumpPath(nsAString& aPath)
  1227   if (!gExceptionHandler)
  1228     return false;
  1230 #ifndef XP_LINUX
  1231   aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
  1232 #else
  1233   aPath = CONVERT_XP_CHAR_TO_UTF16(
  1234       gExceptionHandler->minidump_descriptor().directory().c_str());
  1235 #endif
  1236   return true;
  1239 nsresult SetMinidumpPath(const nsAString& aPath)
  1241   if (!gExceptionHandler)
  1242     return NS_ERROR_NOT_INITIALIZED;
  1244 #ifdef XP_WIN32
  1245   gExceptionHandler->set_dump_path(char16ptr_t(aPath.BeginReading()));
  1246 #elif defined(XP_LINUX)
  1247   gExceptionHandler->set_minidump_descriptor(
  1248       MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath).BeginReading()));
  1249 #else
  1250   gExceptionHandler->set_dump_path(NS_ConvertUTF16toUTF8(aPath).BeginReading());
  1251 #endif
  1252   return NS_OK;
  1255 static nsresult
  1256 WriteDataToFile(nsIFile* aFile, const nsACString& data)
  1258   PRFileDesc* fd;
  1259   nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
  1260   NS_ENSURE_SUCCESS(rv, rv);
  1262   rv = NS_OK;
  1263   if (PR_Write(fd, data.Data(), data.Length()) == -1) {
  1264     rv = NS_ERROR_FAILURE;
  1266   PR_Close(fd);
  1267   return rv;
  1270 static nsresult
  1271 GetFileContents(nsIFile* aFile, nsACString& data)
  1273   PRFileDesc* fd;
  1274   nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
  1275   NS_ENSURE_SUCCESS(rv, rv);
  1277   rv = NS_OK;
  1278   int32_t filesize = PR_Available(fd);
  1279   if (filesize <= 0) {
  1280     rv = NS_ERROR_FILE_NOT_FOUND;
  1282   else {
  1283     data.SetLength(filesize);
  1284     if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
  1285       rv = NS_ERROR_FAILURE;
  1288   PR_Close(fd);
  1289   return rv;
  1292 // Function typedef for initializing a piece of data that we
  1293 // don't already have.
  1294 typedef nsresult (*InitDataFunc)(nsACString&);
  1296 // Attempt to read aFile's contents into aContents, if aFile
  1297 // does not exist, create it and initialize its contents
  1298 // by calling aInitFunc for the data.
  1299 static nsresult
  1300 GetOrInit(nsIFile* aDir, const nsACString& filename,
  1301           nsACString& aContents, InitDataFunc aInitFunc)
  1303   bool exists;
  1305   nsCOMPtr<nsIFile> dataFile;
  1306   nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
  1307   NS_ENSURE_SUCCESS(rv, rv);
  1309   rv = dataFile->AppendNative(filename);
  1310   NS_ENSURE_SUCCESS(rv, rv);
  1312   rv = dataFile->Exists(&exists);
  1313   NS_ENSURE_SUCCESS(rv, rv);
  1315   if (!exists) {
  1316     if (aInitFunc) {
  1317       // get the initial value and write it to the file
  1318       rv = aInitFunc(aContents);
  1319       NS_ENSURE_SUCCESS(rv, rv);
  1320       rv = WriteDataToFile(dataFile, aContents);
  1322     else {
  1323       // didn't pass in an init func
  1324       rv = NS_ERROR_FAILURE;
  1327   else {
  1328     // just get the file's contents
  1329     rv = GetFileContents(dataFile, aContents);
  1332   return rv;
  1335 // Init the "install time" data.  We're taking an easy way out here
  1336 // and just setting this to "the time when this version was first run".
  1337 static nsresult
  1338 InitInstallTime(nsACString& aInstallTime)
  1340   time_t t = time(nullptr);
  1341   char buf[16];
  1342   sprintf(buf, "%ld", t);
  1343   aInstallTime = buf;
  1345   return NS_OK;
  1348 // Ensure a directory exists and create it if missing.
  1349 static nsresult
  1350 EnsureDirectoryExists(nsIFile* dir)
  1352   nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
  1354   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
  1355 	return rv;
  1358   return NS_OK;
  1361 // Annotate the crash report with a Unique User ID and time
  1362 // since install.  Also do some prep work for recording
  1363 // time since last crash, which must be calculated at
  1364 // crash time.
  1365 // If any piece of data doesn't exist, initialize it first.
  1366 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
  1367                         const nsACString& aBuildID)
  1369   nsCOMPtr<nsIFile> dataDirectory;
  1370   nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
  1371   NS_ENSURE_SUCCESS(rv, rv);
  1373   rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
  1374   NS_ENSURE_SUCCESS(rv, rv);
  1376   EnsureDirectoryExists(dataDirectory);
  1378 #if defined(XP_WIN32)
  1379   nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
  1381   nsAutoString dataDirectoryPath;
  1382   rv = dataDirectory->GetPath(dataDirectoryPath);
  1383   NS_ENSURE_SUCCESS(rv, rv);
  1385   dataDirEnv.Append(dataDirectoryPath);
  1387   _wputenv(dataDirEnv.get());
  1388 #else
  1389   // Save this path in the environment for the crash reporter application.
  1390   nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
  1392   nsAutoCString dataDirectoryPath;
  1393   rv = dataDirectory->GetNativePath(dataDirectoryPath);
  1394   NS_ENSURE_SUCCESS(rv, rv);
  1396   dataDirEnv.Append(dataDirectoryPath);
  1398   char* env = ToNewCString(dataDirEnv);
  1399   NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
  1401   PR_SetEnv(env);
  1402 #endif
  1404   nsAutoCString data;
  1405   if(NS_SUCCEEDED(GetOrInit(dataDirectory,
  1406                             NS_LITERAL_CSTRING("InstallTime") + aBuildID,
  1407                             data, InitInstallTime)))
  1408     AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
  1410   // this is a little different, since we can't init it with anything,
  1411   // since it's stored at crash time, and we can't annotate the
  1412   // crash report with the stored value, since we really want
  1413   // (now - LastCrash), so we just get a value if it exists,
  1414   // and store it in a time_t value.
  1415   if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
  1416                             data, nullptr))) {
  1417     lastCrashTime = (time_t)atol(data.get());
  1420   // not really the best place to init this, but I have the path I need here
  1421   nsCOMPtr<nsIFile> lastCrashFile;
  1422   rv = dataDirectory->Clone(getter_AddRefs(lastCrashFile));
  1423   NS_ENSURE_SUCCESS(rv, rv);
  1425   rv = lastCrashFile->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
  1426   NS_ENSURE_SUCCESS(rv, rv);
  1427   memset(lastCrashTimeFilename, 0, sizeof(lastCrashTimeFilename));
  1429 #if defined(XP_WIN32)
  1430   nsAutoString filename;
  1431   rv = lastCrashFile->GetPath(filename);
  1432   NS_ENSURE_SUCCESS(rv, rv);
  1434   if (filename.Length() < XP_PATH_MAX)
  1435     wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
  1436 #else
  1437   nsAutoCString filename;
  1438   rv = lastCrashFile->GetNativePath(filename);
  1439   NS_ENSURE_SUCCESS(rv, rv);
  1441   if (filename.Length() < XP_PATH_MAX)
  1442     strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
  1443 #endif
  1445   if (headlessClient) {
  1446     nsCOMPtr<nsIFile> markerFile;
  1447     rv = dataDirectory->Clone(getter_AddRefs(markerFile));
  1448     NS_ENSURE_SUCCESS(rv, rv);
  1450     rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
  1451     NS_ENSURE_SUCCESS(rv, rv);
  1452     memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
  1454 #if defined(XP_WIN32)
  1455     nsAutoString markerFilename;
  1456     rv = markerFile->GetPath(markerFilename);
  1457     NS_ENSURE_SUCCESS(rv, rv);
  1459     if (markerFilename.Length() < XP_PATH_MAX)
  1460       wcsncpy(crashMarkerFilename, markerFilename.get(),
  1461               markerFilename.Length());
  1462 #else
  1463     nsAutoCString markerFilename;
  1464     rv = markerFile->GetNativePath(markerFilename);
  1465     NS_ENSURE_SUCCESS(rv, rv);
  1467     if (markerFilename.Length() < XP_PATH_MAX)
  1468       strncpy(crashMarkerFilename, markerFilename.get(),
  1469               markerFilename.Length());
  1470 #endif
  1473   return NS_OK;
  1476 static void OOPDeinit();
  1478 nsresult UnsetExceptionHandler()
  1480   if (isSafeToDump) {
  1481     MutexAutoLock lock(*dumpSafetyLock);
  1482     isSafeToDump = false;
  1485 #ifdef XP_WIN
  1486   // allow SetUnhandledExceptionFilter
  1487   gBlockUnhandledExceptionFilter = false;
  1488 #endif
  1490   delete gExceptionHandler;
  1492   // do this here in the unlikely case that we succeeded in allocating
  1493   // our strings but failed to allocate gExceptionHandler.
  1494   delete crashReporterAPIData_Hash;
  1495   crashReporterAPIData_Hash = nullptr;
  1497   delete crashReporterAPILock;
  1498   crashReporterAPILock = nullptr;
  1500   delete notesFieldLock;
  1501   notesFieldLock = nullptr;
  1503   delete crashReporterAPIData;
  1504   crashReporterAPIData = nullptr;
  1506   delete notesField;
  1507   notesField = nullptr;
  1509   delete lastRunCrashID;
  1510   lastRunCrashID = nullptr;
  1512   if (pendingDirectory) {
  1513     NS_Free(pendingDirectory);
  1514     pendingDirectory = nullptr;
  1517   if (crashReporterPath) {
  1518     NS_Free(crashReporterPath);
  1519     crashReporterPath = nullptr;
  1522   if (eventsDirectory) {
  1523     NS_Free(eventsDirectory);
  1524     eventsDirectory = nullptr;
  1527 #ifdef XP_MACOSX
  1528   posix_spawnattr_destroy(&spawnattr);
  1529 #endif
  1531   if (!gExceptionHandler)
  1532     return NS_ERROR_NOT_INITIALIZED;
  1534   gExceptionHandler = nullptr;
  1536   OOPDeinit();
  1538   delete dumpSafetyLock;
  1539   dumpSafetyLock = nullptr;
  1541   return NS_OK;
  1544 static void ReplaceChar(nsCString& str, const nsACString& character,
  1545                         const nsACString& replacement)
  1547   nsCString::const_iterator start, end;
  1549   str.BeginReading(start);
  1550   str.EndReading(end);
  1552   while (FindInReadable(character, start, end)) {
  1553     int32_t pos = end.size_backward();
  1554     str.Replace(pos - 1, 1, replacement);
  1556     str.BeginReading(start);
  1557     start.advance(pos + replacement.Length() - 1);
  1558     str.EndReading(end);
  1562 static bool DoFindInReadable(const nsACString& str, const nsACString& value)
  1564   nsACString::const_iterator start, end;
  1565   str.BeginReading(start);
  1566   str.EndReading(end);
  1568   return FindInReadable(value, start, end);
  1571 static PLDHashOperator EnumerateEntries(const nsACString& key,
  1572                                         nsCString entry,
  1573                                         void* userData)
  1575   crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
  1576                                NS_LITERAL_CSTRING("\n"));
  1577   return PL_DHASH_NEXT;
  1580 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
  1581 #ifdef _MSC_VER
  1582 #pragma optimize("", off)
  1583 #endif
  1584 static nsresult
  1585 EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
  1587   if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
  1588       DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
  1589     return NS_ERROR_INVALID_ARG;
  1591   if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
  1592     return NS_ERROR_INVALID_ARG;
  1594   escapedData = data;
  1596   // escape backslashes
  1597   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
  1598               NS_LITERAL_CSTRING("\\\\"));
  1599   // escape newlines
  1600   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
  1601               NS_LITERAL_CSTRING("\\n"));
  1602   return NS_OK;
  1604 #ifdef _MSC_VER
  1605 #pragma optimize("", on)
  1606 #endif
  1608 class DelayedNote
  1610  public:
  1611   DelayedNote(const nsACString& aKey, const nsACString& aData)
  1612   : mKey(aKey), mData(aData), mType(Annotation) {}
  1614   DelayedNote(const nsACString& aData)
  1615   : mData(aData), mType(AppNote) {}
  1617   void Run()
  1619     if (mType == Annotation) {
  1620       AnnotateCrashReport(mKey, mData);
  1621     } else {
  1622       AppendAppNotesToCrashReport(mData);
  1626  private:
  1627   nsCString mKey;
  1628   nsCString mData;
  1629   enum AnnotationType { Annotation, AppNote } mType;
  1630 };
  1632 static void
  1633 EnqueueDelayedNote(DelayedNote* aNote)
  1635   if (!gDelayedAnnotations) {
  1636     gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
  1638   gDelayedAnnotations->AppendElement(aNote);
  1641 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
  1643   if (!GetEnabled())
  1644     return NS_ERROR_NOT_INITIALIZED;
  1646   nsCString escapedData;
  1647   nsresult rv = EscapeAnnotation(key, data, escapedData);
  1648   if (NS_FAILED(rv))
  1649     return rv;
  1651   if (XRE_GetProcessType() != GeckoProcessType_Default) {
  1652     if (!NS_IsMainThread()) {
  1653       NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
  1654       return NS_ERROR_FAILURE;
  1656     PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
  1657     if (!reporter) {
  1658       EnqueueDelayedNote(new DelayedNote(key, data));
  1659       return NS_OK;
  1661     if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
  1662       return NS_ERROR_FAILURE;
  1663     return NS_OK;
  1666   MutexAutoLock lock(*crashReporterAPILock);
  1668   crashReporterAPIData_Hash->Put(key, escapedData);
  1670   // now rebuild the file contents
  1671   crashReporterAPIData->Truncate(0);
  1672   crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
  1673                                            crashReporterAPIData);
  1675   return NS_OK;
  1678 nsresult SetGarbageCollecting(bool collecting)
  1680   if (!GetEnabled())
  1681     return NS_ERROR_NOT_INITIALIZED;
  1683   isGarbageCollecting = collecting;
  1685   return NS_OK;
  1688 void SetEventloopNestingLevel(uint32_t level)
  1690   eventloopNestingLevel = level;
  1693 nsresult AppendAppNotesToCrashReport(const nsACString& data)
  1695   if (!GetEnabled())
  1696     return NS_ERROR_NOT_INITIALIZED;
  1698   if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
  1699     return NS_ERROR_INVALID_ARG;
  1701   if (XRE_GetProcessType() != GeckoProcessType_Default) {
  1702     if (!NS_IsMainThread()) {
  1703       NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
  1704       return NS_ERROR_FAILURE;
  1706     PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
  1707     if (!reporter) {
  1708       EnqueueDelayedNote(new DelayedNote(data));
  1709       return NS_OK;
  1712     // Since we don't go through AnnotateCrashReport in the parent process,
  1713     // we must ensure that the data is escaped and valid before the parent
  1714     // sees it.
  1715     nsCString escapedData;
  1716     nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
  1717     if (NS_FAILED(rv))
  1718       return rv;
  1720     if (!reporter->SendAppendAppNotes(escapedData))
  1721       return NS_ERROR_FAILURE;
  1722     return NS_OK;
  1725   MutexAutoLock lock(*notesFieldLock);
  1727   notesField->Append(data);
  1728   return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
  1731 // Returns true if found, false if not found.
  1732 bool GetAnnotation(const nsACString& key, nsACString& data)
  1734   if (!gExceptionHandler)
  1735     return false;
  1737   nsAutoCString entry;
  1738   if (!crashReporterAPIData_Hash->Get(key, &entry))
  1739     return false;
  1741   data = entry;
  1742   return true;
  1745 nsresult RegisterAppMemory(void* ptr, size_t length)
  1747   if (!GetEnabled())
  1748     return NS_ERROR_NOT_INITIALIZED;
  1750 #if defined(XP_LINUX) || defined(XP_WIN32)
  1751   gExceptionHandler->RegisterAppMemory(ptr, length);
  1752   return NS_OK;
  1753 #else
  1754   return NS_ERROR_NOT_IMPLEMENTED;
  1755 #endif
  1758 nsresult UnregisterAppMemory(void* ptr)
  1760   if (!GetEnabled())
  1761     return NS_ERROR_NOT_INITIALIZED;
  1763 #if defined(XP_LINUX) || defined(XP_WIN32)
  1764   gExceptionHandler->UnregisterAppMemory(ptr);
  1765   return NS_OK;
  1766 #else
  1767   return NS_ERROR_NOT_IMPLEMENTED;
  1768 #endif
  1771 bool GetServerURL(nsACString& aServerURL)
  1773   if (!gExceptionHandler)
  1774     return false;
  1776   return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
  1779 nsresult SetServerURL(const nsACString& aServerURL)
  1781   // store server URL with the API data
  1782   // the client knows to handle this specially
  1783   return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
  1784                              aServerURL);
  1787 nsresult
  1788 SetRestartArgs(int argc, char** argv)
  1790   if (!gExceptionHandler)
  1791     return NS_OK;
  1793   int i;
  1794   nsAutoCString envVar;
  1795   char *env;
  1796   char *argv0 = getenv("MOZ_APP_LAUNCHER");
  1797   for (i = 0; i < argc; i++) {
  1798     envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
  1799     envVar.AppendInt(i);
  1800     envVar += "=";
  1801     if (argv0 && i == 0) {
  1802       // Is there a request to suppress default binary launcher?
  1803       envVar += argv0;
  1804     } else {
  1805       envVar += argv[i];
  1808     // PR_SetEnv() wants the string to be available for the lifetime
  1809     // of the app, so dup it here
  1810     env = ToNewCString(envVar);
  1811     if (!env)
  1812       return NS_ERROR_OUT_OF_MEMORY;
  1814     PR_SetEnv(env);
  1817   // make sure the arg list is terminated
  1818   envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
  1819   envVar.AppendInt(i);
  1820   envVar += "=";
  1822   // PR_SetEnv() wants the string to be available for the lifetime
  1823   // of the app, so dup it here
  1824   env = ToNewCString(envVar);
  1825   if (!env)
  1826     return NS_ERROR_OUT_OF_MEMORY;
  1828   PR_SetEnv(env);
  1830   // make sure we save the info in XUL_APP_FILE for the reporter
  1831   const char *appfile = PR_GetEnv("XUL_APP_FILE");
  1832   if (appfile && *appfile) {
  1833     envVar = "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
  1834     envVar += appfile;
  1835     env = ToNewCString(envVar);
  1836     PR_SetEnv(env);
  1839   return NS_OK;
  1842 #ifdef XP_WIN32
  1843 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
  1845   if (!gExceptionHandler)
  1846     return NS_ERROR_NOT_INITIALIZED;
  1848   return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
  1850 #endif
  1852 #ifdef XP_LINUX
  1853 bool WriteMinidumpForSigInfo(int signo, siginfo_t* info, void* uc)
  1855   return gExceptionHandler->HandleSignal(signo, info, uc);
  1857 #endif
  1859 #ifdef XP_MACOSX
  1860 nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
  1862   nsAutoCString excString;
  1863   GetObjCExceptionInfo(inException, excString);
  1864   AppendAppNotesToCrashReport(excString);
  1865   return NS_OK;
  1867 #endif
  1869 /*
  1870  * Combined code to get/set the crash reporter submission pref on
  1871  * different platforms.
  1872  */
  1873 static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
  1875   nsresult rv;
  1876 #if defined(XP_WIN32)
  1877   /*
  1878    * NOTE! This needs to stay in sync with the preference checking code
  1879    *       in toolkit/crashreporter/client/crashreporter_win.cpp
  1880    */
  1881   nsCOMPtr<nsIXULAppInfo> appinfo =
  1882     do_GetService("@mozilla.org/xre/app-info;1", &rv);
  1883   NS_ENSURE_SUCCESS(rv, rv);
  1885   nsAutoCString appVendor, appName;
  1886   rv = appinfo->GetVendor(appVendor);
  1887   NS_ENSURE_SUCCESS(rv, rv);
  1888   rv = appinfo->GetName(appName);
  1889   NS_ENSURE_SUCCESS(rv, rv);
  1891   nsCOMPtr<nsIWindowsRegKey> regKey
  1892     (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
  1893   NS_ENSURE_SUCCESS(rv, rv);
  1895   nsAutoCString regPath;
  1897   regPath.AppendLiteral("Software\\");
  1899   // We need to ensure the registry keys are created so we can properly
  1900   // write values to it
  1902   // Create appVendor key
  1903   if(!appVendor.IsEmpty()) {
  1904     regPath.Append(appVendor);
  1905     regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
  1906                    NS_ConvertUTF8toUTF16(regPath),
  1907                    nsIWindowsRegKey::ACCESS_SET_VALUE);
  1908     regPath.AppendLiteral("\\");
  1911   // Create appName key
  1912   regPath.Append(appName);
  1913   regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
  1914                  NS_ConvertUTF8toUTF16(regPath),
  1915                  nsIWindowsRegKey::ACCESS_SET_VALUE);
  1916   regPath.AppendLiteral("\\");
  1918   // Create Crash Reporter key
  1919   regPath.AppendLiteral("Crash Reporter");
  1920   regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
  1921                  NS_ConvertUTF8toUTF16(regPath),
  1922                  nsIWindowsRegKey::ACCESS_SET_VALUE);
  1924   // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
  1925   // and we're done.
  1926   if (writePref) {
  1927     rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
  1928                       NS_ConvertUTF8toUTF16(regPath),
  1929                       nsIWindowsRegKey::ACCESS_SET_VALUE);
  1930     NS_ENSURE_SUCCESS(rv, rv);
  1932     uint32_t value = *aSubmitReports ? 1 : 0;
  1933     rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
  1934     regKey->Close();
  1935     return rv;
  1938   // We're reading the pref value, so we need to first look under
  1939   // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
  1940   // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
  1941   // to "true".
  1942   uint32_t value;
  1943   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
  1944                     NS_ConvertUTF8toUTF16(regPath),
  1945                     nsIWindowsRegKey::ACCESS_QUERY_VALUE);
  1946   if (NS_SUCCEEDED(rv)) {
  1947     rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
  1948     regKey->Close();
  1949     if (NS_SUCCEEDED(rv)) {
  1950       *aSubmitReports = !!value;
  1951       return NS_OK;
  1955   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
  1956                     NS_ConvertUTF8toUTF16(regPath),
  1957                     nsIWindowsRegKey::ACCESS_QUERY_VALUE);
  1958   if (NS_FAILED(rv)) {
  1959     *aSubmitReports = true;
  1960     return NS_OK;
  1963   rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
  1964   // default to true on failure
  1965   if (NS_FAILED(rv)) {
  1966     value = 1;
  1967     rv = NS_OK;
  1969   regKey->Close();
  1971   *aSubmitReports = !!value;
  1972   return NS_OK;
  1973 #elif defined(XP_MACOSX)
  1974   rv = NS_OK;
  1975   if (writePref) {
  1976     CFPropertyListRef cfValue = (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
  1977     ::CFPreferencesSetAppValue(CFSTR("submitReport"),
  1978                                cfValue,
  1979                                reporterClientAppID);
  1980     if (!::CFPreferencesAppSynchronize(reporterClientAppID))
  1981       rv = NS_ERROR_FAILURE;
  1983   else {
  1984     *aSubmitReports = true;
  1985     Boolean keyExistsAndHasValidFormat = false;
  1986     Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
  1987                                                           reporterClientAppID,
  1988                                                           &keyExistsAndHasValidFormat);
  1989     if (keyExistsAndHasValidFormat)
  1990       *aSubmitReports = !!prefValue;
  1992   return rv;
  1993 #elif defined(XP_UNIX)
  1994   /*
  1995    * NOTE! This needs to stay in sync with the preference checking code
  1996    *       in toolkit/crashreporter/client/crashreporter_linux.cpp
  1997    */
  1998   nsCOMPtr<nsIFile> reporterINI;
  1999   rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
  2000   NS_ENSURE_SUCCESS(rv, rv);
  2001   reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
  2002   reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
  2004   bool exists;
  2005   rv = reporterINI->Exists(&exists);
  2006   NS_ENSURE_SUCCESS(rv, rv);
  2007   if (!exists) {
  2008     if (!writePref) {
  2009         // If reading the pref, default to true if .ini doesn't exist.
  2010         *aSubmitReports = true;
  2011         return NS_OK;
  2013     // Create the file so the INI processor can write to it.
  2014     rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
  2015     NS_ENSURE_SUCCESS(rv, rv);
  2018   nsCOMPtr<nsIINIParserFactory> iniFactory =
  2019     do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
  2020   NS_ENSURE_SUCCESS(rv, rv);
  2022   nsCOMPtr<nsIINIParser> iniParser;
  2023   rv = iniFactory->CreateINIParser(reporterINI,
  2024                                    getter_AddRefs(iniParser));
  2025   NS_ENSURE_SUCCESS(rv, rv);
  2027   // If we're writing the pref, just set and we're done.
  2028   if (writePref) {
  2029     nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
  2030     NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
  2032     rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
  2033                               NS_LITERAL_CSTRING("SubmitReport"),
  2034                               *aSubmitReports ?  NS_LITERAL_CSTRING("1") :
  2035                                                  NS_LITERAL_CSTRING("0"));
  2036     NS_ENSURE_SUCCESS(rv, rv);
  2037     rv = iniWriter->WriteFile(nullptr, 0);
  2038     return rv;
  2041   nsAutoCString submitReportValue;
  2042   rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
  2043                             NS_LITERAL_CSTRING("SubmitReport"),
  2044                             submitReportValue);
  2046   // Default to "true" if the pref can't be found.
  2047   if (NS_FAILED(rv))
  2048     *aSubmitReports = true;
  2049   else if (submitReportValue.EqualsASCII("0"))
  2050     *aSubmitReports = false;
  2051   else
  2052     *aSubmitReports = true;
  2054   return NS_OK;
  2055 #else
  2056   return NS_ERROR_NOT_IMPLEMENTED;
  2057 #endif
  2060 nsresult GetSubmitReports(bool* aSubmitReports)
  2062     return PrefSubmitReports(aSubmitReports, false);
  2065 nsresult SetSubmitReports(bool aSubmitReports)
  2067     nsresult rv;
  2069     nsCOMPtr<nsIObserverService> obsServ =
  2070       mozilla::services::GetObserverService();
  2071     if (!obsServ) {
  2072       return NS_ERROR_FAILURE;
  2075     rv = PrefSubmitReports(&aSubmitReports, true);
  2076     if (NS_FAILED(rv)) {
  2077       return rv;
  2080     obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
  2081     return NS_OK;
  2084 void
  2085 UpdateCrashEventsDir()
  2087   nsCOMPtr<nsIFile> eventsDir;
  2089   // We prefer the following locations in order:
  2090   //
  2091   // 1. If environment variable is present, use it. We don't expect
  2092   //    the environment variable except for tests and other atypical setups.
  2093   // 2. Inside the profile directory.
  2094   // 3. Inside the user application data directory (no profile available).
  2095   // 4. A temporary directory (setup likely is invalid / application is buggy).
  2096   const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
  2097   if (env) {
  2098     eventsDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
  2099     if (!eventsDir) {
  2100       return;
  2102     eventsDir->InitWithNativePath(nsDependentCString(env));
  2103     EnsureDirectoryExists(eventsDir);
  2104   } else {
  2105     nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
  2106     if (NS_SUCCEEDED(rv)) {
  2107       eventsDir->Append(NS_LITERAL_STRING("crashes"));
  2108       EnsureDirectoryExists(eventsDir);
  2109       eventsDir->Append(NS_LITERAL_STRING("events"));
  2110       EnsureDirectoryExists(eventsDir);
  2111     } else {
  2112       rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
  2113       if (NS_SUCCEEDED(rv)) {
  2114         eventsDir->Append(NS_LITERAL_STRING("Crash Reports"));
  2115         EnsureDirectoryExists(eventsDir);
  2116         eventsDir->Append(NS_LITERAL_STRING("events"));
  2117         EnsureDirectoryExists(eventsDir);
  2118       } else {
  2119         NS_WARNING("Couldn't get the user appdata directory. Crash events may not be produced.");
  2120         return;
  2125 #ifdef XP_WIN
  2126   nsString path;
  2127   eventsDir->GetPath(path);
  2128   eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
  2129 #else
  2130   nsCString path;
  2131   eventsDir->GetNativePath(path);
  2132   eventsDirectory = ToNewCString(path);
  2133 #endif
  2136 bool GetCrashEventsDir(nsAString& aPath)
  2138   if (!eventsDirectory) {
  2139     return false;
  2142   aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory);
  2143   return true;
  2146 static void
  2147 FindPendingDir()
  2149   if (pendingDirectory)
  2150     return;
  2152   nsCOMPtr<nsIFile> pendingDir;
  2153   nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
  2154   if (NS_FAILED(rv)) {
  2155     NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
  2157   else {
  2158     pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
  2159     pendingDir->Append(NS_LITERAL_STRING("pending"));
  2161 #ifdef XP_WIN
  2162     nsString path;
  2163     pendingDir->GetPath(path);
  2164     pendingDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
  2165 #else
  2166     nsCString path;
  2167     pendingDir->GetNativePath(path);
  2168     pendingDirectory = ToNewCString(path);
  2169 #endif
  2173 // The "pending" dir is Crash Reports/pending, from which minidumps
  2174 // can be submitted. Because this method may be called off the main thread,
  2175 // we store the pending directory as a path.
  2176 static bool
  2177 GetPendingDir(nsIFile** dir)
  2179   MOZ_ASSERT(OOPInitialized());
  2180   if (!pendingDirectory) {
  2181     return false;
  2184   nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
  2185   if (!pending) {
  2186     NS_WARNING("Can't set up pending directory during shutdown.");
  2187     return false;
  2189 #ifdef XP_WIN
  2190   pending->InitWithPath(nsDependentString(pendingDirectory));
  2191 #else
  2192   pending->InitWithNativePath(nsDependentCString(pendingDirectory));
  2193 #endif
  2194   pending.swap(*dir);
  2195   return true;
  2198 // The "limbo" dir is where minidumps go to wait for something else to
  2199 // use them.  If we're |ShouldReport()|, then the "something else" is
  2200 // a minidump submitter, and they're coming from the 
  2201 // Crash Reports/pending/ dir.  Otherwise, we don't know what the
  2202 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
  2203 // limbo.
  2204 static bool
  2205 GetMinidumpLimboDir(nsIFile** dir)
  2207   if (ShouldReport()) {
  2208     return GetPendingDir(dir);
  2210   else {
  2211 #ifndef XP_LINUX
  2212     CreateFileFromPath(gExceptionHandler->dump_path(), dir);
  2213 #else
  2214     CreateFileFromPath(gExceptionHandler->minidump_descriptor().directory(),
  2215                        dir);
  2216 #endif
  2217     return nullptr != *dir;
  2221 bool
  2222 GetMinidumpForID(const nsAString& id, nsIFile** minidump)
  2224   if (!GetMinidumpLimboDir(minidump))
  2225     return false;
  2226   (*minidump)->Append(id + NS_LITERAL_STRING(".dmp")); 
  2227   return true;
  2230 bool
  2231 GetIDFromMinidump(nsIFile* minidump, nsAString& id)
  2233   if (NS_SUCCEEDED(minidump->GetLeafName(id))) {
  2234     id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING(""));
  2235     return true;
  2237   return false;
  2240 bool
  2241 GetExtraFileForID(const nsAString& id, nsIFile** extraFile)
  2243   if (!GetMinidumpLimboDir(extraFile))
  2244     return false;
  2245   (*extraFile)->Append(id + NS_LITERAL_STRING(".extra"));
  2246   return true;
  2249 bool
  2250 GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile)
  2252   nsAutoString leafName;
  2253   nsresult rv = minidump->GetLeafName(leafName);
  2254   if (NS_FAILED(rv))
  2255     return false;
  2257   nsCOMPtr<nsIFile> extraF;
  2258   rv = minidump->Clone(getter_AddRefs(extraF));
  2259   if (NS_FAILED(rv))
  2260     return false;
  2262   leafName.Replace(leafName.Length() - 3, 3,
  2263                    NS_LITERAL_STRING("extra"));
  2264   rv = extraF->SetLeafName(leafName);
  2265   if (NS_FAILED(rv))
  2266     return false;
  2268   *extraFile = nullptr;
  2269   extraF.swap(*extraFile);
  2270   return true;
  2273 bool
  2274 AppendExtraData(const nsAString& id, const AnnotationTable& data)
  2276   nsCOMPtr<nsIFile> extraFile;
  2277   if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
  2278     return false;
  2279   return AppendExtraData(extraFile, data);
  2282 //-----------------------------------------------------------------------------
  2283 // Helpers for AppendExtraData()
  2284 //
  2285 struct Blacklist {
  2286   Blacklist() : mItems(nullptr), mLen(0) { }
  2287   Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
  2289   bool Contains(const nsACString& key) const {
  2290     for (int i = 0; i < mLen; ++i)
  2291       if (key.EqualsASCII(mItems[i]))
  2292         return true;
  2293     return false;
  2296   const char** mItems;
  2297   const int mLen;
  2298 };
  2300 struct EnumerateAnnotationsContext {
  2301   const Blacklist& blacklist;
  2302   PRFileDesc* fd;
  2303 };
  2305 static void
  2306 WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
  2308   PR_Write(fd, key.BeginReading(), key.Length());
  2309   PR_Write(fd, "=", 1);
  2310   PR_Write(fd, value.BeginReading(), value.Length());
  2311   PR_Write(fd, "\n", 1);
  2314 static PLDHashOperator
  2315 EnumerateAnnotations(const nsACString& key,
  2316                      nsCString entry,
  2317                      void* userData)
  2319   EnumerateAnnotationsContext* ctx =
  2320     static_cast<EnumerateAnnotationsContext*>(userData);
  2321   const Blacklist& blacklist = ctx->blacklist;
  2323   // skip entries in the blacklist
  2324   if (blacklist.Contains(key))
  2325       return PL_DHASH_NEXT;
  2327   WriteAnnotation(ctx->fd, key, entry);
  2329   return PL_DHASH_NEXT;
  2332 static bool
  2333 WriteExtraData(nsIFile* extraFile,
  2334                const AnnotationTable& data,
  2335                const Blacklist& blacklist,
  2336                bool writeCrashTime=false,
  2337                bool truncate=false)
  2339   PRFileDesc* fd;
  2340   int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
  2341   nsresult rv = 
  2342     extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
  2343                                 0600, &fd);
  2344   if (NS_FAILED(rv))
  2345     return false;
  2347   EnumerateAnnotationsContext ctx = { blacklist, fd };
  2348   data.EnumerateRead(EnumerateAnnotations, &ctx);
  2350   if (writeCrashTime) {
  2351     time_t crashTime = time(nullptr);
  2352     char crashTimeString[32];
  2353     XP_TTOA(crashTime, crashTimeString, 10);
  2355     WriteAnnotation(fd,
  2356                     nsDependentCString("CrashTime"),
  2357                     nsDependentCString(crashTimeString));
  2360   PR_Close(fd);
  2361   return true;
  2364 bool
  2365 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
  2367   return WriteExtraData(extraFile, data, Blacklist());
  2371 static bool
  2372 WriteExtraForMinidump(nsIFile* minidump,
  2373                       const Blacklist& blacklist,
  2374                       nsIFile** extraFile)
  2376   nsCOMPtr<nsIFile> extra;
  2377   if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra)))
  2378     return false;
  2380   if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
  2381                       blacklist,
  2382                       true /*write crash time*/,
  2383                       true /*truncate*/))
  2384     return false;
  2386   *extraFile = nullptr;
  2387   extra.swap(*extraFile);
  2389   return true;
  2392 // It really only makes sense to call this function when
  2393 // ShouldReport() is true.
  2394 static bool
  2395 MoveToPending(nsIFile* dumpFile, nsIFile* extraFile)
  2397   nsCOMPtr<nsIFile> pendingDir;
  2398   if (!GetPendingDir(getter_AddRefs(pendingDir)))
  2399     return false;
  2401   if (NS_FAILED(dumpFile->MoveTo(pendingDir, EmptyString()))) {
  2402     return false;
  2405   if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, EmptyString()))) {
  2406     return false;
  2409   return true;
  2412 static void
  2413 OnChildProcessDumpRequested(void* aContext,
  2414 #ifdef XP_MACOSX
  2415                             const ClientInfo& aClientInfo,
  2416                             const xpstring& aFilePath
  2417 #else
  2418                             const ClientInfo* aClientInfo,
  2419                             const xpstring* aFilePath
  2420 #endif
  2423   nsCOMPtr<nsIFile> minidump;
  2424   nsCOMPtr<nsIFile> extraFile;
  2426   // Hold the mutex until the current dump request is complete, to
  2427   // prevent UnsetExceptionHandler() from pulling the rug out from
  2428   // under us.
  2429   MutexAutoLock lock(*dumpSafetyLock);
  2430   if (!isSafeToDump)
  2431     return;
  2433   CreateFileFromPath(
  2434 #ifdef XP_MACOSX
  2435                      aFilePath,
  2436 #else
  2437                      *aFilePath,
  2438 #endif
  2439                      getter_AddRefs(minidump));
  2441   if (!WriteExtraForMinidump(minidump,
  2442                              Blacklist(kSubprocessBlacklist,
  2443                                        ArrayLength(kSubprocessBlacklist)),
  2444                              getter_AddRefs(extraFile)))
  2445     return;
  2447   if (ShouldReport())
  2448     MoveToPending(minidump, extraFile);
  2451     uint32_t pid =
  2452 #ifdef XP_MACOSX
  2453       aClientInfo.pid();
  2454 #else
  2455       aClientInfo->pid();
  2456 #endif
  2458 #ifdef MOZ_CRASHREPORTER_INJECTOR
  2459     bool runCallback;
  2460 #endif
  2462       MutexAutoLock lock(*dumpMapLock);
  2463       ChildProcessData* pd = pidToMinidump->PutEntry(pid);
  2464       MOZ_ASSERT(!pd->minidump);
  2465       pd->minidump = minidump;
  2466       pd->sequence = ++crashSequence;
  2467 #ifdef MOZ_CRASHREPORTER_INJECTOR
  2468       runCallback = nullptr != pd->callback;
  2469 #endif
  2471 #ifdef MOZ_CRASHREPORTER_INJECTOR
  2472     if (runCallback)
  2473       NS_DispatchToMainThread(new ReportInjectedCrash(pid));
  2474 #endif
  2478 static bool
  2479 OOPInitialized()
  2481   return pidToMinidump != nullptr;
  2484 #ifdef XP_MACOSX
  2485 static bool ChildFilter(void *context) {
  2486   mozilla::IOInterposer::Disable();
  2487   return true;
  2489 #endif
  2491 void
  2492 OOPInit()
  2494   if (OOPInitialized())
  2495     return;
  2497   MOZ_ASSERT(NS_IsMainThread());
  2499   NS_ABORT_IF_FALSE(gExceptionHandler != nullptr,
  2500                     "attempt to initialize OOP crash reporter before in-process crashreporter!");
  2502 #if defined(XP_WIN)
  2503   childCrashNotifyPipe =
  2504     PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
  2505                 static_cast<int>(::GetCurrentProcessId()));
  2507   const std::wstring dumpPath = gExceptionHandler->dump_path();
  2508   crashServer = new CrashGenerationServer(
  2509     NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get(),
  2510     nullptr,                    // default security attributes
  2511     nullptr, nullptr,           // we don't care about process connect here
  2512     OnChildProcessDumpRequested, nullptr,
  2513     nullptr, nullptr,           // we don't care about process exit here
  2514     nullptr, nullptr,           // we don't care about upload request here
  2515     true,                       // automatically generate dumps
  2516     &dumpPath);
  2518 #elif defined(XP_LINUX)
  2519   if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
  2520                                                   &clientSocketFd))
  2521     NS_RUNTIMEABORT("can't create crash reporter socketpair()");
  2523   const std::string dumpPath =
  2524       gExceptionHandler->minidump_descriptor().directory();
  2525   crashServer = new CrashGenerationServer(
  2526     serverSocketFd,
  2527     OnChildProcessDumpRequested, nullptr,
  2528     nullptr, nullptr,           // we don't care about process exit here
  2529     true,
  2530     &dumpPath);
  2532 #elif defined(XP_MACOSX)
  2533   childCrashNotifyPipe =
  2534     PR_smprintf("gecko-crash-server-pipe.%i",
  2535                 static_cast<int>(getpid()));
  2536   const std::string dumpPath = gExceptionHandler->dump_path();
  2538   crashServer = new CrashGenerationServer(
  2539     childCrashNotifyPipe,
  2540     ChildFilter,
  2541     nullptr,
  2542     OnChildProcessDumpRequested, nullptr,
  2543     nullptr, nullptr,
  2544     true, // automatically generate dumps
  2545     dumpPath);
  2546 #endif
  2548   if (!crashServer->Start())
  2549     NS_RUNTIMEABORT("can't start crash reporter server()");
  2551   pidToMinidump = new ChildMinidumpMap();
  2553   dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
  2555   FindPendingDir();
  2556   UpdateCrashEventsDir();
  2559 static void
  2560 OOPDeinit()
  2562   if (!OOPInitialized()) {
  2563     NS_WARNING("OOPDeinit() without successful OOPInit()");
  2564     return;
  2567 #ifdef MOZ_CRASHREPORTER_INJECTOR
  2568   if (sInjectorThread) {
  2569     sInjectorThread->Shutdown();
  2570     NS_RELEASE(sInjectorThread);
  2572 #endif
  2574   delete crashServer;
  2575   crashServer = nullptr;
  2577   delete dumpMapLock;
  2578   dumpMapLock = nullptr;
  2580   delete pidToMinidump;
  2581   pidToMinidump = nullptr;
  2583 #if defined(XP_WIN)
  2584   PR_Free(childCrashNotifyPipe);
  2585   childCrashNotifyPipe = nullptr;
  2586 #endif
  2589 #if defined(XP_WIN) || defined(XP_MACOSX)
  2590 // Parent-side API for children
  2591 const char*
  2592 GetChildNotificationPipe()
  2594   if (!GetEnabled())
  2595     return kNullNotifyPipe;
  2597   MOZ_ASSERT(OOPInitialized());
  2599   return childCrashNotifyPipe;
  2601 #endif
  2603 #ifdef MOZ_CRASHREPORTER_INJECTOR
  2604 void
  2605 InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
  2607   if (!GetEnabled())
  2608     return;
  2610   if (!OOPInitialized())
  2611     OOPInit();
  2613   if (!sInjectorThread) {
  2614     if (NS_FAILED(NS_NewThread(&sInjectorThread)))
  2615       return;
  2619     MutexAutoLock lock(*dumpMapLock);
  2620     ChildProcessData* pd = pidToMinidump->PutEntry(processID);
  2621     MOZ_ASSERT(!pd->minidump && !pd->callback);
  2622     pd->callback = cb;
  2625   nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
  2626   sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
  2629 NS_IMETHODIMP
  2630 ReportInjectedCrash::Run()
  2632   // Crash reporting may have been disabled after this method was dispatched
  2633   if (!OOPInitialized())
  2634     return NS_OK;
  2636   InjectorCrashCallback* cb;
  2638     MutexAutoLock lock(*dumpMapLock);
  2639     ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
  2640     if (!pd || !pd->callback)
  2641       return NS_OK;
  2643     MOZ_ASSERT(pd->minidump);
  2645     cb = pd->callback;
  2648   cb->OnCrash(mPID);
  2649   return NS_OK;
  2652 void
  2653 UnregisterInjectorCallback(DWORD processID)
  2655   if (!OOPInitialized())
  2656     return;
  2658   MutexAutoLock lock(*dumpMapLock);
  2659   pidToMinidump->RemoveEntry(processID);
  2662 #endif // MOZ_CRASHREPORTER_INJECTOR
  2664 bool
  2665 CheckForLastRunCrash()
  2667   if (lastRunCrashID)
  2668     return true;
  2670   // The exception handler callback leaves the filename of the
  2671   // last minidump in a known file.
  2672   nsCOMPtr<nsIFile> lastCrashFile;
  2673   CreateFileFromPath(crashMarkerFilename,
  2674                      getter_AddRefs(lastCrashFile));
  2676   bool exists;
  2677   if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
  2678     return false;
  2681   nsAutoCString lastMinidump_contents;
  2682   if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
  2683     return false;
  2685   lastCrashFile->Remove(false);
  2687 #ifdef XP_WIN
  2688   // Ugly but effective.
  2689   nsDependentString lastMinidump(
  2690       reinterpret_cast<const char16_t*>(lastMinidump_contents.get()));
  2691 #else
  2692   nsAutoCString lastMinidump = lastMinidump_contents;
  2693 #endif
  2694   nsCOMPtr<nsIFile> lastMinidumpFile;
  2695   CreateFileFromPath(lastMinidump.get(),
  2696                      getter_AddRefs(lastMinidumpFile));
  2698   if (!lastMinidumpFile || NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
  2699     return false;
  2702   nsCOMPtr<nsIFile> lastExtraFile;
  2703   if (!GetExtraFileForMinidump(lastMinidumpFile,
  2704                                getter_AddRefs(lastExtraFile))) {
  2705     return false;
  2708   FindPendingDir();
  2710   // Move {dump,extra} to pending folder
  2711   if (!MoveToPending(lastMinidumpFile, lastExtraFile)) {
  2712     return false;
  2715   lastRunCrashID = new nsString();
  2716   return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
  2719 bool
  2720 GetLastRunCrashID(nsAString& id)
  2722   if (!lastRunCrashID_checked) {
  2723     CheckForLastRunCrash();
  2724     lastRunCrashID_checked = true;
  2727   if (!lastRunCrashID) {
  2728     return false;
  2731   id = *lastRunCrashID;
  2732   return true;
  2735 #if defined(XP_WIN)
  2736 // Child-side API
  2737 bool
  2738 SetRemoteExceptionHandler(const nsACString& crashPipe)
  2740   // crash reporting is disabled
  2741   if (crashPipe.Equals(kNullNotifyPipe))
  2742     return true;
  2744   NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
  2746   gExceptionHandler = new google_breakpad::
  2747     ExceptionHandler(L"",
  2748                      FPEFilter,
  2749                      nullptr,    // no minidump callback
  2750                      nullptr,    // no callback context
  2751                      google_breakpad::ExceptionHandler::HANDLER_ALL,
  2752                      MiniDumpNormal,
  2753                      NS_ConvertASCIItoUTF16(crashPipe).get(),
  2754                      nullptr);
  2755 #ifdef XP_WIN
  2756   gExceptionHandler->set_handle_debug_exceptions(true);
  2757 #endif
  2759   // we either do remote or nothing, no fallback to regular crash reporting
  2760   return gExceptionHandler->IsOutOfProcess();
  2763 //--------------------------------------------------
  2764 #elif defined(XP_LINUX)
  2766 // Parent-side API for children
  2767 bool
  2768 CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
  2770   if (!GetEnabled()) {
  2771     *childCrashFd = -1;
  2772     *childCrashRemapFd = -1;
  2773     return true;
  2776   MOZ_ASSERT(OOPInitialized());
  2778   *childCrashFd = clientSocketFd;
  2779   *childCrashRemapFd = kMagicChildCrashReportFd;
  2781   return true;
  2784 // Child-side API
  2785 bool
  2786 SetRemoteExceptionHandler()
  2788   NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
  2790 #ifndef XP_LINUX
  2791   xpstring path = "";
  2792 #else
  2793   // MinidumpDescriptor requires a non-empty path.
  2794   google_breakpad::MinidumpDescriptor path(".");
  2795 #endif
  2796   gExceptionHandler = new google_breakpad::
  2797     ExceptionHandler(path,
  2798                      nullptr,    // no filter callback
  2799                      nullptr,    // no minidump callback
  2800                      nullptr,    // no callback context
  2801                      true,       // install signal handlers
  2802                      kMagicChildCrashReportFd);
  2804   if (gDelayedAnnotations) {
  2805     for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
  2806       gDelayedAnnotations->ElementAt(i)->Run();
  2808     delete gDelayedAnnotations;
  2811   // we either do remote or nothing, no fallback to regular crash reporting
  2812   return gExceptionHandler->IsOutOfProcess();
  2815 //--------------------------------------------------
  2816 #elif defined(XP_MACOSX)
  2817 // Child-side API
  2818 bool
  2819 SetRemoteExceptionHandler(const nsACString& crashPipe)
  2821   // crash reporting is disabled
  2822   if (crashPipe.Equals(kNullNotifyPipe))
  2823     return true;
  2825   NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
  2827   gExceptionHandler = new google_breakpad::
  2828     ExceptionHandler("",
  2829                      Filter,
  2830                      nullptr,    // no minidump callback
  2831                      nullptr,    // no callback context
  2832                      true,       // install signal handlers
  2833                      crashPipe.BeginReading());
  2835   // we either do remote or nothing, no fallback to regular crash reporting
  2836   return gExceptionHandler->IsOutOfProcess();
  2838 #endif  // XP_WIN
  2841 bool
  2842 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
  2844   if (!GetEnabled())
  2845     return false;
  2847   MutexAutoLock lock(*dumpMapLock);
  2849   ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
  2850   if (!pd)
  2851     return false;
  2853   NS_IF_ADDREF(*dump = pd->minidump);
  2854   if (aSequence) {
  2855     *aSequence = pd->sequence;
  2858   pidToMinidump->RemoveEntry(childPid);
  2860   return !!*dump;
  2863 //-----------------------------------------------------------------------------
  2864 // CreatePairedMinidumps() and helpers
  2865 //
  2867 void
  2868 RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
  2869                            const nsACString& name)
  2871   nsCOMPtr<nsIFile> directory;
  2872   childMinidump->GetParent(getter_AddRefs(directory));
  2873   if (!directory)
  2874     return;
  2876   nsAutoCString leafName;
  2877   childMinidump->GetNativeLeafName(leafName);
  2879   // turn "<id>.dmp" into "<id>-<name>.dmp
  2880   leafName.Insert(NS_LITERAL_CSTRING("-") + name, leafName.Length() - 4);
  2882   minidump->MoveToNative(directory, leafName);
  2885 static bool
  2886 PairedDumpCallback(
  2887 #ifdef XP_LINUX
  2888                    const MinidumpDescriptor& descriptor,
  2889 #else
  2890                    const XP_CHAR* dump_path,
  2891                    const XP_CHAR* minidump_id,
  2892 #endif
  2893                    void* context,
  2894 #ifdef XP_WIN32
  2895                    EXCEPTION_POINTERS* /*unused*/,
  2896                    MDRawAssertionInfo* /*unused*/,
  2897 #endif
  2898                    bool succeeded)
  2900   nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
  2902   xpstring dump;
  2903 #ifdef XP_LINUX
  2904   dump = descriptor.path();
  2905 #else
  2906   dump = dump_path;
  2907   dump += XP_PATH_SEPARATOR;
  2908   dump += minidump_id;
  2909   dump += dumpFileExtension;
  2910 #endif
  2912   CreateFileFromPath(dump, getter_AddRefs(minidump));
  2913   return true;
  2916 static bool
  2917 PairedDumpCallbackExtra(
  2918 #ifdef XP_LINUX
  2919                         const MinidumpDescriptor& descriptor,
  2920 #else
  2921                         const XP_CHAR* dump_path,
  2922                         const XP_CHAR* minidump_id,
  2923 #endif
  2924                         void* context,
  2925 #ifdef XP_WIN32
  2926                         EXCEPTION_POINTERS* /*unused*/,
  2927                         MDRawAssertionInfo* /*unused*/,
  2928 #endif
  2929                         bool succeeded)
  2931   PairedDumpCallback(
  2932 #ifdef XP_LINUX
  2933                      descriptor,
  2934 #else
  2935                      dump_path, minidump_id,
  2936 #endif
  2937                      context,
  2938 #ifdef XP_WIN32
  2939                      nullptr, nullptr,
  2940 #endif
  2941                      succeeded);
  2943   nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
  2945   nsCOMPtr<nsIFile> extra;
  2946   return WriteExtraForMinidump(minidump, Blacklist(), getter_AddRefs(extra));
  2949 ThreadId
  2950 CurrentThreadId()
  2952 #if defined(XP_WIN)
  2953   return ::GetCurrentThreadId();
  2954 #elif defined(XP_LINUX)
  2955   return sys_gettid();
  2956 #elif defined(XP_MACOSX)
  2957   // Just return an index, since Mach ports can't be directly serialized
  2958   thread_act_port_array_t   threads_for_task;
  2959   mach_msg_type_number_t    thread_count;
  2961   if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
  2962     return -1;
  2964   for (unsigned int i = 0; i < thread_count; ++i) {
  2965     if (threads_for_task[i] == mach_thread_self())
  2966       return i;
  2968   abort();
  2969 #else
  2970 #  error "Unsupported platform"
  2971 #endif
  2974 #ifdef XP_MACOSX
  2975 static mach_port_t
  2976 GetChildThread(ProcessHandle childPid, ThreadId childBlamedThread)
  2978   mach_port_t childThread = MACH_PORT_NULL;
  2979   thread_act_port_array_t   threads_for_task;
  2980   mach_msg_type_number_t    thread_count;
  2982   if (task_threads(childPid, &threads_for_task, &thread_count)
  2983       == KERN_SUCCESS && childBlamedThread < thread_count) {
  2984     childThread = threads_for_task[childBlamedThread];
  2987   return childThread;
  2989 #endif
  2991 bool
  2992 CreatePairedMinidumps(ProcessHandle childPid,
  2993                       ThreadId childBlamedThread,
  2994                       nsIFile** childDump)
  2996   if (!GetEnabled())
  2997     return false;
  2999 #ifdef XP_MACOSX
  3000   mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
  3001 #else
  3002   ThreadId childThread = childBlamedThread;
  3003 #endif
  3005   xpstring dump_path;
  3006 #ifndef XP_LINUX
  3007   dump_path = gExceptionHandler->dump_path();
  3008 #else
  3009   dump_path = gExceptionHandler->minidump_descriptor().directory();
  3010 #endif
  3012   // dump the child
  3013   nsCOMPtr<nsIFile> childMinidump;
  3014   if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
  3015          childPid,
  3016          childThread,
  3017          dump_path,
  3018          PairedDumpCallbackExtra,
  3019          static_cast<void*>(&childMinidump)))
  3020     return false;
  3022   nsCOMPtr<nsIFile> childExtra;
  3023   GetExtraFileForMinidump(childMinidump, getter_AddRefs(childExtra));
  3025   // dump the parent
  3026   nsCOMPtr<nsIFile> parentMinidump;
  3027   if (!google_breakpad::ExceptionHandler::WriteMinidump(
  3028          dump_path,
  3029 #ifdef XP_MACOSX
  3030          true,                  // write exception stream
  3031 #endif
  3032          PairedDumpCallback,
  3033          static_cast<void*>(&parentMinidump))) {
  3035     childMinidump->Remove(false);
  3036     childExtra->Remove(false);
  3038     return false;
  3041   // success
  3042   RenameAdditionalHangMinidump(parentMinidump, childMinidump,
  3043                                NS_LITERAL_CSTRING("browser"));
  3045   if (ShouldReport()) {
  3046     MoveToPending(childMinidump, childExtra);
  3047     MoveToPending(parentMinidump, nullptr);
  3050   childMinidump.forget(childDump);
  3052   return true;
  3055 bool
  3056 CreateAdditionalChildMinidump(ProcessHandle childPid,
  3057                               ThreadId childBlamedThread,
  3058                               nsIFile* parentMinidump,
  3059                               const nsACString& name)
  3061   if (!GetEnabled())
  3062     return false;
  3064 #ifdef XP_MACOSX
  3065   mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
  3066 #else
  3067   ThreadId childThread = childBlamedThread;
  3068 #endif
  3070   xpstring dump_path;
  3071 #ifndef XP_LINUX
  3072   dump_path = gExceptionHandler->dump_path();
  3073 #else
  3074   dump_path = gExceptionHandler->minidump_descriptor().directory();
  3075 #endif
  3077   // dump the child
  3078   nsCOMPtr<nsIFile> childMinidump;
  3079   if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
  3080          childPid,
  3081          childThread,
  3082          dump_path,
  3083          PairedDumpCallback,
  3084          static_cast<void*>(&childMinidump))) {
  3085     return false;
  3088   RenameAdditionalHangMinidump(childMinidump, parentMinidump, name);
  3090   return true;
  3093 bool
  3094 UnsetRemoteExceptionHandler()
  3096   delete gExceptionHandler;
  3097   gExceptionHandler = nullptr;
  3098   return true;
  3101 #if defined(MOZ_WIDGET_ANDROID)
  3102 void AddLibraryMapping(const char* library_name,
  3103                        uintptr_t   start_address,
  3104                        size_t      mapping_length,
  3105                        size_t      file_offset)
  3107   if (!gExceptionHandler) {
  3108     mapping_info info;
  3109     info.name = library_name;
  3110     info.start_address = start_address;
  3111     info.length = mapping_length;
  3112     info.file_offset = file_offset;
  3113     library_mappings.push_back(info);
  3115   else {
  3116     u_int8_t guid[sizeof(MDGUID)];
  3117     google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)start_address, guid);
  3118     gExceptionHandler->AddMappingInfo(library_name,
  3119                                       guid,
  3120                                       start_address,
  3121                                       mapping_length,
  3122                                       file_offset);
  3125 #endif
  3127 } // namespace CrashReporter

mercurial