Bug 946407 - Reimplement DumpMemoryInfoToTempDir in terms of GetReports. r=njn

This winds up exposing things in the nsIMemoryReporterManager interface
that arguably don't belong at that level of abstraction -- "minimize
memory usage first" and DMD -- in order to take advantage of the
infrastructure that GetReports already has for managing the child
processes.
This commit is contained in:
Jed Davis 2014-03-04 18:27:13 -08:00
parent b4dd408494
commit 6a4c4691a0
13 changed files with 280 additions and 146 deletions

View File

@ -172,14 +172,24 @@ static bool sNuwaForking = false;
namespace mozilla {
namespace dom {
class MemoryReportRequestChild : public PMemoryReportRequestChild
class MemoryReportRequestChild : public PMemoryReportRequestChild,
public nsIRunnable
{
public:
MemoryReportRequestChild();
NS_DECL_ISUPPORTS
MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent);
virtual ~MemoryReportRequestChild();
NS_IMETHOD Run();
private:
uint32_t mGeneration;
nsString mDMDDumpIdent;
};
MemoryReportRequestChild::MemoryReportRequestChild()
NS_IMPL_ISUPPORTS1(MemoryReportRequestChild, nsIRunnable)
MemoryReportRequestChild::MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent)
: mGeneration(aGeneration), mDMDDumpIdent(aDMDDumpIdent)
{
MOZ_COUNT_CTOR(MemoryReportRequestChild);
}
@ -518,9 +528,13 @@ ContentChild::InitXPCOM()
}
PMemoryReportRequestChild*
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& generation)
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& generation,
const bool &minimizeMemoryUsage,
const nsString& aDMDDumpIdent)
{
return new MemoryReportRequestChild();
MemoryReportRequestChild *actor = new MemoryReportRequestChild(generation, aDMDDumpIdent);
actor->AddRef();
return actor;
}
// This is just a wrapper for InfallibleTArray<MemoryReport> that implements
@ -567,25 +581,44 @@ NS_IMPL_ISUPPORTS1(
bool
ContentChild::RecvPMemoryReportRequestConstructor(
PMemoryReportRequestChild* child,
const uint32_t& generation)
const uint32_t& generation,
const bool& minimizeMemoryUsage,
const nsString& aDMDDumpIdent)
{
MemoryReportRequestChild *actor = static_cast<MemoryReportRequestChild*>(child);
nsresult rv;
if (minimizeMemoryUsage) {
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
rv = mgr->MinimizeMemoryUsage(actor);
// mgr will eventually call actor->Run()
} else {
rv = actor->Run();
}
return !NS_WARN_IF(NS_FAILED(rv));
}
NS_IMETHODIMP MemoryReportRequestChild::Run()
{
ContentChild *child = static_cast<ContentChild*>(Manager());
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
InfallibleTArray<MemoryReport> reports;
nsCString process;
GetProcessName(process);
AppendProcessId(process);
child->GetProcessName(process);
child->AppendProcessId(process);
// Run the reporters. The callback will turn each measurement into a
// MemoryReport.
nsRefPtr<MemoryReportsWrapper> wrappedReports =
new MemoryReportsWrapper(&reports);
nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
mgr->GetReportsForThisProcess(cb, wrappedReports);
mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mDMDDumpIdent);
child->Send__delete__(child, generation, reports);
return true;
bool sent = Send__delete__(this, mGeneration, reports);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
bool
@ -602,19 +635,7 @@ ContentChild::RecvAudioChannelNotify()
bool
ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor)
{
delete actor;
return true;
}
bool
ContentChild::RecvDumpMemoryInfoToTempDir(const nsString& aIdentifier,
const bool& aMinimizeMemoryUsage,
const bool& aDumpChildProcesses)
{
nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
dumper->DumpMemoryInfoToTempDir(aIdentifier, aMinimizeMemoryUsage,
aDumpChildProcesses);
static_cast<MemoryReportRequestChild*>(actor)->Release();
return true;
}

View File

@ -115,22 +115,21 @@ public:
virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor) MOZ_OVERRIDE;
virtual PMemoryReportRequestChild*
AllocPMemoryReportRequestChild(const uint32_t& generation) MOZ_OVERRIDE;
AllocPMemoryReportRequestChild(const uint32_t& generation,
const bool &minimizeMemoryUsage,
const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
virtual bool
DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) MOZ_OVERRIDE;
virtual bool
RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child,
const uint32_t& generation) MOZ_OVERRIDE;
const uint32_t& generation,
const bool &minimizeMemoryUsage,
const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
virtual bool
RecvAudioChannelNotify() MOZ_OVERRIDE;
virtual bool
RecvDumpMemoryInfoToTempDir(const nsString& aIdentifier,
const bool& aMinimizeMemoryUsage,
const bool& aDumpChildProcesses) MOZ_OVERRIDE;
virtual bool
RecvDumpGCAndCCLogsToFile(const nsString& aIdentifier,
const bool& aDumpAllTraces,

View File

@ -2040,10 +2040,28 @@ ContentParent::Observe(nsISupports* aSubject,
return NS_ERROR_NOT_AVAILABLE;
}
else if (!strcmp(aTopic, "child-memory-reporter-request")) {
bool isNuwa = false;
#ifdef MOZ_NUWA_PROCESS
if (!IsNuwaProcess())
isNuwa = IsNuwaProcess();
#endif
unused << SendPMemoryReportRequestConstructor((uint32_t)(uintptr_t)aData);
if (!isNuwa) {
unsigned generation;
int minimize, identOffset = -1;
nsDependentString msg(aData);
NS_ConvertUTF16toUTF8 cmsg(msg);
if (sscanf(cmsg.get(),
"generation=%x minimize=%d DMDident=%n",
&generation, &minimize, &identOffset) < 2
|| identOffset < 0) {
return NS_ERROR_INVALID_ARG;
}
// The pre-%n part of the string should be all ASCII, so the byte
// offset in identOffset should be correct as a char offset.
MOZ_ASSERT(cmsg[identOffset - 1] == '=');
unused << SendPMemoryReportRequestConstructor(
generation, minimize, nsString(Substring(msg, identOffset)));
}
}
else if (!strcmp(aTopic, "child-gc-request")){
unused << SendGarbageCollect();
@ -2504,7 +2522,9 @@ ContentParent::RecvPIndexedDBConstructor(PIndexedDBParent* aActor)
}
PMemoryReportRequestParent*
ContentParent::AllocPMemoryReportRequestParent(const uint32_t& generation)
ContentParent::AllocPMemoryReportRequestParent(const uint32_t& generation,
const bool &minimizeMemoryUsage,
const nsString &aDMDDumpIdent)
{
MemoryReportRequestParent* parent = new MemoryReportRequestParent();
return parent;

View File

@ -359,7 +359,9 @@ private:
virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor) MOZ_OVERRIDE;
virtual PMemoryReportRequestParent*
AllocPMemoryReportRequestParent(const uint32_t& generation) MOZ_OVERRIDE;
AllocPMemoryReportRequestParent(const uint32_t& generation,
const bool &minimizeMemoryUsage,
const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) MOZ_OVERRIDE;
virtual PTestShellParent* AllocPTestShellParent() MOZ_OVERRIDE;

View File

@ -259,7 +259,7 @@ child:
*/
async SetProcessPrivileges(ChildPrivileges privs);
PMemoryReportRequest(uint32_t generation);
PMemoryReportRequest(uint32_t generation, bool minimizeMemoryUsage, nsString DMDDumpIdent);
/**
* Notify the AudioChannelService in the child processes.
@ -268,20 +268,11 @@ child:
async SpeakerManagerNotify();
/**
* Do a memory info dump to a file in our temp directory.
*
* For documentation on the args, see
* MemoryInfoDumper::dumpMemoryInfoToTempDir.
*/
async DumpMemoryInfoToTempDir(nsString identifier,
bool minimizeMemoryUsage,
bool dumpChildProcesses);
/**
* Dump this process's GC and CC logs.
*
* For documentation on the args, see
* MemoryInfoDumper::dumpGCAndCCLogsToFile.
* For documentation on the args, see dumpGCAndCCLogsToFile in
* nsIMemoryInfoDumper.idl
*/
async DumpGCAndCCLogsToFile(nsString identifier,
bool dumpAllTraces,

View File

@ -1412,8 +1412,7 @@ XPCJSRuntime::OutOfMemoryCallback(JSContext *cx)
// If this fails, it fails silently.
dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
/* minimizeMemoryUsage = */ false,
/* dumpChildProcesses = */ false);
/* minimizeMemoryUsage = */ false);
}
size_t

View File

@ -60,6 +60,6 @@ var MemoryObserver = {
dumpMemoryStats: function(aLabel) {
let memDumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
memDumper.dumpMemoryInfoToTempDir(aLabel, false, true);
memDumper.dumpMemoryInfoToTempDir(aLabel, /* minimize = */ false);
},
};

View File

@ -11,7 +11,7 @@ interface nsIFinishDumpingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(294df03b-e2ae-4fdd-b4fc-4c66a501e0ef)]
[scriptable, builtinclass, uuid(815bf31b-f5bd-425d-85c3-4657a7a91dad)]
interface nsIMemoryInfoDumper : nsISupports
{
/**
@ -94,19 +94,17 @@ interface nsIMemoryInfoDumper : nsISupports
/**
* Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
* reports for this process and possibly its child processes (and their
* children, recursively) to a file in the tmp directory called
* memory-reports-<identifier>-<pid>.json.gz (or something similar, such as
* memory-reports-<identifier>-<pid>-1.json.gz; no existing file will be
* overwritten).
* reports for this process and its child processes to files in the tmp
* directory called memory-reports-<identifier>-<pid>.json.gz (or something
* similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no existing
* file will be overwritten).
*
* If DMD is enabled, this method also dumps gzipped DMD output to a file in
* the tmp directory called dmd-<identifier>-<pid>.txt.gz (or something
* similar; again, no existing file will be overwritten).
*
* @param aIdentifier this identifier will appear in the filename of our
* about:memory dump and those of our children (if aDumpChildProcesses is
* true).
* about:memory dump and those of our children.
*
* If the identifier is empty, the implementation may set it arbitrarily
* and use that new value for its own dump and the dumps of its child
@ -116,15 +114,10 @@ interface nsIMemoryInfoDumper : nsISupports
* @param aMinimizeMemoryUsage indicates whether we should run a series of
* gc/cc's in an attempt to reduce our memory usage before collecting our
* memory report.
*
* @param aDumpChildProcesses indicates whether we should call
* dumpMemoryInfoToTempDir in our child processes. If
* so, the child processes will also dump their children, and so on.
*/
void dumpMemoryInfoToTempDir(
in AString aIdentifier,
in bool aMinimizeMemoryUsage,
in bool aDumpChildProcesses);
in bool aMinimizeMemoryUsage);
/**
* Dump GC and CC logs to files in the OS's temp directory (or in

View File

@ -178,7 +178,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(12e08b1b-6590-4f78-996b-d9062c17e856)]
[scriptable, builtinclass, uuid(b6e5ec8a-71d9-48db-8ae9-68b4c5bbf2c3)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@ -242,6 +242,21 @@ interface nsIMemoryReporterManager : nsISupports
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData);
/*
* As above, but: If |minimizeMemoryUsage| is true, then each process will
* minimize its memory usage (see the |minimizeMemoryUsage| method) before
* gathering its report. If DMD is enabled and |DMDDumpIdent| is non-empty
* then write a DMD report to a file in the usual temporary directory (see
* |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
*/
[noscript] void
getReportsExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData,
in boolean minimizeMemoryUsage,
in AString DMDDumpIdent);
/*
* Get memory reports in the current process only. |handleReport| is called
* for each report.
@ -249,6 +264,16 @@ interface nsIMemoryReporterManager : nsISupports
void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData);
/*
* As above, but if DMD is enabled and |DMDDumpIdent| is non-empty
* then write a DMD report to a file in the usual temporary directory (see
* |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
*/
[noscript] void
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in AString DMDDumpIdent);
/*
* The memory reporter manager, for the most part, treats reporters
* registered with it as a black box. However, there are some

View File

@ -60,25 +60,21 @@ class DumpMemoryInfoToTempDirRunnable : public nsRunnable
{
public:
DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
bool aMinimizeMemoryUsage,
bool aDumpChildProcesses)
bool aMinimizeMemoryUsage)
: mIdentifier(aIdentifier)
, mMinimizeMemoryUsage(aMinimizeMemoryUsage)
, mDumpChildProcesses(aDumpChildProcesses)
{}
NS_IMETHOD Run()
{
nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
dumper->DumpMemoryInfoToTempDir(mIdentifier, mMinimizeMemoryUsage,
mDumpChildProcesses);
dumper->DumpMemoryInfoToTempDir(mIdentifier, mMinimizeMemoryUsage);
return NS_OK;
}
private:
const nsString mIdentifier;
const bool mMinimizeMemoryUsage;
const bool mDumpChildProcesses;
};
class GCAndCCLogDumpRunnable : public nsRunnable
@ -357,8 +353,7 @@ public:
bool doMMUFirst = signum == sDumpAboutMemoryAfterMMUSignum;
nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
doMMUFirst,
/* dumpChildProcesses = */ true);
doMMUFirst);
NS_DispatchToMainThread(runnable);
}
else if (signum == sGCAndCCDumpSignum) {
@ -520,8 +515,7 @@ public:
LOG("FifoWatcher dispatching memory report runnable.");
nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
doMMUMemoryReport,
/* dumpChildProcesses = */ true);
doMMUMemoryReport);
NS_DispatchToMainThread(runnable);
} else if (doAllTracesGCCCDump || doSmallGCCCDump) {
LOG("FifoWatcher dispatching GC/CC log runnable.");
@ -830,25 +824,38 @@ DumpFooter(nsIGZFileWriter* aWriter)
return NS_OK;
}
static nsresult
DumpProcessMemoryReportsToGZFileWriter(nsGZFileWriter* aWriter)
class TempDirMemoryFinishCallback MOZ_FINAL : public nsIFinishReportingCallback
{
nsresult rv = DumpHeader(aWriter);
NS_ENSURE_SUCCESS(rv, rv);
public:
NS_DECL_ISUPPORTS
// Process reporters.
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(aWriter);
mgr->GetReportsForThisProcess(dumpReport, nullptr);
TempDirMemoryFinishCallback(nsGZFileWriter *aWriter,
nsIFile *aTmpFile,
const nsCString &aFilename,
const nsString &aIdentifier)
: mrWriter(aWriter)
, mrTmpFile(aTmpFile)
, mrFilename(aFilename)
, mIdentifier(aIdentifier)
{}
return DumpFooter(aWriter);
}
NS_IMETHOD Callback(nsISupports *aData);
nsresult
DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
private:
nsRefPtr<nsGZFileWriter> mrWriter;
nsCOMPtr<nsIFile> mrTmpFile;
nsCString mrFilename;
nsString mIdentifier;
};
NS_IMPL_ISUPPORTS1(TempDirMemoryFinishCallback, nsIFinishReportingCallback)
NS_IMETHODIMP
nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
bool aMinimizeMemoryUsage)
{
MOZ_ASSERT(!aIdentifier.IsEmpty());
nsString identifier(aIdentifier);
EnsureNonEmptyIdentifier(identifier);
#ifdef MOZ_DMD
// Clear DMD's reportedness state before running the memory reporters, to
@ -870,7 +877,12 @@ DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
// Note that |mrFilename| is missing the "incomplete-" prefix; we'll tack
// that on in a moment.
nsCString mrFilename;
MakeFilename("memory-report", aIdentifier, "json.gz", mrFilename);
// The "unified" indicates that we merge the memory reports from all
// processes and write out one file, rather than a separate file for
// each process as was the case before bug 946407. This is so that
// the get_about_memory.py script in the B2G repository can
// determine when it's done waiting for files to appear.
MakeFilename("unified-memory-report", identifier, "json.gz", mrFilename);
nsCOMPtr<nsIFile> mrTmpFile;
nsresult rv;
@ -886,9 +898,29 @@ DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
return rv;
// Dump the memory reports to the file.
DumpProcessMemoryReportsToGZFileWriter(mrWriter);
rv = DumpHeader(mrWriter);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
// Process reporters.
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
nsRefPtr<nsIFinishReportingCallback> finishReport =
new TempDirMemoryFinishCallback(mrWriter, mrTmpFile, mrFilename, identifier);
rv = mgr->GetReportsExtended(dumpReport, nullptr,
finishReport, nullptr,
aMinimizeMemoryUsage,
identifier);
return rv;
}
#ifdef MOZ_DMD
nsresult
nsMemoryInfoDumper::DumpDMD(const nsAString &aIdentifier)
{
nsresult rv;
// Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
// if DMD is enabled.
nsCString dmdFilename;
@ -916,9 +948,19 @@ DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
dmd::Dump(w);
rv = dmdWriter->Finish();
NS_WARN_IF(NS_FAILED(rv));
return rv;
}
#endif // MOZ_DMD
NS_IMETHODIMP
TempDirMemoryFinishCallback::Callback(nsISupports *aData)
{
nsresult rv;
rv = DumpFooter(mrWriter);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
#endif // MOZ_DMD
// The call to Finish() deallocates the memory allocated by mrWriter's first
// DUMP() call (within DumpProcessMemoryReportsToGZFileWriter()). Because
@ -978,44 +1020,6 @@ DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
return cs->LogStringMessage(msg.get());
}
NS_IMETHODIMP
nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
bool aMinimizeMemoryUsage,
bool aDumpChildProcesses)
{
nsString identifier(aIdentifier);
EnsureNonEmptyIdentifier(identifier);
// Kick off memory report dumps in our child processes, if applicable. We
// do this before doing our own report because writing a report may be I/O
// bound, in which case we want to busy the CPU with other reports while we
// work on our own.
if (aDumpChildProcesses) {
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
for (uint32_t i = 0; i < children.Length(); i++) {
unused << children[i]->SendDumpMemoryInfoToTempDir(
identifier, aMinimizeMemoryUsage, aDumpChildProcesses);
}
}
if (aMinimizeMemoryUsage) {
// Minimize memory usage, then run DumpMemoryInfoToTempDir again.
nsRefPtr<DumpMemoryInfoToTempDirRunnable> callback =
new DumpMemoryInfoToTempDirRunnable(identifier,
/* minimizeMemoryUsage = */ false,
/* dumpChildProcesses = */ false);
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (NS_WARN_IF(!mgr))
return NS_ERROR_FAILURE;
mgr->MinimizeMemoryUsage(callback);
return NS_OK;
}
return DumpProcessMemoryInfoToTempDir(identifier);
}
// This dumps the JSON footer and closes the file, and then calls the given
// nsIFinishDumpingCallback.
class FinishReportingCallback MOZ_FINAL : public nsIFinishReportingCallback

View File

@ -38,6 +38,9 @@ public:
* instead.
*/
static nsresult OpenTempFile(const nsACString &aFilename, nsIFile* *aFile);
#ifdef MOZ_DMD
static nsresult DumpDMD(const nsAString &aIdentifier);
#endif
};
#define NS_MEMORY_INFO_DUMPER_CID \

View File

@ -8,6 +8,7 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsMemoryReporterManager.h"
#include "nsITimer.h"
@ -952,6 +953,23 @@ nsMemoryReporterManager::GetReports(
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData)
{
return GetReportsExtended(aHandleReport, aHandleReportData,
aFinishReporting, aFinishReportingData,
/* minimize = */ false,
/* DMDident = */ nsString());
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsExtended(
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
bool aMinimize,
const nsAString& aDMDDumpIdent)
{
nsresult rv;
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
if (!NS_IsMainThread()) {
@ -979,16 +997,19 @@ nsMemoryReporterManager::GetReports(
do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_STATE(obs);
// Casting the uint32_t generation to |const char16_t*| is a hack, but
// simpler than converting the number to an actual string.
nsPrintfCString genStr("generation=%x minimize=%d DMDident=",
generation, aMinimize ? 1 : 0);
nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
msg += aDMDDumpIdent;
obs->NotifyObservers(nullptr, "child-memory-reporter-request",
(const char16_t*)(uintptr_t)generation);
msg.get());
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
nsresult rv = timer->InitWithFuncCallback(TimeoutCallback,
this, kTimeoutLengthMS,
nsITimer::TYPE_ONE_SHOT);
rv = timer->InitWithFuncCallback(TimeoutCallback,
this, kTimeoutLengthMS,
nsITimer::TYPE_ONE_SHOT);
NS_ENSURE_SUCCESS(rv, rv);
mGetReportsState = new GetReportsState(generation,
@ -997,16 +1018,40 @@ nsMemoryReporterManager::GetReports(
aHandleReport,
aHandleReportData,
aFinishReporting,
aFinishReportingData);
aFinishReportingData,
aDMDDumpIdent);
} else {
mGetReportsState = new GetReportsState(generation,
nullptr,
/* mNumChildProcesses = */ 0,
aHandleReport,
aHandleReportData,
aFinishReporting,
aFinishReportingData,
aDMDDumpIdent);
}
if (aMinimize) {
rv = MinimizeMemoryUsage(NS_NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports));
} else {
rv = StartGettingReports();
}
return rv;
}
nsresult
nsMemoryReporterManager::StartGettingReports()
{
GetReportsState *s = mGetReportsState;
// Get reports for this process.
GetReportsForThisProcess(aHandleReport, aHandleReportData);
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mDMDDumpIdent);
// If there are no child processes, we can finish up immediately.
return (mNumChildProcesses == 0)
? aFinishReporting->Callback(aFinishReportingData)
: NS_OK;
return (s->mNumChildProcesses == 0)
? FinishReporting()
: NS_OK;
}
typedef nsCOMArray<nsIMemoryReporter> MemoryReporterArray;
@ -1031,6 +1076,17 @@ NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcess(
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData)
{
return GetReportsForThisProcessExtended(aHandleReport,
aHandleReportData,
nsString());
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
const nsAString& aDMDDumpIdent)
{
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
@ -1038,6 +1094,14 @@ nsMemoryReporterManager::GetReportsForThisProcess(
MOZ_CRASH();
}
#ifdef MOZ_DMD
if (!aDMDDumpIdent.IsEmpty()) {
// Clear DMD's reportedness state before running the memory
// reporters, to avoid spurious twice-reported warnings.
dmd::ClearReports();
}
#endif
MemoryReporterArray allReporters;
{
mozilla::MutexAutoLock autoLock(mMutex);
@ -1048,6 +1112,12 @@ nsMemoryReporterManager::GetReportsForThisProcess(
allReporters[i]->CollectReports(aHandleReport, aHandleReportData);
}
#ifdef MOZ_DMD
if (!aDMDDumpIdent.IsEmpty()) {
return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
}
#endif
return NS_OK;
}
@ -1136,7 +1206,7 @@ nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
mgr->FinishReporting();
}
void
nsresult
nsMemoryReporterManager::FinishReporting()
{
// Memory reporting only happens on the main thread.
@ -1151,11 +1221,12 @@ nsMemoryReporterManager::FinishReporting()
// Call this before deleting |mGetReportsState|. That way, if
// |mFinishReportData| calls GetReports(), it will silently abort, as
// required.
(void)mGetReportsState->mFinishReporting->Callback(
nsresult rv = mGetReportsState->mFinishReporting->Callback(
mGetReportsState->mFinishReportingData);
delete mGetReportsState;
mGetReportsState = nullptr;
return rv;
}
static void

View File

@ -117,7 +117,7 @@ public:
void HandleChildReports(
const uint32_t& generation,
const InfallibleTArray<mozilla::dom::MemoryReport>& aChildReports);
void FinishReporting();
nsresult FinishReporting();
// Functions that (a) implement distinguished amounts, and (b) are outside of
// this module.
@ -157,9 +157,12 @@ public:
private:
nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
bool aForce, bool aStrongRef);
nsresult StartGettingReports();
static void TimeoutCallback(nsITimer* aTimer, void* aData);
static const uint32_t kTimeoutLengthMS = 5000;
// Note: this timeout needs to be long enough to allow for the
// possibility of DMD reports and/or running on a low-end phone.
static const uint32_t kTimeoutLengthMS = 50000;
mozilla::Mutex mMutex;
bool mIsRegistrationBlocked;
@ -184,13 +187,15 @@ private:
nsCOMPtr<nsISupports> mHandleReportData;
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
nsCOMPtr<nsISupports> mFinishReportingData;
nsString mDMDDumpIdent;
GetReportsState(uint32_t aGeneration, nsITimer* aTimer,
uint32_t aNumChildProcesses,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData)
nsISupports* aFinishReportingData,
const nsAString &aDMDDumpIdent)
: mGeneration(aGeneration)
, mTimer(aTimer)
, mNumChildProcesses(aNumChildProcesses)
@ -199,6 +204,7 @@ private:
, mHandleReportData(aHandleReportData)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDDumpIdent(aDMDDumpIdent)
{
}
};