Bug 1402519 - When the crash reporter code is disabled at configure time replace it with a dummy implementation; r?ted.mielczarek draft
authorGabriele Svelto <gsvelto@mozilla.com>
Wed, 11 Oct 2017 11:34:09 +0200
changeset 688673 33f543f3304e5f54a5c715d8db948c72b79e8156
parent 688672 bb715b6735c183fa75e5090e36f391197f87b171
child 688674 e01446641c56600105e7e9c703e92808527927b1
push id86821
push usergsvelto@mozilla.com
push dateMon, 30 Oct 2017 14:19:43 +0000
reviewersted.mielczarek
bugs1402519
milestone58.0a1
Bug 1402519 - When the crash reporter code is disabled at configure time replace it with a dummy implementation; r?ted.mielczarek MozReview-Commit-ID: F5QbaI1LlmZ
js/xpconnect/src/XPCShellImpl.cpp
toolkit/components/crashes/moz.build
toolkit/components/moz.build
toolkit/crashreporter/jar.mn
toolkit/crashreporter/moz.build
toolkit/crashreporter/nsDummyExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/crashreporter/nsExceptionHandlerUtils.cpp
toolkit/crashreporter/nsExceptionHandlerUtils.h
toolkit/moz.build
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1207,17 +1207,17 @@ XRE_XPCShellMain(int argc, char** argv, 
             XRE_AddManifestLocation(NS_APP_LOCATION, lf);
 
             argc -= 2;
             argv += 2;
         }
 
 #ifdef MOZ_CRASHREPORTER
         const char* val = getenv("MOZ_CRASHREPORTER");
-        if (val && *val) {
+        if (val && *val && !CrashReporter::IsDummy()) {
             rv = CrashReporter::SetExceptionHandler(greDir, true);
             if (NS_FAILED(rv)) {
                 printf("CrashReporter::SetExceptionHandler failed!\n");
                 return 1;
             }
             MOZ_ASSERT(CrashReporter::GetEnabled());
         }
 #endif
--- a/toolkit/components/crashes/moz.build
+++ b/toolkit/components/crashes/moz.build
@@ -1,31 +1,33 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 SPHINX_TREES['crash-manager'] = 'docs'
 
-EXTRA_COMPONENTS += [
-    'CrashService.js',
-    'CrashService.manifest',
-]
-
-EXTRA_JS_MODULES += [
-    'CrashManager.jsm',
-]
-
-TESTING_JS_MODULES += [
-    'CrashManagerTest.jsm',
-]
-
-XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
-
 XPIDL_MODULE = 'toolkit_crashservice'
 
 XPIDL_SOURCES += [
     'nsICrashService.idl',
 ]
 
+if CONFIG['MOZ_CRASHREPORTER']:
+    EXTRA_COMPONENTS += [
+        'CrashService.js',
+        'CrashService.manifest',
+    ]
+
+    EXTRA_JS_MODULES += [
+        'CrashManager.jsm',
+    ]
+
+    TESTING_JS_MODULES += [
+        'CrashManagerTest.jsm',
+    ]
+
+    XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
+
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Crash Reporting')
+
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -21,16 +21,17 @@ DIRS += [
     'apppicker',
     'asyncshutdown',
     'backgroundhangmonitor',
     'browser',
     'cloudstorage',
     'commandlines',
     'contentprefs',
     'contextualidentity',
+    'crashes',
     'crashmonitor',
     'diskspacewatcher',
     'downloads',
     'extensions',
     'filewatcher',
     'finalizationwitness',
     'find',
     'jsdownloads',
@@ -76,19 +77,16 @@ if CONFIG['ENABLE_INTL_API']:
     DIRS += ['mozintl']
 
 if CONFIG['MOZ_BUILD_APP'] != 'mobile/android':
     DIRS += ['narrate', 'viewsource'];
 
     if CONFIG['NS_PRINTING']:
         DIRS += ['printing']
 
-if CONFIG['MOZ_CRASHREPORTER']:
-    DIRS += ['crashes']
-
 if CONFIG['BUILD_CTYPES']:
     DIRS += ['ctypes']
 
 if CONFIG['MOZ_FEEDS']:
     DIRS += ['feeds']
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['autocomplete', 'printingui', 'satchel']
--- a/toolkit/crashreporter/jar.mn
+++ b/toolkit/crashreporter/jar.mn
@@ -1,7 +1,9 @@
 # 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/.
 
 toolkit.jar:
+#ifdef MOZ_CRASHREPORTER
   content/global/crashes.xhtml            (content/crashes.xhtml)
   content/global/crashes.js               (content/crashes.js)
+#endif
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -1,115 +1,125 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 SPHINX_TREES['crashreporter'] = 'docs'
 
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DIRS += [
-        'google-breakpad/src/common',
-        'google-breakpad/src/processor',
-        'breakpad-windows-libxul',
-    ]
-
-    if CONFIG['MSVC_HAS_DIA_SDK']:
-        DIRS += ['google-breakpad/src/tools/windows/dump_syms']
-
-    if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
-        DIRS += ['breakpad-windows-standalone']
-
-elif CONFIG['OS_ARCH'] == 'Darwin':
-    DIRS += [
-        'breakpad-client',
-        'breakpad-client/mac/crash_generation',
-        'breakpad-client/mac/handler',
-        'google-breakpad/src/common',
-        'google-breakpad/src/common/mac',
-        'google-breakpad/src/processor',
-        'google-breakpad/src/tools/mac/dump_syms',
-    ]
-
-elif CONFIG['OS_ARCH'] == 'Linux':
-    DIRS += [
-        'breakpad-client',
-        'breakpad-client/linux/',
-        'google-breakpad/src/common',
-        'google-breakpad/src/common/linux',
-        'google-breakpad/src/processor',
-        'google-breakpad/src/tools/linux/dump_syms',
-    ]
-
-
-DIRS += [
-    'client',
-    'minidump-analyzer',
-]
-
-if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
-    DIRS += ['injector']
-    UNIFIED_SOURCES += [
-        'InjectCrashReporter.cpp',
-        'LoadLibraryRemote.cpp',
-    ]
-
-if CONFIG['ENABLE_TESTS']:
-    DIRS += ['test/gtest']
-
-TEST_DIRS += ['test']
-
 EXPORTS += [
     'nsExceptionHandler.h',
 ]
 
-UNIFIED_SOURCES += [
-    'nsExceptionHandler.cpp',
-    'ThreadAnnotation.cpp',
-]
+JAR_MANIFESTS += ['jar.mn']
 
-if CONFIG['OS_ARCH'] == 'Darwin':
-    UNIFIED_SOURCES += [
-        'mac_utils.mm',
-    ]
-
-EXTRA_JS_MODULES += [
-    'CrashReports.jsm',
-    'CrashSubmit.jsm',
-    'KeyValueParser.jsm',
+UNIFIED_SOURCES = [
+    'nsExceptionHandlerUtils.cpp',
 ]
 
-include('/ipc/chromium/chromium-config.mozbuild')
-
 FINAL_LIBRARY = 'xul'
 
-if CONFIG['OS_TARGET'] == 'Android':
-    DEFINES['ANDROID_NDK_MAJOR_VERSION'] = CONFIG['ANDROID_NDK_MAJOR_VERSION']
-    DEFINES['ANDROID_NDK_MINOR_VERSION'] = CONFIG['ANDROID_NDK_MINOR_VERSION']
-    DEFINES['ANDROID_PACKAGE_NAME'] = '"%s"' % CONFIG['ANDROID_PACKAGE_NAME']
-    # NDK5 workarounds
-    DEFINES['_STLP_CONST_CONSTRUCTOR_BUG'] = True
-    DEFINES['_STLP_NO_MEMBER_TEMPLATES'] = True
-    LOCAL_INCLUDES += [
-        '/toolkit/crashreporter/google-breakpad/src/common/android/include',
+if CONFIG['MOZ_CRASHREPORTER']:
+    if CONFIG['OS_ARCH'] == 'WINNT':
+        DIRS += [
+            'google-breakpad/src/common',
+            'google-breakpad/src/processor',
+            'breakpad-windows-libxul',
+        ]
+
+        if CONFIG['MSVC_HAS_DIA_SDK']:
+            DIRS += ['google-breakpad/src/tools/windows/dump_syms']
+
+        if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
+            DIRS += ['breakpad-windows-standalone']
+
+    elif CONFIG['OS_ARCH'] == 'Darwin':
+        DIRS += [
+            'breakpad-client',
+            'breakpad-client/mac/crash_generation',
+            'breakpad-client/mac/handler',
+            'google-breakpad/src/common',
+            'google-breakpad/src/common/mac',
+            'google-breakpad/src/processor',
+            'google-breakpad/src/tools/mac/dump_syms',
+        ]
+
+    elif CONFIG['OS_ARCH'] == 'Linux':
+        DIRS += [
+            'breakpad-client',
+            'breakpad-client/linux/',
+            'google-breakpad/src/common',
+            'google-breakpad/src/common/linux',
+            'google-breakpad/src/processor',
+            'google-breakpad/src/tools/linux/dump_syms',
+        ]
+
+
+    DIRS += [
+        'client',
+        'minidump-analyzer',
     ]
 
-DEFINES['UNICODE'] = True
-DEFINES['_UNICODE'] = True
+    if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
+        DIRS += ['injector']
+        UNIFIED_SOURCES += [
+            'InjectCrashReporter.cpp',
+            'LoadLibraryRemote.cpp',
+        ]
+
+    if CONFIG['ENABLE_TESTS']:
+        DIRS += ['test/gtest']
+
+    TEST_DIRS += ['test']
 
-JAR_MANIFESTS += ['jar.mn']
+    UNIFIED_SOURCES += [
+        'nsExceptionHandler.cpp',
+        'ThreadAnnotation.cpp',
+    ]
+
+    if CONFIG['OS_ARCH'] == 'Darwin':
+        UNIFIED_SOURCES += [
+            'mac_utils.mm',
+        ]
+
+    EXTRA_JS_MODULES += [
+        'CrashReports.jsm',
+        'CrashSubmit.jsm',
+        'KeyValueParser.jsm',
+    ]
+
+    include('/ipc/chromium/chromium-config.mozbuild')
 
-LOCAL_INCLUDES += [
-    'google-breakpad/src',
-]
+    if CONFIG['OS_TARGET'] == 'Android':
+        DEFINES['ANDROID_NDK_MAJOR_VERSION'] = CONFIG['ANDROID_NDK_MAJOR_VERSION']
+        DEFINES['ANDROID_NDK_MINOR_VERSION'] = CONFIG['ANDROID_NDK_MINOR_VERSION']
+        DEFINES['ANDROID_PACKAGE_NAME'] = '"%s"' % CONFIG['ANDROID_PACKAGE_NAME']
+        # NDK5 workarounds
+        DEFINES['_STLP_CONST_CONSTRUCTOR_BUG'] = True
+        DEFINES['_STLP_NO_MEMBER_TEMPLATES'] = True
+        LOCAL_INCLUDES += [
+            '/toolkit/crashreporter/google-breakpad/src/common/android/include',
+        ]
+
+    DEFINES['UNICODE'] = True
+    DEFINES['_UNICODE'] = True
 
-PYTHON_UNITTEST_MANIFESTS += [
-    'tools/python.ini',
-]
+    LOCAL_INCLUDES += [
+        'google-breakpad/src',
+    ]
+
+    PYTHON_UNITTEST_MANIFESTS += [
+        'tools/python.ini',
+    ]
 
-include('/toolkit/crashreporter/crashreporter.mozbuild')
+    include('/toolkit/crashreporter/crashreporter.mozbuild')
+
+    if CONFIG['GNU_CXX']:
+        CXXFLAGS += ['-Wno-shadow']
+else:
+    UNIFIED_SOURCES += [
+        'nsDummyExceptionHandler.cpp',
+    ]
+
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Crash Reporting')
-
-if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-shadow']
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/nsDummyExceptionHandler.cpp
@@ -0,0 +1,420 @@
+/* -*- 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 "nsExceptionHandlerUtils.h"
+
+namespace CrashReporter {
+
+void
+AnnotateOOMAllocationSize(size_t size)
+{
+}
+
+void
+AnnotateTexturesSize(size_t size)
+{
+}
+
+void
+AnnotatePendingIPC(size_t aNumOfPendingIPC,
+                   uint32_t aTopPendingIPCCount,
+                   const char* aTopPendingIPCName,
+                   uint32_t aTopPendingIPCType)
+{
+}
+
+nsresult
+SetExceptionHandler(nsIFile* aXREDirectory,
+                    bool force/*=false*/)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+bool
+GetEnabled()
+{
+  return false;
+}
+
+bool
+GetMinidumpPath(nsAString& aPath)
+{
+  return false;
+}
+
+nsresult
+SetMinidumpPath(const nsAString& aPath)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+SetupExtraData(nsIFile* aAppDataDirectory,
+               const nsACString& aBuildID)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+UnsetExceptionHandler()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+AnnotateCrashReport(const nsACString& key,
+                    const nsACString& data)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoveCrashReportAnnotation(const nsACString& key)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+SetGarbageCollecting(bool collecting)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+SetEventloopNestingLevel(uint32_t level)
+{
+}
+
+nsresult
+AppendAppNotesToCrashReport(const nsACString& data)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+bool
+GetAnnotation(const nsACString& key, nsACString& data)
+{
+  return false;
+}
+
+nsresult
+RegisterAppMemory(void* ptr, size_t length)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+UnregisterAppMemory(void* ptr)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+SetIncludeContextHeap(bool aValue)
+{
+}
+
+bool
+GetServerURL(nsACString& aServerURL)
+{
+  return false;
+}
+
+nsresult
+SetServerURL(const nsACString& aServerURL)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+SetRestartArgs(int argc, char** argv)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#ifdef XP_WIN32
+nsresult
+WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+#ifdef XP_LINUX
+bool
+WriteMinidumpForSigInfo(int signo,
+                        siginfo_t* info,
+                        void* uc)
+{
+  return false;
+}
+#endif
+
+#ifdef XP_MACOSX
+nsresult
+AppendObjCExceptionInfoToAppNotes(void *inException)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+nsresult
+GetSubmitReports(bool* aSubmitReports)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+SetSubmitReports(bool aSubmitReports)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+SetProfileDirectory(nsIFile* aDir)
+{
+}
+
+void
+SetUserAppDataDirectory(nsIFile* aDir)
+{
+}
+
+void
+UpdateCrashEventsDir()
+{
+}
+
+bool
+GetCrashEventsDir(nsAString& aPath)
+{
+  return false;
+}
+
+void
+SetMemoryReportFile(nsIFile* aFile)
+{
+}
+
+nsresult
+GetDefaultMemoryReportFile(nsIFile** aFile)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+SetTelemetrySessionId(const nsACString& id)
+{
+}
+
+void
+DeleteMinidumpFilesForID(const nsAString& id)
+{
+}
+
+bool
+GetMinidumpForID(const nsAString& id, nsIFile** minidump)
+{
+  return false;
+}
+
+bool
+GetIDFromMinidump(nsIFile* minidump, nsAString& id)
+{
+  return false;
+}
+
+bool
+GetExtraFileForID(const nsAString& id,
+                  nsIFile** extraFile)
+{
+  return false;
+}
+
+bool
+GetExtraFileForMinidump(nsIFile* minidump,
+                        nsIFile** extraFile)
+{
+  return false;
+}
+
+bool
+AppendExtraData(const nsAString& id,
+                const AnnotationTable& data)
+{
+  return false;
+}
+
+bool
+AppendExtraData(nsIFile* extraFile,
+                const AnnotationTable& data)
+{
+  return false;
+}
+
+void
+OOPInit()
+{
+}
+
+void
+GetChildProcessTmpDir(nsIFile** aOutTmpDir)
+{
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+const char*
+GetChildNotificationPipe()
+{
+  return nullptr;
+}
+#endif
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+void
+InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
+{
+}
+
+void
+UnregisterInjectorCallback(DWORD processID)
+{
+}
+
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+bool
+GetLastRunCrashID(nsAString& id)
+{
+  return false;
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+void
+InitChildProcessTmpDir(nsIFile* aDirOverride)
+{
+}
+#endif // defined(XP_WIN) || defined(XP_MACOSX)
+
+#if defined(XP_WIN)
+bool
+SetRemoteExceptionHandler(const nsACString& crashPipe)
+{
+  return false;
+}
+
+#elif defined(XP_LINUX)
+
+bool
+CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
+{
+  return false;
+}
+
+bool
+SetRemoteExceptionHandler()
+{
+  return false;
+}
+
+#elif defined(XP_MACOSX)
+
+bool
+SetRemoteExceptionHandler(const nsACString& crashPipe)
+{
+  return false;
+}
+#endif  // XP_WIN
+
+bool
+TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
+{
+  return false;
+}
+
+void
+RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
+                             const nsACString& name)
+{
+}
+
+ThreadId
+CurrentThreadId()
+{
+  return -1;
+}
+
+bool
+TakeMinidump(nsIFile** aResult, bool aMoveToPending)
+{
+  return false;
+}
+
+void
+CreateMinidumpsAndPair(ProcessHandle aTargetPid,
+                       ThreadId aTargetBlamedThread,
+                       const nsACString& aIncomingPairName,
+                       nsIFile* aIncomingDumpToPair,
+                       nsIFile** aMainDumpOut,
+                       std::function<void(bool)>&& aCallback,
+                       bool aAsync)
+{
+}
+
+bool
+CreateAdditionalChildMinidump(ProcessHandle childPid,
+                              ThreadId childBlamedThread,
+                              nsIFile* parentMinidump,
+                              const nsACString& name)
+{
+  return false;
+}
+
+bool
+UnsetRemoteExceptionHandler()
+{
+  return false;
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void
+SetNotificationPipeForChild(int childCrashFd)
+{
+}
+
+void
+AddLibraryMapping(const char* library_name,
+                  uintptr_t   start_address,
+                  size_t      mapping_length,
+                  size_t      file_offset)
+{
+}
+#endif
+
+// From ThreadAnnotation.cpp
+
+void
+InitThreadAnnotation()
+{
+}
+
+void
+SetCurrentThreadName(const char* aName)
+{
+}
+
+void
+GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback)
+{
+}
+
+void
+ShutdownThreadAnnotation()
+{
+}
+
+} // namespace CrashReporter
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "nsExceptionHandlerUtils.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryService.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
@@ -82,17 +83,16 @@ using mozilla::InjectCrashRunnable;
 #include <prio.h>
 #include "mozilla/Mutex.h"
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include <map>
 #include <vector>
 
-#include "mozilla/double-conversion.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/mozalloc_oom.h"
 #include "mozilla/WindowsDllBlocklist.h"
 
 #if defined(XP_MACOSX)
 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
@@ -419,64 +419,16 @@ typedef struct {
   size_t      length;
   size_t      file_offset;
 } mapping_info;
 static std::vector<mapping_info> library_mappings;
 typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
 #endif
 }
 
-// Format a non-negative double to a string, without using C-library functions,
-// which need to be avoided (.e.g. bug 1240160, comment 10).  Leave the utility
-// non-file static so that we can gtest it.  Return false if we failed to
-// get the formatting done correctly.
-bool SimpleNoCLibDtoA(double aValue, char* aBuffer, int aBufferLength)
-{
-  // aBufferLength is the size of the buffer.  Be paranoid.
-  aBuffer[aBufferLength-1] = '\0';
-
-  if (aValue < 0) {
-    return false;
-  }
-
-  int length, point, i;
-  bool sign;
-  bool ok = true;
-  double_conversion::DoubleToStringConverter::DoubleToAscii(
-                                     aValue,
-                                     double_conversion::DoubleToStringConverter::SHORTEST,
-                                     8,
-                                     aBuffer,
-                                     aBufferLength,
-                                     &sign,
-                                     &length,
-                                     &point);
-
-  // length does not account for the 0 terminator.
-  if (length > point && (length+1) < (aBufferLength-1)) {
-    // We have to insert a decimal point.  Not worried about adding a leading zero
-    // in the < 1 (point == 0) case.
-    aBuffer[length+1] = '\0';
-    for (i=length; i>point; i-=1) {
-      aBuffer[i] = aBuffer[i-1];
-    }
-    aBuffer[i] = '.'; // Not worried about locales
-  } else if (length < point) {
-    // Trailing zeros scenario
-    for (i=length; i<point; i+=1) {
-      if (i >= aBufferLength-2) {
-        ok = false;
-      }
-      aBuffer[i] = '0';
-    }
-    aBuffer[i] = '\0';
-  }
-  return ok;
-}
-
 namespace CrashReporter {
 
 #ifdef XP_LINUX
 static inline void
 my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
 {
   my_memset(buffer, 0, buffer_length);
   my_uitos(buffer, t, my_uint_len(t));
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -1,13 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
+// This header has two implementations, the real one in nsExceptionHandler.cpp
+// and a dummy in nsDummyExceptionHandler.cpp. The latter is used in builds
+// configured with --disable-crashreporter. If you add or remove a function
+// from this header you must update both implementations otherwise you'll break
+// builds that disable the crash reporter.
+
 #ifndef nsExceptionHandler_h__
 #define nsExceptionHandler_h__
 
 #include "mozilla/Assertions.h"
 
 #include <functional>
 #include <stddef.h>
 #include <stdint.h>
@@ -29,16 +35,29 @@
 #include <signal.h>
 #endif
 
 class nsIFile;
 template<class KeyClass, class DataType> class nsDataHashtable;
 class nsCStringHashKey;
 
 namespace CrashReporter {
+
+/**
+ * Returns true if the crash reporter is using the dummy implementation.
+ */
+static inline bool
+IsDummy() {
+#ifdef MOZ_CRASHREPORTER
+  return false;
+#else
+  return true;
+#endif
+}
+
 nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force=false);
 nsresult UnsetExceptionHandler();
 
 /**
  * Tell the crash reporter to recalculate where crash events files should go.
  * SetCrashEventsDir is used before XPCOM is initialized from the startup
  * code.
  *
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/nsExceptionHandlerUtils.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 "nsExceptionHandlerUtils.h"
+
+#include "mozilla/double-conversion.h"
+
+// Format a non-negative double to a string, without using C-library functions,
+// which need to be avoided (.e.g. bug 1240160, comment 10).  Return false if
+// we failed to get the formatting done correctly.
+bool SimpleNoCLibDtoA(double aValue, char* aBuffer, int aBufferLength)
+{
+  // aBufferLength is the size of the buffer.  Be paranoid.
+  aBuffer[aBufferLength-1] = '\0';
+
+  if (aValue < 0) {
+    return false;
+  }
+
+  int length, point, i;
+  bool sign;
+  bool ok = true;
+  double_conversion::DoubleToStringConverter::DoubleToAscii(
+                                     aValue,
+                                     double_conversion::DoubleToStringConverter::SHORTEST,
+                                     8,
+                                     aBuffer,
+                                     aBufferLength,
+                                     &sign,
+                                     &length,
+                                     &point);
+
+  // length does not account for the 0 terminator.
+  if (length > point && (length+1) < (aBufferLength-1)) {
+    // We have to insert a decimal point.  Not worried about adding a leading zero
+    // in the < 1 (point == 0) case.
+    aBuffer[length+1] = '\0';
+    for (i=length; i>point; i-=1) {
+      aBuffer[i] = aBuffer[i-1];
+    }
+    aBuffer[i] = '.'; // Not worried about locales
+  } else if (length < point) {
+    // Trailing zeros scenario
+    for (i=length; i<point; i+=1) {
+      if (i >= aBufferLength-2) {
+        ok = false;
+      }
+      aBuffer[i] = '0';
+    }
+    aBuffer[i] = '\0';
+  }
+  return ok;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/nsExceptionHandlerUtils.h
@@ -0,0 +1,12 @@
+/* -*- 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/. */
+
+#ifndef nsExceptionHandlerUtils_h__
+#define nsExceptionHandlerUtils_h__
+
+bool SimpleNoCLibDtoA(double aValue, char* aBuffer, int aBufferLength);
+
+#endif // nsExceptionHandlerUtils_h__
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += [
     'components',
     'content',
+    'crashreporter',
     'forgetaboutsite',
     'locales',
     'modules',
     'mozapps/downloads',
     'mozapps/extensions',
     'mozapps/handling',
     'mozapps/preferences',
     'pluginproblem',
@@ -41,19 +42,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk
     DIRS += ['system/unixproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     DIRS += ['system/osxproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DIRS += ['system/windowsproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     DIRS += ['system/androidproxy']
 
-if CONFIG['MOZ_CRASHREPORTER']:
-    DIRS += ['crashreporter']
-
 TEST_HARNESS_FILES.testing.mochitest.browser.toolkit.crashreporter.test.browser += [
     'crashreporter/test/browser/crashreport.sjs',
 ]
 
 with Files('moz.*'):
     BUG_COMPONENT = ('Core', 'Build Config')
 
 with Files('toolkit.mozbuild'):