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.

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

mercurial