Bug 956961 - Open content processes' DMD log files in the parent process. r=njn
☠☠ backed out by 466138f414d8 ☠ ☠
authorJed Davis <jld@mozilla.com>
Wed, 02 Jul 2014 11:27:48 -0700
changeset 212808 272b01e4f856261c6c54e031402a34e6a785d74c
parent 212807 56907af18c66f83a53a045368b75bb83fe9e3f08
child 212809 f1be89cb58b92f458c14f5fe5b3670d56cb6ea6e
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs956961
milestone33.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 956961 - Open content processes' DMD log files in the parent process. r=njn
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
xpcom/base/nsGZFileWriter.cpp
xpcom/base/nsIGZFileWriter.idl
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryInfoDumper.cpp
xpcom/base/nsMemoryInfoDumper.h
xpcom/base/nsMemoryReporterManager.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -54,16 +54,17 @@
 #include "nsICycleCollectorListener.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMutable.h"
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsMemoryInfoDumper.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsXULAppAPI.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsJSEnvironment.h"
 #include "SandboxHal.h"
 #include "nsDebugImpl.h"
@@ -187,32 +188,32 @@ namespace dom {
 
 class MemoryReportRequestChild : public PMemoryReportRequestChild,
                                  public nsIRunnable
 {
 public:
     NS_DECL_ISUPPORTS
 
     MemoryReportRequestChild(uint32_t aGeneration, bool aAnonymize,
-                             const nsAString& aDMDDumpIdent);
+                             const FileDescriptor& aDMDFile);
     NS_IMETHOD Run();
 private:
     virtual ~MemoryReportRequestChild();
 
     uint32_t mGeneration;
     bool     mAnonymize;
-    nsString mDMDDumpIdent;
+    FileDescriptor mDMDFile;
 };
 
 NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
 
 MemoryReportRequestChild::MemoryReportRequestChild(
-    uint32_t aGeneration, bool aAnonymize, const nsAString& aDMDDumpIdent)
+    uint32_t aGeneration, bool aAnonymize, const FileDescriptor& aDMDFile)
   : mGeneration(aGeneration), mAnonymize(aAnonymize),
-    mDMDDumpIdent(aDMDDumpIdent)
+    mDMDFile(aDMDFile)
 {
     MOZ_COUNT_CTOR(MemoryReportRequestChild);
 }
 
 MemoryReportRequestChild::~MemoryReportRequestChild()
 {
     MOZ_COUNT_DTOR(MemoryReportRequestChild);
 }
@@ -687,20 +688,20 @@ ContentChild::InitXPCOM()
 
     InitOnContentProcessCreated(/* aAfterNuwaFork = */false);
 }
 
 PMemoryReportRequestChild*
 ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
                                              const bool &aAnonymize,
                                              const bool &aMinimizeMemoryUsage,
-                                             const nsString& aDMDDumpIdent)
+                                             const FileDescriptor& aDMDFile)
 {
     MemoryReportRequestChild *actor =
-        new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDDumpIdent);
+        new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDFile);
     actor->AddRef();
     return actor;
 }
 
 // This is just a wrapper for InfallibleTArray<MemoryReport> that implements
 // nsISupports, so it can be passed to nsIMemoryReporter::CollectReports.
 class MemoryReportsWrapper MOZ_FINAL : public nsISupports {
     ~MemoryReportsWrapper() {}
@@ -745,17 +746,17 @@ NS_IMPL_ISUPPORTS(
 )
 
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(
     PMemoryReportRequestChild* aChild,
     const uint32_t& aGeneration,
     const bool& aAnonymize,
     const bool& aMinimizeMemoryUsage,
-    const nsString& aDMDDumpIdent)
+    const FileDescriptor& aDMDFile)
 {
     MemoryReportRequestChild *actor =
         static_cast<MemoryReportRequestChild*>(aChild);
     nsresult rv;
 
     if (aMinimizeMemoryUsage) {
         nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
         rv = mgr->MinimizeMemoryUsage(actor);
@@ -779,17 +780,17 @@ NS_IMETHODIMP MemoryReportRequestChild::
     child->AppendProcessId(process);
 
     // Run the reporters.  The callback will turn each measurement into a
     // MemoryReport.
     nsRefPtr<MemoryReportsWrapper> wrappedReports =
         new MemoryReportsWrapper(&reports);
     nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
     mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mAnonymize,
-                                          mDMDDumpIdent);
+                                          FileDescriptorToFILE(mDMDFile, "wb"));
 
     bool sent = Send__delete__(this, mGeneration, reports);
     return sent ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
 ContentChild::RecvAudioChannelNotify()
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -150,26 +150,26 @@ public:
 
     virtual PIndexedDBChild* AllocPIndexedDBChild() MOZ_OVERRIDE;
     virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor) MOZ_OVERRIDE;
 
     virtual PMemoryReportRequestChild*
     AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
                                    const bool& aAnonymize,
                                    const bool& aMinimizeMemoryUsage,
-                                   const nsString& aDMDDumpIdent) MOZ_OVERRIDE;
+                                   const FileDescriptor& aDMDFile) MOZ_OVERRIDE;
     virtual bool
     DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) MOZ_OVERRIDE;
 
     virtual bool
     RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* aChild,
                                         const uint32_t& aGeneration,
                                         const bool& aAnonymize,
                                         const bool &aMinimizeMemoryUsage,
-                                        const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
+                                        const FileDescriptor &aDMDFile) MOZ_OVERRIDE;
 
     virtual PCycleCollectWithLogsChild*
     AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
                                     const FileDescriptor& aGCLog,
                                     const FileDescriptor& aCCLog) MOZ_OVERRIDE;
     virtual bool
     DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor) MOZ_OVERRIDE;
     virtual bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -98,16 +98,17 @@
 #include "nsIPresShell.h"
 #include "nsIRemoteBlob.h"
 #include "nsIScriptError.h"
 #include "nsIStyleSheet.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIURIFixup.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULRuntime.h"
+#include "nsMemoryInfoDumper.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsWidgetsCID.h"
 #include "PreallocatedProcessManager.h"
 #include "ProcessPriorityManager.h"
@@ -2452,19 +2453,32 @@ ContentParent::Observe(nsISupports* aSub
                        "generation=%x anonymize=%d minimize=%d DMDident=%n",
                        &generation, &anonymize, &minimize, &identOffset) < 3
                 || identOffset < 0) {
                 return NS_ERROR_INVALID_ARG;
             }
             // The pre-%n part of the string should be all ASCII, so the byte
             // offset in identOffset should be correct as a char offset.
             MOZ_ASSERT(cmsg[identOffset - 1] == '=');
+            FileDescriptor dmdFileDesc;
+#ifdef MOZ_DMD
+            FILE *dmdFile;
+            nsAutoString dmdIdent(Substring(msg, identOffset));
+            nsresult rv = nsMemoryInfoDumper::OpenDMDFile(dmdIdent, Pid(), &dmdFile);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+                // Proceed with the memory report as if DMD were disabled.
+                dmdFile = nullptr;
+            }
+            if (dmdFile) {
+                dmdFileDesc = FILEToFileDescriptor(dmdFile);
+                fclose(dmdFile);
+            }
+#endif
             unused << SendPMemoryReportRequestConstructor(
-              generation, anonymize, minimize,
-              nsString(Substring(msg, identOffset)));
+              generation, anonymize, minimize, dmdFileDesc);
         }
     }
     else if (!strcmp(aTopic, "child-gc-request")){
         unused << SendGarbageCollect();
     }
     else if (!strcmp(aTopic, "child-cc-request")){
         unused << SendCycleCollect();
     }
@@ -2799,17 +2813,17 @@ ContentParent::RecvPIndexedDBConstructor
 
     return true;
 }
 
 PMemoryReportRequestParent*
 ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                                const bool &aAnonymize,
                                                const bool &aMinimizeMemoryUsage,
-                                               const nsString &aDMDDumpIdent)
+                                               const FileDescriptor &aDMDFile)
 {
     MemoryReportRequestParent* parent = new MemoryReportRequestParent();
     return parent;
 }
 
 bool
 ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -417,17 +417,17 @@ private:
     virtual bool DeallocPHalParent(PHalParent*) MOZ_OVERRIDE;
 
     virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor) MOZ_OVERRIDE;
 
     virtual PMemoryReportRequestParent*
     AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                     const bool &aAnonymize,
                                     const bool &aMinimizeMemoryUsage,
-                                    const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
+                                    const FileDescriptor &aDMDFile) MOZ_OVERRIDE;
     virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) MOZ_OVERRIDE;
 
     virtual PCycleCollectWithLogsParent*
     AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
                                      const FileDescriptor& aGCLog,
                                      const FileDescriptor& aCCLog) MOZ_OVERRIDE;
     virtual bool
     DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) MOZ_OVERRIDE;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -347,17 +347,17 @@ child:
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox();
 
     PMemoryReportRequest(uint32_t generation, bool anonymize,
-                         bool minimizeMemoryUsage, nsString DMDDumpIdent);
+                         bool minimizeMemoryUsage, FileDescriptor DMDFile);
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     async SpeakerManagerNotify();
 
--- a/xpcom/base/nsGZFileWriter.cpp
+++ b/xpcom/base/nsGZFileWriter.cpp
@@ -42,19 +42,24 @@ nsGZFileWriter::Init(nsIFile* aFile)
   // Get a FILE out of our nsIFile.  Convert that into a file descriptor which
   // gzip can own.  Then close our FILE, leaving only gzip's fd open.
 
   FILE* file;
   nsresult rv = aFile->OpenANSIFileDesc("wb", &file);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+  return InitANSIFileDesc(file);
+}
 
-  mGZFile = gzdopen(dup(fileno(file)), "wb");
-  fclose(file);
+NS_IMETHODIMP
+nsGZFileWriter::InitANSIFileDesc(FILE* aFile)
+{
+  mGZFile = gzdopen(dup(fileno(aFile)), "wb");
+  fclose(aFile);
 
   // gzdopen returns nullptr on error.
   if (NS_WARN_IF(!mGZFile)) {
     return NS_ERROR_FAILURE;
   }
 
   mInitialized = true;
 
--- a/xpcom/base/nsIGZFileWriter.idl
+++ b/xpcom/base/nsIGZFileWriter.idl
@@ -3,43 +3,51 @@
 /* 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 "nsISupports.idl"
 
 %{C++
 #include "nsDependentString.h"
+#include <stdio.h>
 %}
 
 interface nsIFile;
+[ptr] native FILE(FILE);
 
 /**
  * A simple interface for writing to a .gz file.
  *
  * Note that the file that this interface produces has a different format than
  * what you'd get if you compressed your data as a gzip stream and dumped the
  * result to a file.
  *
  * The standard gunzip tool cannot decompress a raw gzip stream, but can handle
  * the files produced by this interface.
  */
-[scriptable, uuid(a256f26a-c603-459e-b5a4-53b4877f2cd8)]
+[scriptable, uuid(6bd5642c-1b90-4499-ba4b-199f27efaba5)]
 interface nsIGZFileWriter : nsISupports
 {
   /**
    * Initialize this object.  We'll write our gzip'ed data to the given file,
    * overwriting its contents if the file exists.
    *
    * init() will return an error if called twice.  It's an error to call any
    * other method on this interface without first calling init().
    */
   void init(in nsIFile file);
 
   /**
+   * Alternate version of init() for use when the file is already opened;
+   * e.g., with a FileDescriptor passed over IPC.
+   */
+  [noscript] void initANSIFileDesc(in FILE file);
+
+  /**
    * Write the given string to the file.
    */
   void write(in AUTF8String str);
 
   /*
    * The following two overloads of Write() are C++ because we can't overload
    * methods in XPIDL.  Anyway, they don't add much functionality for JS
    * callers.
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -1,19 +1,23 @@
 /* -*- 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 "nsISupports.idl"
+%{C++
+#include <stdio.h>
+%}
 
 interface nsIDOMWindow;
 interface nsIRunnable;
 interface nsISimpleEnumerator;
+[ptr] native FILE(FILE);
 
 /*
  * Memory reporters measure Firefox's memory usage.  They are primarily used to
  * generate the about:memory page.  You should read
  * https://wiki.mozilla.org/Memory_Reporting before writing a memory
  * reporter.
  */
 
@@ -196,17 +200,17 @@ interface nsIMemoryReporter : nsISupport
 };
 
 [scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
 interface nsIFinishReportingCallback : nsISupports
 {
   void callback(in nsISupports data);
 };
 
-[scriptable, builtinclass, uuid(c27f8662-a0b7-45b3-8207-14d66b02b9c5)]
+[scriptable, builtinclass, uuid(51e17609-e98a-47cc-9f95-095ef3c3823e)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Initialize.
    */
   void init();
 
   /*
@@ -289,25 +293,24 @@ interface nsIMemoryReporterManager : nsI
    * Get memory reports in the current process only.  |handleReport| is called
    * for each report.
    */
   void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport,
                                 in nsISupports handleReportData,
                                 in boolean anonymize);
 
   /*
-   * As above, but if DMD is enabled and |DMDDumpIdent| is non-empty
-   * then write a DMD report to a file in the usual temporary directory (see
-   * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
+   * As above, but if DMD is enabled and |DMDFile| is non-null then
+   * write a DMD report to that file and close it.
    */
   [noscript] void
     getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
                                      in nsISupports handleReportData,
                                      in boolean anonymize,
-                                     in AString DMDDumpIdent);
+                                     in FILE DMDFile);
 
   /*
    * The memory reporter manager, for the most part, treats reporters
    * registered with it as a black box.  However, there are some
    * "distinguished" amounts (as could be reported by a memory reporter) that
    * the manager provides as attributes, because they are sufficiently
    * interesting that we want external code (e.g. telemetry) to be able to rely
    * on them.
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -503,22 +503,22 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(DumpReportCallback, nsIHandleReportCallback)
 
 } // namespace mozilla
 
 static void
 MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
-             const char* aSuffix, nsACString& aResult)
+             int aPid, const char* aSuffix, nsACString& aResult)
 {
   aResult = nsPrintfCString("%s-%s-%d.%s",
                             aPrefix,
                             NS_ConvertUTF16toUTF8(aIdentifier).get(),
-                            getpid(), aSuffix);
+                            aPid, aSuffix);
 }
 
 #ifdef MOZ_DMD
 struct DMDWriteState
 {
   static const size_t kBufSize = 4096;
   char mBuf[kBufSize];
   nsRefPtr<nsGZFileWriter> mGZWriter;
@@ -628,17 +628,18 @@ nsMemoryInfoDumper::DumpMemoryInfoToTemp
   // Note that |mrFilename| is missing the "incomplete-" prefix; we'll tack
   // that on in a moment.
   nsCString mrFilename;
   // The "unified" indicates that we merge the memory reports from all
   // 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);
+  MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
+               mrFilename);
 
   nsCOMPtr<nsIFile> mrTmpFile;
   nsresult rv;
   // 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,
@@ -671,58 +672,80 @@ nsMemoryInfoDumper::DumpMemoryInfoToTemp
                                aAnonymize,
                                aMinimizeMemoryUsage,
                                identifier);
   return rv;
 }
 
 #ifdef MOZ_DMD
 nsresult
-nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
+nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
+                                FILE** aOutFile)
 {
   if (!dmd::IsRunning()) {
+    *aOutFile = nullptr;
     return NS_OK;
   }
 
-  nsresult rv;
-
   // Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
   // if DMD is enabled.
   nsCString dmdFilename;
-  MakeFilename("dmd", aIdentifier, "txt.gz", dmdFilename);
+  MakeFilename("dmd", aIdentifier, aPid, "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).
 
+  nsresult rv;
   nsCOMPtr<nsIFile> dmdFile;
   rv = nsDumpUtils::OpenTempFile(dmdFilename,
                                  getter_AddRefs(dmdFile),
                                  NS_LITERAL_CSTRING("memory-reports"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+  rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
+  NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+}
 
+nsresult
+nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
+{
   nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
-  rv = dmdWriter->Init(dmdFile);
+  nsresult rv = dmdWriter->InitANSIFileDesc(aFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Dump DMD output to the file.
-
   DMDWriteState state(dmdWriter);
   dmd::Writer w(DMDWrite, &state);
   dmd::Dump(w);
 
   rv = dmdWriter->Finish();
   NS_WARN_IF(NS_FAILED(rv));
   return rv;
 }
+
+nsresult
+nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
+{
+  nsresult rv;
+  FILE* dmdFile;
+  rv = OpenDMDFile(aIdentifier, getpid(), &dmdFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!dmdFile) {
+    return NS_OK;
+  }
+  return DumpDMDToFile(dmdFile);
+}
 #endif  // MOZ_DMD
 
 NS_IMETHODIMP
 TempDirMemoryFinishCallback::Callback(nsISupports* aData)
 {
   nsresult rv = DumpFooter(mrWriter);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
--- a/xpcom/base/nsMemoryInfoDumper.h
+++ b/xpcom/base/nsMemoryInfoDumper.h
@@ -3,16 +3,17 @@
 /* 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_nsMemoryInfoDumper_h
 #define mozilla_nsMemoryInfoDumper_h
 
 #include "nsIMemoryInfoDumper.h"
+#include <stdio.h>
 
 class nsACString;
 
 /**
  * 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
@@ -26,17 +27,24 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYINFODUMPER
 
   nsMemoryInfoDumper();
 
   static void Initialize();
 
 #ifdef MOZ_DMD
+  // Write a DMD report.
   static nsresult DumpDMD(const nsAString& aIdentifier);
+  // Open an appropriately named file for a DMD report.  If DMD is
+  // disabled, return a null FILE* instead.
+  static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid,
+                              FILE** aOutFile);
+  // Write a DMD report to the given file and close it.
+  static nsresult DumpDMDToFile(FILE* aFile);
 #endif
 };
 
 #define NS_MEMORY_INFO_DUMPER_CID \
 { 0x00bd71fb, 0x7f09, 0x4ec3, \
 { 0x96, 0xaf, 0xa0, 0xb5, 0x22, 0xb7, 0x79, 0x69 } }
 
 #endif
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1099,18 +1099,27 @@ nsMemoryReporterManager::GetReportsExten
 }
 
 nsresult
 nsMemoryReporterManager::StartGettingReports()
 {
   GetReportsState* s = mGetReportsState;
 
   // Get reports for this process.
+  FILE *parentDMDFile = nullptr;
+#ifdef MOZ_DMD
+  nsresult rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
+                                                &parentDMDFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    // Proceed with the memory report as if DMD were disabled.
+    parentDMDFile = nullptr;
+  }
+#endif
   GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
-                                   s->mAnonymize, s->mDMDDumpIdent);
+                                   s->mAnonymize, parentDMDFile);
   s->mParentDone = true;
 
   // If there are no remaining child processes, we can finish up immediately.
   return (s->mNumChildProcessesCompleted >= s->mNumChildProcesses)
     ? FinishReporting()
     : NS_OK;
 }
 
@@ -1133,52 +1142,54 @@ WeakEnumerator(nsPtrHashKey<nsIMemoryRep
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsForThisProcess(
   nsIHandleReportCallback* aHandleReport,
   nsISupports* aHandleReportData, bool aAnonymize)
 {
   return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
-                                          aAnonymize, nsString());
+                                          aAnonymize, nullptr);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsForThisProcessExtended(
   nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
-  bool aAnonymize, const nsAString& aDMDDumpIdent)
+  bool aAnonymize, FILE* aDMDFile)
 {
   // Memory reporters are not necessarily threadsafe, so this function must
   // be called from the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
   }
 
 #ifdef MOZ_DMD
-  if (!aDMDDumpIdent.IsEmpty()) {
+  if (aDMDFile) {
     // Clear DMD's reportedness state before running the memory
     // reporters, to avoid spurious twice-reported warnings.
     dmd::ClearReports();
   }
+#else
+  MOZ_ASSERT(!aDMDFile);
 #endif
 
   MemoryReporterArray allReporters;
   {
     mozilla::MutexAutoLock autoLock(mMutex);
     mStrongReporters->EnumerateEntries(StrongEnumerator, &allReporters);
     mWeakReporters->EnumerateEntries(WeakEnumerator, &allReporters);
   }
   for (uint32_t i = 0; i < allReporters.Length(); i++) {
     allReporters[i]->CollectReports(aHandleReport, aHandleReportData,
                                     aAnonymize);
   }
 
 #ifdef MOZ_DMD
-  if (!aDMDDumpIdent.IsEmpty()) {
-    return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
+  if (aDMDFile) {
+    return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
   }
 #endif
 
   return NS_OK;
 }
 
 // This function has no return value.  If something goes wrong, there's no
 // clear place to report the problem to, but that's ok -- we will end up