xpcom/base/nsMemoryInfoDumper.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/nsMemoryInfoDumper.h"
     8 #include "nsDumpUtils.h"
    10 #include "mozilla/unused.h"
    11 #include "mozilla/dom/ContentParent.h"
    12 #include "mozilla/dom/ContentChild.h"
    13 #include "nsIConsoleService.h"
    14 #include "nsICycleCollectorListener.h"
    15 #include "nsIMemoryReporter.h"
    16 #include "nsDirectoryServiceDefs.h"
    17 #include "nsGZFileWriter.h"
    18 #include "nsJSEnvironment.h"
    19 #include "nsPrintfCString.h"
    20 #include "nsISimpleEnumerator.h"
    21 #include "nsServiceManagerUtils.h"
    22 #include "nsIFile.h"
    24 #ifdef XP_WIN
    25 #include <process.h>
    26 #define getpid _getpid
    27 #else
    28 #include <unistd.h>
    29 #endif
    31 #ifdef XP_UNIX
    32 #define MOZ_SUPPORTS_FIFO 1
    33 #endif
    35 #if defined(XP_LINUX) || defined(__FreeBSD__)
    36 #define MOZ_SUPPORTS_RT_SIGNALS 1
    37 #endif
    39 #if defined(MOZ_SUPPORTS_RT_SIGNALS)
    40 #include <fcntl.h>
    41 #include <sys/types.h>
    42 #include <sys/stat.h>
    43 #endif
    45 #if defined(MOZ_SUPPORTS_FIFO)
    46 #include "mozilla/Preferences.h"
    47 #endif
    49 using namespace mozilla;
    50 using namespace mozilla::dom;
    52 namespace {
    54 class DumpMemoryInfoToTempDirRunnable : public nsRunnable
    55 {
    56 public:
    57   DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
    58                                   bool aMinimizeMemoryUsage)
    59       : mIdentifier(aIdentifier)
    60       , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
    61   {}
    63   NS_IMETHOD Run()
    64   {
    65     nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
    66     dumper->DumpMemoryInfoToTempDir(mIdentifier, mMinimizeMemoryUsage);
    67     return NS_OK;
    68   }
    70 private:
    71   const nsString mIdentifier;
    72   const bool mMinimizeMemoryUsage;
    73 };
    75 class GCAndCCLogDumpRunnable : public nsRunnable
    76 {
    77 public:
    78   GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
    79                          bool aDumpAllTraces,
    80                          bool aDumpChildProcesses)
    81     : mIdentifier(aIdentifier)
    82     , mDumpAllTraces(aDumpAllTraces)
    83     , mDumpChildProcesses(aDumpChildProcesses)
    84   {}
    86   NS_IMETHOD Run()
    87   {
    88     nsCOMPtr<nsIMemoryInfoDumper> dumper =
    89       do_GetService("@mozilla.org/memory-info-dumper;1");
    91     nsString ccLogPath, gcLogPath;
    92     dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
    93                                   mDumpChildProcesses, gcLogPath, ccLogPath);
    94     return NS_OK;
    95   }
    97 private:
    98   const nsString mIdentifier;
    99   const bool mDumpAllTraces;
   100   const bool mDumpChildProcesses;
   101 };
   103 } // anonymous namespace
   105 #if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
   106 namespace {
   108 /*
   109  * The following code supports dumping about:memory upon receiving a signal.
   110  *
   111  * We listen for the following signals:
   112  *
   113  *  - SIGRTMIN:     Dump our memory reporters (and those of our child
   114  *                  processes),
   115  *  - SIGRTMIN + 1: Dump our memory reporters (and those of our child
   116  *                  processes) after minimizing memory usage, and
   117  *  - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
   118  *
   119  * When we receive one of these signals, we write the signal number to a pipe.
   120  * The IO thread then notices that the pipe has been written to, and kicks off
   121  * the appropriate task on the main thread.
   122  *
   123  * This scheme is similar to using signalfd(), except it's portable and it
   124  * doesn't require the use of sigprocmask, which is problematic because it
   125  * masks signals received by child processes.
   126  *
   127  * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
   128  * But that uses libevent, which does not handle the realtime signals (bug
   129  * 794074).
   130  */
   132 // It turns out that at least on some systems, SIGRTMIN is not a compile-time
   133 // constant, so these have to be set at runtime.
   134 static uint8_t sDumpAboutMemorySignum;         // SIGRTMIN
   135 static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
   136 static uint8_t sGCAndCCDumpSignum;             // SIGRTMIN + 2
   138 void doMemoryReport(const uint8_t recvSig)
   139 {
   140   // Dump our memory reports (but run this on the main thread!).
   141   bool doMMUFirst = recvSig == sDumpAboutMemoryAfterMMUSignum;
   142   LOG("SignalWatcher(sig %d) dispatching memory report runnable.", recvSig);
   143   nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
   144     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
   145                                         doMMUFirst);
   146   NS_DispatchToMainThread(runnable);
   147 }
   149 void doGCCCDump(const uint8_t recvSig)
   150 {
   151   LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", recvSig);
   152   // Dump GC and CC logs (from the main thread).
   153   nsRefPtr<GCAndCCLogDumpRunnable> runnable =
   154     new GCAndCCLogDumpRunnable(
   155         /* identifier = */ EmptyString(),
   156         /* allTraces = */ true,
   157         /* dumpChildProcesses = */ true);
   158   NS_DispatchToMainThread(runnable);
   159 }
   161 } // anonymous namespace
   162 #endif // MOZ_SUPPORTS_RT_SIGNALS }
   164 #if defined(MOZ_SUPPORTS_FIFO) // {
   165 namespace {
   167 void doMemoryReport(const nsCString& inputStr)
   168 {
   169   bool doMMUMemoryReport = inputStr == NS_LITERAL_CSTRING("minimize memory report");
   170   LOG("FifoWatcher(command:%s) dispatching memory report runnable.", inputStr.get());
   171   nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
   172     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
   173                                         doMMUMemoryReport);
   174   NS_DispatchToMainThread(runnable);
   175 }
   177 void doGCCCDump(const nsCString& inputStr)
   178 {
   179   bool doAllTracesGCCCDump = inputStr == NS_LITERAL_CSTRING("gc log");
   180   LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", inputStr.get());
   181   nsRefPtr<GCAndCCLogDumpRunnable> runnable =
   182     new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
   183                               doAllTracesGCCCDump,
   184                               /* dumpChildProcesses = */ true);
   185   NS_DispatchToMainThread(runnable);
   186 }
   188 bool SetupFifo()
   189 {
   190   static bool fifoCallbacksRegistered = false;
   192   if (!FifoWatcher::MaybeCreate()) {
   193     return false;
   194   }
   196   MOZ_ASSERT(!fifoCallbacksRegistered,
   197              "FifoWatcher callbacks should be registered only once");
   199   FifoWatcher* fw = FifoWatcher::GetSingleton();
   200   // Dump our memory reports (but run this on the main thread!).
   201   fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
   202                        doMemoryReport);
   203   fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
   204                        doMemoryReport);
   205   // Dump GC and CC logs (from the main thread).
   206   fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
   207                        doGCCCDump);
   208   fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
   209                        doGCCCDump);
   211   fifoCallbacksRegistered = true;
   212   return true;
   213 }
   215 void OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
   216 {
   217   LOG("%s changed", FifoWatcher::kPrefName);
   218   if (SetupFifo()) {
   219     Preferences::UnregisterCallback(OnFifoEnabledChange,
   220                                     FifoWatcher::kPrefName,
   221                                     nullptr);
   222   }
   223 }
   225 } // anonymous namespace
   226 #endif // MOZ_SUPPORTS_FIFO }
   228 NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
   230 nsMemoryInfoDumper::nsMemoryInfoDumper()
   231 {
   232 }
   234 nsMemoryInfoDumper::~nsMemoryInfoDumper()
   235 {
   236 }
   238 /* static */ void
   239 nsMemoryInfoDumper::Initialize()
   240 {
   241 #if defined(MOZ_SUPPORTS_RT_SIGNALS)
   242   SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
   244   // Dump memory reporters (and those of our child processes)
   245   sDumpAboutMemorySignum = SIGRTMIN;
   246   sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
   247   // Dump our memory reporters after minimizing memory usage
   248   sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
   249   sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
   250   // Dump the GC and CC logs in this and our child processes.
   251   sGCAndCCDumpSignum = SIGRTMIN + 2;
   252   sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
   253 #endif
   255 #if defined(MOZ_SUPPORTS_FIFO)
   256   if (!SetupFifo()) {
   257     // NB: This gets loaded early enough that it's possible there is a user pref
   258     //     set to enable the fifo watcher that has not been loaded yet. Register
   259     //     to attempt to initialize if the fifo watcher becomes enabled by
   260     //     a user pref.
   261     Preferences::RegisterCallback(OnFifoEnabledChange,
   262                                   FifoWatcher::kPrefName,
   263                                   nullptr);
   264   }
   265 #endif
   266 }
   268 static void
   269 EnsureNonEmptyIdentifier(nsAString& aIdentifier)
   270 {
   271   if (!aIdentifier.IsEmpty()) {
   272     return;
   273   }
   275   // If the identifier is empty, set it to the number of whole seconds since the
   276   // epoch.  This identifier will appear in the files that this process
   277   // generates and also the files generated by this process's children, allowing
   278   // us to identify which files are from the same memory report request.
   279   aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
   280 }
   282 NS_IMETHODIMP
   283 nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
   284                                           bool aDumpAllTraces,
   285                                           bool aDumpChildProcesses,
   286                                           nsAString& aGCLogPath,
   287                                           nsAString& aCCLogPath)
   288 {
   289   nsString identifier(aIdentifier);
   290   EnsureNonEmptyIdentifier(identifier);
   292   if (aDumpChildProcesses) {
   293     nsTArray<ContentParent*> children;
   294     ContentParent::GetAll(children);
   295     for (uint32_t i = 0; i < children.Length(); i++) {
   296       unused << children[i]->SendDumpGCAndCCLogsToFile(
   297         identifier, aDumpAllTraces, aDumpChildProcesses);
   298     }
   299   }
   301   nsCOMPtr<nsICycleCollectorListener> logger =
   302     do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
   303   logger->SetFilenameIdentifier(identifier);
   305   if (aDumpAllTraces) {
   306     nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
   307     logger->AllTraces(getter_AddRefs(allTracesLogger));
   308     logger = allTracesLogger;
   309   }
   311   nsJSContext::CycleCollectNow(logger);
   313   logger->GetGcLogPath(aGCLogPath);
   314   logger->GetCcLogPath(aCCLogPath);
   316   return NS_OK;
   317 }
   319 namespace mozilla {
   321 #define DUMP(o, s) \
   322   do { \
   323     nsresult rv = (o)->Write(s); \
   324     if (NS_WARN_IF(NS_FAILED(rv))) \
   325       return rv; \
   326   } while (0)
   328 class DumpReportCallback MOZ_FINAL : public nsIHandleReportCallback
   329 {
   330 public:
   331   NS_DECL_ISUPPORTS
   333   DumpReportCallback(nsGZFileWriter* aWriter)
   334     : mIsFirst(true)
   335     , mWriter(aWriter)
   336   {}
   338   NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
   339       int32_t aKind, int32_t aUnits, int64_t aAmount,
   340       const nsACString &aDescription,
   341       nsISupports *aData)
   342   {
   343     if (mIsFirst) {
   344       DUMP(mWriter, "[");
   345       mIsFirst = false;
   346     } else {
   347       DUMP(mWriter, ",");
   348     }
   350     nsAutoCString process;
   351     if (aProcess.IsEmpty()) {
   352       // If the process is empty, the report originated with the process doing
   353       // the dumping.  In that case, generate the process identifier, which is of
   354       // the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we don't
   355       // have a process name.  If we're the main process, we let $PROCESS_NAME be
   356       // "Main Process".
   357       if (XRE_GetProcessType() == GeckoProcessType_Default) {
   358         // We're the main process.
   359         process.AssignLiteral("Main Process");
   360       } else if (ContentChild *cc = ContentChild::GetSingleton()) {
   361         // Try to get the process name from ContentChild.
   362         cc->GetProcessName(process);
   363       }
   364       ContentChild::AppendProcessId(process);
   366     } else {
   367       // Otherwise, the report originated with another process and already has a
   368       // process name.  Just use that.
   369       process = aProcess;
   370     }
   372     DUMP(mWriter, "\n    {\"process\": \"");
   373     DUMP(mWriter, process);
   375     DUMP(mWriter, "\", \"path\": \"");
   376     nsCString path(aPath);
   377     path.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
   378     path.ReplaceSubstring("\"", "\\\"");    // " --> \"
   379     DUMP(mWriter, path);
   381     DUMP(mWriter, "\", \"kind\": ");
   382     DUMP(mWriter, nsPrintfCString("%d", aKind));
   384     DUMP(mWriter, ", \"units\": ");
   385     DUMP(mWriter, nsPrintfCString("%d", aUnits));
   387     DUMP(mWriter, ", \"amount\": ");
   388     DUMP(mWriter, nsPrintfCString("%lld", aAmount));
   390     nsCString description(aDescription);
   391     description.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
   392     description.ReplaceSubstring("\"", "\\\"");    // " --> \"
   393     description.ReplaceSubstring("\n", "\\n");     // <newline> --> \n
   394     DUMP(mWriter, ", \"description\": \"");
   395     DUMP(mWriter, description);
   396     DUMP(mWriter, "\"}");
   398     return NS_OK;
   399   }
   401 private:
   402   bool mIsFirst;
   403   nsRefPtr<nsGZFileWriter> mWriter;
   404 };
   406 NS_IMPL_ISUPPORTS(DumpReportCallback, nsIHandleReportCallback)
   408 } // namespace mozilla
   410 static void
   411 MakeFilename(const char *aPrefix, const nsAString &aIdentifier,
   412              const char *aSuffix, nsACString &aResult)
   413 {
   414   aResult = nsPrintfCString("%s-%s-%d.%s",
   415                             aPrefix,
   416                             NS_ConvertUTF16toUTF8(aIdentifier).get(),
   417                             getpid(), aSuffix);
   418 }
   420 #ifdef MOZ_DMD
   421 struct DMDWriteState
   422 {
   423   static const size_t kBufSize = 4096;
   424   char mBuf[kBufSize];
   425   nsRefPtr<nsGZFileWriter> mGZWriter;
   427   DMDWriteState(nsGZFileWriter *aGZWriter)
   428     : mGZWriter(aGZWriter)
   429   {}
   430 };
   432 static void
   433 DMDWrite(void* aState, const char* aFmt, va_list ap)
   434 {
   435   DMDWriteState *state = (DMDWriteState*)aState;
   436   vsnprintf(state->mBuf, state->kBufSize, aFmt, ap);
   437   unused << state->mGZWriter->Write(state->mBuf);
   438 }
   439 #endif
   441 static nsresult
   442 DumpHeader(nsIGZFileWriter* aWriter)
   443 {
   444   // Increment this number if the format changes.
   445   //
   446   // This is the first write to the file, and it causes |aWriter| to allocate
   447   // over 200 KiB of memory.
   448   //
   449   DUMP(aWriter, "{\n  \"version\": 1,\n");
   451   DUMP(aWriter, "  \"hasMozMallocUsableSize\": ");
   453   nsCOMPtr<nsIMemoryReporterManager> mgr =
   454     do_GetService("@mozilla.org/memory-reporter-manager;1");
   455   if (NS_WARN_IF(!mgr))
   456     return NS_ERROR_UNEXPECTED;
   458   DUMP(aWriter, mgr->GetHasMozMallocUsableSize() ? "true" : "false");
   459   DUMP(aWriter, ",\n");
   460   DUMP(aWriter, "  \"reports\": ");
   462   return NS_OK;
   463 }
   465 static nsresult
   466 DumpFooter(nsIGZFileWriter* aWriter)
   467 {
   468   DUMP(aWriter, "\n  ]\n}\n");
   470   return NS_OK;
   471 }
   473 class TempDirMemoryFinishCallback MOZ_FINAL : public nsIFinishReportingCallback
   474 {
   475 public:
   476   NS_DECL_ISUPPORTS
   478   TempDirMemoryFinishCallback(nsGZFileWriter *aWriter,
   479                               nsIFile *aTmpFile,
   480                               const nsCString &aFilename,
   481                               const nsString &aIdentifier)
   482     : mrWriter(aWriter)
   483     , mrTmpFile(aTmpFile)
   484     , mrFilename(aFilename)
   485     , mIdentifier(aIdentifier)
   486   {}
   488   NS_IMETHOD Callback(nsISupports *aData);
   490 private:
   491   nsRefPtr<nsGZFileWriter> mrWriter;
   492   nsCOMPtr<nsIFile> mrTmpFile;
   493   nsCString mrFilename;
   494   nsString mIdentifier;
   495 };
   497 NS_IMPL_ISUPPORTS(TempDirMemoryFinishCallback, nsIFinishReportingCallback)
   499 NS_IMETHODIMP
   500 nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
   501                                             bool aMinimizeMemoryUsage)
   502 {
   503   nsString identifier(aIdentifier);
   504   EnsureNonEmptyIdentifier(identifier);
   506 #ifdef MOZ_DMD
   507   // Clear DMD's reportedness state before running the memory reporters, to
   508   // avoid spurious twice-reported warnings.
   509   dmd::ClearReports();
   510 #endif
   512   // Open a new file named something like
   513   //
   514   //   incomplete-memory-report-<identifier>-<pid>.json.gz
   515   //
   516   // in NS_OS_TEMP_DIR for writing.  When we're finished writing the report,
   517   // we'll rename this file and get rid of the "incomplete-" prefix.
   518   //
   519   // We do this because we don't want scripts which poll the filesystem
   520   // looking for memory report dumps to grab a file before we're finished
   521   // writing to it.
   523   // Note that |mrFilename| is missing the "incomplete-" prefix; we'll tack
   524   // that on in a moment.
   525   nsCString mrFilename;
   526   // The "unified" indicates that we merge the memory reports from all
   527   // processes and write out one file, rather than a separate file for
   528   // each process as was the case before bug 946407.  This is so that
   529   // the get_about_memory.py script in the B2G repository can
   530   // determine when it's done waiting for files to appear.
   531   MakeFilename("unified-memory-report", identifier, "json.gz", mrFilename);
   533   nsCOMPtr<nsIFile> mrTmpFile;
   534   nsresult rv;
   535   // In Android case, this function will open a file named aFilename under
   536   // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
   537   // open a file named aFilename under "NS_OS_TEMP_DIR".
   538   rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
   539                                  mrFilename,
   540                                  getter_AddRefs(mrTmpFile),
   541                                  NS_LITERAL_CSTRING("memory-reports"));
   542   if (NS_WARN_IF(NS_FAILED(rv)))
   543     return rv;
   545   nsRefPtr<nsGZFileWriter> mrWriter = new nsGZFileWriter();
   546   rv = mrWriter->Init(mrTmpFile);
   547   if (NS_WARN_IF(NS_FAILED(rv)))
   548     return rv;
   550   // Dump the memory reports to the file.
   551   rv = DumpHeader(mrWriter);
   552   if (NS_WARN_IF(NS_FAILED(rv)))
   553     return rv;
   555   // Process reporters.
   556   nsCOMPtr<nsIMemoryReporterManager> mgr =
   557     do_GetService("@mozilla.org/memory-reporter-manager;1");
   558   nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   559   nsRefPtr<nsIFinishReportingCallback> finishReport =
   560     new TempDirMemoryFinishCallback(mrWriter, mrTmpFile, mrFilename, identifier);
   561   rv = mgr->GetReportsExtended(dumpReport, nullptr,
   562                                finishReport, nullptr,
   563                                aMinimizeMemoryUsage,
   564                                identifier);
   565   return rv;
   566 }
   568 #ifdef MOZ_DMD
   569 nsresult
   570 nsMemoryInfoDumper::DumpDMD(const nsAString &aIdentifier)
   571 {
   572   if (!dmd::IsEnabled()) {
   573     return NS_OK;
   574   }
   576   nsresult rv;
   578   // Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
   579   // if DMD is enabled.
   580   nsCString dmdFilename;
   581   MakeFilename("dmd", aIdentifier, "txt.gz", dmdFilename);
   583   // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
   584   // and dump DMD output to it.  This must occur after the memory reporters
   585   // have been run (above), but before the memory-reports file has been
   586   // renamed (so scripts can detect the DMD file, if present).
   588   nsCOMPtr<nsIFile> dmdFile;
   589   rv = nsDumpUtils::OpenTempFile(dmdFilename,
   590                                  getter_AddRefs(dmdFile),
   591                                  NS_LITERAL_CSTRING("memory-reports"));
   592   if (NS_WARN_IF(NS_FAILED(rv)))
   593     return rv;
   595   nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
   596   rv = dmdWriter->Init(dmdFile);
   597   if (NS_WARN_IF(NS_FAILED(rv)))
   598     return rv;
   600   // Dump DMD output to the file.
   602   DMDWriteState state(dmdWriter);
   603   dmd::Writer w(DMDWrite, &state);
   604   dmd::Dump(w);
   606   rv = dmdWriter->Finish();
   607   NS_WARN_IF(NS_FAILED(rv));
   608   return rv;
   609 }
   610 #endif  // MOZ_DMD
   612 NS_IMETHODIMP
   613 TempDirMemoryFinishCallback::Callback(nsISupports *aData)
   614 {
   615   nsresult rv;
   617   rv = DumpFooter(mrWriter);
   618   if (NS_WARN_IF(NS_FAILED(rv)))
   619     return rv;
   621   // The call to Finish() deallocates the memory allocated by mrWriter's first
   622   // DUMP() call (within DumpProcessMemoryReportsToGZFileWriter()).  Because
   623   // that memory was live while the memory reporters ran and thus measured by
   624   // them -- by "heap-allocated" if nothing else -- we want DMD to see it as
   625   // well.  So we deliberately don't call Finish() until after DMD finishes.
   626   rv = mrWriter->Finish();
   627   if (NS_WARN_IF(NS_FAILED(rv)))
   628     return rv;
   630   // Rename the memory reports file, now that we're done writing all the files.
   631   // Its final name is "memory-report<-identifier>-<pid>.json.gz".
   633   nsCOMPtr<nsIFile> mrFinalFile;
   634   rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mrFinalFile));
   635   if (NS_WARN_IF(NS_FAILED(rv)))
   636     return rv;
   638 #ifdef ANDROID
   639   rv = mrFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
   640   if (NS_WARN_IF(NS_FAILED(rv)))
   641     return rv;
   642 #endif
   644   rv = mrFinalFile->AppendNative(mrFilename);
   645   if (NS_WARN_IF(NS_FAILED(rv)))
   646     return rv;
   648   rv = mrFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   649   if (NS_WARN_IF(NS_FAILED(rv)))
   650     return rv;
   652   nsAutoString mrActualFinalFilename;
   653   rv = mrFinalFile->GetLeafName(mrActualFinalFilename);
   654   if (NS_WARN_IF(NS_FAILED(rv)))
   655     return rv;
   657   rv = mrTmpFile->MoveTo(/* directory */ nullptr, mrActualFinalFilename);
   658   if (NS_WARN_IF(NS_FAILED(rv)))
   659     return rv;
   661   // Write a message to the console.
   663   nsCOMPtr<nsIConsoleService> cs =
   664     do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
   665   if (NS_WARN_IF(NS_FAILED(rv)))
   666     return rv;
   668   nsString path;
   669   mrTmpFile->GetPath(path);
   670   if (NS_WARN_IF(NS_FAILED(rv)))
   671     return rv;
   673   nsString msg = NS_LITERAL_STRING(
   674     "nsIMemoryInfoDumper dumped reports to ");
   675   msg.Append(path);
   676   return cs->LogStringMessage(msg.get());
   677 }
   679 // This dumps the JSON footer and closes the file, and then calls the given
   680 // nsIFinishDumpingCallback.
   681 class FinishReportingCallback MOZ_FINAL : public nsIFinishReportingCallback
   682 {
   683 public:
   684   NS_DECL_ISUPPORTS
   686   FinishReportingCallback(nsIFinishDumpingCallback* aFinishDumping,
   687                           nsISupports* aFinishDumpingData)
   688     : mFinishDumping(aFinishDumping)
   689     , mFinishDumpingData(aFinishDumpingData)
   690   {}
   692   NS_IMETHOD Callback(nsISupports* aData)
   693   {
   694     nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
   695     NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
   697     nsresult rv = DumpFooter(writer);
   698     NS_ENSURE_SUCCESS(rv, rv);
   700     rv = writer->Finish();
   701     NS_ENSURE_SUCCESS(rv, rv);
   703     if (!mFinishDumping) {
   704       return NS_OK;
   705     }
   707     return mFinishDumping->Callback(mFinishDumpingData);
   708   }
   710 private:
   711   nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
   712   nsCOMPtr<nsISupports> mFinishDumpingData;
   713 };
   715 NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)
   717 NS_IMETHODIMP
   718 nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
   719   const nsAString& aFilename,
   720   nsIFinishDumpingCallback* aFinishDumping,
   721   nsISupports* aFinishDumpingData)
   722 {
   723   MOZ_ASSERT(!aFilename.IsEmpty());
   725   // Create the file.
   727   nsCOMPtr<nsIFile> mrFile;
   728   nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(mrFile));
   729   if (NS_WARN_IF(NS_FAILED(rv)))
   730     return rv;
   732   mrFile->InitWithPath(aFilename);
   733   if (NS_WARN_IF(NS_FAILED(rv)))
   734     return rv;
   736   bool exists;
   737   rv = mrFile->Exists(&exists);
   738   if (NS_WARN_IF(NS_FAILED(rv)))
   739     return rv;
   741   if (!exists) {
   742     rv = mrFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   743     if (NS_WARN_IF(NS_FAILED(rv)))
   744       return rv;
   745   }
   747   // Write the memory reports to the file.
   749   nsRefPtr<nsGZFileWriter> mrWriter = new nsGZFileWriter();
   750   rv = mrWriter->Init(mrFile);
   751   if (NS_WARN_IF(NS_FAILED(rv)))
   752     return rv;
   754   rv = DumpHeader(mrWriter);
   755   if (NS_WARN_IF(NS_FAILED(rv)))
   756     return rv;
   758   // Process reports and finish up.
   759   nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   760   nsRefPtr<FinishReportingCallback> finishReporting =
   761     new FinishReportingCallback(aFinishDumping, aFinishDumpingData);
   762   nsCOMPtr<nsIMemoryReporterManager> mgr =
   763     do_GetService("@mozilla.org/memory-reporter-manager;1");
   764   return mgr->GetReports(dumpReport, nullptr, finishReporting, mrWriter);
   765 }
   767 #undef DUMP

mercurial