LCOV - code coverage report
Current view: top level - xpcom/base - nsMemoryInfoDumper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 15 270 5.6 %
Date: 2017-07-14 16:53:18 Functions: 4 60 6.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "mozilla/JSONWriter.h"
       8             : #include "mozilla/UniquePtr.h"
       9             : #include "mozilla/nsMemoryInfoDumper.h"
      10             : #include "mozilla/DebugOnly.h"
      11             : #include "nsDumpUtils.h"
      12             : 
      13             : #include "mozilla/Unused.h"
      14             : #include "mozilla/dom/ContentParent.h"
      15             : #include "mozilla/dom/ContentChild.h"
      16             : #include "nsIConsoleService.h"
      17             : #include "nsCycleCollector.h"
      18             : #include "nsICycleCollectorListener.h"
      19             : #include "nsIMemoryReporter.h"
      20             : #include "nsDirectoryServiceDefs.h"
      21             : #include "nsGZFileWriter.h"
      22             : #include "nsJSEnvironment.h"
      23             : #include "nsPrintfCString.h"
      24             : #include "nsISimpleEnumerator.h"
      25             : #include "nsServiceManagerUtils.h"
      26             : #include "nsIFile.h"
      27             : 
      28             : #ifdef XP_WIN
      29             : #include <process.h>
      30             : #ifndef getpid
      31             : #define getpid _getpid
      32             : #endif
      33             : #else
      34             : #include <unistd.h>
      35             : #endif
      36             : 
      37             : #ifdef XP_UNIX
      38             : #define MOZ_SUPPORTS_FIFO 1
      39             : #endif
      40             : 
      41             : #if defined(XP_LINUX) || defined(__FreeBSD__)
      42             : #define MOZ_SUPPORTS_RT_SIGNALS 1
      43             : #endif
      44             : 
      45             : #if defined(MOZ_SUPPORTS_RT_SIGNALS)
      46             : #include <fcntl.h>
      47             : #include <sys/types.h>
      48             : #include <sys/stat.h>
      49             : #endif
      50             : 
      51             : #if defined(MOZ_SUPPORTS_FIFO)
      52             : #include "mozilla/Preferences.h"
      53             : #endif
      54             : 
      55             : using namespace mozilla;
      56             : using namespace mozilla::dom;
      57             : 
      58             : namespace {
      59             : 
      60           0 : class DumpMemoryInfoToTempDirRunnable : public Runnable
      61             : {
      62             : public:
      63           0 :   DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
      64             :                                   bool aAnonymize,
      65             :                                   bool aMinimizeMemoryUsage)
      66           0 :     : mozilla::Runnable("DumpMemoryInfoToTempDirRunnable")
      67             :     , mIdentifier(aIdentifier)
      68             :     , mAnonymize(aAnonymize)
      69           0 :     , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
      70             :   {
      71           0 :   }
      72             : 
      73           0 :   NS_IMETHOD Run() override
      74             :   {
      75             :     nsCOMPtr<nsIMemoryInfoDumper> dumper =
      76           0 :       do_GetService("@mozilla.org/memory-info-dumper;1");
      77           0 :     dumper->DumpMemoryInfoToTempDir(mIdentifier, mAnonymize,
      78           0 :                                     mMinimizeMemoryUsage);
      79           0 :     return NS_OK;
      80             :   }
      81             : 
      82             : private:
      83             :   const nsString mIdentifier;
      84             :   const bool mAnonymize;
      85             :   const bool mMinimizeMemoryUsage;
      86             : };
      87             : 
      88             : class GCAndCCLogDumpRunnable final
      89             :   : public Runnable
      90             :   , public nsIDumpGCAndCCLogsCallback
      91             : {
      92             : public:
      93             :   NS_DECL_ISUPPORTS_INHERITED
      94             : 
      95           0 :   GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
      96             :                          bool aDumpAllTraces,
      97             :                          bool aDumpChildProcesses)
      98           0 :     : mozilla::Runnable("GCAndCCLogDumpRunnable")
      99             :     , mIdentifier(aIdentifier)
     100             :     , mDumpAllTraces(aDumpAllTraces)
     101           0 :     , mDumpChildProcesses(aDumpChildProcesses)
     102             :   {
     103           0 :   }
     104             : 
     105           0 :   NS_IMETHOD Run() override
     106             :   {
     107             :     nsCOMPtr<nsIMemoryInfoDumper> dumper =
     108           0 :       do_GetService("@mozilla.org/memory-info-dumper;1");
     109             : 
     110           0 :     dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
     111           0 :                                   mDumpChildProcesses, this);
     112           0 :     return NS_OK;
     113             :   }
     114             : 
     115           0 :   NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
     116             :   {
     117           0 :     return NS_OK;
     118             :   }
     119             : 
     120           0 :   NS_IMETHOD OnFinish() override
     121             :   {
     122           0 :     return NS_OK;
     123             :   }
     124             : 
     125             : private:
     126           0 :   ~GCAndCCLogDumpRunnable() {}
     127             : 
     128             :   const nsString mIdentifier;
     129             :   const bool mDumpAllTraces;
     130             :   const bool mDumpChildProcesses;
     131             : };
     132             : 
     133           0 : NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, Runnable,
     134             :                             nsIDumpGCAndCCLogsCallback)
     135             : 
     136             : } // namespace
     137             : 
     138             : #if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
     139             : namespace {
     140             : 
     141             : /*
     142             :  * The following code supports dumping about:memory upon receiving a signal.
     143             :  *
     144             :  * We listen for the following signals:
     145             :  *
     146             :  *  - SIGRTMIN:     Dump our memory reporters (and those of our child
     147             :  *                  processes),
     148             :  *  - SIGRTMIN + 1: Dump our memory reporters (and those of our child
     149             :  *                  processes) after minimizing memory usage, and
     150             :  *  - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
     151             :  *
     152             :  * When we receive one of these signals, we write the signal number to a pipe.
     153             :  * The IO thread then notices that the pipe has been written to, and kicks off
     154             :  * the appropriate task on the main thread.
     155             :  *
     156             :  * This scheme is similar to using signalfd(), except it's portable and it
     157             :  * doesn't require the use of sigprocmask, which is problematic because it
     158             :  * masks signals received by child processes.
     159             :  *
     160             :  * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
     161             :  * But that uses libevent, which does not handle the realtime signals (bug
     162             :  * 794074).
     163             :  */
     164             : 
     165             : // It turns out that at least on some systems, SIGRTMIN is not a compile-time
     166             : // constant, so these have to be set at runtime.
     167             : static uint8_t sDumpAboutMemorySignum;         // SIGRTMIN
     168             : static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
     169             : static uint8_t sGCAndCCDumpSignum;             // SIGRTMIN + 2
     170             : 
     171           0 : void doMemoryReport(const uint8_t aRecvSig)
     172             : {
     173             :   // Dump our memory reports (but run this on the main thread!).
     174           0 :   bool minimize = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
     175             :   LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig);
     176             :   RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
     177           0 :     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
     178             :                                         /* anonymize = */ false,
     179           0 :                                         minimize);
     180           0 :   NS_DispatchToMainThread(runnable);
     181           0 : }
     182             : 
     183           0 : void doGCCCDump(const uint8_t aRecvSig)
     184             : {
     185             :   LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig);
     186             :   // Dump GC and CC logs (from the main thread).
     187             :   RefPtr<GCAndCCLogDumpRunnable> runnable =
     188           0 :     new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
     189             :                                /* allTraces = */ true,
     190           0 :                                /* dumpChildProcesses = */ true);
     191           0 :   NS_DispatchToMainThread(runnable);
     192           0 : }
     193             : 
     194             : } // namespace
     195             : #endif // MOZ_SUPPORTS_RT_SIGNALS }
     196             : 
     197             : #if defined(MOZ_SUPPORTS_FIFO) // {
     198             : namespace {
     199             : 
     200             : void
     201           0 : doMemoryReport(const nsCString& aInputStr)
     202             : {
     203           0 :   bool minimize = aInputStr.EqualsLiteral("minimize memory report");
     204             :   LOG("FifoWatcher(command:%s) dispatching memory report runnable.",
     205             :       aInputStr.get());
     206             :   RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
     207           0 :     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
     208             :                                         /* anonymize = */ false,
     209           0 :                                         minimize);
     210           0 :   NS_DispatchToMainThread(runnable);
     211           0 : }
     212             : 
     213             : void
     214           0 : doGCCCDump(const nsCString& aInputStr)
     215             : {
     216           0 :   bool doAllTracesGCCCDump = aInputStr.EqualsLiteral("gc log");
     217             :   LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", aInputStr.get());
     218             :   RefPtr<GCAndCCLogDumpRunnable> runnable =
     219           0 :     new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
     220             :                                doAllTracesGCCCDump,
     221           0 :                                /* dumpChildProcesses = */ true);
     222           0 :   NS_DispatchToMainThread(runnable);
     223           0 : }
     224             : 
     225             : bool
     226           3 : SetupFifo()
     227             : {
     228             : #ifdef DEBUG
     229             :   static bool fifoCallbacksRegistered = false;
     230             : #endif
     231             : 
     232           3 :   if (!FifoWatcher::MaybeCreate()) {
     233           3 :     return false;
     234             :   }
     235             : 
     236           0 :   MOZ_ASSERT(!fifoCallbacksRegistered,
     237             :              "FifoWatcher callbacks should be registered only once");
     238             : 
     239           0 :   FifoWatcher* fw = FifoWatcher::GetSingleton();
     240             :   // Dump our memory reports (but run this on the main thread!).
     241           0 :   fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
     242           0 :                        doMemoryReport);
     243           0 :   fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
     244           0 :                        doMemoryReport);
     245             :   // Dump GC and CC logs (from the main thread).
     246           0 :   fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
     247           0 :                        doGCCCDump);
     248           0 :   fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
     249           0 :                        doGCCCDump);
     250             : 
     251             : #ifdef DEBUG
     252           0 :   fifoCallbacksRegistered = true;
     253             : #endif
     254           0 :   return true;
     255             : }
     256             : 
     257             : void
     258           0 : OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
     259             : {
     260             :   LOG("%s changed", FifoWatcher::kPrefName);
     261           0 :   if (SetupFifo()) {
     262             :     Preferences::UnregisterCallback(OnFifoEnabledChange,
     263             :                                     FifoWatcher::kPrefName,
     264           0 :                                     nullptr);
     265             :   }
     266           0 : }
     267             : 
     268             : } // namespace
     269             : #endif // MOZ_SUPPORTS_FIFO }
     270             : 
     271           0 : NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
     272             : 
     273           0 : nsMemoryInfoDumper::nsMemoryInfoDumper()
     274             : {
     275           0 : }
     276             : 
     277           0 : nsMemoryInfoDumper::~nsMemoryInfoDumper()
     278             : {
     279           0 : }
     280             : 
     281             : /* static */ void
     282           3 : nsMemoryInfoDumper::Initialize()
     283             : {
     284             : #if defined(MOZ_SUPPORTS_RT_SIGNALS)
     285           3 :   SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
     286             : 
     287             :   // Dump memory reporters (and those of our child processes)
     288           3 :   sDumpAboutMemorySignum = SIGRTMIN;
     289           3 :   sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
     290             :   // Dump our memory reporters after minimizing memory usage
     291           3 :   sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
     292           3 :   sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
     293             :   // Dump the GC and CC logs in this and our child processes.
     294           3 :   sGCAndCCDumpSignum = SIGRTMIN + 2;
     295           3 :   sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
     296             : #endif
     297             : 
     298             : #if defined(MOZ_SUPPORTS_FIFO)
     299           3 :   if (!SetupFifo()) {
     300             :     // NB: This gets loaded early enough that it's possible there is a user pref
     301             :     //     set to enable the fifo watcher that has not been loaded yet. Register
     302             :     //     to attempt to initialize if the fifo watcher becomes enabled by
     303             :     //     a user pref.
     304             :     Preferences::RegisterCallback(OnFifoEnabledChange,
     305             :                                   FifoWatcher::kPrefName,
     306           3 :                                   nullptr);
     307             :   }
     308             : #endif
     309           3 : }
     310             : 
     311             : static void
     312           0 : EnsureNonEmptyIdentifier(nsAString& aIdentifier)
     313             : {
     314           0 :   if (!aIdentifier.IsEmpty()) {
     315           0 :     return;
     316             :   }
     317             : 
     318             :   // If the identifier is empty, set it to the number of whole seconds since the
     319             :   // epoch.  This identifier will appear in the files that this process
     320             :   // generates and also the files generated by this process's children, allowing
     321             :   // us to identify which files are from the same memory report request.
     322           0 :   aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
     323             : }
     324             : 
     325             : // Use XPCOM refcounting to fire |onFinish| when all reference-holders
     326             : // (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself)
     327             : // have gone away.
     328             : class nsDumpGCAndCCLogsCallbackHolder final
     329             :   : public nsIDumpGCAndCCLogsCallback
     330             : {
     331             : public:
     332             :   NS_DECL_ISUPPORTS
     333             : 
     334           0 :   explicit nsDumpGCAndCCLogsCallbackHolder(nsIDumpGCAndCCLogsCallback* aCallback)
     335           0 :     : mCallback(aCallback)
     336             :   {
     337           0 :   }
     338             : 
     339           0 :   NS_IMETHOD OnFinish() override
     340             :   {
     341           0 :     return NS_ERROR_UNEXPECTED;
     342             :   }
     343             : 
     344           0 :   NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
     345             :   {
     346           0 :     return mCallback->OnDump(aGCLog, aCCLog, aIsParent);
     347             :   }
     348             : 
     349             : private:
     350           0 :   ~nsDumpGCAndCCLogsCallbackHolder()
     351           0 :   {
     352           0 :     Unused << mCallback->OnFinish();
     353           0 :   }
     354             : 
     355             :   nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
     356             : };
     357             : 
     358           0 : NS_IMPL_ISUPPORTS(nsDumpGCAndCCLogsCallbackHolder, nsIDumpGCAndCCLogsCallback)
     359             : 
     360             : NS_IMETHODIMP
     361           0 : nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
     362             :                                           bool aDumpAllTraces,
     363             :                                           bool aDumpChildProcesses,
     364             :                                           nsIDumpGCAndCCLogsCallback* aCallback)
     365             : {
     366           0 :   nsString identifier(aIdentifier);
     367           0 :   EnsureNonEmptyIdentifier(identifier);
     368             :   nsCOMPtr<nsIDumpGCAndCCLogsCallback> callbackHolder =
     369           0 :     new nsDumpGCAndCCLogsCallbackHolder(aCallback);
     370             : 
     371           0 :   if (aDumpChildProcesses) {
     372           0 :     nsTArray<ContentParent*> children;
     373           0 :     ContentParent::GetAll(children);
     374           0 :     for (uint32_t i = 0; i < children.Length(); i++) {
     375           0 :       ContentParent* cp = children[i];
     376             :       nsCOMPtr<nsICycleCollectorLogSink> logSink =
     377           0 :         nsCycleCollector_createLogSink();
     378             : 
     379           0 :       logSink->SetFilenameIdentifier(identifier);
     380           0 :       logSink->SetProcessIdentifier(cp->Pid());
     381             : 
     382           0 :       Unused << cp->CycleCollectWithLogs(aDumpAllTraces, logSink,
     383             :                                          callbackHolder);
     384             :     }
     385             :   }
     386             : 
     387             :   nsCOMPtr<nsICycleCollectorListener> logger =
     388           0 :     do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
     389             : 
     390           0 :   if (aDumpAllTraces) {
     391           0 :     nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
     392           0 :     logger->AllTraces(getter_AddRefs(allTracesLogger));
     393           0 :     logger = allTracesLogger;
     394             :   }
     395             : 
     396           0 :   nsCOMPtr<nsICycleCollectorLogSink> logSink;
     397           0 :   logger->GetLogSink(getter_AddRefs(logSink));
     398             : 
     399           0 :   logSink->SetFilenameIdentifier(identifier);
     400             : 
     401           0 :   nsJSContext::CycleCollectNow(logger);
     402             : 
     403           0 :   nsCOMPtr<nsIFile> gcLog, ccLog;
     404           0 :   logSink->GetGcLog(getter_AddRefs(gcLog));
     405           0 :   logSink->GetCcLog(getter_AddRefs(ccLog));
     406           0 :   callbackHolder->OnDump(gcLog, ccLog, /* parent = */ true);
     407             : 
     408           0 :   return NS_OK;
     409             : }
     410             : 
     411             : NS_IMETHODIMP
     412           0 : nsMemoryInfoDumper::DumpGCAndCCLogsToSink(bool aDumpAllTraces,
     413             :                                           nsICycleCollectorLogSink* aSink)
     414             : {
     415             :   nsCOMPtr<nsICycleCollectorListener> logger =
     416           0 :     do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
     417             : 
     418           0 :   if (aDumpAllTraces) {
     419           0 :     nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
     420           0 :     logger->AllTraces(getter_AddRefs(allTracesLogger));
     421           0 :     logger = allTracesLogger;
     422             :   }
     423             : 
     424           0 :   logger->SetLogSink(aSink);
     425             : 
     426           0 :   nsJSContext::CycleCollectNow(logger);
     427             : 
     428           0 :   return NS_OK;
     429             : }
     430             : 
     431             : static void
     432           0 : MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
     433             :              int aPid, const char* aSuffix, nsACString& aResult)
     434             : {
     435           0 :   aResult = nsPrintfCString("%s-%s-%d.%s",
     436             :                             aPrefix,
     437           0 :                             NS_ConvertUTF16toUTF8(aIdentifier).get(),
     438           0 :                             aPid, aSuffix);
     439           0 : }
     440             : 
     441             : // This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
     442             : // the following two problems:
     443             : // - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
     444             : // - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
     445           0 : class GZWriterWrapper : public JSONWriteFunc
     446             : {
     447             : public:
     448           0 :   explicit GZWriterWrapper(nsGZFileWriter* aGZWriter)
     449           0 :     : mGZWriter(aGZWriter)
     450           0 :   {}
     451             : 
     452           0 :   void Write(const char* aStr)
     453             :   {
     454             :     // Ignore any failure because JSONWriteFunc doesn't have a mechanism for
     455             :     // handling errors.
     456           0 :     Unused << mGZWriter->Write(aStr);
     457           0 :   }
     458             : 
     459           0 :   nsresult Finish() { return mGZWriter->Finish(); }
     460             : 
     461             : private:
     462             :   RefPtr<nsGZFileWriter> mGZWriter;
     463             : };
     464             : 
     465             : // We need two callbacks: one that handles reports, and one that is called at
     466             : // the end of reporting. Both the callbacks need access to the same JSONWriter,
     467             : // so we implement both of them in this one class.
     468             : class HandleReportAndFinishReportingCallbacks final
     469             :   : public nsIHandleReportCallback, public nsIFinishReportingCallback
     470             : {
     471             : public:
     472             :   NS_DECL_ISUPPORTS
     473             : 
     474           0 :   HandleReportAndFinishReportingCallbacks(UniquePtr<JSONWriter> aWriter,
     475             :                                           nsIFinishDumpingCallback* aFinishDumping,
     476             :                                           nsISupports* aFinishDumpingData)
     477           0 :     : mWriter(Move(aWriter))
     478             :     , mFinishDumping(aFinishDumping)
     479           0 :     , mFinishDumpingData(aFinishDumpingData)
     480             :   {
     481           0 :   }
     482             : 
     483             :   // This is the callback for nsIHandleReportCallback.
     484           0 :   NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
     485             :                       int32_t aKind, int32_t aUnits, int64_t aAmount,
     486             :                       const nsACString& aDescription,
     487             :                       nsISupports* aData) override
     488             :   {
     489           0 :     nsAutoCString process;
     490           0 :     if (aProcess.IsEmpty()) {
     491             :       // If the process is empty, the report originated with the process doing
     492             :       // the dumping.  In that case, generate the process identifier, which is
     493             :       // of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we
     494             :       // don't have a process name.  If we're the main process, we let
     495             :       // $PROCESS_NAME be "Main Process".
     496           0 :       if (XRE_IsParentProcess()) {
     497             :         // We're the main process.
     498           0 :         process.AssignLiteral("Main Process");
     499           0 :       } else if (ContentChild* cc = ContentChild::GetSingleton()) {
     500             :         // Try to get the process name from ContentChild.
     501           0 :         cc->GetProcessName(process);
     502             :       }
     503           0 :       ContentChild::AppendProcessId(process);
     504             : 
     505             :     } else {
     506             :       // Otherwise, the report originated with another process and already has a
     507             :       // process name.  Just use that.
     508           0 :       process = aProcess;
     509             :     }
     510             : 
     511           0 :     mWriter->StartObjectElement();
     512             :     {
     513           0 :       mWriter->StringProperty("process", process.get());
     514           0 :       mWriter->StringProperty("path", PromiseFlatCString(aPath).get());
     515           0 :       mWriter->IntProperty("kind", aKind);
     516           0 :       mWriter->IntProperty("units", aUnits);
     517           0 :       mWriter->IntProperty("amount", aAmount);
     518           0 :       mWriter->StringProperty("description",
     519           0 :                               PromiseFlatCString(aDescription).get());
     520             :     }
     521           0 :     mWriter->EndObject();
     522             : 
     523           0 :     return NS_OK;
     524             :   }
     525             : 
     526             :   // This is the callback for nsIFinishReportingCallback.
     527           0 :   NS_IMETHOD Callback(nsISupports* aData) override
     528             :   {
     529           0 :     mWriter->EndArray();  // end of "reports" array
     530           0 :     mWriter->End();
     531             : 
     532             :     // The call to Finish() deallocates the memory allocated by the first Write
     533             :     // call. Because that memory was live while the memory reporters ran and
     534             :     // was measured by them -- by "heap-allocated" if nothing else -- we want
     535             :     // DMD to see it as well. So we deliberately don't call Finish() until
     536             :     // after DMD finishes.
     537           0 :     nsresult rv = static_cast<GZWriterWrapper*>(mWriter->WriteFunc())->Finish();
     538           0 :     NS_ENSURE_SUCCESS(rv, rv);
     539             : 
     540           0 :     if (!mFinishDumping) {
     541           0 :       return NS_OK;
     542             :     }
     543             : 
     544           0 :     return mFinishDumping->Callback(mFinishDumpingData);
     545             :   }
     546             : 
     547             : private:
     548           0 :   ~HandleReportAndFinishReportingCallbacks() {}
     549             : 
     550             :   UniquePtr<JSONWriter> mWriter;
     551             :   nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
     552             :   nsCOMPtr<nsISupports> mFinishDumpingData;
     553             : };
     554             : 
     555           0 : NS_IMPL_ISUPPORTS(HandleReportAndFinishReportingCallbacks,
     556             :                   nsIHandleReportCallback, nsIFinishReportingCallback)
     557             : 
     558             : class TempDirFinishCallback final : public nsIFinishDumpingCallback
     559             : {
     560             : public:
     561             :   NS_DECL_ISUPPORTS
     562             : 
     563           0 :   TempDirFinishCallback(nsIFile* aReportsTmpFile,
     564             :                         const nsCString& aReportsFinalFilename)
     565           0 :     : mReportsTmpFile(aReportsTmpFile)
     566           0 :     , mReportsFilename(aReportsFinalFilename)
     567             :   {
     568           0 :   }
     569             : 
     570           0 :   NS_IMETHOD Callback(nsISupports* aData) override
     571             :   {
     572             :     // Rename the memory reports file, now that we're done writing all the
     573             :     // files. Its final name is "memory-report<-identifier>-<pid>.json.gz".
     574             : 
     575           0 :     nsCOMPtr<nsIFile> reportsFinalFile;
     576           0 :     nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
     577           0 :                                          getter_AddRefs(reportsFinalFile));
     578           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     579           0 :       return rv;
     580             :     }
     581             : 
     582             :   #ifdef ANDROID
     583             :     rv = reportsFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
     584             :     if (NS_WARN_IF(NS_FAILED(rv))) {
     585             :       return rv;
     586             :     }
     587             :   #endif
     588             : 
     589           0 :     rv = reportsFinalFile->AppendNative(mReportsFilename);
     590           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     591           0 :       return rv;
     592             :     }
     593             : 
     594           0 :     rv = reportsFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
     595           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     596           0 :       return rv;
     597             :     }
     598             : 
     599           0 :     nsAutoString reportsFinalFilename;
     600           0 :     rv = reportsFinalFile->GetLeafName(reportsFinalFilename);
     601           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     602           0 :       return rv;
     603             :     }
     604             : 
     605           0 :     rv = mReportsTmpFile->MoveTo(/* directory */ nullptr,
     606           0 :                                  reportsFinalFilename);
     607           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     608           0 :       return rv;
     609             :     }
     610             : 
     611             :     // Write a message to the console.
     612             : 
     613             :     nsCOMPtr<nsIConsoleService> cs =
     614           0 :       do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
     615           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     616           0 :       return rv;
     617             :     }
     618             : 
     619           0 :     nsString path;
     620           0 :     mReportsTmpFile->GetPath(path);
     621           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     622           0 :       return rv;
     623             :     }
     624             : 
     625           0 :     nsString msg = NS_LITERAL_STRING("nsIMemoryInfoDumper dumped reports to ");
     626           0 :     msg.Append(path);
     627           0 :     return cs->LogStringMessage(msg.get());
     628             :   }
     629             : 
     630             : private:
     631           0 :   ~TempDirFinishCallback() {}
     632             : 
     633             :   nsCOMPtr<nsIFile> mReportsTmpFile;
     634             :   nsCString mReportsFilename;
     635             : };
     636             : 
     637           0 : NS_IMPL_ISUPPORTS(TempDirFinishCallback, nsIFinishDumpingCallback)
     638             : 
     639             : static nsresult
     640           0 : DumpMemoryInfoToFile(
     641             :   nsIFile* aReportsFile,
     642             :   nsIFinishDumpingCallback* aFinishDumping,
     643             :   nsISupports* aFinishDumpingData,
     644             :   bool aAnonymize,
     645             :   bool aMinimizeMemoryUsage,
     646             :   nsAString& aDMDIdentifier)
     647             : {
     648           0 :   RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
     649           0 :   nsresult rv = gzWriter->Init(aReportsFile);
     650           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     651           0 :     return rv;
     652             :   }
     653             :   auto jsonWriter =
     654           0 :     MakeUnique<JSONWriter>(MakeUnique<GZWriterWrapper>(gzWriter));
     655             : 
     656             :   nsCOMPtr<nsIMemoryReporterManager> mgr =
     657           0 :     do_GetService("@mozilla.org/memory-reporter-manager;1");
     658             : 
     659             :   // This is the first write to the file, and it causes |aWriter| to allocate
     660             :   // over 200 KiB of memory.
     661           0 :   jsonWriter->Start();
     662             :   {
     663             :     // Increment this number if the format changes.
     664           0 :     jsonWriter->IntProperty("version", 1);
     665           0 :     jsonWriter->BoolProperty("hasMozMallocUsableSize",
     666           0 :                              mgr->GetHasMozMallocUsableSize());
     667           0 :     jsonWriter->StartArrayProperty("reports");
     668             :   }
     669             : 
     670             :   RefPtr<HandleReportAndFinishReportingCallbacks>
     671             :     handleReportAndFinishReporting =
     672           0 :       new HandleReportAndFinishReportingCallbacks(Move(jsonWriter),
     673             :                                                   aFinishDumping,
     674           0 :                                                   aFinishDumpingData);
     675           0 :   rv = mgr->GetReportsExtended(handleReportAndFinishReporting, nullptr,
     676             :                                handleReportAndFinishReporting, nullptr,
     677             :                                aAnonymize,
     678             :                                aMinimizeMemoryUsage,
     679           0 :                                aDMDIdentifier);
     680           0 :   return rv;
     681             : }
     682             : 
     683             : NS_IMETHODIMP
     684           0 : nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
     685             :   const nsAString& aFilename,
     686             :   nsIFinishDumpingCallback* aFinishDumping,
     687             :   nsISupports* aFinishDumpingData,
     688             :   bool aAnonymize)
     689             : {
     690           0 :   MOZ_ASSERT(!aFilename.IsEmpty());
     691             : 
     692             :   // Create the file.
     693             : 
     694           0 :   nsCOMPtr<nsIFile> reportsFile;
     695           0 :   nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(reportsFile));
     696           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     697           0 :     return rv;
     698             :   }
     699             : 
     700           0 :   reportsFile->InitWithPath(aFilename);
     701           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     702           0 :     return rv;
     703             :   }
     704             : 
     705             :   bool exists;
     706           0 :   rv = reportsFile->Exists(&exists);
     707           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     708           0 :     return rv;
     709             :   }
     710             : 
     711           0 :   if (!exists) {
     712           0 :     rv = reportsFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
     713           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     714           0 :       return rv;
     715             :     }
     716             :   }
     717             : 
     718           0 :   nsString dmdIdent = EmptyString();
     719           0 :   return DumpMemoryInfoToFile(reportsFile, aFinishDumping, aFinishDumpingData,
     720             :                               aAnonymize, /* minimizeMemoryUsage = */ false,
     721           0 :                               dmdIdent);
     722             : }
     723             : 
     724             : NS_IMETHODIMP
     725           0 : nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
     726             :                                             bool aAnonymize,
     727             :                                             bool aMinimizeMemoryUsage)
     728             : {
     729           0 :   nsString identifier(aIdentifier);
     730           0 :   EnsureNonEmptyIdentifier(identifier);
     731             : 
     732             :   // Open a new file named something like
     733             :   //
     734             :   //   incomplete-memory-report-<identifier>-<pid>.json.gz
     735             :   //
     736             :   // in NS_OS_TEMP_DIR for writing.  When we're finished writing the report,
     737             :   // we'll rename this file and get rid of the "incomplete-" prefix.
     738             :   //
     739             :   // We do this because we don't want scripts which poll the filesystem
     740             :   // looking for memory report dumps to grab a file before we're finished
     741             :   // writing to it.
     742             : 
     743             :   // The "unified" indicates that we merge the memory reports from all
     744             :   // processes and write out one file, rather than a separate file for
     745             :   // each process as was the case before bug 946407.  This is so that
     746             :   // the get_about_memory.py script in the B2G repository can
     747             :   // determine when it's done waiting for files to appear.
     748           0 :   nsCString reportsFinalFilename;
     749           0 :   MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
     750           0 :                reportsFinalFilename);
     751             : 
     752           0 :   nsCOMPtr<nsIFile> reportsTmpFile;
     753             :   nsresult rv;
     754             :   // In Android case, this function will open a file named aFilename under
     755             :   // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
     756             :   // open a file named aFilename under "NS_OS_TEMP_DIR".
     757           0 :   rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
     758           0 :                                  reportsFinalFilename,
     759           0 :                                  getter_AddRefs(reportsTmpFile),
     760           0 :                                  NS_LITERAL_CSTRING("memory-reports"));
     761           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     762           0 :     return rv;
     763             :   }
     764             : 
     765             :   RefPtr<TempDirFinishCallback> finishDumping =
     766           0 :     new TempDirFinishCallback(reportsTmpFile, reportsFinalFilename);
     767             : 
     768           0 :   return DumpMemoryInfoToFile(reportsTmpFile, finishDumping, nullptr,
     769           0 :                               aAnonymize, aMinimizeMemoryUsage, identifier);
     770           9 : }
     771             : 
     772             : #ifdef MOZ_DMD
     773             : dmd::DMDFuncs::Singleton dmd::DMDFuncs::sSingleton;
     774             : 
     775             : nsresult
     776             : nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
     777             :                                 FILE** aOutFile)
     778             : {
     779             :   if (!dmd::IsRunning()) {
     780             :     *aOutFile = nullptr;
     781             :     return NS_OK;
     782             :   }
     783             : 
     784             :   // Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
     785             :   // if DMD is enabled.
     786             :   nsCString dmdFilename;
     787             :   MakeFilename("dmd", aIdentifier, aPid, "json.gz", dmdFilename);
     788             : 
     789             :   // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
     790             :   // and dump DMD output to it.  This must occur after the memory reporters
     791             :   // have been run (above), but before the memory-reports file has been
     792             :   // renamed (so scripts can detect the DMD file, if present).
     793             : 
     794             :   nsresult rv;
     795             :   nsCOMPtr<nsIFile> dmdFile;
     796             :   rv = nsDumpUtils::OpenTempFile(dmdFilename,
     797             :                                  getter_AddRefs(dmdFile),
     798             :                                  NS_LITERAL_CSTRING("memory-reports"));
     799             :   if (NS_WARN_IF(NS_FAILED(rv))) {
     800             :     return rv;
     801             :   }
     802             :   rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
     803             :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "OpenANSIFileDesc failed");
     804             : 
     805             :   // Print the path, because on some platforms (e.g. Mac) it's not obvious.
     806             :   nsCString path;
     807             :   rv = dmdFile->GetNativePath(path);
     808             :   if (NS_WARN_IF(NS_FAILED(rv))) {
     809             :     return rv;
     810             :   }
     811             :   dmd::StatusMsg("opened %s for writing\n", path.get());
     812             : 
     813             :   return rv;
     814             : }
     815             : 
     816             : nsresult
     817             : nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
     818             : {
     819             :   RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
     820             :   nsresult rv = gzWriter->InitANSIFileDesc(aFile);
     821             :   if (NS_WARN_IF(NS_FAILED(rv))) {
     822             :     return rv;
     823             :   }
     824             : 
     825             :   // Dump DMD's memory reports analysis to the file.
     826             :   dmd::Analyze(MakeUnique<GZWriterWrapper>(gzWriter));
     827             : 
     828             :   rv = gzWriter->Finish();
     829             :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Finish failed");
     830             :   return rv;
     831             : }
     832             : #endif  // MOZ_DMD
     833             : 

Generated by: LCOV version 1.13