Bug 1236108: Add support for exception-context annotations for content processes to the crash reporter; r?bsmedberg draft
authorAaron Klotz <aklotz@mozilla.com>
Mon, 07 Mar 2016 12:48:58 -0800
changeset 338189 e08e98a99b8ea7ba34ef933033b69712f1a155e2
parent 338188 faf03a6a45b1cfb68ba1e8f78d43b3c1af7c54dd
child 515755 7863783cccfabdadd7a5ddc4f3108dc57cb9fb03
push id12460
push useraklotz@mozilla.com
push dateTue, 08 Mar 2016 19:04:31 +0000
reviewersbsmedberg
bugs1236108
milestone47.0a1
Bug 1236108: Add support for exception-context annotations for content processes to the crash reporter; r?bsmedberg This patch redefines XP_PATH_MAX on Windows to be MAX_PATH + 1. I did this because the longer definition would actually not work with most Windows APIs. Some APIs can work with longer lengths if the path is prefixed with "\\?\", but that is not guaranteed in general. See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath MozReview-Commit-ID: LhT5oM5hlkl
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
toolkit/crashreporter/test/unit_ipc/test_content_exception_time_annotation.js
toolkit/crashreporter/test/unit_ipc/xpcshell.ini
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsExceptionHandler.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/unused.h"
 #include "mozilla/Snprintf.h"
 #include "mozilla/SyncRunnable.h"
@@ -116,18 +119,17 @@ typedef wchar_t XP_CHAR;
 typedef std::wstring xpstring;
 #define XP_TEXT(x) L##x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
 #define XP_STRLEN(x) wcslen(x)
 #define my_strlen strlen
 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
 #define PATH_SEPARATOR "\\"
 #define XP_PATH_SEPARATOR L"\\"
-// sort of arbitrary, but MAX_PATH is kinda small
-#define XP_PATH_MAX 4096
+#define XP_PATH_MAX (MAX_PATH + 1)
 // "<reporter path>" "<minidump path>"
 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
 #ifdef _USE_32BIT_TIME_T
 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
 #else
 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
 #endif
 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
@@ -152,20 +154,27 @@ typedef std::string xpstring;
 #define sys_close close
 #define sys_fork fork
 #define sys_open open
 #define sys_read read
 #define sys_write write
 #endif
 #endif // XP_WIN32
 
+#if defined(__GNUC__)
+#define MAYBE_UNUSED __attribute__((unused))
+#else
+#define MAYBE_UNUSED
+#endif // defined(__GNUC__)
+
 #ifndef XP_LINUX
 static const XP_CHAR dumpFileExtension[] = XP_TEXT(".dmp");
 #endif
 
+static const XP_CHAR childCrashAnnotationBaseName[] = XP_TEXT("GeckoChildCrash");
 static const XP_CHAR extraFileExtension[] = XP_TEXT(".extra");
 static const XP_CHAR memoryReportExtension[] = XP_TEXT(".memory.json.gz");
 
 // A whitelist of crash annotations which do not contain sensitive data
 // and are saved in the crash record and sent with Firefox Health Report.
 static char const * const kCrashEventAnnotations[] = {
   "AsyncShutdownTimeout",
   "BuildID",
@@ -250,16 +259,21 @@ static uint32_t eventloopNestingLevel = 
 
 // Avoid a race during application termination.
 static Mutex* dumpSafetyLock;
 static bool isSafeToDump = false;
 
 // OOP crash reporting
 static CrashGenerationServer* crashServer; // chrome process has this
 
+#if (defined(XP_MACOSX) || defined(XP_WIN)) && defined(MOZ_CONTENT_SANDBOX)
+// This field is only valid in the chrome process, not content.
+static xpstring* contentProcessTmpDir = nullptr;
+#endif
+
 #  if defined(XP_WIN) || defined(XP_MACOSX)
 // If crash reporting is disabled, we hand out this "null" pipe to the
 // child process and don't attempt to connect to a parent server.
 static const char kNullNotifyPipe[] = "-";
 static char* childCrashNotifyPipe;
 
 #  elif defined(XP_LINUX)
 static int serverSocketFd = -1;
@@ -481,22 +495,44 @@ CreateFileFromPath(const xpstring& path,
   NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
 }
 
 static void
 CreateFileFromPath(const wchar_t* path, nsIFile** file)
 {
   CreateFileFromPath(std::wstring(path), file);
 }
+
+static xpstring*
+CreatePathFromFile(nsIFile* file)
+{
+  nsAutoString path;
+  nsresult rv = file->GetPath(path);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+  return new xpstring(path.get(), path.Length());
+}
 #else
 static void
 CreateFileFromPath(const xpstring& path, nsIFile** file)
 {
   NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
 }
+
+MAYBE_UNUSED static xpstring*
+CreatePathFromFile(nsIFile* file)
+{
+  nsAutoCString path;
+  nsresult rv = file->GetNativePath(path);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+  return new xpstring(path.get(), path.Length());
+}
 #endif
 
 static XP_CHAR*
 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
 {
   int appendLen = XP_STRLEN(toAppend);
   if (appendLen >= *size) appendLen = *size - 1;
 
@@ -575,16 +611,17 @@ bool copy_file(const char* from, const c
   sys_close(fdfrom);
   sys_close(fdto);
 
   return ok;
 }
 #endif
 
 #ifdef XP_WIN
+
 class PlatformWriter
 {
 public:
   PlatformWriter()
     : mHandle(INVALID_HANDLE_VALUE)
   { }
 
   explicit PlatformWriter(const wchar_t* path)
@@ -693,16 +730,44 @@ static void
 WriteAnnotation(PlatformWriter& pw, const char (&name)[N],
                 const char* value) {
   WriteLiteral(pw, name);
   WriteLiteral(pw, "=");
   WriteString(pw, value);
   WriteLiteral(pw, "\n");
 };
 
+/**
+ * If minidump_id is null, we assume that dump_path contains the full
+ * dump file path.
+ */
+static void
+OpenAPIData(PlatformWriter& aWriter,
+            const XP_CHAR* dump_path, const XP_CHAR* minidump_id = nullptr
+           )
+{
+  static XP_CHAR extraDataPath[XP_PATH_MAX];
+  int size = XP_PATH_MAX;
+  XP_CHAR* p;
+  if (minidump_id) {
+    p = Concat(extraDataPath, dump_path, &size);
+    p = Concat(p, XP_PATH_SEPARATOR, &size);
+    p = Concat(p, minidump_id, &size);
+  } else {
+    p = Concat(extraDataPath, dump_path, &size);
+    // Skip back past the .dmp extension, if any.
+    if (*(p - 4) == XP_TEXT('.')) {
+      p -= 4;
+      size += 4;
+    }
+  }
+  Concat(p, extraFileExtension, &size);
+  aWriter.Open(extraDataPath);
+}
+
 bool MinidumpCallback(
 #ifdef XP_LINUX
                       const MinidumpDescriptor& descriptor,
 #else
                       const XP_CHAR* dump_path,
                       const XP_CHAR* minidump_id,
 #endif
                       void* context,
@@ -721,29 +786,16 @@ bool MinidumpCallback(
   p = Concat(minidumpPath, dump_path, &size);
   p = Concat(p, XP_PATH_SEPARATOR, &size);
   p = Concat(p, minidump_id, &size);
   Concat(p, dumpFileExtension, &size);
 #else
   Concat(minidumpPath, descriptor.path(), &size);
 #endif
 
-  static XP_CHAR extraDataPath[XP_PATH_MAX];
-  size = XP_PATH_MAX;
-#ifndef XP_LINUX
-  p = Concat(extraDataPath, dump_path, &size);
-  p = Concat(p, XP_PATH_SEPARATOR, &size);
-  p = Concat(p, minidump_id, &size);
-#else
-  p = Concat(extraDataPath, descriptor.path(), &size);
-  // Skip back past the .dmp extension.
-  p -= 4;
-#endif
-  Concat(p, extraFileExtension, &size);
-
   static XP_CHAR memoryReportLocalPath[XP_PATH_MAX];
   size = XP_PATH_MAX;
 #ifndef XP_LINUX
   p = Concat(memoryReportLocalPath, dump_path, &size);
   p = Concat(p, XP_PATH_SEPARATOR, &size);
   p = Concat(p, minidump_id, &size);
 #else
   p = Concat(memoryReportLocalPath, descriptor.path(), &size);
@@ -855,17 +907,21 @@ bool MinidumpCallback(
       WriteLiteral(eventFile, "\n");
       if (crashEventAPIData) {
         eventFile.WriteBuffer(crashEventAPIData->get(), crashEventAPIData->Length());
       }
     }
 
     if (!crashReporterAPIData->IsEmpty()) {
       // write out API data
-      apiData.Open(extraDataPath);
+#ifdef XP_LINUX
+      OpenAPIData(apiData, descriptor.path());
+#else
+      OpenAPIData(apiData, dump_path, minidump_id);
+#endif
       apiData.WriteBuffer(crashReporterAPIData->get(), crashReporterAPIData->Length());
     }
     WriteAnnotation(apiData, "CrashTime", crashTimeString);
     WriteAnnotation(apiData, "UptimeTS", uptimeTSString);
 
     if (timeSinceLastCrash != 0) {
       WriteAnnotation(apiData, "SecondsSinceLastCrash",
                       timeSinceLastCrashString);
@@ -1044,16 +1100,164 @@ bool MinidumpCallback(
 #endif
   }
 #endif // XP_MACOSX
 #endif // XP_UNIX
 
   return returnValue;
 }
 
+#if defined(XP_MACOSX) || defined(__ANDROID__)
+static size_t
+EnsureTrailingSlash(char* aBuf, size_t aBufLen)
+{
+  size_t len = XP_STRLEN(aBuf);
+  if ((len + 2) < aBufLen && aBuf[len - 1] != '/') {
+    aBuf[len] = '/';
+    ++len;
+    aBuf[len] = 0;
+  }
+  return len;
+}
+#endif
+
+#if defined(XP_WIN32)
+
+static size_t
+BuildTempPath(wchar_t* aBuf, size_t aBufLen)
+{
+  // first figure out buffer size
+  DWORD pathLen = GetTempPath(0, nullptr);
+  if (pathLen == 0 || pathLen >= aBufLen) {
+    return 0;
+  }
+
+  return GetTempPath(pathLen, aBuf);
+}
+
+#elif defined(XP_MACOSX)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+  if (aBufLen < PATH_MAX) {
+    return 0;
+  }
+
+  FSRef fsRef;
+  OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
+                           kCreateFolder, &fsRef);
+  if (err != noErr) {
+    return 0;
+  }
+
+  OSStatus status = FSRefMakePath(&fsRef, (UInt8*)aBuf, PATH_MAX);
+  if (status != noErr) {
+    return 0;
+  }
+
+  return EnsureTrailingSlash(aBuf, aBufLen);
+}
+
+#elif defined(__ANDROID__)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+  // GeckoAppShell or Gonk's init.rc sets this in the environment
+  const char *tempenv = PR_GetEnv("TMPDIR");
+  if (!tempenv) {
+    return false;
+  }
+  int size = (int)aBufLen;
+  Concat(aBuf, tempenv, &size);
+  return EnsureTrailingSlash(aBuf, aBufLen);
+}
+
+#elif defined(XP_UNIX)
+
+static size_t
+BuildTempPath(char* aBuf, size_t aBufLen)
+{
+  // we assume it's always /tmp on unix systems
+  NS_NAMED_LITERAL_CSTRING(tmpPath, "/tmp/");
+  int size = (int)aBufLen;
+  Concat(aBuf, tmpPath.get(), &size);
+  return tmpPath.Length();
+}
+
+#else
+#error "Implement this for your platform"
+#endif
+
+template <typename CharT, size_t N>
+static size_t
+BuildTempPath(CharT (&aBuf)[N])
+{
+  static_assert(N >= XP_PATH_MAX, "char array length is too small");
+  return BuildTempPath(&aBuf[0], N);
+}
+
+template <typename PathStringT>
+static bool
+BuildTempPath(PathStringT& aResult)
+{
+  aResult.SetLength(XP_PATH_MAX);
+  size_t actualLen = BuildTempPath(aResult.BeginWriting(), XP_PATH_MAX);
+  if (!actualLen) {
+    return false;
+  }
+  aResult.SetLength(actualLen);
+  return true;
+}
+
+static void
+PrepareChildExceptionTimeAnnotations()
+{
+  MOZ_ASSERT(!XRE_IsParentProcess());
+  static XP_CHAR tempPath[XP_PATH_MAX] = {0};
+
+  // Get the temp path
+  size_t tempPathLen = BuildTempPath(tempPath);
+  if (!tempPathLen) {
+    return;
+  }
+
+  // Generate and append the file name
+  int size = XP_PATH_MAX - tempPathLen;
+  XP_CHAR* p = tempPath + tempPathLen;
+  p = Concat(p, childCrashAnnotationBaseName, &size);
+  XP_CHAR pidBuffer[32] = XP_TEXT("");
+#if defined(XP_WIN32)
+  _ui64tow(GetCurrentProcessId(), pidBuffer, 10);
+#else
+  XP_STOA(getpid(), pidBuffer, 10);
+#endif
+  p = Concat(p, pidBuffer, &size);
+
+  // Now open the file...
+  PlatformWriter apiData;
+  OpenAPIData(apiData, tempPath);
+
+  // ...and write out any annotations. These should be escaped if necessary
+  // (but don't call EscapeAnnotation here, because it touches the heap).
+  char oomAllocationSizeBuffer[32] = "";
+  if (gOOMAllocationSize) {
+    XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
+  }
+
+  if (oomAllocationSizeBuffer[0]) {
+    WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
+  }
+
+  if (gMozCrashReason) {
+    WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
+  }
+}
+
 #ifdef XP_WIN
 static void
 ReserveBreakpadVM()
 {
   if (!gBreakpadReservedVM) {
     gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
                                        PAGE_NOACCESS);
   }
@@ -1094,16 +1298,28 @@ static bool FPEFilter(void* context, EXC
     case STATUS_FLOAT_MULTIPLE_FAULTS:
     case STATUS_FLOAT_MULTIPLE_TRAPS:
       return false; // Don't write minidump, continue exception search
   }
   mozilla::IOInterposer::Disable();
   FreeBreakpadVM();
   return true;
 }
+
+static bool
+ChildFPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
+               MDRawAssertionInfo* assertion)
+{
+  bool result = FPEFilter(context, exinfo, assertion);
+  if (result) {
+    PrepareChildExceptionTimeAnnotations();
+  }
+  return result;
+}
+
 #endif // XP_WIN
 
 static bool ShouldReport()
 {
   // this environment variable prevents us from launching
   // the crash reporter client
   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
   if (envvar && *envvar) {
@@ -1113,24 +1329,33 @@ static bool ShouldReport()
   envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
   if (envvar && *envvar) {
     return false;
   }
 
   return true;
 }
 
-namespace {
-  bool Filter(void* context) {
-    mozilla::IOInterposer::Disable();
-    return true;
+static bool
+Filter(void* context)
+{
+  mozilla::IOInterposer::Disable();
+  return true;
+}
+
+static bool
+ChildFilter(void* context)
+{
+  bool result = Filter(context);
+  if (result) {
+    PrepareChildExceptionTimeAnnotations();
   }
+  return result;
 }
 
-
 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
                              bool force/*=false*/)
 {
   if (gExceptionHandler)
     return NS_ERROR_ALREADY_INITIALIZED;
 
 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
   // In non-debug builds, enable the crash reporter by default, and allow
@@ -1214,51 +1439,22 @@ nsresult SetExceptionHandler(nsIFile* aX
       crashReporterPath = ToNewCString(package);
     }
 #endif
   }
 
   // get temp path to use for minidump path
 #if defined(XP_WIN32)
   nsString tempPath;
-
-  // first figure out buffer size
-  int pathLen = GetTempPath(0, nullptr);
-  if (pathLen == 0)
-    return NS_ERROR_FAILURE;
-
-  tempPath.SetLength(pathLen);
-  GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
-#elif defined(XP_MACOSX)
+#else
   nsCString tempPath;
-  FSRef fsRef;
-  OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
-                           kCreateFolder, &fsRef);
-  if (err != noErr)
+#endif
+  if (!BuildTempPath(tempPath)) {
     return NS_ERROR_FAILURE;
-
-  char path[PATH_MAX];
-  OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
-  if (status != noErr)
-    return NS_ERROR_FAILURE;
-
-  tempPath = path;
-
-#elif defined(__ANDROID__)
-  // GeckoAppShell or Gonk's init.rc sets this in the environment
-  const char *tempenv = PR_GetEnv("TMPDIR");
-  if (!tempenv)
-    return NS_ERROR_FAILURE;
-  nsCString tempPath(tempenv);
-#elif defined(XP_UNIX)
-  // we assume it's always /tmp on unix systems
-  nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
-#else
-#error "Implement this for your platform"
-#endif
+  }
 
 #ifdef XP_MACOSX
   // Initialize spawn attributes, since this calls malloc.
   if (posix_spawnattr_init(&spawnattr) != 0) {
     return NS_ERROR_FAILURE;
   }
 
   // Set spawn attributes.
@@ -2672,34 +2868,174 @@ WriteExtraData(nsIFile* extraFile,
 }
 
 bool
 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
 {
   return WriteExtraData(extraFile, data, Blacklist());
 }
 
+static bool
+GetExtraFileForChildPid(nsIFile* aMinidump, uint32_t aPid, nsIFile** aExtraFile)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  nsCOMPtr<nsIFile> extraFile;
+  nsresult rv;
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+#  if defined(MOZ_CONTENT_SANDBOX)
+  if (!contentProcessTmpDir) {
+    return false;
+  }
+  CreateFileFromPath(*contentProcessTmpDir, getter_AddRefs(extraFile));
+  if (!extraFile) {
+    return false;
+  }
+#  else
+  rv = aMinidump->Clone(getter_AddRefs(extraFile));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+#  endif // defined(MOZ_CONTENT_SANDBOX)
+#elif defined(XP_UNIX)
+  rv = NS_NewLocalFile(NS_LITERAL_STRING("/tmp"), false,
+                       getter_AddRefs(extraFile));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+#else
+#error "Implement this for your platform"
+#endif
+
+  nsAutoString leafName;
+#if defined(XP_WIN)
+  leafName.AppendPrintf("%S%u%S", childCrashAnnotationBaseName, aPid,
+                        extraFileExtension);
+#else
+  leafName.AppendPrintf("%s%u%s", childCrashAnnotationBaseName, aPid,
+                        extraFileExtension);
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+#  if defined(MOZ_CONTENT_SANDBOX)
+  rv = extraFile->Append(leafName);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+#  else
+  rv = extraFile->SetLeafName(leafName);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+#  endif // defined(MOZ_CONTENT_SANDBOX)
+#elif defined(XP_UNIX)
+  rv = extraFile->Append(leafName);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+#else
+#error "Implement this for your platform"
+#endif
+
+  extraFile.forget(aExtraFile);
+  return true;
+}
 
 static bool
+IsDataEscaped(char* aData)
+{
+  if (strchr(aData, '\n')) {
+    // There should not be any newlines
+    return false;
+  }
+  char* pos = aData;
+  while ((pos = strchr(pos, '\\'))) {
+    if (*(pos + 1) != '\\') {
+      return false;
+    }
+    // Add 2 to account for the second pos
+    pos += 2;
+  }
+  return true;
+}
+
+static void
+ReadAndValidateExceptionTimeAnnotations(FILE*& aFd,
+                                        AnnotationTable& aAnnotations)
+{
+  char line[0x1000];
+  while (fgets(line, sizeof(line), aFd)) {
+    char* data = strchr(line, '=');
+    if (!data) {
+      // bad data? Abort!
+      break;
+    }
+    // Move past the '='
+    *data = 0;
+    ++data;
+    size_t dataLen = strlen(data);
+    // Chop off any trailing newline
+    if (dataLen > 0 && data[dataLen - 1] == '\n') {
+      data[dataLen - 1] = 0;
+      --dataLen;
+    }
+    // There should not be any newlines in the key
+    if (strchr(line, '\n')) {
+      break;
+    }
+    // Data should have been escaped by the child
+    if (!IsDataEscaped(data)) {
+      break;
+    }
+    // Looks good, save the (line,data) pair
+    aAnnotations.Put(nsDependentCString(line),
+                     nsDependentCString(data, dataLen));
+  }
+}
+
+/**
+ * NOTE: One side effect of this function is that it deletes the
+ * GeckoChildCrash<pid>.extra file if it exists, once processed.
+ */
+static bool
 WriteExtraForMinidump(nsIFile* minidump,
+                      uint32_t pid,
                       const Blacklist& blacklist,
                       nsIFile** extraFile)
 {
   nsCOMPtr<nsIFile> extra;
-  if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra)))
+  if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra))) {
     return false;
+  }
 
   if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
                       blacklist,
                       true /*write crash time*/,
-                      true /*truncate*/))
+                      true /*truncate*/)) {
     return false;
-
-  *extraFile = nullptr;
-  extra.swap(*extraFile);
+  }
+
+  nsCOMPtr<nsIFile> exceptionTimeExtra;
+  FILE* fd;
+  if (pid && GetExtraFileForChildPid(minidump, pid,
+                                     getter_AddRefs(exceptionTimeExtra)) &&
+      NS_SUCCEEDED(exceptionTimeExtra->OpenANSIFileDesc("r", &fd))) {
+    AnnotationTable exceptionTimeAnnotations;
+    ReadAndValidateExceptionTimeAnnotations(fd, exceptionTimeAnnotations);
+    fclose(fd);
+    if (!AppendExtraData(extra, exceptionTimeAnnotations)) {
+      return false;
+    }
+  }
+  if (exceptionTimeExtra) {
+    exceptionTimeExtra->Remove(false);
+  }
+
+  extra.forget(extraFile);
 
   return true;
 }
 
 // It really only makes sense to call this function when
 // ShouldReport() is true.
 static bool
 MoveToPending(nsIFile* dumpFile, nsIFile* extraFile)
@@ -2743,32 +3079,33 @@ OnChildProcessDumpRequested(void* aConte
   CreateFileFromPath(
 #ifdef XP_MACOSX
                      aFilePath,
 #else
                      *aFilePath,
 #endif
                      getter_AddRefs(minidump));
 
-  if (!WriteExtraForMinidump(minidump,
+  uint32_t pid =
+#ifdef XP_MACOSX
+    aClientInfo.pid();
+#else
+    aClientInfo->pid();
+#endif
+
+  if (!WriteExtraForMinidump(minidump, pid,
                              Blacklist(kSubprocessBlacklist,
                                        ArrayLength(kSubprocessBlacklist)),
                              getter_AddRefs(extraFile)))
     return;
 
   if (ShouldReport())
     MoveToPending(minidump, extraFile);
 
   {
-    uint32_t pid =
-#ifdef XP_MACOSX
-      aClientInfo.pid();
-#else
-      aClientInfo->pid();
-#endif
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     bool runCallback;
 #endif
     {
       MutexAutoLock lock(*dumpMapLock);
       ChildProcessData* pd = pidToMinidump->PutEntry(pid);
       MOZ_ASSERT(!pd->minidump);
@@ -2786,23 +3123,16 @@ OnChildProcessDumpRequested(void* aConte
 }
 
 static bool
 OOPInitialized()
 {
   return pidToMinidump != nullptr;
 }
 
-#ifdef XP_MACOSX
-static bool ChildFilter(void *context) {
-  mozilla::IOInterposer::Disable();
-  return true;
-}
-#endif
-
 void
 OOPInit()
 {
   class ProxyToMainThread : public nsRunnable
   {
   public:
     NS_IMETHOD Run() {
       OOPInit();
@@ -2819,16 +3149,24 @@ OOPInit()
   if (OOPInitialized())
     return;
 
   MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_ASSERT(gExceptionHandler != nullptr,
              "attempt to initialize OOP crash reporter before in-process crashreporter!");
 
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+  nsCOMPtr<nsIFile> tmpDir;
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+  if (NS_SUCCEEDED(rv)) {
+    contentProcessTmpDir = CreatePathFromFile(tmpDir);
+  }
+#endif
+
 #if defined(XP_WIN)
   childCrashNotifyPipe =
     PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
                 static_cast<int>(::GetCurrentProcessId()));
 
   const std::wstring dumpPath = gExceptionHandler->dump_path();
   crashServer = new CrashGenerationServer(
     std::wstring(NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get()),
@@ -2857,17 +3195,17 @@ OOPInit()
 #elif defined(XP_MACOSX)
   childCrashNotifyPipe =
     PR_smprintf("gecko-crash-server-pipe.%i",
                 static_cast<int>(getpid()));
   const std::string dumpPath = gExceptionHandler->dump_path();
 
   crashServer = new CrashGenerationServer(
     childCrashNotifyPipe,
-    ChildFilter,
+    nullptr,
     nullptr,
     OnChildProcessDumpRequested, nullptr,
     nullptr, nullptr,
     true, // automatically generate dumps
     dumpPath);
 #endif
 
   if (!crashServer->Start())
@@ -3065,26 +3403,26 @@ SetRemoteExceptionHandler(const nsACStri
   // crash reporting is disabled
   if (crashPipe.Equals(kNullNotifyPipe))
     return true;
 
   MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
 
   gExceptionHandler = new google_breakpad::
     ExceptionHandler(L"",
-                     FPEFilter,
+                     ChildFPEFilter,
                      nullptr,    // no minidump callback
                      nullptr,    // no callback context
                      google_breakpad::ExceptionHandler::HANDLER_ALL,
                      MiniDumpNormal,
                      NS_ConvertASCIItoUTF16(crashPipe).get(),
                      nullptr);
-#ifdef XP_WIN
   gExceptionHandler->set_handle_debug_exceptions(true);
-#endif
+
+  mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 
 //--------------------------------------------------
 #elif defined(XP_LINUX)
 
@@ -3107,37 +3445,36 @@ CreateNotificationPipeForChild(int* chil
 }
 
 // Child-side API
 bool
 SetRemoteExceptionHandler()
 {
   MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
 
-#ifndef XP_LINUX
-  xpstring path = "";
-#else
   // MinidumpDescriptor requires a non-empty path.
   google_breakpad::MinidumpDescriptor path(".");
-#endif
+
   gExceptionHandler = new google_breakpad::
     ExceptionHandler(path,
-                     nullptr,    // no filter callback
+                     ChildFilter,
                      nullptr,    // no minidump callback
                      nullptr,    // no callback context
                      true,       // install signal handlers
                      kMagicChildCrashReportFd);
 
   if (gDelayedAnnotations) {
     for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
       gDelayedAnnotations->ElementAt(i)->Run();
     }
     delete gDelayedAnnotations;
   }
 
+  mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 
 //--------------------------------------------------
 #elif defined(XP_MACOSX)
 // Child-side API
 bool
@@ -3146,22 +3483,24 @@ SetRemoteExceptionHandler(const nsACStri
   // crash reporting is disabled
   if (crashPipe.Equals(kNullNotifyPipe))
     return true;
 
   MOZ_ASSERT(!gExceptionHandler, "crash client already init'd");
 
   gExceptionHandler = new google_breakpad::
     ExceptionHandler("",
-                     Filter,
+                     ChildFilter,
                      nullptr,    // no minidump callback
                      nullptr,    // no callback context
                      true,       // install signal handlers
                      crashPipe.BeginReading());
 
+  mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 #endif  // XP_WIN
 
 
 bool
 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
@@ -3265,17 +3604,17 @@ PairedDumpCallbackExtra(
 #ifdef XP_WIN32
                      nullptr, nullptr,
 #endif
                      succeeded);
 
   nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
 
   nsCOMPtr<nsIFile> extra;
-  return WriteExtraForMinidump(minidump, Blacklist(), getter_AddRefs(extra));
+  return WriteExtraForMinidump(minidump, 0, Blacklist(), getter_AddRefs(extra));
 }
 
 ThreadId
 CurrentThreadId()
 {
 #if defined(XP_WIN)
   return ::GetCurrentThreadId();
 #elif defined(XP_LINUX)
--- a/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
@@ -2,21 +2,21 @@ load("../unit/head_crashreporter.js");
 
 function run_test()
 {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
     dump("INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
-  // Try crashing with a pure virtual call
+  // Try crashing with a runtime abort
   do_content_crash(function() {
                      crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
                      crashReporter.annotateCrashReport("TestKey", "TestValue");
                      crashReporter.appendAppNotesToCrashReport("!!!foo!!!");
-		   },
+                   },
                    function(mdump, extra) {
                      do_check_eq(extra.TestKey, "TestValue");
                      do_check_true('StartupTime' in extra);
                      do_check_true('ProcessType' in extra);
                      do_check_neq(extra.Notes.indexOf("!!!foo!!!"), -1);
                    });
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_exception_time_annotation.js
@@ -0,0 +1,17 @@
+load("../unit/head_crashreporter.js");
+
+function run_test()
+{
+  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
+    dump("INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n");
+    return;
+  }
+
+  // Try crashing with an OOM
+  do_content_crash(function() {
+                     crashType = CrashTestUtils.CRASH_OOM;
+                   },
+                   function(mdump, extra) {
+                     do_check_true('OOMAllocationSize' in extra);
+                   });
+}
--- a/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 head =
 tail =
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 
 [test_content_annotation.js]
+[test_content_exception_time_annotation.js]