Bug 964636 - Part 1: Move common classes and functions out of nsMemoryInfoDumper. r=dhylands
authorAlphan Chen<alchen@mozilla.com>
Thu, 20 Mar 2014 15:29:51 +0800
changeset 193868 5643caf4797ff843bc8e27fe309d506796cda4c2
parent 193867 8f865d4a25d8e59a1a3c3f879a075f0733ef9183
child 193869 5e337eb07938f82095d4de160f85705730daa9eb
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands
bugs964636
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 964636 - Part 1: Move common classes and functions out of nsMemoryInfoDumper. r=dhylands
xpcom/base/moz.build
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsDumpUtils.cpp
xpcom/base/nsDumpUtils.h
xpcom/base/nsMemoryInfoDumper.cpp
xpcom/base/nsMemoryInfoDumper.h
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -91,16 +91,17 @@ SOURCES['nsDebugImpl.cpp'].no_pgo = True
 UNIFIED_SOURCES += [
     'AvailableMemoryTracker.cpp',
     'ClearOnShutdown.cpp',
     'CycleCollectedJSRuntime.cpp',
     'Debug.cpp',
     'nsConsoleMessage.cpp',
     'nsConsoleService.cpp',
     'nsCycleCollector.cpp',
+    'nsDumpUtils.cpp',
     'nsErrorService.cpp',
     'nsGZFileWriter.cpp',
     'nsInterfaceRequestorAgg.cpp',
     'nsMemoryImpl.cpp',
     'nsMemoryInfoDumper.cpp',
     'nsMemoryReporterManager.cpp',
     'nsMessageLoop.cpp',
     'nsSecurityConsoleMessage.cpp',
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -168,17 +168,17 @@
 #include "prenv.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsIConsoleService.h"
 #include "mozilla/Attributes.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIMemoryReporter.h"
 #include "nsIFile.h"
-#include "nsMemoryInfoDumper.h"
+#include "nsDumpUtils.h"
 #include "xpcpublic.h"
 #include "GeckoProfiler.h"
 #include "js/SliceBudget.h"
 #include <stdint.h>
 #include <stdio.h>
 
 #include "mozilla/Likely.h"
 #include "mozilla/PoisonIOInterposer.h"
@@ -1722,17 +1722,24 @@ private:
         // the fallback directories in OpenTempFile.  We don't use an nsCOMPtr
         // here because OpenTempFile uses an in/out param and getter_AddRefs
         // wouldn't work.
         nsIFile* logFile = nullptr;
         if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) {
             NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true,
                                   &logFile);
         }
-        nsresult rv = nsMemoryInfoDumper::OpenTempFile(filename, &logFile);
+
+        // In Android case, this function will open a file named aFilename under
+        // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
+        // open a file named aFilename under "NS_OS_TEMP_DIR".
+        nsresult rv = nsDumpUtils::OpenTempFile(
+                                     filename,
+                                     &logFile,
+                                     NS_LITERAL_CSTRING("memory-reports"));
         if (NS_FAILED(rv)) {
           NS_IF_RELEASE(logFile);
           return nullptr;
         }
 
         return dont_AddRef(logFile);
     }
 
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.cpp
@@ -0,0 +1,476 @@
+/* -*- 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 "nsDumpUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "prenv.h"
+#include <errno.h>
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+ #include "mozilla/ClearOnShutdown.h"
+
+#if defined(XP_LINUX) || defined(__FreeBSD__) // {
+#include "mozilla/Preferences.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * The following code supports triggering a registered callback upon
+ * receiving a specific signal.
+ *
+ * Take about:memory for example, we register
+ * 1. doGCCCDump for doMemoryReport
+ * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN)
+ *                       and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1).
+ *
+ * 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).
+ */
+
+// This is the write-end of a pipe that we use to notice when a
+// specific signal occurs.
+static Atomic<int> sDumpPipeWriteFd(-1);
+
+static void
+DumpSignalHandler(int aSignum)
+{
+  // This is a signal handler, so everything in here needs to be
+  // async-signal-safe.  Be careful!
+
+  if (sDumpPipeWriteFd != -1) {
+    uint8_t signum = static_cast<int>(aSignum);
+    write(sDumpPipeWriteFd, &signum, sizeof(signum));
+  }
+}
+
+NS_IMPL_ISUPPORTS1(FdWatcher, nsIObserver);
+
+void FdWatcher::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
+
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &FdWatcher::StartWatching));
+}
+
+// Implementations may call this function multiple times if they ensure that
+// it's safe to call OpenFd() multiple times and they call StopWatching()
+// first.
+void FdWatcher::StartWatching()
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+  MOZ_ASSERT(mFd == -1);
+
+  mFd = OpenFd();
+  if (mFd == -1) {
+    LOG("FdWatcher: OpenFd failed.");
+    return;
+  }
+
+  MessageLoopForIO::current()->WatchFileDescriptor(
+    mFd, /* persistent = */ true,
+    MessageLoopForIO::WATCH_READ,
+    &mReadWatcher, this);
+}
+
+// Since implementations can call StartWatching() multiple times, they can of
+// course call StopWatching() multiple times.
+void FdWatcher::StopWatching()
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+  mReadWatcher.StopWatchingFileDescriptor();
+  if (mFd != -1) {
+    close(mFd);
+    mFd = -1;
+  }
+}
+
+StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton;
+
+/* static */ SignalPipeWatcher*
+SignalPipeWatcher::GetSingleton()
+{
+  if (!sSingleton) {
+    sSingleton = new SignalPipeWatcher();
+    sSingleton->Init();
+    ClearOnShutdown(&sSingleton);
+  }
+  return sSingleton;
+}
+
+/* static */ void
+SignalPipeWatcher::RegisterCallback(const uint8_t aSignal,
+                                    PipeCallback aCallback)
+{
+  for (SignalInfoArray::index_type i = 0; 
+       i < SignalPipeWatcher::mSignalInfo.Length(); i++)
+  {
+    if (SignalPipeWatcher::mSignalInfo[i].mSignal == aSignal) {
+      LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
+      return;
+    }
+  }
+  SignalInfo aSignalInfo = { aSignal, aCallback };
+  SignalPipeWatcher::mSignalInfo.AppendElement(aSignalInfo);
+  SignalPipeWatcher::RegisterSignalHandler(aSignalInfo.mSignal);
+}
+
+/* static */ void
+SignalPipeWatcher::RegisterSignalHandler(const uint8_t aSignal)
+{
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  sigemptyset(&action.sa_mask);
+  action.sa_handler = DumpSignalHandler;
+
+  if (aSignal) {
+    if (sigaction(aSignal, &action, nullptr)) {
+      LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
+    }
+  } else {
+    for (SignalInfoArray::index_type i = 0; i < SignalPipeWatcher::mSignalInfo.Length(); i++) {
+      if (sigaction(SignalPipeWatcher::mSignalInfo[i].mSignal, &action, nullptr)) {
+        LOG("SignalPipeWatcher failed to register signal(%d) "
+            "dump signal handler.",SignalPipeWatcher::mSignalInfo[i].mSignal);
+      }
+    }
+  }
+}
+
+SignalPipeWatcher::~SignalPipeWatcher()
+{
+  if (sDumpPipeWriteFd != -1)
+    SignalPipeWatcher::StopWatching();
+}
+
+int SignalPipeWatcher::OpenFd()
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+  // 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)) {
+    LOG("SignalPipeWatcher failed to create pipe.");
+    return -1;
+  }
+
+  // Close this pipe on calls to exec().
+  fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
+  fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
+
+  int readFd = pipeFds[0];
+  sDumpPipeWriteFd = pipeFds[1];
+
+  SignalPipeWatcher::RegisterSignalHandler();
+  return readFd;
+}
+
+void SignalPipeWatcher::StopWatching()
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+  // Close sDumpPipeWriteFd /after/ setting the fd to -1.
+  // Otherwise we have the (admittedly far-fetched) race where we
+  //
+  //  1) close sDumpPipeWriteFd
+  //  2) open a new fd with the same number as sDumpPipeWriteFd
+  //     had.
+  //  3) receive a signal, then write to the fd.
+  int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
+  close(pipeWriteFd);
+
+  FdWatcher::StopWatching();
+}
+
+void SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+  uint8_t signum;
+  ssize_t numReceived = read(aFd, &signum, sizeof(signum));
+  if (numReceived != sizeof(signum)) {
+    LOG("Error reading from buffer in "
+        "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
+    return;
+  }
+
+  for (SignalInfoArray::index_type i = 0; i < SignalPipeWatcher::mSignalInfo.Length(); i++) {
+    if(signum == SignalPipeWatcher::mSignalInfo[i].mSignal) {
+      SignalPipeWatcher::mSignalInfo[i].mCallback(signum);
+      return;
+    }
+  }
+  LOG("SignalPipeWatcher got unexpected signum.");
+}
+
+StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton;
+
+/* static */ FifoWatcher*
+FifoWatcher::GetSingleton()
+{
+  if (!sSingleton) {
+    nsAutoCString dirPath;
+    Preferences::GetCString(
+      "memory_info_dumper.watch_fifo.directory", &dirPath);
+    sSingleton = new FifoWatcher(dirPath);
+    sSingleton->Init();
+    ClearOnShutdown(&sSingleton);
+  }
+  return sSingleton;
+}
+
+/* static */ bool
+FifoWatcher::MaybeCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    // We want this to be main-process only, since two processes can't listen
+    // to the same fifo.
+    return false;
+  }
+
+  if (!Preferences::GetBool("memory_info_dumper.watch_fifo.enabled", false)) {
+    LOG("Fifo watcher disabled via pref.");
+    return false;
+  }
+
+  // The FifoWatcher is held alive by the observer service.
+  if (!FifoWatcher::sSingleton) {
+    FifoWatcher::GetSingleton();
+  }
+  return true;
+}
+
+void
+FifoWatcher::RegisterCallback(const nsCString& aCommand,FifoCallback aCallback)
+{
+  for (FifoInfoArray::index_type i = 0;
+       i < FifoWatcher::mFifoInfo.Length(); i++)
+  {
+    if (FifoWatcher::mFifoInfo[i].mCommand.Equals(aCommand)) {
+      LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get());
+      return;
+    }
+  }
+  FifoInfo aFifoInfo = { aCommand, aCallback };
+  FifoWatcher::mFifoInfo.AppendElement(aFifoInfo);
+}
+
+FifoWatcher::~FifoWatcher()
+{
+}
+
+int FifoWatcher::OpenFd()
+{
+  // If the memory_info_dumper.directory pref is specified, put the fifo
+  // there.  Otherwise, put it into the system's tmp directory.
+
+  nsCOMPtr<nsIFile> file;
+
+  nsresult rv;
+  if (mDirPath.Length() > 0) {
+    rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
+    if (NS_FAILED(rv)) {
+      LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
+      return -1;
+    }
+  } else {
+    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
+    if (NS_WARN_IF(NS_FAILED(rv)))
+      return -1;
+  }
+
+  rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return -1;
+
+  nsAutoCString path;
+  rv = file->GetNativePath(path);
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return -1;
+
+  // unlink might fail because the file doesn't exist, or for other reasons.
+  // But we don't care it fails; any problems will be detected later, when we
+  // try to mkfifo or open the file.
+  if (unlink(path.get())) {
+    LOG("FifoWatcher::OpenFifo unlink failed; errno=%d.  "
+        "Continuing despite error.", errno);
+  }
+
+  if (mkfifo(path.get(), 0766)) {
+    LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
+    return -1;
+  }
+
+#ifdef ANDROID
+    // Android runs with a umask, so we need to chmod our fifo to make it
+    // world-writable.
+    chmod(path.get(), 0666);
+#endif
+
+  int fd;
+  do {
+    // The fifo will block until someone else has written to it.  In
+    // particular, open() will block until someone else has opened it for
+    // writing!  We want open() to succeed and read() to block, so we open
+    // with NONBLOCK and then fcntl that away.
+    fd = open(path.get(), O_RDONLY | O_NONBLOCK);
+  } while (fd == -1 && errno == EINTR);
+
+  if (fd == -1) {
+    LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
+    return -1;
+  }
+
+  // Make fd blocking now that we've opened it.
+  if (fcntl(fd, F_SETFL, 0)) {
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+void FifoWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+  char buf[1024];
+  int nread;
+  do {
+    // sizeof(buf) - 1 to leave space for the null-terminator.
+    nread = read(aFd, buf, sizeof(buf));
+  } while(nread == -1 && errno == EINTR);
+
+  if (nread == -1) {
+    // We want to avoid getting into a situation where
+    // OnFileCanReadWithoutBlocking is called in an infinite loop, so when
+    // something goes wrong, stop watching the fifo altogether.
+    LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
+    StopWatching();
+    return;
+  }
+
+  if (nread == 0) {
+    // If we get EOF, that means that the other side closed the fifo.  We need
+    // to close and re-open the fifo; if we don't,
+    // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
+
+    LOG("FifoWatcher closing and re-opening fifo.");
+    StopWatching();
+    StartWatching();
+    return;
+  }
+
+  nsAutoCString inputStr;
+  inputStr.Append(buf, nread);
+
+  // Trimming whitespace is important because if you do
+  //   |echo "foo" >> debug_info_trigger|,
+  // it'll actually write "foo\n" to the fifo.
+  inputStr.Trim("\b\t\r\n");
+
+  for (FifoInfoArray::index_type i = 0; i < FifoWatcher::mFifoInfo.Length(); i++) {
+    const nsCString commandStr = FifoWatcher::mFifoInfo[i].mCommand;
+    if(inputStr == commandStr.get()) {
+      FifoWatcher::mFifoInfo[i].mCallback(inputStr);
+      return;
+    }
+  }
+  LOG("Got unexpected value from fifo; ignoring it.");
+}
+
+#endif // XP_LINUX }
+
+// In Android case, this function will open a file named aFilename under
+// /data/local/tmp/"aFoldername".
+// Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR".
+/* static */ nsresult
+nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile,
+                          const nsACString& aFoldername)
+{
+#ifdef ANDROID
+  // For Android, first try the downloads directory which is world-readable
+  // rather than the temp directory which is not.
+  if (!*aFile) {
+    char *env = PR_GetEnv("DOWNLOADS_DIRECTORY");
+    if (env) {
+      NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
+    }
+  }
+#endif
+  nsresult rv;
+  if (!*aFile) {
+    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
+    if (NS_WARN_IF(NS_FAILED(rv)))
+      return rv;
+  }
+
+#ifdef ANDROID
+  // /data/local/tmp is a true tmp directory; anyone can create a file there,
+  // but only the user which created the file can remove it.  We want non-root
+  // users to be able to remove these files, so we write them into a
+  // subdirectory of the temp directory and chmod 777 that directory.
+  if (aFoldername != EmptyCString()) {
+    rv = (*aFile)->AppendNative(aFoldername);
+    if (NS_WARN_IF(NS_FAILED(rv)))
+      return rv;
+
+    // It's OK if this fails; that probably just means that the directory already
+    // exists.
+    (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
+
+    nsAutoCString dirPath;
+    rv = (*aFile)->GetNativePath(dirPath);
+    if (NS_WARN_IF(NS_FAILED(rv)))
+      return rv;
+
+    while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {}
+  }
+#endif
+
+  nsCOMPtr<nsIFile> file(*aFile);
+
+  rv = file->AppendNative(aFilename);
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return rv;
+
+  rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return rv;
+
+#ifdef ANDROID
+    // Make this file world-read/writable; the permissions passed to the
+    // CreateUnique call above are not sufficient on Android, which runs with a
+    // umask.
+    nsAutoCString path;
+    rv = file->GetNativePath(path);
+    if (NS_WARN_IF(NS_FAILED(rv)))
+      return rv;
+
+    while (chmod(path.get(), 0666) == -1 && errno == EINTR) {}
+#endif
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.h
@@ -0,0 +1,187 @@
+/* -*- 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 mozilla_nsDumpUtils_h
+#define mozilla_nsDumpUtils_h
+
+#include "nsIObserver.h"
+#include "base/message_loop.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "mozilla/StaticPtr.h"
+#include "nsTArray.h"
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef ANDROID
+#include "android/log.h"
+#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko:DumpUtils", ## __VA_ARGS__)
+#else
+#define LOG(...)
+#endif
+
+using namespace mozilla;
+
+#if defined(XP_LINUX) || defined(__FreeBSD__) // {
+
+/**
+ * Abstract base class for something which watches an fd and takes action when
+ * we can read from it without blocking.
+ */
+class FdWatcher : public MessageLoopForIO::Watcher
+                , public nsIObserver
+{
+protected:
+  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+  int mFd;
+
+public:
+  FdWatcher()
+    : mFd(-1)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  virtual ~FdWatcher()
+  {
+    // StopWatching should have run.
+    MOZ_ASSERT(mFd == -1);
+  }
+
+  /**
+   * Open the fd to watch.  If we encounter an error, return -1.
+   */
+  virtual int OpenFd() = 0;
+
+  /**
+   * Called when you can read() from the fd without blocking.  Note that this
+   * function is also called when you're at eof (read() returns 0 in this case).
+   */
+  virtual void OnFileCanReadWithoutBlocking(int aFd) = 0;
+  virtual void OnFileCanWriteWithoutBlocking(int aFd) {};
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  /**
+   * Initialize this object.  This should be called right after the object is
+   * constructed.  (This would go in the constructor, except we interact with
+   * XPCOM, which we can't do from a constructor because our refcount is 0 at
+   * that point.)
+   */
+  void Init();
+
+  // Implementations may call this function multiple times if they ensure that
+
+  virtual void StartWatching();
+
+  // Since implementations can call StartWatching() multiple times, they can of
+  // course call StopWatching() multiple times.
+  virtual void StopWatching();
+
+  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+                     const char16_t* aData)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &FdWatcher::StopWatching));
+
+    return NS_OK;
+  }
+};
+
+typedef void (* FifoCallback)(const nsCString& inputStr);
+struct FifoInfo {
+  nsCString mCommand;
+  FifoCallback mCallback;
+};
+typedef nsTArray<FifoInfo> FifoInfoArray;
+
+class FifoWatcher : public FdWatcher
+{
+public:
+  static FifoWatcher* GetSingleton();
+
+  static bool MaybeCreate();
+
+  void RegisterCallback(const nsCString& aCommand, FifoCallback aCallback);
+
+  virtual ~FifoWatcher();
+
+  virtual int OpenFd();
+
+  virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+  nsAutoCString mDirPath;
+
+  static StaticRefPtr<FifoWatcher> sSingleton;
+
+  FifoWatcher(nsCString aPath)
+    : mDirPath(aPath)
+  {}
+
+  FifoInfoArray mFifoInfo;
+};
+
+typedef void (* PipeCallback)(const uint8_t recvSig);
+struct SignalInfo {
+  uint8_t mSignal;
+  PipeCallback mCallback;
+};
+typedef nsTArray<SignalInfo> SignalInfoArray;
+
+class SignalPipeWatcher : public FdWatcher
+{
+public:
+  static SignalPipeWatcher* GetSingleton();
+
+  void RegisterCallback(const uint8_t aSignal, PipeCallback aCallback);
+
+  void RegisterSignalHandler(const uint8_t aSignal = 0);
+
+  virtual ~SignalPipeWatcher();
+
+  virtual int OpenFd();
+
+  virtual void StopWatching();
+
+  virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+  static StaticRefPtr<SignalPipeWatcher> sSingleton;
+
+  SignalPipeWatcher()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  SignalInfoArray mSignalInfo;
+};
+
+#endif // XP_LINUX }
+
+
+class nsDumpUtils
+{
+public:
+  /**
+   * This function creates a new unique file based on |aFilename| in a
+   * world-readable temp directory. This is the system temp directory
+   * or, in the case of Android, the downloads directory. If |aFile| is
+   * non-null, it is assumed to point to a folder, and that folder is used
+   * instead.
+   */
+  static nsresult OpenTempFile(const nsACString& aFilename,
+                        nsIFile** aFile,
+                        const nsACString& aFoldername = EmptyCString());
+};
+
+#endif
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -1,61 +1,44 @@
 /* -*- 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 "mozilla/nsMemoryInfoDumper.h"
+#include "nsDumpUtils.h"
 
-#if defined(XP_LINUX) || defined(__FreeBSD__)
-#include "mozilla/Preferences.h"
-#endif
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "nsIConsoleService.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIMemoryReporter.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsGZFileWriter.h"
 #include "nsJSEnvironment.h"
 #include "nsPrintfCString.h"
 #include "nsISimpleEnumerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
-#include <errno.h>
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
 
 #if defined(XP_LINUX) || defined(__FreeBSD__)
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #endif
 
-#ifdef ANDROID
-#include "android/log.h"
-#endif
-
-#ifdef LOG
-#undef LOG
-#endif
-
-#ifdef ANDROID
-#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko:MemoryInfoDumper", ## __VA_ARGS__)
-#else
-#define LOG(...)
-#endif
-
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace {
 
 class DumpMemoryInfoToTempDirRunnable : public nsRunnable
 {
 public:
@@ -131,419 +114,63 @@ namespace {
  *
  * 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 Atomic<int> sDumpAboutMemoryPipeWriteFd(-1);
+static uint8_t sDumpAboutMemorySignum;         // SIGRTMIN
+static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
+static uint8_t sGCAndCCDumpSignum;             // SIGRTMIN + 2
 
-void
-DumpAboutMemorySignalHandler(int aSignum)
+void doMemoryReport(const nsCString& inputStr)
 {
-  // This is a signal handler, so everything in here needs to be
-  // async-signal-safe.  Be careful!
-
-  if (sDumpAboutMemoryPipeWriteFd != -1) {
-    uint8_t signum = static_cast<int>(aSignum);
-    write(sDumpAboutMemoryPipeWriteFd, &signum, sizeof(signum));
-  }
+  bool doMMUMemoryReport = inputStr == NS_LITERAL_CSTRING("minimize memory report");
+  LOG("FifoWatcher(command:%s) dispatching memory report runnable.", inputStr.get());
+  nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+    new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+                                        doMMUMemoryReport);
+  NS_DispatchToMainThread(runnable);
 }
 
-/**
- * Abstract base class for something which watches an fd and takes action when
- * we can read from it without blocking.
- */
-class FdWatcher : public MessageLoopForIO::Watcher
-                , public nsIObserver
+void doMemoryReport(const uint8_t recvSig)
 {
-protected:
-  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
-  int mFd;
-
-public:
-  FdWatcher()
-    : mFd(-1)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  virtual ~FdWatcher()
-  {
-    // StopWatching should have run.
-    MOZ_ASSERT(mFd == -1);
-  }
-
-  /**
-   * Open the fd to watch.  If we encounter an error, return -1.
-   */
-  virtual int OpenFd() = 0;
-
-  /**
-   * Called when you can read() from the fd without blocking.  Note that this
-   * function is also called when you're at eof (read() returns 0 in this case).
-   */
-  virtual void OnFileCanReadWithoutBlocking(int aFd) = 0;
-  virtual void OnFileCanWriteWithoutBlocking(int aFd) {};
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  /**
-   * Initialize this object.  This should be called right after the object is
-   * constructed.  (This would go in the constructor, except we interact with
-   * XPCOM, which we can't do from a constructor because our refcount is 0 at
-   * that point.)
-   */
-  void Init()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-    os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
-
-    XRE_GetIOMessageLoop()->PostTask(
-        FROM_HERE,
-        NewRunnableMethod(this, &FdWatcher::StartWatching));
-  }
-
-  // Implementations may call this function multiple times if they ensure that
-  // it's safe to call OpenFd() multiple times and they call StopWatching()
-  // first.
-  virtual void StartWatching()
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-    MOZ_ASSERT(mFd == -1);
-
-    mFd = OpenFd();
-    if (mFd == -1) {
-      LOG("FdWatcher: OpenFd failed.");
-      return;
-    }
-
-    MessageLoopForIO::current()->WatchFileDescriptor(
-      mFd, /* persistent = */ true,
-      MessageLoopForIO::WATCH_READ,
-      &mReadWatcher, this);
-  }
-
-  // Since implementations can call StartWatching() multiple times, they can of
-  // course call StopWatching() multiple times.
-  virtual void StopWatching()
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-
-    mReadWatcher.StopWatchingFileDescriptor();
-    if (mFd != -1) {
-      close(mFd);
-      mFd = -1;
-    }
-  }
-
-  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
-                     const char16_t* aData)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
-
-    XRE_GetIOMessageLoop()->PostTask(
-        FROM_HERE,
-        NewRunnableMethod(this, &FdWatcher::StopWatching));
-
-    return NS_OK;
-  }
-};
-
-NS_IMPL_ISUPPORTS1(FdWatcher, nsIObserver);
-
-class SignalPipeWatcher : public FdWatcher
-{
-public:
-  static void Create()
-  {
-    nsRefPtr<SignalPipeWatcher> sw = new SignalPipeWatcher();
-    sw->Init();
-  }
-
-  virtual ~SignalPipeWatcher()
-  {
-    MOZ_ASSERT(sDumpAboutMemoryPipeWriteFd == -1);
-  }
-
-  virtual int OpenFd()
-  {
-    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)) {
-      LOG("SignalPipeWatcher failed to create pipe.");
-      return -1;
-    }
-
-    // Close this pipe on calls to exec().
-    fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
-    fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
-
-    int readFd = 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)) {
-      LOG("SignalPipeWatcher failed to register about:memory "
-          "dump signal handler.");
-    }
-    if (sigaction(sDumpAboutMemoryAfterMMUSignum, &action, nullptr)) {
-      LOG("SignalPipeWatcher failed to register about:memory "
-          "dump after MMU signal handler.");
-    }
-    if (sigaction(sGCAndCCDumpSignum, &action, nullptr)) {
-      LOG("Failed to register GC+CC dump signal handler.");
-    }
-
-    return readFd;
-  }
-
-  virtual void StopWatching()
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-
-    // Close sDumpAboutMemoryPipeWriteFd /after/ setting the fd to -1.
-    // Otherwise we have the (admittedly far-fetched) race where we
-    //
-    //  1) close sDumpAboutMemoryPipeWriteFd
-    //  2) open a new fd with the same number as sDumpAboutMemoryPipeWriteFd
-    //     had.
-    //  3) receive a signal, then write to the fd.
-    int pipeWriteFd = sDumpAboutMemoryPipeWriteFd.exchange(-1);
-    close(pipeWriteFd);
-
-    FdWatcher::StopWatching();
-  }
-
-  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)) {
-      LOG("Error reading from buffer in "
-          "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
-      return;
-    }
+  // Dump our memory reports (but run this on the main thread!).
+  bool doMMUFirst = recvSig == sDumpAboutMemoryAfterMMUSignum;
+  LOG("SignalWatcher(sig %d) dispatching memory report runnable.", recvSig);
+  nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+    new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+                                        doMMUFirst);
+  NS_DispatchToMainThread(runnable);
+}
 
-    if (signum == sDumpAboutMemorySignum ||
-        signum == sDumpAboutMemoryAfterMMUSignum) {
-      // Dump our memory reports (but run this on the main thread!).
-      bool doMMUFirst = signum == sDumpAboutMemoryAfterMMUSignum;
-      nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
-        new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
-                                            doMMUFirst);
-      NS_DispatchToMainThread(runnable);
-    }
-    else if (signum == sGCAndCCDumpSignum) {
-      // Dump GC and CC logs (from the main thread).
-      nsRefPtr<GCAndCCLogDumpRunnable> runnable =
-        new GCAndCCLogDumpRunnable(
-            /* identifier = */ EmptyString(),
-            /* allTraces = */ true,
-            /* dumpChildProcesses = */ true);
-      NS_DispatchToMainThread(runnable);
-    }
-    else {
-      LOG("SignalPipeWatcher got unexpected signum.");
-    }
-  }
-};
-
-class FifoWatcher : public FdWatcher
+void doGCCCDump(const nsCString& inputStr)
 {
-public:
-  FifoWatcher(nsCString aPath)
-    : mDirPath(aPath)
-    {}
-
-  static void MaybeCreate()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (XRE_GetProcessType() != GeckoProcessType_Default) {
-      // We want this to be main-process only, since two processes can't listen
-      // to the same fifo.
-      return;
-    }
-
-    if (!Preferences::GetBool("memory_info_dumper.watch_fifo.enabled", false)) {
-      LOG("Fifo watcher disabled via pref.");
-      return;
-    }
-
-    nsAutoCString dirPath;
-    Preferences::GetCString(
-      "memory_info_dumper.watch_fifo.directory", &dirPath);
-
-    // The FifoWatcher is held alive by the observer service.
-    nsRefPtr<FifoWatcher> fw = new FifoWatcher(dirPath);
-    fw->Init();
-  }
-
-  virtual int OpenFd()
-  {
-    // If the memory_info_dumper.directory pref is specified, put the fifo
-    // there.  Otherwise, put it into the system's tmp directory.
-
-    nsCOMPtr<nsIFile> file;
-
-    nsresult rv;
-    if (mDirPath.Length() > 0) {
-      rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
-      if (NS_FAILED(rv)) {
-        LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
-        return -1;
-      }
-    } else {
-      rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
-      if (NS_WARN_IF(NS_FAILED(rv)))
-        return -1;
-    }
-
-    rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
-    if (NS_WARN_IF(NS_FAILED(rv)))
-      return -1;
-
-    nsAutoCString path;
-    rv = file->GetNativePath(path);
-    if (NS_WARN_IF(NS_FAILED(rv)))
-      return -1;
-
-    // unlink might fail because the file doesn't exist, or for other reasons.
-    // But we don't care it fails; any problems will be detected later, when we
-    // try to mkfifo or open the file.
-    if (unlink(path.get())) {
-      LOG("FifoWatcher::OpenFifo unlink failed; errno=%d.  "
-          "Continuing despite error.", errno);
-    }
+  bool doAllTracesGCCCDump = inputStr == NS_LITERAL_CSTRING("gc log");
+  LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", inputStr.get());
+  nsRefPtr<GCAndCCLogDumpRunnable> runnable =
+    new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
+                              doAllTracesGCCCDump,
+                              /* dumpChildProcesses = */ true);
+  NS_DispatchToMainThread(runnable);
+}
 
-    if (mkfifo(path.get(), 0766)) {
-      LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
-      return -1;
-    }
-
-#ifdef ANDROID
-    // Android runs with a umask, so we need to chmod our fifo to make it
-    // world-writable.
-    chmod(path.get(), 0666);
-#endif
-
-    int fd;
-    do {
-      // The fifo will block until someone else has written to it.  In
-      // particular, open() will block until someone else has opened it for
-      // writing!  We want open() to succeed and read() to block, so we open
-      // with NONBLOCK and then fcntl that away.
-      fd = open(path.get(), O_RDONLY | O_NONBLOCK);
-    } while (fd == -1 && errno == EINTR);
-
-    if (fd == -1) {
-      LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
-      return -1;
-    }
-
-    // Make fd blocking now that we've opened it.
-    if (fcntl(fd, F_SETFL, 0)) {
-      close(fd);
-      return -1;
-    }
-
-    return fd;
-  }
-
-  virtual void OnFileCanReadWithoutBlocking(int aFd)
-  {
-    MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
-
-    char buf[1024];
-    int nread;
-    do {
-      // sizeof(buf) - 1 to leave space for the null-terminator.
-      nread = read(aFd, buf, sizeof(buf));
-    } while(nread == -1 && errno == EINTR);
-
-    if (nread == -1) {
-      // We want to avoid getting into a situation where
-      // OnFileCanReadWithoutBlocking is called in an infinite loop, so when
-      // something goes wrong, stop watching the fifo altogether.
-      LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
-      StopWatching();
-      return;
-    }
-
-    if (nread == 0) {
-      // If we get EOF, that means that the other side closed the fifo.  We need
-      // to close and re-open the fifo; if we don't,
-      // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
-
-      LOG("FifoWatcher closing and re-opening fifo.");
-      StopWatching();
-      StartWatching();
-      return;
-    }
-
-    nsAutoCString inputStr;
-    inputStr.Append(buf, nread);
-
-    // Trimming whitespace is important because if you do
-    //   |echo "foo" >> debug_info_trigger|,
-    // it'll actually write "foo\n" to the fifo.
-    inputStr.Trim("\b\t\r\n");
-
-    bool doMemoryReport = inputStr == NS_LITERAL_CSTRING("memory report");
-    bool doMMUMemoryReport = inputStr == NS_LITERAL_CSTRING("minimize memory report");
-    bool doAllTracesGCCCDump = inputStr == NS_LITERAL_CSTRING("gc log");
-    bool doSmallGCCCDump = inputStr == NS_LITERAL_CSTRING("abbreviated gc log");
-
-    if (doMemoryReport || doMMUMemoryReport) {
-      LOG("FifoWatcher dispatching memory report runnable.");
-      nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
-        new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
-                                            doMMUMemoryReport);
-      NS_DispatchToMainThread(runnable);
-    } else if (doAllTracesGCCCDump || doSmallGCCCDump) {
-      LOG("FifoWatcher dispatching GC/CC log runnable.");
-      nsRefPtr<GCAndCCLogDumpRunnable> runnable =
-        new GCAndCCLogDumpRunnable(
-            /* identifier = */ EmptyString(),
-            doAllTracesGCCCDump,
-            /* dumpChildProcesses = */ true);
-      NS_DispatchToMainThread(runnable);
-    } else {
-      LOG("Got unexpected value from fifo; ignoring it.");
-    }
-  }
-
-private:
-  nsAutoCString mDirPath;
-};
+void doGCCCDump(const uint8_t recvSig)
+{
+  LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", recvSig);
+  // Dump GC and CC logs (from the main thread).
+  nsRefPtr<GCAndCCLogDumpRunnable> runnable =
+    new GCAndCCLogDumpRunnable(
+        /* identifier = */ EmptyString(),
+        /* allTraces = */ true,
+        /* dumpChildProcesses = */ true);
+  NS_DispatchToMainThread(runnable);
+}
 
 } // anonymous namespace
 #endif // XP_LINUX }
 
 NS_IMPL_ISUPPORTS1(nsMemoryInfoDumper, nsIMemoryInfoDumper)
 
 nsMemoryInfoDumper::nsMemoryInfoDumper()
 {
@@ -552,18 +179,41 @@ nsMemoryInfoDumper::nsMemoryInfoDumper()
 nsMemoryInfoDumper::~nsMemoryInfoDumper()
 {
 }
 
 /* static */ void
 nsMemoryInfoDumper::Initialize()
 {
 #if defined(XP_LINUX) || defined(__FreeBSD__)
-  SignalPipeWatcher::Create();
-  FifoWatcher::MaybeCreate();
+  SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
+
+  // Dump memory reporters (and those of our child processes)
+  sDumpAboutMemorySignum = SIGRTMIN;
+  sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
+  // Dump our memory reporters after minimizing memory usage
+  sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
+  sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
+  // Dump the GC and CC logs in this and our child processes.
+  sGCAndCCDumpSignum = SIGRTMIN + 2;
+  sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
+
+  if (FifoWatcher::MaybeCreate()) {
+    FifoWatcher* fw = FifoWatcher::GetSingleton();
+    // Dump our memory reports (but run this on the main thread!).
+    fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
+                         doMemoryReport);
+    fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
+                         doMemoryReport);
+    // Dump GC and CC logs (from the main thread).
+    fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
+                         doGCCCDump);
+    fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
+                         doGCCCDump);
+  }
 #endif
 }
 
 static void
 EnsureNonEmptyIdentifier(nsAString& aIdentifier)
 {
   if (!aIdentifier.IsEmpty()) {
     return;
@@ -709,82 +359,16 @@ MakeFilename(const char *aPrefix, const 
              const char *aSuffix, nsACString &aResult)
 {
   aResult = nsPrintfCString("%s-%s-%d.%s",
                             aPrefix,
                             NS_ConvertUTF16toUTF8(aIdentifier).get(),
                             getpid(), aSuffix);
 }
 
-/* static */ nsresult
-nsMemoryInfoDumper::OpenTempFile(const nsACString &aFilename, nsIFile* *aFile)
-{
-#ifdef ANDROID
-  // For Android, first try the downloads directory which is world-readable
-  // rather than the temp directory which is not.
-  if (!*aFile) {
-    char *env = PR_GetEnv("DOWNLOADS_DIRECTORY");
-    if (env) {
-      NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
-    }
-  }
-#endif
-  nsresult rv;
-  if (!*aFile) {
-    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
-    if (NS_WARN_IF(NS_FAILED(rv)))
-      return rv;
-  }
-
-#ifdef ANDROID
-  // /data/local/tmp is a true tmp directory; anyone can create a file there,
-  // but only the user which created the file can remove it.  We want non-root
-  // users to be able to remove these files, so we write them into a
-  // subdirectory of the temp directory and chmod 777 that directory.
-  rv = (*aFile)->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
-  if (NS_WARN_IF(NS_FAILED(rv)))
-    return rv;
-
-  // It's OK if this fails; that probably just means that the directory already
-  // exists.
-  (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
-
-  nsAutoCString dirPath;
-  rv = (*aFile)->GetNativePath(dirPath);
-  if (NS_WARN_IF(NS_FAILED(rv)))
-    return rv;
-
-  while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {}
-#endif
-
-  nsCOMPtr<nsIFile> file(*aFile);
-
-  rv = file->AppendNative(aFilename);
-  if (NS_WARN_IF(NS_FAILED(rv)))
-    return rv;
-
-  rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
-  if (NS_WARN_IF(NS_FAILED(rv)))
-    return rv;
-
-#ifdef ANDROID
-    // Make this file world-read/writable; the permissions passed to the
-    // CreateUnique call above are not sufficient on Android, which runs with a
-    // umask.
-    nsAutoCString path;
-    rv = file->GetNativePath(path);
-    if (NS_WARN_IF(NS_FAILED(rv)))
-      return rv;
-
-    while (chmod(path.get(), 0666) == -1 && errno == EINTR) {}
-#endif
-
-  return NS_OK;
-}
-
 #ifdef MOZ_DMD
 struct DMDWriteState
 {
   static const size_t kBufSize = 4096;
   char mBuf[kBufSize];
   nsRefPtr<nsGZFileWriter> mGZWriter;
 
   DMDWriteState(nsGZFileWriter *aGZWriter)
@@ -890,19 +474,23 @@ nsMemoryInfoDumper::DumpMemoryInfoToTemp
   // processes and write out one file, rather than a separate file for
   // each process as was the case before bug 946407.  This is so that
   // the get_about_memory.py script in the B2G repository can
   // determine when it's done waiting for files to appear.
   MakeFilename("unified-memory-report", identifier, "json.gz", mrFilename);
 
   nsCOMPtr<nsIFile> mrTmpFile;
   nsresult rv;
-  rv = nsMemoryInfoDumper::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
-                                        mrFilename,
-                                        getter_AddRefs(mrTmpFile));
+  // In Android case, this function will open a file named aFilename under
+  // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
+  // open a file named aFilename under "NS_OS_TEMP_DIR".
+  rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
+                                 mrFilename,
+                                 getter_AddRefs(mrTmpFile),
+                                 NS_LITERAL_CSTRING("memory-reports"));
   if (NS_WARN_IF(NS_FAILED(rv)))
     return rv;
 
   nsRefPtr<nsGZFileWriter> mrWriter = new nsGZFileWriter();
   rv = mrWriter->Init(mrTmpFile);
   if (NS_WARN_IF(NS_FAILED(rv)))
     return rv;
 
@@ -936,17 +524,19 @@ nsMemoryInfoDumper::DumpDMD(const nsAStr
   MakeFilename("dmd", aIdentifier, "txt.gz", dmdFilename);
 
   // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
   // and dump DMD output to it.  This must occur after the memory reporters
   // have been run (above), but before the memory-reports file has been
   // renamed (so scripts can detect the DMD file, if present).
 
   nsCOMPtr<nsIFile> dmdFile;
-  rv = nsMemoryInfoDumper::OpenTempFile(dmdFilename, getter_AddRefs(dmdFile));
+  rv = nsDumpUtils::OpenTempFile(dmdFilename,
+                                 getter_AddRefs(dmdFile),
+                                 NS_LITERAL_CSTRING("memory-reports"));
   if (NS_WARN_IF(NS_FAILED(rv)))
     return rv;
 
   nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
   rv = dmdWriter->Init(dmdFile);
   if (NS_WARN_IF(NS_FAILED(rv)))
     return rv;
 
--- a/xpcom/base/nsMemoryInfoDumper.h
+++ b/xpcom/base/nsMemoryInfoDumper.h
@@ -25,24 +25,16 @@ public:
   NS_DECL_NSIMEMORYINFODUMPER
 
   nsMemoryInfoDumper();
   virtual ~nsMemoryInfoDumper();
 
 public:
   static void Initialize();
 
-  /**
-   * This function creates a new unique file based on |aFilename| in a
-   * world-readable temp directory. This is the system temp directory
-   * or, in the case of Android, the downloads directory. If |aFile| is
-   * non-null, it is assumed to point to a folder, and that folder is used
-   * instead.
-   */
-  static nsresult OpenTempFile(const nsACString &aFilename, nsIFile* *aFile);
 #ifdef MOZ_DMD
   static nsresult DumpDMD(const nsAString &aIdentifier);
 #endif
 };
 
 #define NS_MEMORY_INFO_DUMPER_CID \
 { 0x00bd71fb, 0x7f09, 0x4ec3, \
 { 0x96, 0xaf, 0xa0, 0xb5, 0x22, 0xb7, 0x79, 0x69 } }