xpcom/base/nsMemoryInfoDumper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/base/nsMemoryInfoDumper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,767 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/nsMemoryInfoDumper.h"
    1.11 +#include "nsDumpUtils.h"
    1.12 +
    1.13 +#include "mozilla/unused.h"
    1.14 +#include "mozilla/dom/ContentParent.h"
    1.15 +#include "mozilla/dom/ContentChild.h"
    1.16 +#include "nsIConsoleService.h"
    1.17 +#include "nsICycleCollectorListener.h"
    1.18 +#include "nsIMemoryReporter.h"
    1.19 +#include "nsDirectoryServiceDefs.h"
    1.20 +#include "nsGZFileWriter.h"
    1.21 +#include "nsJSEnvironment.h"
    1.22 +#include "nsPrintfCString.h"
    1.23 +#include "nsISimpleEnumerator.h"
    1.24 +#include "nsServiceManagerUtils.h"
    1.25 +#include "nsIFile.h"
    1.26 +
    1.27 +#ifdef XP_WIN
    1.28 +#include <process.h>
    1.29 +#define getpid _getpid
    1.30 +#else
    1.31 +#include <unistd.h>
    1.32 +#endif
    1.33 +
    1.34 +#ifdef XP_UNIX
    1.35 +#define MOZ_SUPPORTS_FIFO 1
    1.36 +#endif
    1.37 +
    1.38 +#if defined(XP_LINUX) || defined(__FreeBSD__)
    1.39 +#define MOZ_SUPPORTS_RT_SIGNALS 1
    1.40 +#endif
    1.41 +
    1.42 +#if defined(MOZ_SUPPORTS_RT_SIGNALS)
    1.43 +#include <fcntl.h>
    1.44 +#include <sys/types.h>
    1.45 +#include <sys/stat.h>
    1.46 +#endif
    1.47 +
    1.48 +#if defined(MOZ_SUPPORTS_FIFO)
    1.49 +#include "mozilla/Preferences.h"
    1.50 +#endif
    1.51 +
    1.52 +using namespace mozilla;
    1.53 +using namespace mozilla::dom;
    1.54 +
    1.55 +namespace {
    1.56 +
    1.57 +class DumpMemoryInfoToTempDirRunnable : public nsRunnable
    1.58 +{
    1.59 +public:
    1.60 +  DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
    1.61 +                                  bool aMinimizeMemoryUsage)
    1.62 +      : mIdentifier(aIdentifier)
    1.63 +      , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
    1.64 +  {}
    1.65 +
    1.66 +  NS_IMETHOD Run()
    1.67 +  {
    1.68 +    nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
    1.69 +    dumper->DumpMemoryInfoToTempDir(mIdentifier, mMinimizeMemoryUsage);
    1.70 +    return NS_OK;
    1.71 +  }
    1.72 +
    1.73 +private:
    1.74 +  const nsString mIdentifier;
    1.75 +  const bool mMinimizeMemoryUsage;
    1.76 +};
    1.77 +
    1.78 +class GCAndCCLogDumpRunnable : public nsRunnable
    1.79 +{
    1.80 +public:
    1.81 +  GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
    1.82 +                         bool aDumpAllTraces,
    1.83 +                         bool aDumpChildProcesses)
    1.84 +    : mIdentifier(aIdentifier)
    1.85 +    , mDumpAllTraces(aDumpAllTraces)
    1.86 +    , mDumpChildProcesses(aDumpChildProcesses)
    1.87 +  {}
    1.88 +
    1.89 +  NS_IMETHOD Run()
    1.90 +  {
    1.91 +    nsCOMPtr<nsIMemoryInfoDumper> dumper =
    1.92 +      do_GetService("@mozilla.org/memory-info-dumper;1");
    1.93 +
    1.94 +    nsString ccLogPath, gcLogPath;
    1.95 +    dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
    1.96 +                                  mDumpChildProcesses, gcLogPath, ccLogPath);
    1.97 +    return NS_OK;
    1.98 +  }
    1.99 +
   1.100 +private:
   1.101 +  const nsString mIdentifier;
   1.102 +  const bool mDumpAllTraces;
   1.103 +  const bool mDumpChildProcesses;
   1.104 +};
   1.105 +
   1.106 +} // anonymous namespace
   1.107 +
   1.108 +#if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
   1.109 +namespace {
   1.110 +
   1.111 +/*
   1.112 + * The following code supports dumping about:memory upon receiving a signal.
   1.113 + *
   1.114 + * We listen for the following signals:
   1.115 + *
   1.116 + *  - SIGRTMIN:     Dump our memory reporters (and those of our child
   1.117 + *                  processes),
   1.118 + *  - SIGRTMIN + 1: Dump our memory reporters (and those of our child
   1.119 + *                  processes) after minimizing memory usage, and
   1.120 + *  - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
   1.121 + *
   1.122 + * When we receive one of these signals, we write the signal number to a pipe.
   1.123 + * The IO thread then notices that the pipe has been written to, and kicks off
   1.124 + * the appropriate task on the main thread.
   1.125 + *
   1.126 + * This scheme is similar to using signalfd(), except it's portable and it
   1.127 + * doesn't require the use of sigprocmask, which is problematic because it
   1.128 + * masks signals received by child processes.
   1.129 + *
   1.130 + * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
   1.131 + * But that uses libevent, which does not handle the realtime signals (bug
   1.132 + * 794074).
   1.133 + */
   1.134 +
   1.135 +// It turns out that at least on some systems, SIGRTMIN is not a compile-time
   1.136 +// constant, so these have to be set at runtime.
   1.137 +static uint8_t sDumpAboutMemorySignum;         // SIGRTMIN
   1.138 +static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
   1.139 +static uint8_t sGCAndCCDumpSignum;             // SIGRTMIN + 2
   1.140 +
   1.141 +void doMemoryReport(const uint8_t recvSig)
   1.142 +{
   1.143 +  // Dump our memory reports (but run this on the main thread!).
   1.144 +  bool doMMUFirst = recvSig == sDumpAboutMemoryAfterMMUSignum;
   1.145 +  LOG("SignalWatcher(sig %d) dispatching memory report runnable.", recvSig);
   1.146 +  nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
   1.147 +    new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
   1.148 +                                        doMMUFirst);
   1.149 +  NS_DispatchToMainThread(runnable);
   1.150 +}
   1.151 +
   1.152 +void doGCCCDump(const uint8_t recvSig)
   1.153 +{
   1.154 +  LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", recvSig);
   1.155 +  // Dump GC and CC logs (from the main thread).
   1.156 +  nsRefPtr<GCAndCCLogDumpRunnable> runnable =
   1.157 +    new GCAndCCLogDumpRunnable(
   1.158 +        /* identifier = */ EmptyString(),
   1.159 +        /* allTraces = */ true,
   1.160 +        /* dumpChildProcesses = */ true);
   1.161 +  NS_DispatchToMainThread(runnable);
   1.162 +}
   1.163 +
   1.164 +} // anonymous namespace
   1.165 +#endif // MOZ_SUPPORTS_RT_SIGNALS }
   1.166 +
   1.167 +#if defined(MOZ_SUPPORTS_FIFO) // {
   1.168 +namespace {
   1.169 +
   1.170 +void doMemoryReport(const nsCString& inputStr)
   1.171 +{
   1.172 +  bool doMMUMemoryReport = inputStr == NS_LITERAL_CSTRING("minimize memory report");
   1.173 +  LOG("FifoWatcher(command:%s) dispatching memory report runnable.", inputStr.get());
   1.174 +  nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
   1.175 +    new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
   1.176 +                                        doMMUMemoryReport);
   1.177 +  NS_DispatchToMainThread(runnable);
   1.178 +}
   1.179 +
   1.180 +void doGCCCDump(const nsCString& inputStr)
   1.181 +{
   1.182 +  bool doAllTracesGCCCDump = inputStr == NS_LITERAL_CSTRING("gc log");
   1.183 +  LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", inputStr.get());
   1.184 +  nsRefPtr<GCAndCCLogDumpRunnable> runnable =
   1.185 +    new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
   1.186 +                              doAllTracesGCCCDump,
   1.187 +                              /* dumpChildProcesses = */ true);
   1.188 +  NS_DispatchToMainThread(runnable);
   1.189 +}
   1.190 +
   1.191 +bool SetupFifo()
   1.192 +{
   1.193 +  static bool fifoCallbacksRegistered = false;
   1.194 +
   1.195 +  if (!FifoWatcher::MaybeCreate()) {
   1.196 +    return false;
   1.197 +  }
   1.198 +
   1.199 +  MOZ_ASSERT(!fifoCallbacksRegistered,
   1.200 +             "FifoWatcher callbacks should be registered only once");
   1.201 +
   1.202 +  FifoWatcher* fw = FifoWatcher::GetSingleton();
   1.203 +  // Dump our memory reports (but run this on the main thread!).
   1.204 +  fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
   1.205 +                       doMemoryReport);
   1.206 +  fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
   1.207 +                       doMemoryReport);
   1.208 +  // Dump GC and CC logs (from the main thread).
   1.209 +  fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
   1.210 +                       doGCCCDump);
   1.211 +  fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
   1.212 +                       doGCCCDump);
   1.213 +
   1.214 +  fifoCallbacksRegistered = true;
   1.215 +  return true;
   1.216 +}
   1.217 +
   1.218 +void OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
   1.219 +{
   1.220 +  LOG("%s changed", FifoWatcher::kPrefName);
   1.221 +  if (SetupFifo()) {
   1.222 +    Preferences::UnregisterCallback(OnFifoEnabledChange,
   1.223 +                                    FifoWatcher::kPrefName,
   1.224 +                                    nullptr);
   1.225 +  }
   1.226 +}
   1.227 +
   1.228 +} // anonymous namespace
   1.229 +#endif // MOZ_SUPPORTS_FIFO }
   1.230 +
   1.231 +NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
   1.232 +
   1.233 +nsMemoryInfoDumper::nsMemoryInfoDumper()
   1.234 +{
   1.235 +}
   1.236 +
   1.237 +nsMemoryInfoDumper::~nsMemoryInfoDumper()
   1.238 +{
   1.239 +}
   1.240 +
   1.241 +/* static */ void
   1.242 +nsMemoryInfoDumper::Initialize()
   1.243 +{
   1.244 +#if defined(MOZ_SUPPORTS_RT_SIGNALS)
   1.245 +  SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
   1.246 +
   1.247 +  // Dump memory reporters (and those of our child processes)
   1.248 +  sDumpAboutMemorySignum = SIGRTMIN;
   1.249 +  sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
   1.250 +  // Dump our memory reporters after minimizing memory usage
   1.251 +  sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
   1.252 +  sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
   1.253 +  // Dump the GC and CC logs in this and our child processes.
   1.254 +  sGCAndCCDumpSignum = SIGRTMIN + 2;
   1.255 +  sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
   1.256 +#endif
   1.257 +
   1.258 +#if defined(MOZ_SUPPORTS_FIFO)
   1.259 +  if (!SetupFifo()) {
   1.260 +    // NB: This gets loaded early enough that it's possible there is a user pref
   1.261 +    //     set to enable the fifo watcher that has not been loaded yet. Register
   1.262 +    //     to attempt to initialize if the fifo watcher becomes enabled by
   1.263 +    //     a user pref.
   1.264 +    Preferences::RegisterCallback(OnFifoEnabledChange,
   1.265 +                                  FifoWatcher::kPrefName,
   1.266 +                                  nullptr);
   1.267 +  }
   1.268 +#endif
   1.269 +}
   1.270 +
   1.271 +static void
   1.272 +EnsureNonEmptyIdentifier(nsAString& aIdentifier)
   1.273 +{
   1.274 +  if (!aIdentifier.IsEmpty()) {
   1.275 +    return;
   1.276 +  }
   1.277 +
   1.278 +  // If the identifier is empty, set it to the number of whole seconds since the
   1.279 +  // epoch.  This identifier will appear in the files that this process
   1.280 +  // generates and also the files generated by this process's children, allowing
   1.281 +  // us to identify which files are from the same memory report request.
   1.282 +  aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
   1.283 +}
   1.284 +
   1.285 +NS_IMETHODIMP
   1.286 +nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
   1.287 +                                          bool aDumpAllTraces,
   1.288 +                                          bool aDumpChildProcesses,
   1.289 +                                          nsAString& aGCLogPath,
   1.290 +                                          nsAString& aCCLogPath)
   1.291 +{
   1.292 +  nsString identifier(aIdentifier);
   1.293 +  EnsureNonEmptyIdentifier(identifier);
   1.294 +
   1.295 +  if (aDumpChildProcesses) {
   1.296 +    nsTArray<ContentParent*> children;
   1.297 +    ContentParent::GetAll(children);
   1.298 +    for (uint32_t i = 0; i < children.Length(); i++) {
   1.299 +      unused << children[i]->SendDumpGCAndCCLogsToFile(
   1.300 +        identifier, aDumpAllTraces, aDumpChildProcesses);
   1.301 +    }
   1.302 +  }
   1.303 +
   1.304 +  nsCOMPtr<nsICycleCollectorListener> logger =
   1.305 +    do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
   1.306 +  logger->SetFilenameIdentifier(identifier);
   1.307 +
   1.308 +  if (aDumpAllTraces) {
   1.309 +    nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
   1.310 +    logger->AllTraces(getter_AddRefs(allTracesLogger));
   1.311 +    logger = allTracesLogger;
   1.312 +  }
   1.313 +
   1.314 +  nsJSContext::CycleCollectNow(logger);
   1.315 +
   1.316 +  logger->GetGcLogPath(aGCLogPath);
   1.317 +  logger->GetCcLogPath(aCCLogPath);
   1.318 +
   1.319 +  return NS_OK;
   1.320 +}
   1.321 +
   1.322 +namespace mozilla {
   1.323 +
   1.324 +#define DUMP(o, s) \
   1.325 +  do { \
   1.326 +    nsresult rv = (o)->Write(s); \
   1.327 +    if (NS_WARN_IF(NS_FAILED(rv))) \
   1.328 +      return rv; \
   1.329 +  } while (0)
   1.330 +
   1.331 +class DumpReportCallback MOZ_FINAL : public nsIHandleReportCallback
   1.332 +{
   1.333 +public:
   1.334 +  NS_DECL_ISUPPORTS
   1.335 +
   1.336 +  DumpReportCallback(nsGZFileWriter* aWriter)
   1.337 +    : mIsFirst(true)
   1.338 +    , mWriter(aWriter)
   1.339 +  {}
   1.340 +
   1.341 +  NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
   1.342 +      int32_t aKind, int32_t aUnits, int64_t aAmount,
   1.343 +      const nsACString &aDescription,
   1.344 +      nsISupports *aData)
   1.345 +  {
   1.346 +    if (mIsFirst) {
   1.347 +      DUMP(mWriter, "[");
   1.348 +      mIsFirst = false;
   1.349 +    } else {
   1.350 +      DUMP(mWriter, ",");
   1.351 +    }
   1.352 +
   1.353 +    nsAutoCString process;
   1.354 +    if (aProcess.IsEmpty()) {
   1.355 +      // If the process is empty, the report originated with the process doing
   1.356 +      // the dumping.  In that case, generate the process identifier, which is of
   1.357 +      // the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we don't
   1.358 +      // have a process name.  If we're the main process, we let $PROCESS_NAME be
   1.359 +      // "Main Process".
   1.360 +      if (XRE_GetProcessType() == GeckoProcessType_Default) {
   1.361 +        // We're the main process.
   1.362 +        process.AssignLiteral("Main Process");
   1.363 +      } else if (ContentChild *cc = ContentChild::GetSingleton()) {
   1.364 +        // Try to get the process name from ContentChild.
   1.365 +        cc->GetProcessName(process);
   1.366 +      }
   1.367 +      ContentChild::AppendProcessId(process);
   1.368 +
   1.369 +    } else {
   1.370 +      // Otherwise, the report originated with another process and already has a
   1.371 +      // process name.  Just use that.
   1.372 +      process = aProcess;
   1.373 +    }
   1.374 +
   1.375 +    DUMP(mWriter, "\n    {\"process\": \"");
   1.376 +    DUMP(mWriter, process);
   1.377 +
   1.378 +    DUMP(mWriter, "\", \"path\": \"");
   1.379 +    nsCString path(aPath);
   1.380 +    path.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
   1.381 +    path.ReplaceSubstring("\"", "\\\"");    // " --> \"
   1.382 +    DUMP(mWriter, path);
   1.383 +
   1.384 +    DUMP(mWriter, "\", \"kind\": ");
   1.385 +    DUMP(mWriter, nsPrintfCString("%d", aKind));
   1.386 +
   1.387 +    DUMP(mWriter, ", \"units\": ");
   1.388 +    DUMP(mWriter, nsPrintfCString("%d", aUnits));
   1.389 +
   1.390 +    DUMP(mWriter, ", \"amount\": ");
   1.391 +    DUMP(mWriter, nsPrintfCString("%lld", aAmount));
   1.392 +
   1.393 +    nsCString description(aDescription);
   1.394 +    description.ReplaceSubstring("\\", "\\\\");    /* <backslash> --> \\ */
   1.395 +    description.ReplaceSubstring("\"", "\\\"");    // " --> \"
   1.396 +    description.ReplaceSubstring("\n", "\\n");     // <newline> --> \n
   1.397 +    DUMP(mWriter, ", \"description\": \"");
   1.398 +    DUMP(mWriter, description);
   1.399 +    DUMP(mWriter, "\"}");
   1.400 +
   1.401 +    return NS_OK;
   1.402 +  }
   1.403 +
   1.404 +private:
   1.405 +  bool mIsFirst;
   1.406 +  nsRefPtr<nsGZFileWriter> mWriter;
   1.407 +};
   1.408 +
   1.409 +NS_IMPL_ISUPPORTS(DumpReportCallback, nsIHandleReportCallback)
   1.410 +
   1.411 +} // namespace mozilla
   1.412 +
   1.413 +static void
   1.414 +MakeFilename(const char *aPrefix, const nsAString &aIdentifier,
   1.415 +             const char *aSuffix, nsACString &aResult)
   1.416 +{
   1.417 +  aResult = nsPrintfCString("%s-%s-%d.%s",
   1.418 +                            aPrefix,
   1.419 +                            NS_ConvertUTF16toUTF8(aIdentifier).get(),
   1.420 +                            getpid(), aSuffix);
   1.421 +}
   1.422 +
   1.423 +#ifdef MOZ_DMD
   1.424 +struct DMDWriteState
   1.425 +{
   1.426 +  static const size_t kBufSize = 4096;
   1.427 +  char mBuf[kBufSize];
   1.428 +  nsRefPtr<nsGZFileWriter> mGZWriter;
   1.429 +
   1.430 +  DMDWriteState(nsGZFileWriter *aGZWriter)
   1.431 +    : mGZWriter(aGZWriter)
   1.432 +  {}
   1.433 +};
   1.434 +
   1.435 +static void
   1.436 +DMDWrite(void* aState, const char* aFmt, va_list ap)
   1.437 +{
   1.438 +  DMDWriteState *state = (DMDWriteState*)aState;
   1.439 +  vsnprintf(state->mBuf, state->kBufSize, aFmt, ap);
   1.440 +  unused << state->mGZWriter->Write(state->mBuf);
   1.441 +}
   1.442 +#endif
   1.443 +
   1.444 +static nsresult
   1.445 +DumpHeader(nsIGZFileWriter* aWriter)
   1.446 +{
   1.447 +  // Increment this number if the format changes.
   1.448 +  //
   1.449 +  // This is the first write to the file, and it causes |aWriter| to allocate
   1.450 +  // over 200 KiB of memory.
   1.451 +  //
   1.452 +  DUMP(aWriter, "{\n  \"version\": 1,\n");
   1.453 +
   1.454 +  DUMP(aWriter, "  \"hasMozMallocUsableSize\": ");
   1.455 +
   1.456 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
   1.457 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
   1.458 +  if (NS_WARN_IF(!mgr))
   1.459 +    return NS_ERROR_UNEXPECTED;
   1.460 +
   1.461 +  DUMP(aWriter, mgr->GetHasMozMallocUsableSize() ? "true" : "false");
   1.462 +  DUMP(aWriter, ",\n");
   1.463 +  DUMP(aWriter, "  \"reports\": ");
   1.464 +
   1.465 +  return NS_OK;
   1.466 +}
   1.467 +
   1.468 +static nsresult
   1.469 +DumpFooter(nsIGZFileWriter* aWriter)
   1.470 +{
   1.471 +  DUMP(aWriter, "\n  ]\n}\n");
   1.472 +
   1.473 +  return NS_OK;
   1.474 +}
   1.475 +
   1.476 +class TempDirMemoryFinishCallback MOZ_FINAL : public nsIFinishReportingCallback
   1.477 +{
   1.478 +public:
   1.479 +  NS_DECL_ISUPPORTS
   1.480 +
   1.481 +  TempDirMemoryFinishCallback(nsGZFileWriter *aWriter,
   1.482 +                              nsIFile *aTmpFile,
   1.483 +                              const nsCString &aFilename,
   1.484 +                              const nsString &aIdentifier)
   1.485 +    : mrWriter(aWriter)
   1.486 +    , mrTmpFile(aTmpFile)
   1.487 +    , mrFilename(aFilename)
   1.488 +    , mIdentifier(aIdentifier)
   1.489 +  {}
   1.490 +
   1.491 +  NS_IMETHOD Callback(nsISupports *aData);
   1.492 +
   1.493 +private:
   1.494 +  nsRefPtr<nsGZFileWriter> mrWriter;
   1.495 +  nsCOMPtr<nsIFile> mrTmpFile;
   1.496 +  nsCString mrFilename;
   1.497 +  nsString mIdentifier;
   1.498 +};
   1.499 +
   1.500 +NS_IMPL_ISUPPORTS(TempDirMemoryFinishCallback, nsIFinishReportingCallback)
   1.501 +
   1.502 +NS_IMETHODIMP
   1.503 +nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
   1.504 +                                            bool aMinimizeMemoryUsage)
   1.505 +{
   1.506 +  nsString identifier(aIdentifier);
   1.507 +  EnsureNonEmptyIdentifier(identifier);
   1.508 +
   1.509 +#ifdef MOZ_DMD
   1.510 +  // Clear DMD's reportedness state before running the memory reporters, to
   1.511 +  // avoid spurious twice-reported warnings.
   1.512 +  dmd::ClearReports();
   1.513 +#endif
   1.514 +
   1.515 +  // Open a new file named something like
   1.516 +  //
   1.517 +  //   incomplete-memory-report-<identifier>-<pid>.json.gz
   1.518 +  //
   1.519 +  // in NS_OS_TEMP_DIR for writing.  When we're finished writing the report,
   1.520 +  // we'll rename this file and get rid of the "incomplete-" prefix.
   1.521 +  //
   1.522 +  // We do this because we don't want scripts which poll the filesystem
   1.523 +  // looking for memory report dumps to grab a file before we're finished
   1.524 +  // writing to it.
   1.525 +
   1.526 +  // Note that |mrFilename| is missing the "incomplete-" prefix; we'll tack
   1.527 +  // that on in a moment.
   1.528 +  nsCString mrFilename;
   1.529 +  // The "unified" indicates that we merge the memory reports from all
   1.530 +  // processes and write out one file, rather than a separate file for
   1.531 +  // each process as was the case before bug 946407.  This is so that
   1.532 +  // the get_about_memory.py script in the B2G repository can
   1.533 +  // determine when it's done waiting for files to appear.
   1.534 +  MakeFilename("unified-memory-report", identifier, "json.gz", mrFilename);
   1.535 +
   1.536 +  nsCOMPtr<nsIFile> mrTmpFile;
   1.537 +  nsresult rv;
   1.538 +  // In Android case, this function will open a file named aFilename under
   1.539 +  // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
   1.540 +  // open a file named aFilename under "NS_OS_TEMP_DIR".
   1.541 +  rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
   1.542 +                                 mrFilename,
   1.543 +                                 getter_AddRefs(mrTmpFile),
   1.544 +                                 NS_LITERAL_CSTRING("memory-reports"));
   1.545 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.546 +    return rv;
   1.547 +
   1.548 +  nsRefPtr<nsGZFileWriter> mrWriter = new nsGZFileWriter();
   1.549 +  rv = mrWriter->Init(mrTmpFile);
   1.550 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.551 +    return rv;
   1.552 +
   1.553 +  // Dump the memory reports to the file.
   1.554 +  rv = DumpHeader(mrWriter);
   1.555 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.556 +    return rv;
   1.557 +
   1.558 +  // Process reporters.
   1.559 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
   1.560 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
   1.561 +  nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   1.562 +  nsRefPtr<nsIFinishReportingCallback> finishReport =
   1.563 +    new TempDirMemoryFinishCallback(mrWriter, mrTmpFile, mrFilename, identifier);
   1.564 +  rv = mgr->GetReportsExtended(dumpReport, nullptr,
   1.565 +                               finishReport, nullptr,
   1.566 +                               aMinimizeMemoryUsage,
   1.567 +                               identifier);
   1.568 +  return rv;
   1.569 +}
   1.570 +
   1.571 +#ifdef MOZ_DMD
   1.572 +nsresult
   1.573 +nsMemoryInfoDumper::DumpDMD(const nsAString &aIdentifier)
   1.574 +{
   1.575 +  if (!dmd::IsEnabled()) {
   1.576 +    return NS_OK;
   1.577 +  }
   1.578 +
   1.579 +  nsresult rv;
   1.580 +
   1.581 +  // Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
   1.582 +  // if DMD is enabled.
   1.583 +  nsCString dmdFilename;
   1.584 +  MakeFilename("dmd", aIdentifier, "txt.gz", dmdFilename);
   1.585 +
   1.586 +  // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
   1.587 +  // and dump DMD output to it.  This must occur after the memory reporters
   1.588 +  // have been run (above), but before the memory-reports file has been
   1.589 +  // renamed (so scripts can detect the DMD file, if present).
   1.590 +
   1.591 +  nsCOMPtr<nsIFile> dmdFile;
   1.592 +  rv = nsDumpUtils::OpenTempFile(dmdFilename,
   1.593 +                                 getter_AddRefs(dmdFile),
   1.594 +                                 NS_LITERAL_CSTRING("memory-reports"));
   1.595 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.596 +    return rv;
   1.597 +
   1.598 +  nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
   1.599 +  rv = dmdWriter->Init(dmdFile);
   1.600 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.601 +    return rv;
   1.602 +
   1.603 +  // Dump DMD output to the file.
   1.604 +
   1.605 +  DMDWriteState state(dmdWriter);
   1.606 +  dmd::Writer w(DMDWrite, &state);
   1.607 +  dmd::Dump(w);
   1.608 +
   1.609 +  rv = dmdWriter->Finish();
   1.610 +  NS_WARN_IF(NS_FAILED(rv));
   1.611 +  return rv;
   1.612 +}
   1.613 +#endif  // MOZ_DMD
   1.614 +
   1.615 +NS_IMETHODIMP
   1.616 +TempDirMemoryFinishCallback::Callback(nsISupports *aData)
   1.617 +{
   1.618 +  nsresult rv;
   1.619 +
   1.620 +  rv = DumpFooter(mrWriter);
   1.621 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.622 +    return rv;
   1.623 +
   1.624 +  // The call to Finish() deallocates the memory allocated by mrWriter's first
   1.625 +  // DUMP() call (within DumpProcessMemoryReportsToGZFileWriter()).  Because
   1.626 +  // that memory was live while the memory reporters ran and thus measured by
   1.627 +  // them -- by "heap-allocated" if nothing else -- we want DMD to see it as
   1.628 +  // well.  So we deliberately don't call Finish() until after DMD finishes.
   1.629 +  rv = mrWriter->Finish();
   1.630 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.631 +    return rv;
   1.632 +
   1.633 +  // Rename the memory reports file, now that we're done writing all the files.
   1.634 +  // Its final name is "memory-report<-identifier>-<pid>.json.gz".
   1.635 +
   1.636 +  nsCOMPtr<nsIFile> mrFinalFile;
   1.637 +  rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mrFinalFile));
   1.638 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.639 +    return rv;
   1.640 +
   1.641 +#ifdef ANDROID
   1.642 +  rv = mrFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
   1.643 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.644 +    return rv;
   1.645 +#endif
   1.646 +
   1.647 +  rv = mrFinalFile->AppendNative(mrFilename);
   1.648 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.649 +    return rv;
   1.650 +
   1.651 +  rv = mrFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   1.652 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.653 +    return rv;
   1.654 +
   1.655 +  nsAutoString mrActualFinalFilename;
   1.656 +  rv = mrFinalFile->GetLeafName(mrActualFinalFilename);
   1.657 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.658 +    return rv;
   1.659 +
   1.660 +  rv = mrTmpFile->MoveTo(/* directory */ nullptr, mrActualFinalFilename);
   1.661 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.662 +    return rv;
   1.663 +
   1.664 +  // Write a message to the console.
   1.665 +
   1.666 +  nsCOMPtr<nsIConsoleService> cs =
   1.667 +    do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
   1.668 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.669 +    return rv;
   1.670 +
   1.671 +  nsString path;
   1.672 +  mrTmpFile->GetPath(path);
   1.673 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.674 +    return rv;
   1.675 +
   1.676 +  nsString msg = NS_LITERAL_STRING(
   1.677 +    "nsIMemoryInfoDumper dumped reports to ");
   1.678 +  msg.Append(path);
   1.679 +  return cs->LogStringMessage(msg.get());
   1.680 +}
   1.681 +
   1.682 +// This dumps the JSON footer and closes the file, and then calls the given
   1.683 +// nsIFinishDumpingCallback.
   1.684 +class FinishReportingCallback MOZ_FINAL : public nsIFinishReportingCallback
   1.685 +{
   1.686 +public:
   1.687 +  NS_DECL_ISUPPORTS
   1.688 +
   1.689 +  FinishReportingCallback(nsIFinishDumpingCallback* aFinishDumping,
   1.690 +                          nsISupports* aFinishDumpingData)
   1.691 +    : mFinishDumping(aFinishDumping)
   1.692 +    , mFinishDumpingData(aFinishDumpingData)
   1.693 +  {}
   1.694 +
   1.695 +  NS_IMETHOD Callback(nsISupports* aData)
   1.696 +  {
   1.697 +    nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
   1.698 +    NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
   1.699 +
   1.700 +    nsresult rv = DumpFooter(writer);
   1.701 +    NS_ENSURE_SUCCESS(rv, rv);
   1.702 +
   1.703 +    rv = writer->Finish();
   1.704 +    NS_ENSURE_SUCCESS(rv, rv);
   1.705 +
   1.706 +    if (!mFinishDumping) {
   1.707 +      return NS_OK;
   1.708 +    }
   1.709 +
   1.710 +    return mFinishDumping->Callback(mFinishDumpingData);
   1.711 +  }
   1.712 +
   1.713 +private:
   1.714 +  nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
   1.715 +  nsCOMPtr<nsISupports> mFinishDumpingData;
   1.716 +};
   1.717 +
   1.718 +NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)
   1.719 +
   1.720 +NS_IMETHODIMP
   1.721 +nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
   1.722 +  const nsAString& aFilename,
   1.723 +  nsIFinishDumpingCallback* aFinishDumping,
   1.724 +  nsISupports* aFinishDumpingData)
   1.725 +{
   1.726 +  MOZ_ASSERT(!aFilename.IsEmpty());
   1.727 +
   1.728 +  // Create the file.
   1.729 +
   1.730 +  nsCOMPtr<nsIFile> mrFile;
   1.731 +  nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(mrFile));
   1.732 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.733 +    return rv;
   1.734 +
   1.735 +  mrFile->InitWithPath(aFilename);
   1.736 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.737 +    return rv;
   1.738 +
   1.739 +  bool exists;
   1.740 +  rv = mrFile->Exists(&exists);
   1.741 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.742 +    return rv;
   1.743 +
   1.744 +  if (!exists) {
   1.745 +    rv = mrFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   1.746 +    if (NS_WARN_IF(NS_FAILED(rv)))
   1.747 +      return rv;
   1.748 +  }
   1.749 +
   1.750 +  // Write the memory reports to the file.
   1.751 +
   1.752 +  nsRefPtr<nsGZFileWriter> mrWriter = new nsGZFileWriter();
   1.753 +  rv = mrWriter->Init(mrFile);
   1.754 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.755 +    return rv;
   1.756 +
   1.757 +  rv = DumpHeader(mrWriter);
   1.758 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.759 +    return rv;
   1.760 +
   1.761 +  // Process reports and finish up.
   1.762 +  nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   1.763 +  nsRefPtr<FinishReportingCallback> finishReporting =
   1.764 +    new FinishReportingCallback(aFinishDumping, aFinishDumpingData);
   1.765 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
   1.766 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
   1.767 +  return mgr->GetReports(dumpReport, nullptr, finishReporting, mrWriter);
   1.768 +}
   1.769 +
   1.770 +#undef DUMP

mercurial