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