Bug 800486 - Part 2: Move signal-watching and memory-report-dumping code out of nsMemoryReporterManager and into a new file, MemoryInfoDumper. r=njn
authorJustin Lebar <justin.lebar@gmail.com>
Mon, 15 Oct 2012 22:12:14 -0400
changeset 110497 295110f8c8922817f9e0e001a6b47d7cc9beb9a3
parent 110496 e676a99a7a8dddc52583c391e0ac0a9d92320d50
child 110498 bdb3e55bdddae77e5c982070b0973c1101a61792
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersnjn
bugs800486
milestone19.0a1
Bug 800486 - Part 2: Move signal-watching and memory-report-dumping code out of nsMemoryReporterManager and into a new file, MemoryInfoDumper. r=njn
dom/ipc/ContentChild.cpp
dom/ipc/PContent.ipdl
xpcom/base/Makefile.in
xpcom/base/MemoryInfoDumper.cpp
xpcom/base/MemoryInfoDumper.h
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -15,16 +15,17 @@
 #include "ContentChild.h"
 #include "CrashReporterChild.h"
 #include "TabChild.h"
 #if defined(MOZ_SYDNEYAUDIO)
 #include "AudioChild.h"
 #endif
 
 #include "mozilla/Attributes.h"
+#include "mozilla/MemoryInfoDumper.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/StorageChild.h"
 #include "mozilla/Hal.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/jsipc/PContextWrapperChild.h"
@@ -442,33 +443,27 @@ ContentChild::DeallocPMemoryReportReques
     return true;
 }
 
 bool
 ContentChild::RecvDumpMemoryReportsToFile(const nsString& aIdentifier,
                                           const bool& aMinimizeMemoryUsage,
                                           const bool& aDumpChildProcesses)
 {
-    nsCOMPtr<nsIMemoryReporterManager> mgr =
-        do_GetService("@mozilla.org/memory-reporter-manager;1");
-    NS_ENSURE_TRUE(mgr, true);
-    mgr->DumpMemoryReportsToFile(aIdentifier,
-                                 aMinimizeMemoryUsage,
-                                 aDumpChildProcesses);
+    MemoryInfoDumper::DumpMemoryReportsToFile(
+        aIdentifier, aMinimizeMemoryUsage, aDumpChildProcesses);
     return true;
 }
 
 bool
 ContentChild::RecvDumpGCAndCCLogsToFile(const nsString& aIdentifier,
                                         const bool& aDumpChildProcesses)
 {
-    nsCOMPtr<nsIMemoryReporterManager> mgr =
-        do_GetService("@mozilla.org/memory-reporter-manager;1");
-    NS_ENSURE_TRUE(mgr, true);
-    mgr->DumpGCAndCCLogsToFile(aIdentifier, aDumpChildProcesses);
+    MemoryInfoDumper::DumpGCAndCCLogsToFile(
+        aIdentifier, aDumpChildProcesses);
     return true;
 }
 
 PCompositorChild*
 ContentChild::AllocPCompositor(mozilla::ipc::Transport* aTransport,
                                base::ProcessId aOtherProcess)
 {
     return CompositorChild::Create(aTransport, aOtherProcess);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -197,27 +197,27 @@ both:
 
 child:
     PMemoryReportRequest();
 
     /**
      * Dump the contents of about:memory to a file in our temp directory.
      *
      * For documentation on the args, see
-     * nsIMemoryReporterManager::dumpMemoryReportsToFile.
+     * MemoryInfoDumper::dumpMemoryReportsToFile.
      */
     async DumpMemoryReportsToFile(nsString identifier,
                                   bool minimizeMemoryUsage,
                                   bool dumpChildProcesses);
 
     /**
      * Dump this process's GC and CC logs.
      *
      * For documentation on the args, see
-     * nsIMemoryReporterManager::dumpGCAndCCLogsToFile.
+     * MemoryInfoDumper::dumpGCAndCCLogsToFile.
      */
     async DumpGCAndCCLogsToFile(nsString identifier,
                                 bool dumpChildProcesses);
 
     PTestShell();
 
     RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
                    OverrideMapping[] overrides, nsCString locale);
--- a/xpcom/base/Makefile.in
+++ b/xpcom/base/Makefile.in
@@ -36,16 +36,17 @@ CPPSRCS		= \
 		nsSystemInfo.cpp \
 		nsCycleCollector.cpp \
 		nsStackWalk.cpp \
 		nsMemoryReporterManager.cpp \
 		ClearOnShutdown.cpp \
 		VisualEventTracer.cpp \
 		nsErrorAsserts.cpp \
 		nsGZFileWriter.cpp \
+		MemoryInfoDumper.cpp \
 		$(NULL)
 
 ifeq ($(OS_ARCH),Linux)
 CPPSRCS += MapsMemoryReporter.cpp
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS		+= nsMacUtilsImpl.cpp
@@ -66,16 +67,17 @@ EXPORTS		= \
 		nsInterfaceRequestorAgg.h \
 		nsGZFileWriter.h \
 		$(NULL)
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
 	MapsMemoryReporter.h \
+	MemoryInfoDumper.h \
 	ClearOnShutdown.h \
 	StaticPtr.h \
 	AvailableMemoryTracker.h \
 	StackWalk.h \
 	VisualEventTracer.h \
 	$(NULL)
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
new file mode 100644
--- /dev/null
+++ b/xpcom/base/MemoryInfoDumper.cpp
@@ -0,0 +1,599 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=50 et cin 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 "mozilla/MemoryInfoDumper.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsIConsoleService.h"
+#include "nsICycleCollectorListener.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsGZFileWriter.h"
+#include "nsJSEnvironment.h"
+#include "nsPrintfCString.h"
+
+#ifdef XP_LINUX
+#include <fcntl.h>
+#endif
+
+#ifdef ANDROID
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace {
+
+class DumpMemoryReportsRunnable : public nsRunnable
+{
+public:
+  DumpMemoryReportsRunnable(const nsAString& aIdentifier,
+                            bool aMinimizeMemoryUsage,
+                            bool aDumpChildProcesses)
+
+      : mIdentifier(aIdentifier)
+      , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
+      , mDumpChildProcesses(aDumpChildProcesses)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    MemoryInfoDumper::DumpMemoryReportsToFile(
+      mIdentifier, mMinimizeMemoryUsage, mDumpChildProcesses);
+    return NS_OK;
+  }
+
+private:
+  const nsString mIdentifier;
+  const bool mMinimizeMemoryUsage;
+  const bool mDumpChildProcesses;
+};
+
+class GCAndCCLogDumpRunnable : public nsRunnable
+{
+public:
+  GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
+                         bool aDumpChildProcesses)
+    : mIdentifier(aIdentifier)
+    , mDumpChildProcesses(aDumpChildProcesses)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    MemoryInfoDumper::DumpGCAndCCLogsToFile(
+      mIdentifier, mDumpChildProcesses);
+    return NS_OK;
+  }
+
+private:
+  const nsString mIdentifier;
+  const bool mDumpChildProcesses;
+};
+
+} // anonymous namespace
+
+#ifdef XP_LINUX // {
+namespace {
+
+/*
+ * The following code supports dumping about:memory upon receiving a signal.
+ *
+ * We listen for the following signals:
+ *
+ *  - SIGRTMIN:     Dump our memory reporters (and those of our child
+ *                  processes),
+ *  - SIGRTMIN + 1: Dump our memory reporters (and those of our child
+ *                  processes) after minimizing memory usage, and
+ *  - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
+ *
+ * When we receive one of these signals, we write the signal number to a pipe.
+ * The IO thread then notices that the pipe has been written to, and kicks off
+ * the appropriate task on the main thread.
+ *
+ * This scheme is similar to using signalfd(), except it's portable and it
+ * doesn't require the use of sigprocmask, which is problematic because it
+ * masks signals received by child processes.
+ *
+ * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
+ * But that uses libevent, which does not handle the realtime signals (bug
+ * 794074).
+ */
+
+// It turns out that at least on some systems, SIGRTMIN is not a compile-time
+// constant, so these have to be set at runtime.
+static int sDumpAboutMemorySignum;         // SIGRTMIN
+static int sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
+static int sGCAndCCDumpSignum;             // SIGRTMIN + 2
+
+// This is the write-end of a pipe that we use to notice when a
+// dump-about-memory signal occurs.
+static int sDumpAboutMemoryPipeWriteFd;
+
+void
+DumpAboutMemorySignalHandler(int aSignum)
+{
+  // This is a signal handler, so everything in here needs to be
+  // async-signal-safe.  Be careful!
+
+  if (sDumpAboutMemoryPipeWriteFd != 0) {
+    uint8_t signum = static_cast<int>(aSignum);
+    write(sDumpAboutMemoryPipeWriteFd, &signum, sizeof(signum));
+  }
+}
+
+class SignalPipeWatcher : public MessageLoopForIO::Watcher
+{
+public:
+  SignalPipeWatcher()
+  {}
+
+  ~SignalPipeWatcher()
+  {
+    // This is somewhat paranoid, but we want to avoid the race condition where
+    // we close sDumpAboutMemoryPipeWriteFd before setting it to 0, then we
+    // reuse that fd for some other file, and then the signal handler runs.
+    int pipeWriteFd = sDumpAboutMemoryPipeWriteFd;
+    PR_ATOMIC_SET(&sDumpAboutMemoryPipeWriteFd, 0);
+
+    // Stop watching the pipe's file descriptor /before/ we close it!
+    mReadWatcher.StopWatchingFileDescriptor();
+
+    close(pipeWriteFd);
+    close(mPipeReadFd);
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SignalPipeWatcher)
+
+  bool Start()
+  {
+    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+    sDumpAboutMemorySignum = SIGRTMIN;
+    sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
+    sGCAndCCDumpSignum = SIGRTMIN + 2;
+
+    // Create a pipe.  When we receive a signal in our signal handler, we'll
+    // write the signum to the write-end of this pipe.
+    int pipeFds[2];
+    if (pipe(pipeFds)) {
+      NS_WARNING("Failed to create pipe.");
+      return false;
+    }
+
+    // Close this pipe on calls to exec().
+    fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
+    fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
+
+    mPipeReadFd = pipeFds[0];
+    sDumpAboutMemoryPipeWriteFd = pipeFds[1];
+
+    struct sigaction action;
+    memset(&action, 0, sizeof(action));
+    sigemptyset(&action.sa_mask);
+    action.sa_handler = DumpAboutMemorySignalHandler;
+
+    if (sigaction(sDumpAboutMemorySignum, &action, nullptr)) {
+      NS_WARNING("Failed to register about:memory dump signal handler.");
+    }
+    if (sigaction(sDumpAboutMemoryAfterMMUSignum, &action, nullptr)) {
+      NS_WARNING("Failed to register about:memory dump after MMU signal handler.");
+    }
+    if (sigaction(sGCAndCCDumpSignum, &action, nullptr)) {
+      NS_WARNING("Failed to register GC+CC dump signal handler.");
+    }
+
+    // Start watching the read end of the pipe on the IO thread.
+    return MessageLoopForIO::current()->WatchFileDescriptor(
+      mPipeReadFd, /* persistent = */ true,
+      MessageLoopForIO::WATCH_READ,
+      &mReadWatcher, this);
+  }
+
+  virtual void OnFileCanReadWithoutBlocking(int aFd)
+  {
+    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+    uint8_t signum;
+    ssize_t numReceived = read(aFd, &signum, sizeof(signum));
+    if (numReceived != sizeof(signum)) {
+      NS_WARNING("Error reading from buffer in "
+                 "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
+      return;
+    }
+
+    if (signum == sDumpAboutMemorySignum ||
+        signum == sDumpAboutMemoryAfterMMUSignum) {
+      // Dump our memory reports (but run this on the main thread!).
+      nsRefPtr<DumpMemoryReportsRunnable> runnable =
+        new DumpMemoryReportsRunnable(
+            /* identifier = */ EmptyString(),
+            signum == sDumpAboutMemoryAfterMMUSignum,
+            /* dumpChildProcesses = */ true);
+      NS_DispatchToMainThread(runnable);
+    }
+    else if (signum == sGCAndCCDumpSignum) {
+      // Dump GC and CC logs (from the main thread).
+      nsRefPtr<GCAndCCLogDumpRunnable> runnable =
+        new GCAndCCLogDumpRunnable(
+            /* identifier = */ EmptyString(),
+            /* dumpChildProcesses = */ true);
+      NS_DispatchToMainThread(runnable);
+    }
+    else {
+      NS_WARNING("Got unexpected signum.");
+    }
+  }
+
+  virtual void OnFileCanWriteWithoutBlocking(int aFd)
+  {}
+
+private:
+  int mPipeReadFd;
+  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+};
+
+StaticRefPtr<SignalPipeWatcher> sSignalPipeWatcher;
+
+void
+InitializeSignalWatcher()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sSignalPipeWatcher);
+
+  sSignalPipeWatcher = new SignalPipeWatcher();
+  ClearOnShutdown(&sSignalPipeWatcher);
+
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(sSignalPipeWatcher.get(),
+                        &SignalPipeWatcher::Start));
+}
+
+} // anonymous namespace
+#endif // } XP_LINUX
+
+/* static */ void
+MemoryInfoDumper::Initialize()
+{
+#ifdef XP_LINUX
+  InitializeSignalWatcher();
+#endif
+}
+
+  /* static */ void
+MemoryInfoDumper::DumpMemoryReportsToFile(
+    const nsAString& aIdentifier,
+    bool aMinimizeMemoryUsage,
+    bool aDumpChildProcesses)
+{
+  // If the identifier is empty, set it to the number of whole seconds since
+  // the epoch.  This identifier will appear in our memory report as well as
+  // our children's, allowing us to identify which files are from the same
+  // memory report request.
+  nsString identifier(aIdentifier);
+  if (identifier.IsEmpty()) {
+    identifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
+  }
+
+  // Kick off memory report dumps in our child processes, if applicable.  We
+  // do this before doing our own report because writing a report may be I/O
+  // bound, in which case we want to busy the CPU with other reports while we
+  // work on our own.
+  if (aDumpChildProcesses) {
+    nsTArray<ContentParent*> children;
+    ContentParent::GetAll(children);
+    for (uint32_t i = 0; i < children.Length(); i++) {
+      unused << children[i]->SendDumpMemoryReportsToFile(
+          identifier, aMinimizeMemoryUsage, aDumpChildProcesses);
+    }
+  }
+
+  if (aMinimizeMemoryUsage) {
+    // Minimize memory usage, then run DumpMemoryReportsToFile again.
+    nsRefPtr<DumpMemoryReportsRunnable> callback =
+      new DumpMemoryReportsRunnable(identifier,
+          /* minimizeMemoryUsage = */ false,
+          /* dumpChildProcesses = */ false);
+    nsCOMPtr<nsIMemoryReporterManager> mgr =
+      do_GetService("@mozilla.org/memory-reporter-manager;1");
+    NS_ENSURE_TRUE(mgr,);
+    mgr->MinimizeMemoryUsage(callback);
+  }
+
+  DumpMemoryReportsToFileImpl(identifier);
+}
+
+/* static */ void
+MemoryInfoDumper::DumpGCAndCCLogsToFile(
+  const nsAString& aIdentifier,
+  bool aDumpChildProcesses)
+{
+  // If the identifier is empty, set it to the number of whole seconds since
+  // the epoch.  This identifier will appear in our logs as well as our
+  // children's, allowing us to identify which files are from the same
+  // request.
+  nsString identifier(aIdentifier);
+  if (identifier.IsEmpty()) {
+    identifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
+  }
+
+  if (aDumpChildProcesses) {
+    nsTArray<ContentParent*> children;
+    ContentParent::GetAll(children);
+    for (uint32_t i = 0; i < children.Length(); i++) {
+      unused << children[i]->SendDumpGCAndCCLogsToFile(
+          identifier, aDumpChildProcesses);
+    }
+  }
+
+  nsCOMPtr<nsICycleCollectorListener> logger =
+    do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+  logger->SetFilenameIdentifier(identifier);
+
+  nsJSContext::CycleCollectNow(logger);
+}
+
+#define DUMP(o, s) \
+  do { \
+    nsresult rv = (o)->Write(s); \
+    NS_ENSURE_SUCCESS(rv, rv); \
+  } while (0)
+
+static nsresult
+DumpReport(nsIGZFileWriter *aWriter, bool aIsFirst,
+  const nsACString &aProcess, const nsACString &aPath, int32_t aKind,
+  int32_t aUnits, int64_t aAmount, const nsACString &aDescription)
+{
+  DUMP(aWriter, aIsFirst ? "[" : ",");
+
+  // We only want to dump reports for this process.  If |aProcess| is
+  // non-NULL that means we've received it from another process in response
+  // to a "child-memory-reporter-request" event;  ignore such reports.
+  if (!aProcess.IsEmpty()) {
+    return NS_OK;
+  }
+
+  // Generate the process identifier, which is of the form "$PROCESS_NAME
+  // (pid $PID)", or just "(pid $PID)" if we don't have a process name.  If
+  // we're the main process, we let $PROCESS_NAME be "Main Process".
+  nsAutoCString processId;
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    // We're the main process.
+    processId.AssignLiteral("Main Process ");
+  } else if (ContentChild *cc = ContentChild::GetSingleton()) {
+    // Try to get the process name from ContentChild.
+    nsAutoString processName;
+    cc->GetProcessName(processName);
+    processId.Assign(NS_ConvertUTF16toUTF8(processName));
+    if (!processId.IsEmpty()) {
+      processId.AppendLiteral(" ");
+    }
+  }
+
+  // Add the PID to the identifier.
+  unsigned pid = getpid();
+  processId.Append(nsPrintfCString("(pid %u)", pid));
+
+  DUMP(aWriter, "\n    {\"process\": \"");
+  DUMP(aWriter, processId);
+
+  DUMP(aWriter, "\", \"path\": \"");
+  nsCString path(aPath);
+  path.ReplaceSubstring("\\", "\\\\");    // escape backslashes for JSON
+  DUMP(aWriter, path);
+
+  DUMP(aWriter, "\", \"kind\": ");
+  DUMP(aWriter, nsPrintfCString("%d", aKind));
+
+  DUMP(aWriter, ", \"units\": ");
+  DUMP(aWriter, nsPrintfCString("%d", aUnits));
+
+  DUMP(aWriter, ", \"amount\": ");
+  DUMP(aWriter, nsPrintfCString("%lld", aAmount));
+
+  nsCString description(aDescription);
+  description.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
+  description.ReplaceSubstring("\"", "\\\"");    // " --> \"
+  description.ReplaceSubstring("\n", "\\n");     // <newline> --> \n
+  DUMP(aWriter, ", \"description\": \"");
+  DUMP(aWriter, description);
+  DUMP(aWriter, "\"}");
+
+  return NS_OK;
+}
+
+class DumpMultiReporterCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
+{
+  public:
+    NS_DECL_ISUPPORTS
+
+      NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
+          int32_t aKind, int32_t aUnits, int64_t aAmount,
+          const nsACString &aDescription,
+          nsISupports *aData)
+      {
+        nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
+        NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
+
+        // The |isFirst = false| assumes that at least one single reporter is
+        // present and so will have been processed in
+        // DumpMemoryReportsToFileImpl() below.
+        return DumpReport(writer, /* isFirst = */ false, aProcess, aPath,
+            aKind, aUnits, aAmount, aDescription);
+        return NS_OK;
+      }
+};
+
+NS_IMPL_ISUPPORTS1(
+    DumpMultiReporterCallback
+    , nsIMemoryMultiReporterCallback
+    )
+
+/* static */ nsresult
+MemoryInfoDumper::DumpMemoryReportsToFileImpl(
+  const nsAString& aIdentifier)
+{
+  // Open a new file named something like
+  //
+  //   incomplete-memory-report-<-identifier>-<pid>-42.json.gz
+  //
+  // in NS_OS_TEMP_DIR for writing.  When we're finished writing the report,
+  // we'll rename this file and get rid of the "incomplete-" prefix.
+  //
+  // We do this because we don't want scripts which poll the filesystem
+  // looking for memory report dumps to grab a file before we're finished
+  // writing to it.
+
+  nsCOMPtr<nsIFile> tmpFile;
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+                                       getter_AddRefs(tmpFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Note that |filename| is missing the "incomplete-" prefix; we'll tack
+  // that on in a moment.
+  nsAutoCString filename;
+  filename.AppendLiteral("memory-report");
+  if (!aIdentifier.IsEmpty()) {
+    filename.AppendLiteral("-");
+    filename.Append(NS_ConvertUTF16toUTF8(aIdentifier));
+  }
+  filename.AppendLiteral("-");
+  filename.AppendInt(getpid());
+  filename.AppendLiteral(".json.gz");
+
+  rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("incomplete-") + filename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // XXX This ifdef does not belong here; it should be pushed down into the
+  // CreateUnique implementation.  Please don't copy/paste this elsewhere.
+#ifdef ANDROID
+    // Set umask to 0 while we create the file because on Android the default
+    // umask is 0077.
+    mode_t mask = umask(0);
+#endif
+    rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
+#ifdef ANDROID
+    umask(mask);
+#endif
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsGZFileWriter> writer = new nsGZFileWriter();
+  rv = writer->Init(tmpFile);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Dump the memory reports to the file.
+
+  // Increment this number if the format changes.
+  DUMP(writer, "{\n  \"version\": 1,\n");
+
+  DUMP(writer, "  \"hasMozMallocUsableSize\": ");
+
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+  NS_ENSURE_STATE(mgr);
+
+  DUMP(writer, mgr->GetHasMozMallocUsableSize() ? "true" : "false");
+  DUMP(writer, ",\n");
+  DUMP(writer, "  \"reports\": ");
+
+  // Process single reporters.
+  bool isFirst = true;
+  bool more;
+  nsCOMPtr<nsISimpleEnumerator> e;
+  mgr->EnumerateReporters(getter_AddRefs(e));
+  while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+    nsCOMPtr<nsIMemoryReporter> r;
+    e->GetNext(getter_AddRefs(r));
+
+    nsCString process;
+    rv = r->GetProcess(process);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCString path;
+    rv = r->GetPath(path);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int32_t kind;
+    rv = r->GetKind(&kind);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int32_t units;
+    rv = r->GetUnits(&units);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int64_t amount;
+    rv = r->GetAmount(&amount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCString description;
+    rv = r->GetDescription(description);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = DumpReport(writer, isFirst, process, path, kind, units, amount,
+                    description);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    isFirst = false;
+  }
+
+  // Process multi-reporters.
+  nsCOMPtr<nsISimpleEnumerator> e2;
+  mgr->EnumerateMultiReporters(getter_AddRefs(e2));
+  nsRefPtr<DumpMultiReporterCallback> cb = new DumpMultiReporterCallback();
+  while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
+    nsCOMPtr<nsIMemoryMultiReporter> r;
+    e2->GetNext(getter_AddRefs(r));
+    r->CollectReports(cb, writer);
+  }
+
+  DUMP(writer, "\n  ]\n}");
+
+  rv = writer->Finish();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Rename the file, now that we're done dumping the report.  The file's
+  // ultimate destination is "memory-report<-identifier>-<pid>.json.gz".
+
+  nsCOMPtr<nsIFile> dstFile;
+  rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dstFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dstFile->AppendNative(filename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dstFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString dstFileName;
+  rv = dstFile->GetLeafName(dstFileName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = tmpFile->MoveTo(/* directory */ nullptr, dstFileName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIConsoleService> cs =
+      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString path;
+  tmpFile->GetPath(path);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString msg = NS_LITERAL_STRING("nsIMemoryReporterManager::dumpReports() "
+                                   "dumped reports to ");
+  msg.Append(path);
+  return cs->LogStringMessage(msg.get());
+}
+
+#undef DUMP
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/xpcom/base/MemoryInfoDumper.h
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=50 et cin 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 mozilla_MemoryInfoDumper_h
+#define mozilla_MemoryInfoDumper_h
+
+#include "nsString.h"
+#include "mozilla/StandardInteger.h"
+class nsIGZFileWriter;
+
+namespace mozilla {
+
+/**
+ * This class facilitates dumping information about our memory usage to disk.
+ *
+ * Its cpp file also has Linux-only code which watches various OS signals and
+ * dumps memory info upon receiving a signal.  You can activate these listeners
+ * by calling Initialize().
+ */
+class MemoryInfoDumper
+{
+public:
+  static void Initialize();
+
+  /**
+   * DumpMemoryReportsToFile dumps the memory reports for this process and
+   * possibly our child processes (and their children, recursively) to a file in
+   * the tmp directory called memory-reports-<identifier>-<pid>.json.gz (or
+   * something similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no
+   * existing file will be overwritten).
+   *
+   * @param identifier this identifier will appear in the filename of our
+   *   about:memory dump and those of our children (if dumpChildProcesses is
+   *   true).
+   *
+   *   If the identifier is empty, the dumpMemoryReportsToFile implementation
+   *   may set it arbitrarily and use that new value for its own dump and the
+   *   dumps of its child processes.  For example, the dumpMemoryReportsToFile
+   *   implementation may set |identifier| to the number of seconds since the
+   *   epoch.
+   *
+   * @param minimizeMemoryUsage indicates whether we should run a series of
+   *   gc/cc's in an attempt to reduce our memory usage before collecting our
+   *   memory report.
+   *
+   * @param dumpChildProcesses indicates whether we should call
+   *   dumpMemoryReportsToFile in our child processes.  If so, the child
+   *   processes will also dump their children, and so on.
+   *
+   *
+   * Sample output:
+   *
+   * {
+   *   "hasMozMallocUsableSize":true,
+   *   "reports": [
+   *     {"process":"", "path":"explicit/foo/bar", "kind":1, "units":0,
+   *      "amount":2000000, "description":"Foo bar."},
+   *     {"process":"", "path":"heap-allocated", "kind":1, "units":0,
+   *      "amount":3000000, "description":"Heap allocated."},
+   *     {"process":"", "path":"vsize", "kind":1, "units":0,
+   *      "amount":10000000, "description":"Vsize."}
+   *   ]
+   * }
+   *
+   * JSON schema for the output.
+   *
+   * {
+   *   "properties": {
+   *     "hasMozMallocUsableSize": {
+   *       "type": "boolean",
+   *       "description": "nsIMemoryReporterManager::hasMozMallocUsableSize",
+   *       "required": true
+   *     },
+   *     "reports": {
+   *       "type": "array",
+   *       "description": "The memory reports.",
+   *       "required": true
+   *       "minItems": 1,
+   *       "items": {
+   *         "type": "object",
+   *         "properties": {
+   *           "process": {
+   *             "type": "string",
+   *             "description": "nsIMemoryReporter::process",
+   *             "required": true
+   *           },
+   *           "path": {
+   *             "type": "string",
+   *             "description": "nsIMemoryReporter::path",
+   *             "required": true,
+   *             "minLength": 1
+   *           },
+   *           "kind": {
+   *             "type": "integer",
+   *             "description": "nsIMemoryReporter::kind",
+   *             "required": true
+   *           },
+   *           "units": {
+   *             "type": "integer",
+   *             "description": "nsIMemoryReporter::units",
+   *             "required": true
+   *           },
+   *           "amount": {
+   *             "type": "integer",
+   *             "description": "nsIMemoryReporter::amount",
+   *             "required": true
+   *           },
+   *           "description": {
+   *             "type": "string",
+   *             "description": "nsIMemoryReporter::description",
+   *             "required": true
+   *           }
+   *         }
+   *       }
+   *     }
+   *   }
+   * }
+   */
+  static void
+  DumpMemoryReportsToFile(const nsAString& aIdentifier,
+                          bool aMinimizeMemoryUsage,
+                          bool aDumpChildProcesses);
+
+  /**
+   * Dump GC and CC logs to files in the OS's temp directory (or in
+   * $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
+   *
+   * @param aIdentifier If aIdentifier is non-empty, this string will appear in
+   *   the filenames of the logs we create (both for this process and, if
+   *   aDumpChildProcesses is true, for our child processes).
+   *
+   *   If aIdentifier is empty, the implementation may set it to an
+   *   arbitrary value; for example, it may set aIdentifier to the number
+   *   of seconds since the epoch.
+   *
+   * @param aDumpChildProcesses indicates whether we should call
+   *   DumpGCAndCCLogsToFile in our child processes.  If so, the child processes
+   *   will dump their children, and so on.
+   */
+  static void
+  DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
+                        bool aDumpChildProcesses);
+
+private:
+  static nsresult
+  DumpMemoryReportsToFileImpl(const nsAString& aIdentifier);
+};
+
+} // namespace mozilla
+#endif
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -221,17 +221,17 @@ interface nsIMemoryMultiReporter : nsISu
    * nsIMemoryReporterManager::explicit efficiently, which is important --
    * multi-reporters can special-case this operation so it's much faster
    * than getting all the reports, filtering out the unneeded ones, and
    * summing the remainder.
    */
   readonly attribute int64_t explicitNonHeap;
 };
 
-[scriptable, uuid(7aa2fcfc-eb8f-4bc8-95cc-2bc5e3374df7)]
+[scriptable, builtinclass, uuid(8b670411-ea2a-44c2-a36b-529db0670821)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
   nsISimpleEnumerator enumerateReporters ();
 
   /*
@@ -286,128 +286,23 @@ interface nsIMemoryReporterManager : nsI
    * is special-cased because it's interesting, and is moderately difficult
    * to compute in JS.  Accesses can fail.
    */
   readonly attribute int64_t explicit;
 
   /*
    * This attribute indicates if moz_malloc_usable_size() works.
    */
-  readonly attribute boolean hasMozMallocUsableSize;
+  [infallible] readonly attribute boolean hasMozMallocUsableSize;
 
   /*
    * Run a series of GC/CC's in an attempt to minimize the application's memory
    * usage.  When we're finished, we invoke the given runnable.
    */
   void minimizeMemoryUsage(in nsIRunnable callback);
-
-  /*
-   * This dumps the memory reports for this process and possibly all our child
-   * processes (and all their children, recursively) to a file in the tmp
-   * directory called memory-reports-<identifier>-<pid>.json.gz (or something
-   * similar, such as memory-reports-<identifier>-<pid>-1.json.gz;  no existing
-   * file will be overwritten).
-   *
-   * @param identifier this identifier will appear in the filename of our
-   *   about:memory dump and those of our children (if dumpChildProcesses is
-   *   true).
-   *
-   *   If the identifier is empty, the dumpMemoryReportsToFile implementation
-   *   may set it arbitrarily and use that new value for its own dump and the
-   *   dumps of its child processes.  For example, the dumpMemoryReportsToFile
-   *   implementation may set |identifier| to the number of seconds since the
-   *   epoch.
-   *
-   * @param minimizeMemoryUsage indicates whether we should run a series of
-   *   gc/cc's in an attempt to reduce our memory usage before collecting our
-   *   memory report.
-   *
-   * @param dumpChildProcesses indicates whether we should call
-   *   dumpMemoryReportsToFile in our child processes.  If so, the child
-   *   processes will also dump their children, and so on.
-   *
-   *
-   * Sample output:
-   *
-   * {
-   *   "hasMozMallocUsableSize":true,
-   *   "reports": [
-   *     {"process":"", "path":"explicit/foo/bar", "kind":1, "units":0,
-   *      "amount":2000000, "description":"Foo bar."},
-   *     {"process":"", "path":"heap-allocated", "kind":1, "units":0,
-   *      "amount":3000000, "description":"Heap allocated."},
-   *     {"process":"", "path":"vsize", "kind":1, "units":0,
-   *      "amount":10000000, "description":"Vsize."}
-   *   ]
-   * }
-   *
-   * JSON schema for the output.
-   *
-   * {
-   *   "properties": {
-   *     "hasMozMallocUsableSize": {
-   *       "type": "boolean",
-   *       "description": "nsIMemoryReporterManager::hasMozMallocUsableSize",
-   *       "required": true
-   *     },
-   *     "reports": {
-   *       "type": "array",
-   *       "description": "The memory reports.",
-   *       "required": true
-   *       "minItems": 1,
-   *       "items": {
-   *         "type": "object",
-   *         "properties": {
-   *           "process": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::process",
-   *             "required": true
-   *           },
-   *           "path": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::path",
-   *             "required": true,
-   *             "minLength": 1
-   *           },
-   *           "kind": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::kind",
-   *             "required": true
-   *           },
-   *           "units": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::units",
-   *             "required": true
-   *           },
-   *           "amount": {
-   *             "type": "integer",
-   *             "description": "nsIMemoryReporter::amount",
-   *             "required": true
-   *           },
-   *           "description": {
-   *             "type": "string",
-   *             "description": "nsIMemoryReporter::description",
-   *             "required": true
-   *           }
-   *         }
-   *       }
-   *     }
-   *   }
-   * }
-   */
-  void dumpMemoryReportsToFile (in AString identifier,
-                                in bool minimizeMemoryUsage,
-                                in bool dumpChildProcesses);
-
-  /**
-   * Dump GC and CC logs to files in the OS's temp directory (or in
-   * $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
-   */
-  void dumpGCAndCCLogsToFile (in AString identifier,
-                              in bool dumpChildProcesses);
 };
 
 %{C++
 
 /*
  * Note that this defaults 'process' to "", which is usually what's desired.
  */
 #define NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_classname, _path, _kind, _units, _amountFunction, _desc, _ts) \
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1,57 +1,40 @@
 /* -*- Mode: C++; tab-width: 50; 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/. */
 
 #include "nsAtomTable.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
-#include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemoryReporterManager.h"
 #include "nsArrayEnumerator.h"
-#include "nsIConsoleService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #include "nsThread.h"
-#include "nsGZFileWriter.h"
-#include "nsJSEnvironment.h"
-#include "nsICycleCollectorListener.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Services.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/FileUtils.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/ClearOnShutdown.h"
-#include "mozilla/unused.h"
+#include "mozilla/MemoryInfoDumper.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
-#include <fcntl.h>
-#endif
-
-#ifdef ANDROID
-#include <sys/types.h>
-#include <sys/stat.h>
 #endif
 
 using namespace mozilla;
-using namespace mozilla::dom;
 
 #if defined(MOZ_MEMORY)
 #  define HAVE_JEMALLOC_STATS 1
 #  include "jemalloc.h"
 #endif  // MOZ_MEMORY
 
 #ifdef XP_UNIX
 
@@ -641,252 +624,16 @@ NS_MEMORY_REPORTER_IMPLEMENT(AtomTable,
     "Memory used by the dynamic and static atoms tables.")
 
 /**
  ** nsMemoryReporterManager implementation
  **/
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsMemoryReporterManager, nsIMemoryReporterManager)
 
-namespace {
-
-class DumpMemoryReportsRunnable : public nsRunnable
-{
-public:
-  DumpMemoryReportsRunnable(const nsAString& aIdentifier,
-                            bool aMinimizeMemoryUsage,
-                            bool aDumpChildProcesses)
-
-      : mIdentifier(aIdentifier)
-      , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
-      , mDumpChildProcesses(aDumpChildProcesses)
-  {}
-
-  NS_IMETHOD Run()
-  {
-      nsCOMPtr<nsIMemoryReporterManager> mgr =
-          do_GetService("@mozilla.org/memory-reporter-manager;1");
-      NS_ENSURE_STATE(mgr);
-      mgr->DumpMemoryReportsToFile(mIdentifier,
-                                   mMinimizeMemoryUsage,
-                                   mDumpChildProcesses);
-      return NS_OK;
-  }
-
-private:
-  const nsString mIdentifier;
-  const bool mMinimizeMemoryUsage;
-  const bool mDumpChildProcesses;
-};
-
-class GCAndCCLogDumpRunnable : public nsRunnable
-{
-public:
-  GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
-                         bool aDumpChildProcesses)
-    : mIdentifier(aIdentifier)
-    , mDumpChildProcesses(aDumpChildProcesses)
-  {}
-
-  NS_IMETHOD Run()
-  {
-      nsCOMPtr<nsIMemoryReporterManager> mgr =
-          do_GetService("@mozilla.org/memory-reporter-manager;1");
-      NS_ENSURE_STATE(mgr);
-      mgr->DumpGCAndCCLogsToFile(mIdentifier, mDumpChildProcesses);
-      return NS_OK;
-  }
-
-private:
-  const nsString mIdentifier;
-  const bool mDumpChildProcesses;
-};
-
-} // anonymous namespace
-
-#ifdef XP_LINUX // {
-namespace {
-
-/*
- * The following code supports dumping about:memory upon receiving a signal.
- *
- * We listen for the following signals:
- *
- *  - SIGRTMIN:     Dump our memory reporters (and those of our child
- *                  processes),
- *  - SIGRTMIN + 1: Dump our memory reporters (and those of our child
- *                  processes) after minimizing memory usage, and
- *  - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
- *
- * When we receive one of these signals, we write the signal number to a pipe.
- * The IO thread then notices that the pipe has been written to, and kicks off
- * the appropriate task on the main thread.
- *
- * This scheme is similar to using signalfd(), except it's portable and it
- * doesn't require the use of sigprocmask, which is problematic because it
- * masks signals received by child processes.
- *
- * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
- * But that uses libevent, which does not handle the realtime signals (bug
- * 794074).
- */
-
-// It turns out that at least on some systems, SIGRTMIN is not a compile-time
-// constant, so these have to be set at runtime.
-static int sDumpAboutMemorySignum;         // SIGRTMIN
-static int sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
-static int sGCAndCCDumpSignum;             // SIGRTMIN + 2
-
-// This is the write-end of a pipe that we use to notice when a
-// dump-about-memory signal occurs.
-static int sDumpAboutMemoryPipeWriteFd;
-
-void
-DumpAboutMemorySignalHandler(int aSignum)
-{
-  // This is a signal handler, so everything in here needs to be
-  // async-signal-safe.  Be careful!
-
-  if (sDumpAboutMemoryPipeWriteFd != 0) {
-    uint8_t signum = static_cast<int>(aSignum);
-    write(sDumpAboutMemoryPipeWriteFd, &signum, sizeof(signum));
-  }
-}
-
-class SignalPipeWatcher : public MessageLoopForIO::Watcher
-{
-public:
-  SignalPipeWatcher()
-  {}
-
-  ~SignalPipeWatcher()
-  {
-    // This is somewhat paranoid, but we want to avoid the race condition where
-    // we close sDumpAboutMemoryPipeWriteFd before setting it to 0, then we
-    // reuse that fd for some other file, and then the signal handler runs.
-    int pipeWriteFd = sDumpAboutMemoryPipeWriteFd;
-    PR_ATOMIC_SET(&sDumpAboutMemoryPipeWriteFd, 0);
-
-    // Stop watching the pipe's file descriptor /before/ we close it!
-    mReadWatcher.StopWatchingFileDescriptor();
-
-    close(pipeWriteFd);
-    close(mPipeReadFd);
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SignalPipeWatcher)
-
-  bool Start()
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-
-    sDumpAboutMemorySignum = SIGRTMIN;
-    sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
-    sGCAndCCDumpSignum = SIGRTMIN + 2;
-
-    // Create a pipe.  When we receive a signal in our signal handler, we'll
-    // write the signum to the write-end of this pipe.
-    int pipeFds[2];
-    if (pipe(pipeFds)) {
-        NS_WARNING("Failed to create pipe.");
-        return false;
-    }
-
-    // Close this pipe on calls to exec().
-    fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
-    fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
-
-    mPipeReadFd = pipeFds[0];
-    sDumpAboutMemoryPipeWriteFd = pipeFds[1];
-
-    struct sigaction action;
-    memset(&action, 0, sizeof(action));
-    sigemptyset(&action.sa_mask);
-    action.sa_handler = DumpAboutMemorySignalHandler;
-
-    if (sigaction(sDumpAboutMemorySignum, &action, nullptr)) {
-      NS_WARNING("Failed to register about:memory dump signal handler.");
-    }
-    if (sigaction(sDumpAboutMemoryAfterMMUSignum, &action, nullptr)) {
-      NS_WARNING("Failed to register about:memory dump after MMU signal handler.");
-    }
-    if (sigaction(sGCAndCCDumpSignum, &action, nullptr)) {
-      NS_WARNING("Failed to register GC+CC dump signal handler.");
-    }
-
-    // Start watching the read end of the pipe on the IO thread.
-    return MessageLoopForIO::current()->WatchFileDescriptor(
-        mPipeReadFd, /* persistent = */ true,
-        MessageLoopForIO::WATCH_READ,
-        &mReadWatcher, this);
-  }
-
-  virtual void OnFileCanReadWithoutBlocking(int aFd)
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-
-    uint8_t signum;
-    ssize_t numReceived = read(aFd, &signum, sizeof(signum));
-    if (numReceived != sizeof(signum)) {
-      NS_WARNING("Error reading from buffer in "
-                 "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
-      return;
-    }
-
-    if (signum == sDumpAboutMemorySignum ||
-        signum == sDumpAboutMemoryAfterMMUSignum) {
-      // Dump our memory reports (but run this on the main thread!).
-      nsRefPtr<DumpMemoryReportsRunnable> runnable =
-        new DumpMemoryReportsRunnable(
-            /* identifier = */ EmptyString(),
-            signum == sDumpAboutMemoryAfterMMUSignum,
-            /* dumpChildProcesses = */ true);
-      NS_DispatchToMainThread(runnable);
-    }
-    else if (signum == sGCAndCCDumpSignum) {
-      // Dump GC and CC logs (from the main thread).
-      nsRefPtr<GCAndCCLogDumpRunnable> runnable =
-        new GCAndCCLogDumpRunnable(
-            /* identifier = */ EmptyString(),
-            /* dumpChildProcesses = */ true);
-      NS_DispatchToMainThread(runnable);
-    }
-    else {
-      NS_WARNING("Got unexpected signum.");
-    }
-  }
-
-  virtual void OnFileCanWriteWithoutBlocking(int aFd)
-  {}
-
-private:
-  int mPipeReadFd;
-  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
-};
-
-StaticRefPtr<SignalPipeWatcher> sSignalPipeWatcher;
-
-void
-InitializeDumpAboutMemoryWatcher()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!sSignalPipeWatcher);
-
-  sSignalPipeWatcher = new SignalPipeWatcher();
-  ClearOnShutdown(&sSignalPipeWatcher);
-
-  XRE_GetIOMessageLoop()->PostTask(
-      FROM_HERE,
-      NewRunnableMethod(sSignalPipeWatcher.get(),
-                        &SignalPipeWatcher::Start));
-}
-
-} // anonymous namespace
-#endif // } XP_LINUX
-
 NS_IMETHODIMP
 nsMemoryReporterManager::Init()
 {
 #if HAVE_JEMALLOC_STATS && defined(XP_LINUX)
     if (!jemalloc_stats)
         return NS_ERROR_FAILURE;
 #endif
 
@@ -923,17 +670,17 @@ nsMemoryReporterManager::Init()
 #elif defined(HAVE_HEAP_ZONE0_REPORTERS)
     REGISTER(HeapZone0Committed);
     REGISTER(HeapZone0Used);
 #endif
 
     REGISTER(AtomTable);
 
 #if defined(XP_LINUX)
-    InitializeDumpAboutMemoryWatcher();
+    MemoryInfoDumper::Initialize();
 #endif
 
     return NS_OK;
 }
 
 nsMemoryReporterManager::nsMemoryReporterManager()
   : mMutex("nsMemoryReporterManager::mMutex")
 {
@@ -1180,337 +927,16 @@ nsMemoryReporterManager::GetHasMozMalloc
         return NS_ERROR_OUT_OF_MEMORY;
     }
     size_t usable = moz_malloc_usable_size(p);
     free(p);
     *aHas = !!(usable > 0);
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsMemoryReporterManager::DumpMemoryReportsToFile(
-    const nsAString& aIdentifier,
-    bool aMinimizeMemoryUsage,
-    bool aDumpChildProcesses)
-{
-    // If the identifier is empty, set it to the number of whole seconds since
-    // the epoch.  This identifier will appear in our memory report as well as
-    // our children's, allowing us to identify which files are from the same
-    // memory report request.
-    nsString identifier(aIdentifier);
-    if (identifier.IsEmpty()) {
-        identifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
-    }
-
-    // Kick off memory report dumps in our child processes, if applicable.  We
-    // do this before doing our own report because writing a report may be I/O
-    // bound, in which case we want to busy the CPU with other reports while we
-    // work on our own.
-    if (aDumpChildProcesses) {
-        nsTArray<ContentParent*> children;
-        ContentParent::GetAll(children);
-        for (uint32_t i = 0; i < children.Length(); i++) {
-            unused << children[i]->SendDumpMemoryReportsToFile(
-                identifier, aMinimizeMemoryUsage, aDumpChildProcesses);
-        }
-    }
-
-    if (aMinimizeMemoryUsage) {
-        // Minimize memory usage, then run DumpMemoryReportsToFile again.
-        nsRefPtr<DumpMemoryReportsRunnable> callback =
-            new DumpMemoryReportsRunnable(identifier,
-                                          /* minimizeMemoryUsage = */ false,
-                                          /* dumpChildProcesses = */ false);
-        return MinimizeMemoryUsage(callback);
-    }
-
-    return DumpMemoryReportsToFileImpl(identifier);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::DumpGCAndCCLogsToFile(
-    const nsAString& aIdentifier,
-    bool aDumpChildProcesses)
-{
-    // If the identifier is empty, set it to the number of whole seconds since
-    // the epoch.  This identifier will appear in our logs as well as our
-    // children's, allowing us to identify which files are from the same
-    // request.
-    nsString identifier(aIdentifier);
-    if (identifier.IsEmpty()) {
-        identifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
-    }
-
-    if (aDumpChildProcesses) {
-        nsTArray<ContentParent*> children;
-        ContentParent::GetAll(children);
-        for (uint32_t i = 0; i < children.Length(); i++) {
-            unused << children[i]->SendDumpGCAndCCLogsToFile(
-                identifier, aDumpChildProcesses);
-        }
-    }
-
-    nsCOMPtr<nsICycleCollectorListener> logger =
-      do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
-    logger->SetFilenameIdentifier(identifier);
-
-    nsJSContext::CycleCollectNow(logger);
-    return NS_OK;
-}
-
-#define DUMP(o, s) \
-    do { \
-        nsresult rv = (o)->Write(s); \
-        NS_ENSURE_SUCCESS(rv, rv); \
-    } while (0)
-
-static nsresult
-DumpReport(nsIGZFileWriter *aWriter, bool aIsFirst,
-           const nsACString &aProcess, const nsACString &aPath, int32_t aKind,
-           int32_t aUnits, int64_t aAmount, const nsACString &aDescription)
-{
-    DUMP(aWriter, aIsFirst ? "[" : ",");
-
-    // We only want to dump reports for this process.  If |aProcess| is
-    // non-NULL that means we've received it from another process in response
-    // to a "child-memory-reporter-request" event;  ignore such reports.
-    if (!aProcess.IsEmpty()) {
-        return NS_OK;
-    }
-
-    // Generate the process identifier, which is of the form "$PROCESS_NAME
-    // (pid $PID)", or just "(pid $PID)" if we don't have a process name.  If
-    // we're the main process, we let $PROCESS_NAME be "Main Process".
-    nsAutoCString processId;
-    if (XRE_GetProcessType() == GeckoProcessType_Default) {
-      // We're the main process.
-      processId.AssignLiteral("Main Process ");
-    } else if (ContentChild *cc = ContentChild::GetSingleton()) {
-      // Try to get the process name from ContentChild.
-      nsAutoString processName;
-      cc->GetProcessName(processName);
-      processId.Assign(NS_ConvertUTF16toUTF8(processName));
-      if (!processId.IsEmpty()) {
-        processId.AppendLiteral(" ");
-      }
-    }
-
-    // Add the PID to the identifier.
-    unsigned pid = getpid();
-    processId.Append(nsPrintfCString("(pid %u)", pid));
-
-    DUMP(aWriter, "\n    {\"process\": \"");
-    DUMP(aWriter, processId);
-
-    DUMP(aWriter, "\", \"path\": \"");
-    nsCString path(aPath);
-    path.ReplaceSubstring("\\", "\\\\");    // escape backslashes for JSON
-    DUMP(aWriter, path);
-
-    DUMP(aWriter, "\", \"kind\": ");
-    DUMP(aWriter, nsPrintfCString("%d", aKind));
-
-    DUMP(aWriter, ", \"units\": ");
-    DUMP(aWriter, nsPrintfCString("%d", aUnits));
-
-    DUMP(aWriter, ", \"amount\": ");
-    DUMP(aWriter, nsPrintfCString("%lld", aAmount));
-
-    nsCString description(aDescription);
-    description.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
-    description.ReplaceSubstring("\"", "\\\"");    // " --> \"
-    description.ReplaceSubstring("\n", "\\n");     // <newline> --> \n
-    DUMP(aWriter, ", \"description\": \"");
-    DUMP(aWriter, description);
-    DUMP(aWriter, "\"}");
-
-    return NS_OK;
-}
-
-class DumpMultiReporterCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
-                        int32_t aKind, int32_t aUnits, int64_t aAmount,
-                        const nsACString &aDescription,
-                        nsISupports *aData)
-    {
-        nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
-        NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
-
-        // The |isFirst = false| assumes that at least one single reporter is
-        // present and so will have been processed in
-        // DumpMemoryReportsToFileImpl() below.
-        return DumpReport(writer, /* isFirst = */ false, aProcess, aPath,
-                          aKind, aUnits, aAmount, aDescription);
-        return NS_OK;
-    }
-};
-
-NS_IMPL_ISUPPORTS1(
-  DumpMultiReporterCallback
-, nsIMemoryMultiReporterCallback
-)
-
-nsresult
-nsMemoryReporterManager::DumpMemoryReportsToFileImpl(
-    const nsAString& aIdentifier)
-{
-    // Open a new file named something like
-    //
-    //   incomplete-memory-report-<-identifier>-<pid>-42.json.gz
-    //
-    // in NS_OS_TEMP_DIR for writing.  When we're finished writing the report,
-    // we'll rename this file and get rid of the "incomplete-" prefix.
-    //
-    // We do this because we don't want scripts which poll the filesystem
-    // looking for memory report dumps to grab a file before we're finished
-    // writing to it.
-
-    nsCOMPtr<nsIFile> tmpFile;
-    nsresult rv =
-        NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Note that |filename| is missing the "incomplete-" prefix; we'll tack
-    // that on in a moment.
-    nsAutoCString filename;
-    filename.AppendLiteral("memory-report");
-    if (!aIdentifier.IsEmpty()) {
-        filename.AppendLiteral("-");
-        filename.Append(NS_ConvertUTF16toUTF8(aIdentifier));
-    }
-    filename.AppendLiteral("-");
-    filename.AppendInt(getpid());
-    filename.AppendLiteral(".json.gz");
-
-    rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("incomplete-") + filename);
-    NS_ENSURE_SUCCESS(rv, rv);
-   
-#ifdef ANDROID
-    // Set umask to 0 while we create the file because on Android the default
-    // umask is 0077.
-    mode_t mask = umask(0);
-#endif
-    rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
-#ifdef ANDROID
-    umask(mask);
-#endif
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsRefPtr<nsGZFileWriter> writer = new nsGZFileWriter();
-    rv = writer->Init(tmpFile);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Dump the memory reports to the file.
-
-    // Increment this number if the format changes.
-    DUMP(writer, "{\n  \"version\": 1,\n");
-
-    DUMP(writer, "  \"hasMozMallocUsableSize\": ");
-
-    bool hasMozMallocUsableSize;
-    GetHasMozMallocUsableSize(&hasMozMallocUsableSize);
-    DUMP(writer, hasMozMallocUsableSize ? "true" : "false");
-    DUMP(writer, ",\n");
-    DUMP(writer, "  \"reports\": ");
-
-    // Process single reporters.
-    bool isFirst = true;
-    bool more;
-    nsCOMPtr<nsISimpleEnumerator> e;
-    EnumerateReporters(getter_AddRefs(e));
-    while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
-        nsCOMPtr<nsIMemoryReporter> r;
-        e->GetNext(getter_AddRefs(r));
-
-        nsCString process;
-        rv = r->GetProcess(process);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCString path;
-        rv = r->GetPath(path);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        int32_t kind;
-        rv = r->GetKind(&kind);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        int32_t units;
-        rv = r->GetUnits(&units);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        int64_t amount;
-        rv = r->GetAmount(&amount);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCString description;
-        rv = r->GetDescription(description);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        rv = DumpReport(writer, isFirst, process, path, kind, units, amount,
-                        description);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        isFirst = false;
-    }
-
-    // Process multi-reporters.
-    nsCOMPtr<nsISimpleEnumerator> e2;
-    EnumerateMultiReporters(getter_AddRefs(e2));
-    nsRefPtr<DumpMultiReporterCallback> cb = new DumpMultiReporterCallback();
-    while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
-      nsCOMPtr<nsIMemoryMultiReporter> r;
-      e2->GetNext(getter_AddRefs(r));
-      r->CollectReports(cb, writer);
-    }
-
-    DUMP(writer, "\n  ]\n}");
-
-    rv = writer->Finish();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Rename the file, now that we're done dumping the report.  The file's
-    // ultimate destination is "memory-report<-identifier>-<pid>.json.gz".
-
-    nsCOMPtr<nsIFile> dstFile;
-    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dstFile));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = dstFile->AppendNative(filename);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = dstFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsAutoString dstFileName;
-    rv = dstFile->GetLeafName(dstFileName);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = tmpFile->MoveTo(/* directory */ nullptr, dstFileName);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIConsoleService> cs =
-        do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsString path;
-    tmpFile->GetPath(path);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsString msg =
-        NS_LITERAL_STRING("nsIMemoryReporterManager::dumpReports() dumped reports to ");
-    msg.Append(path);
-    return cs->LogStringMessage(msg.get());
-}
-
-#undef DUMP
-
 namespace {
 
 /**
  * This runnable lets us implement nsIMemoryReporterManager::MinimizeMemoryUsage().
  * We fire a heap-minimize notification, spin the event loop, and repeat this
  * process a few times.
  *
  * When this sequence finishes, we invoke the callback function passed to the
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -41,18 +41,16 @@ class nsMemoryReporterManager : public n
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYREPORTERMANAGER
 
     nsMemoryReporterManager();
     virtual ~nsMemoryReporterManager();
 
 private:
-    nsresult DumpMemoryReportsToFileImpl(const nsAString& aIdentifier);
-
     nsCOMArray<nsIMemoryReporter>      mReporters;
     nsCOMArray<nsIMemoryMultiReporter> mMultiReporters;
     Mutex                              mMutex;
 };
 
 #define NS_MEMORY_REPORTER_MANAGER_CID \
 { 0xfb97e4f5, 0x32dd, 0x497a, \
 { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }