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/JSONWriter.h"
8 #include "mozilla/UniquePtr.h"
9 #include "mozilla/nsMemoryInfoDumper.h"
10 #include "mozilla/DebugOnly.h"
11 #include "nsDumpUtils.h"
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 "nsServiceManagerUtils.h"
30 # define getpid _getpid
37 # define MOZ_SUPPORTS_FIFO 1
40 // Some Android devices seem to send RT signals to Firefox so we want to avoid
41 // consuming those as they're not user triggered.
42 #if !defined(ANDROID) && (defined(XP_LINUX) || defined(__FreeBSD__))
43 # define MOZ_SUPPORTS_RT_SIGNALS 1
46 #if defined(MOZ_SUPPORTS_RT_SIGNALS)
48 # include <sys/types.h>
49 # include <sys/stat.h>
52 #if defined(MOZ_SUPPORTS_FIFO)
53 # include "mozilla/Preferences.h"
56 using namespace mozilla
;
57 using namespace mozilla::dom
;
61 class DumpMemoryInfoToTempDirRunnable
: public Runnable
{
63 DumpMemoryInfoToTempDirRunnable(const nsAString
& aIdentifier
, bool aAnonymize
,
64 bool aMinimizeMemoryUsage
)
65 : mozilla::Runnable("DumpMemoryInfoToTempDirRunnable"),
66 mIdentifier(aIdentifier
),
67 mAnonymize(aAnonymize
),
68 mMinimizeMemoryUsage(aMinimizeMemoryUsage
) {}
70 NS_IMETHOD
Run() override
{
71 nsCOMPtr
<nsIMemoryInfoDumper
> dumper
=
72 do_GetService("@mozilla.org/memory-info-dumper;1");
73 dumper
->DumpMemoryInfoToTempDir(mIdentifier
, mAnonymize
,
74 mMinimizeMemoryUsage
);
79 const nsString mIdentifier
;
80 const bool mAnonymize
;
81 const bool mMinimizeMemoryUsage
;
84 class GCAndCCLogDumpRunnable final
: public Runnable
,
85 public nsIDumpGCAndCCLogsCallback
{
87 NS_DECL_ISUPPORTS_INHERITED
89 GCAndCCLogDumpRunnable(const nsAString
& aIdentifier
, bool aDumpAllTraces
,
90 bool aDumpChildProcesses
)
91 : mozilla::Runnable("GCAndCCLogDumpRunnable"),
92 mIdentifier(aIdentifier
),
93 mDumpAllTraces(aDumpAllTraces
),
94 mDumpChildProcesses(aDumpChildProcesses
) {}
96 NS_IMETHOD
Run() override
{
97 nsCOMPtr
<nsIMemoryInfoDumper
> dumper
=
98 do_GetService("@mozilla.org/memory-info-dumper;1");
100 dumper
->DumpGCAndCCLogsToFile(mIdentifier
, mDumpAllTraces
,
101 mDumpChildProcesses
, this);
105 NS_IMETHOD
OnDump(nsIFile
* aGCLog
, nsIFile
* aCCLog
, bool aIsParent
) override
{
109 NS_IMETHOD
OnFinish() override
{ return NS_OK
; }
112 ~GCAndCCLogDumpRunnable() = default;
114 const nsString mIdentifier
;
115 const bool mDumpAllTraces
;
116 const bool mDumpChildProcesses
;
119 NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable
, Runnable
,
120 nsIDumpGCAndCCLogsCallback
)
124 #if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
128 * The following code supports dumping about:memory upon receiving a signal.
130 * We listen for the following signals:
132 * - SIGRTMIN: Dump our memory reporters (and those of our child
134 * - SIGRTMIN + 1: Dump our memory reporters (and those of our child
135 * processes) after minimizing memory usage, and
136 * - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
138 * When we receive one of these signals, we write the signal number to a pipe.
139 * The IO thread then notices that the pipe has been written to, and kicks off
140 * the appropriate task on the main thread.
142 * This scheme is similar to using signalfd(), except it's portable and it
143 * doesn't require the use of sigprocmask, which is problematic because it
144 * masks signals received by child processes.
146 * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
147 * But that uses libevent, which does not handle the realtime signals (bug
151 // It turns out that at least on some systems, SIGRTMIN is not a compile-time
152 // constant, so these have to be set at runtime.
153 static uint8_t sDumpAboutMemorySignum
; // SIGRTMIN
154 static uint8_t sDumpAboutMemoryAfterMMUSignum
; // SIGRTMIN + 1
155 static uint8_t sGCAndCCDumpSignum
; // SIGRTMIN + 2
157 void doMemoryReport(const uint8_t aRecvSig
) {
158 // Dump our memory reports (but run this on the main thread!).
159 bool minimize
= aRecvSig
== sDumpAboutMemoryAfterMMUSignum
;
160 LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig
);
161 RefPtr
<DumpMemoryInfoToTempDirRunnable
> runnable
=
162 new DumpMemoryInfoToTempDirRunnable(/* identifier = */ u
""_ns
,
163 /* anonymize = */ false, minimize
);
164 NS_DispatchToMainThread(runnable
);
167 void doGCCCDump(const uint8_t aRecvSig
) {
168 LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig
);
169 // Dump GC and CC logs (from the main thread).
170 RefPtr
<GCAndCCLogDumpRunnable
> runnable
=
171 new GCAndCCLogDumpRunnable(/* identifier = */ u
""_ns
,
172 /* allTraces = */ true,
173 /* dumpChildProcesses = */ true);
174 NS_DispatchToMainThread(runnable
);
178 #endif // MOZ_SUPPORTS_RT_SIGNALS }
180 #if defined(MOZ_SUPPORTS_FIFO) // {
183 void doMemoryReport(const nsCString
& aInputStr
) {
184 bool minimize
= aInputStr
.EqualsLiteral("minimize memory report");
185 LOG("FifoWatcher(command:%s) dispatching memory report runnable.",
187 RefPtr
<DumpMemoryInfoToTempDirRunnable
> runnable
=
188 new DumpMemoryInfoToTempDirRunnable(/* identifier = */ u
""_ns
,
189 /* anonymize = */ false, minimize
);
190 NS_DispatchToMainThread(runnable
);
193 void doGCCCDump(const nsCString
& aInputStr
) {
194 bool doAllTracesGCCCDump
= aInputStr
.EqualsLiteral("gc log");
195 LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.",
197 RefPtr
<GCAndCCLogDumpRunnable
> runnable
= new GCAndCCLogDumpRunnable(
198 /* identifier = */ u
""_ns
, doAllTracesGCCCDump
,
199 /* dumpChildProcesses = */ true);
200 NS_DispatchToMainThread(runnable
);
205 static bool fifoCallbacksRegistered
= false;
208 if (!FifoWatcher::MaybeCreate()) {
212 MOZ_ASSERT(!fifoCallbacksRegistered
,
213 "FifoWatcher callbacks should be registered only once");
215 FifoWatcher
* fw
= FifoWatcher::GetSingleton();
216 // Dump our memory reports (but run this on the main thread!).
217 fw
->RegisterCallback("memory report"_ns
, doMemoryReport
);
218 fw
->RegisterCallback("minimize memory report"_ns
, doMemoryReport
);
219 // Dump GC and CC logs (from the main thread).
220 fw
->RegisterCallback("gc log"_ns
, doGCCCDump
);
221 fw
->RegisterCallback("abbreviated gc log"_ns
, doGCCCDump
);
224 fifoCallbacksRegistered
= true;
229 void OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/) {
230 LOG("%s changed", FifoWatcher::kPrefName
);
232 Preferences::UnregisterCallback(OnFifoEnabledChange
,
233 FifoWatcher::kPrefName
);
238 #endif // MOZ_SUPPORTS_FIFO }
240 NS_IMPL_ISUPPORTS(nsMemoryInfoDumper
, nsIMemoryInfoDumper
)
242 nsMemoryInfoDumper::nsMemoryInfoDumper() = default;
244 nsMemoryInfoDumper::~nsMemoryInfoDumper() = default;
247 void nsMemoryInfoDumper::Initialize() {
248 #if defined(MOZ_SUPPORTS_RT_SIGNALS)
249 SignalPipeWatcher
* sw
= SignalPipeWatcher::GetSingleton();
251 // Dump memory reporters (and those of our child processes)
252 sDumpAboutMemorySignum
= SIGRTMIN
;
253 sw
->RegisterCallback(sDumpAboutMemorySignum
, doMemoryReport
);
254 // Dump our memory reporters after minimizing memory usage
255 sDumpAboutMemoryAfterMMUSignum
= SIGRTMIN
+ 1;
256 sw
->RegisterCallback(sDumpAboutMemoryAfterMMUSignum
, doMemoryReport
);
257 // Dump the GC and CC logs in this and our child processes.
258 sGCAndCCDumpSignum
= SIGRTMIN
+ 2;
259 sw
->RegisterCallback(sGCAndCCDumpSignum
, doGCCCDump
);
262 #if defined(MOZ_SUPPORTS_FIFO)
264 // NB: This gets loaded early enough that it's possible there is a user pref
265 // set to enable the fifo watcher that has not been loaded yet. Register
266 // to attempt to initialize if the fifo watcher becomes enabled by
268 Preferences::RegisterCallback(OnFifoEnabledChange
, FifoWatcher::kPrefName
);
273 static void EnsureNonEmptyIdentifier(nsAString
& aIdentifier
) {
274 if (!aIdentifier
.IsEmpty()) {
278 // If the identifier is empty, set it to the number of whole seconds since the
279 // epoch. This identifier will appear in the files that this process
280 // generates and also the files generated by this process's children, allowing
281 // us to identify which files are from the same memory report request.
282 aIdentifier
.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
285 // Use XPCOM refcounting to fire |onFinish| when all reference-holders
286 // (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself)
288 class nsDumpGCAndCCLogsCallbackHolder final
289 : public nsIDumpGCAndCCLogsCallback
{
293 explicit nsDumpGCAndCCLogsCallbackHolder(
294 nsIDumpGCAndCCLogsCallback
* aCallback
)
295 : mCallback(aCallback
) {}
297 NS_IMETHOD
OnFinish() override
{ return NS_ERROR_UNEXPECTED
; }
299 NS_IMETHOD
OnDump(nsIFile
* aGCLog
, nsIFile
* aCCLog
, bool aIsParent
) override
{
300 return mCallback
->OnDump(aGCLog
, aCCLog
, aIsParent
);
304 ~nsDumpGCAndCCLogsCallbackHolder() { Unused
<< mCallback
->OnFinish(); }
306 nsCOMPtr
<nsIDumpGCAndCCLogsCallback
> mCallback
;
309 NS_IMPL_ISUPPORTS(nsDumpGCAndCCLogsCallbackHolder
, nsIDumpGCAndCCLogsCallback
)
312 nsMemoryInfoDumper::DumpGCAndCCLogsToFile(
313 const nsAString
& aIdentifier
, bool aDumpAllTraces
, bool aDumpChildProcesses
,
314 nsIDumpGCAndCCLogsCallback
* aCallback
) {
315 nsString
identifier(aIdentifier
);
316 EnsureNonEmptyIdentifier(identifier
);
317 nsCOMPtr
<nsIDumpGCAndCCLogsCallback
> callbackHolder
=
318 new nsDumpGCAndCCLogsCallbackHolder(aCallback
);
320 if (aDumpChildProcesses
) {
321 nsTArray
<ContentParent
*> children
;
322 ContentParent::GetAll(children
);
323 for (uint32_t i
= 0; i
< children
.Length(); i
++) {
324 ContentParent
* cp
= children
[i
];
325 nsCOMPtr
<nsICycleCollectorLogSink
> logSink
=
326 nsCycleCollector_createLogSink();
328 logSink
->SetFilenameIdentifier(identifier
);
329 logSink
->SetProcessIdentifier(cp
->Pid());
331 Unused
<< cp
->CycleCollectWithLogs(aDumpAllTraces
, logSink
,
336 nsCOMPtr
<nsICycleCollectorListener
> logger
= nsCycleCollector_createLogger();
338 if (aDumpAllTraces
) {
339 nsCOMPtr
<nsICycleCollectorListener
> allTracesLogger
;
340 logger
->AllTraces(getter_AddRefs(allTracesLogger
));
341 logger
= allTracesLogger
;
344 nsCOMPtr
<nsICycleCollectorLogSink
> logSink
;
345 logger
->GetLogSink(getter_AddRefs(logSink
));
347 logSink
->SetFilenameIdentifier(identifier
);
349 nsJSContext::CycleCollectNow(logger
);
351 nsCOMPtr
<nsIFile
> gcLog
, ccLog
;
352 logSink
->GetGcLog(getter_AddRefs(gcLog
));
353 logSink
->GetCcLog(getter_AddRefs(ccLog
));
354 callbackHolder
->OnDump(gcLog
, ccLog
, /* parent = */ true);
360 nsMemoryInfoDumper::DumpGCAndCCLogsToSink(bool aDumpAllTraces
,
361 nsICycleCollectorLogSink
* aSink
) {
362 nsCOMPtr
<nsICycleCollectorListener
> logger
= nsCycleCollector_createLogger();
364 if (aDumpAllTraces
) {
365 nsCOMPtr
<nsICycleCollectorListener
> allTracesLogger
;
366 logger
->AllTraces(getter_AddRefs(allTracesLogger
));
367 logger
= allTracesLogger
;
370 logger
->SetLogSink(aSink
);
372 nsJSContext::CycleCollectNow(logger
);
377 static void MakeFilename(const char* aPrefix
, const nsAString
& aIdentifier
,
378 int aPid
, const char* aSuffix
, nsACString
& aResult
) {
380 nsPrintfCString("%s-%s-%d.%s", aPrefix
,
381 NS_ConvertUTF16toUTF8(aIdentifier
).get(), aPid
, aSuffix
);
384 // This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
385 // the following two problems:
386 // - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
387 // - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
388 class GZWriterWrapper
: public JSONWriteFunc
{
390 explicit GZWriterWrapper(nsGZFileWriter
* aGZWriter
) : mGZWriter(aGZWriter
) {}
392 void Write(const Span
<const char>& aStr
) override
{
393 // Ignore any failure because JSONWriteFunc doesn't have a mechanism for
395 Unused
<< mGZWriter
->Write(aStr
.data(), aStr
.size());
398 nsresult
Finish() { return mGZWriter
->Finish(); }
401 RefPtr
<nsGZFileWriter
> mGZWriter
;
404 // We need two callbacks: one that handles reports, and one that is called at
405 // the end of reporting. Both the callbacks need access to the same JSONWriter,
406 // so we implement both of them in this one class.
407 class HandleReportAndFinishReportingCallbacks final
408 : public nsIHandleReportCallback
,
409 public nsIFinishReportingCallback
{
413 HandleReportAndFinishReportingCallbacks(
414 UniquePtr
<JSONWriter
> aWriter
, nsIFinishDumpingCallback
* aFinishDumping
,
415 nsISupports
* aFinishDumpingData
)
416 : mWriter(std::move(aWriter
)),
417 mFinishDumping(aFinishDumping
),
418 mFinishDumpingData(aFinishDumpingData
) {}
420 // This is the callback for nsIHandleReportCallback.
421 NS_IMETHOD
Callback(const nsACString
& aProcess
, const nsACString
& aPath
,
422 int32_t aKind
, int32_t aUnits
, int64_t aAmount
,
423 const nsACString
& aDescription
,
424 nsISupports
* aData
) override
{
425 nsAutoCString process
;
426 if (aProcess
.IsEmpty()) {
427 // If the process is empty, the report originated with the process doing
428 // the dumping. In that case, generate the process identifier, which is
429 // of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we
430 // don't have a process name. If we're the main process, we let
431 // $PROCESS_NAME be "Main Process".
433 // `appendAboutMemoryMain()` in aboutMemory.js does much the same thing
434 // for live memory reports.
435 if (XRE_IsParentProcess()) {
436 // We're the main process.
437 process
.AssignLiteral("Main Process");
438 } else if (ContentChild
* cc
= ContentChild::GetSingleton()) {
439 // Try to get the process name from ContentChild.
440 cc
->GetProcessName(process
);
442 ContentChild::AppendProcessId(process
);
445 // Otherwise, the report originated with another process and already has a
446 // process name. Just use that.
450 mWriter
->StartObjectElement();
452 mWriter
->StringProperty("process", process
);
453 mWriter
->StringProperty("path", PromiseFlatCString(aPath
));
454 mWriter
->IntProperty("kind", aKind
);
455 mWriter
->IntProperty("units", aUnits
);
456 mWriter
->IntProperty("amount", aAmount
);
457 mWriter
->StringProperty("description", PromiseFlatCString(aDescription
));
459 mWriter
->EndObject();
464 // This is the callback for nsIFinishReportingCallback.
465 NS_IMETHOD
Callback(nsISupports
* aData
) override
{
466 mWriter
->EndArray(); // end of "reports" array
469 // The call to Finish() deallocates the memory allocated by the first Write
470 // call. Because that memory was live while the memory reporters ran and
471 // was measured by them -- by "heap-allocated" if nothing else -- we want
472 // DMD to see it as well. So we deliberately don't call Finish() until
473 // after DMD finishes.
474 nsresult rv
= static_cast<GZWriterWrapper
*>(mWriter
->WriteFunc())->Finish();
475 NS_ENSURE_SUCCESS(rv
, rv
);
477 if (!mFinishDumping
) {
481 return mFinishDumping
->Callback(mFinishDumpingData
);
485 ~HandleReportAndFinishReportingCallbacks() = default;
487 UniquePtr
<JSONWriter
> mWriter
;
488 nsCOMPtr
<nsIFinishDumpingCallback
> mFinishDumping
;
489 nsCOMPtr
<nsISupports
> mFinishDumpingData
;
492 NS_IMPL_ISUPPORTS(HandleReportAndFinishReportingCallbacks
,
493 nsIHandleReportCallback
, nsIFinishReportingCallback
)
495 class TempDirFinishCallback final
: public nsIFinishDumpingCallback
{
499 TempDirFinishCallback(nsIFile
* aReportsTmpFile
,
500 const nsCString
& aReportsFinalFilename
)
501 : mReportsTmpFile(aReportsTmpFile
),
502 mReportsFilename(aReportsFinalFilename
) {}
504 NS_IMETHOD
Callback(nsISupports
* aData
) override
{
505 // Rename the memory reports file, now that we're done writing all the
506 // files. Its final name is "memory-report<-identifier>-<pid>.json.gz".
508 nsCOMPtr
<nsIFile
> reportsFinalFile
;
509 nsresult rv
= NS_GetSpecialDirectory(NS_OS_TEMP_DIR
,
510 getter_AddRefs(reportsFinalFile
));
511 if (NS_WARN_IF(NS_FAILED(rv
))) {
516 rv
= reportsFinalFile
->AppendNative("memory-reports"_ns
);
517 if (NS_WARN_IF(NS_FAILED(rv
))) {
522 rv
= reportsFinalFile
->AppendNative(mReportsFilename
);
523 if (NS_WARN_IF(NS_FAILED(rv
))) {
527 rv
= reportsFinalFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
528 if (NS_WARN_IF(NS_FAILED(rv
))) {
532 nsAutoString reportsFinalFilename
;
533 rv
= reportsFinalFile
->GetLeafName(reportsFinalFilename
);
534 if (NS_WARN_IF(NS_FAILED(rv
))) {
538 rv
= mReportsTmpFile
->MoveTo(/* directory */ nullptr, reportsFinalFilename
);
539 if (NS_WARN_IF(NS_FAILED(rv
))) {
543 // Write a message to the console.
545 nsCOMPtr
<nsIConsoleService
> cs
=
546 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
);
547 if (NS_WARN_IF(NS_FAILED(rv
))) {
552 mReportsTmpFile
->GetPath(path
);
553 if (NS_WARN_IF(NS_FAILED(rv
))) {
557 nsString msg
= u
"nsIMemoryInfoDumper dumped reports to "_ns
;
559 return cs
->LogStringMessage(msg
.get());
563 ~TempDirFinishCallback() = default;
565 nsCOMPtr
<nsIFile
> mReportsTmpFile
;
566 nsCString mReportsFilename
;
569 NS_IMPL_ISUPPORTS(TempDirFinishCallback
, nsIFinishDumpingCallback
)
571 static nsresult
DumpMemoryInfoToFile(nsIFile
* aReportsFile
,
572 nsIFinishDumpingCallback
* aFinishDumping
,
573 nsISupports
* aFinishDumpingData
,
574 bool aAnonymize
, bool aMinimizeMemoryUsage
,
575 nsAString
& aDMDIdentifier
) {
576 RefPtr
<nsGZFileWriter
> gzWriter
= new nsGZFileWriter();
577 nsresult rv
= gzWriter
->Init(aReportsFile
);
578 if (NS_WARN_IF(NS_FAILED(rv
))) {
582 MakeUnique
<JSONWriter
>(MakeUnique
<GZWriterWrapper
>(gzWriter
));
584 nsCOMPtr
<nsIMemoryReporterManager
> mgr
=
585 do_GetService("@mozilla.org/memory-reporter-manager;1");
587 // This is the first write to the file, and it causes |aWriter| to allocate
588 // over 200 KiB of memory.
591 // Increment this number if the format changes.
592 jsonWriter
->IntProperty("version", 1);
593 jsonWriter
->BoolProperty("hasMozMallocUsableSize",
594 mgr
->GetHasMozMallocUsableSize());
595 jsonWriter
->StartArrayProperty("reports");
598 RefPtr
<HandleReportAndFinishReportingCallbacks
>
599 handleReportAndFinishReporting
=
600 new HandleReportAndFinishReportingCallbacks(
601 std::move(jsonWriter
), aFinishDumping
, aFinishDumpingData
);
602 rv
= mgr
->GetReportsExtended(
603 handleReportAndFinishReporting
, nullptr, handleReportAndFinishReporting
,
604 nullptr, aAnonymize
, aMinimizeMemoryUsage
, aDMDIdentifier
);
609 nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
610 const nsAString
& aFilename
, nsIFinishDumpingCallback
* aFinishDumping
,
611 nsISupports
* aFinishDumpingData
, bool aAnonymize
,
612 bool aMinimizeMemoryUsage
) {
613 MOZ_ASSERT(!aFilename
.IsEmpty());
617 nsCOMPtr
<nsIFile
> reportsFile
;
618 nsresult rv
= NS_NewLocalFile(aFilename
, false, getter_AddRefs(reportsFile
));
619 if (NS_WARN_IF(NS_FAILED(rv
))) {
623 reportsFile
->InitWithPath(aFilename
);
624 if (NS_WARN_IF(NS_FAILED(rv
))) {
629 rv
= reportsFile
->Exists(&exists
);
630 if (NS_WARN_IF(NS_FAILED(rv
))) {
635 rv
= reportsFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
636 if (NS_WARN_IF(NS_FAILED(rv
))) {
642 return DumpMemoryInfoToFile(reportsFile
, aFinishDumping
, aFinishDumpingData
,
643 aAnonymize
, aMinimizeMemoryUsage
, dmdIdent
);
647 nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString
& aIdentifier
,
649 bool aMinimizeMemoryUsage
) {
650 nsString
identifier(aIdentifier
);
651 EnsureNonEmptyIdentifier(identifier
);
653 // Open a new file named something like
655 // incomplete-memory-report-<identifier>-<pid>.json.gz
657 // in NS_OS_TEMP_DIR for writing. When we're finished writing the report,
658 // we'll rename this file and get rid of the "incomplete-" prefix.
660 // We do this because we don't want scripts which poll the filesystem
661 // looking for memory report dumps to grab a file before we're finished
664 // The "unified" indicates that we merge the memory reports from all
665 // processes and write out one file, rather than a separate file for
666 // each process as was the case before bug 946407. This is so that
667 // the get_about_memory.py script in the B2G repository can
668 // determine when it's done waiting for files to appear.
669 nsCString reportsFinalFilename
;
670 MakeFilename("unified-memory-report", identifier
, getpid(), "json.gz",
671 reportsFinalFilename
);
673 nsCOMPtr
<nsIFile
> reportsTmpFile
;
675 // In Android case, this function will open a file named aFilename under
676 // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
677 // open a file named aFilename under "NS_OS_TEMP_DIR".
678 rv
= nsDumpUtils::OpenTempFile("incomplete-"_ns
+ reportsFinalFilename
,
679 getter_AddRefs(reportsTmpFile
),
680 "memory-reports"_ns
);
681 if (NS_WARN_IF(NS_FAILED(rv
))) {
685 RefPtr
<TempDirFinishCallback
> finishDumping
=
686 new TempDirFinishCallback(reportsTmpFile
, reportsFinalFilename
);
688 return DumpMemoryInfoToFile(reportsTmpFile
, finishDumping
, nullptr,
689 aAnonymize
, aMinimizeMemoryUsage
, identifier
);
693 dmd::DMDFuncs::Singleton
dmd::DMDFuncs::sSingleton
;
695 nsresult
nsMemoryInfoDumper::OpenDMDFile(const nsAString
& aIdentifier
, int aPid
,
697 if (!dmd::IsRunning()) {
702 // Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
703 // if DMD is enabled.
704 nsCString dmdFilename
;
705 MakeFilename("dmd", aIdentifier
, aPid
, "json.gz", dmdFilename
);
707 // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
708 // and dump DMD output to it. This must occur after the memory reporters
709 // have been run (above), but before the memory-reports file has been
710 // renamed (so scripts can detect the DMD file, if present).
713 nsCOMPtr
<nsIFile
> dmdFile
;
714 rv
= nsDumpUtils::OpenTempFile(dmdFilename
, getter_AddRefs(dmdFile
),
715 "memory-reports"_ns
);
716 if (NS_WARN_IF(NS_FAILED(rv
))) {
719 rv
= dmdFile
->OpenANSIFileDesc("wb", aOutFile
);
720 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "OpenANSIFileDesc failed");
722 // Print the path, because on some platforms (e.g. Mac) it's not obvious.
723 dmd::StatusMsg("opened %s for writing\n", dmdFile
->HumanReadablePath().get());
728 nsresult
nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile
) {
729 RefPtr
<nsGZFileWriter
> gzWriter
= new nsGZFileWriter();
730 nsresult rv
= gzWriter
->InitANSIFileDesc(aFile
);
731 if (NS_WARN_IF(NS_FAILED(rv
))) {
735 // Dump DMD's memory reports analysis to the file.
736 dmd::Analyze(MakeUnique
<GZWriterWrapper
>(gzWriter
));
738 rv
= gzWriter
->Finish();
739 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Finish failed");