Bug 1194555 - Part 6: Run reporters asynchronously. r=njn,jld,ted

This commit is contained in:
Eric Rahm 2015-10-14 16:52:59 -07:00
parent e93f0f86a3
commit 80fca20565
9 changed files with 183 additions and 23 deletions

View File

@ -900,6 +900,32 @@ NS_IMPL_ISUPPORTS(
, nsIMemoryReporterCallback
)
class MemoryReportFinishedCallback final : public nsIFinishReportingCallback
{
public:
NS_DECL_ISUPPORTS
explicit MemoryReportFinishedCallback(MemoryReportRequestChild* aActor)
: mActor(aActor)
{
}
NS_IMETHOD Callback(nsISupports* aUnused) override
{
bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
private:
~MemoryReportFinishedCallback() {}
nsRefPtr<MemoryReportRequestChild> mActor;
};
NS_IMPL_ISUPPORTS(
MemoryReportFinishedCallback
, nsIFinishReportingCallback
)
bool
ContentChild::RecvPMemoryReportRequestConstructor(
PMemoryReportRequestChild* aChild,
@ -936,11 +962,12 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
// MemoryReport.
nsRefPtr<MemoryReportCallback> cb =
new MemoryReportCallback(this, process);
mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
FileDescriptorToFILE(mDMDFile, "wb"));
nsRefPtr<MemoryReportFinishedCallback> finished =
new MemoryReportFinishedCallback(this);
bool sent = Send__delete__(this);
return sent ? NS_OK : NS_ERROR_FAILURE;
return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
FileDescriptorToFILE(mDMDFile, "wb"),
finished, nullptr);
}
bool

View File

@ -552,7 +552,14 @@ End of 5th\n\
SimpleTest.waitForClipboard(
function(aActual) {
mostRecentActual = aActual;
return aActual.trim() === aExpected.trim();
let rslt = aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
},
function() {
synthesizeKey("A", {accelKey: true});

View File

@ -106,7 +106,14 @@
SimpleTest.waitForClipboard(
function(aActual) {
mostRecentActual = aActual;
return aActual.trim() === aExpected.trim();
let rslt = aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
},
function() {
synthesizeKey("A", {accelKey: true});

View File

@ -30,3 +30,4 @@ var curDirURI = ios.newFileURI(cwd);
protocolHandler.setSubstitution("test", curDirURI);
Components.utils.import("resource://test/CrashTestUtils.jsm");
var crashType = CrashTestUtils.CRASH_INVALID_POINTER_DEREF;
var shouldDelay = false;

View File

@ -1,2 +1,15 @@
// Let the event loop process a bit before crashing.
if (shouldDelay) {
let shouldCrashNow = false;
let thr = Components.classes["@mozilla.org/thread-manager;1"]
.getService().currentThread;
thr.dispatch({ run: () => { shouldCrashNow = true; } },
Components.interfaces.nsIThread.DISPATCH_NORMAL);
while (!shouldCrashNow) {
thr.processNextEvent(true);
}
}
// now actually crash
CrashTestUtils.crash(crashType);

View File

@ -11,6 +11,9 @@ function run_test()
do_crash(
function() {
// Delay crashing so that the memory report has time to complete.
shouldDelay = true;
let Cc = Components.classes;
let Ci = Components.interfaces;

View File

@ -205,7 +205,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(d473bb53-4b77-48c4-aa0e-921f7d748dae)]
[scriptable, builtinclass, uuid(61de6dc7-ed11-4104-a577-79941f22f434)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@ -299,7 +299,14 @@ interface nsIMemoryReporterManager : nsISupports
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize,
in FILE DMDFile);
in FILE DMDFile,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData);
/*
* Called by an asynchronous memory reporter upon completion.
*/
[noscript] void endReport();
/*
* The memory reporter manager, for the most part, treats reporters

View File

@ -1357,6 +1357,7 @@ nsMemoryReporterManager::nsMemoryReporterManager()
, mSavedWeakReporters(nullptr)
, mNextGeneration(1)
, mPendingProcessesState(nullptr)
, mPendingReportersState(nullptr)
{
}
@ -1466,8 +1467,11 @@ nsMemoryReporterManager::StartGettingReports()
}
}
#endif
// This is async.
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mAnonymize, parentDMDFile);
s->mAnonymize, parentDMDFile,
s->mFinishReporting, s->mFinishReportingData);
nsTArray<ContentParent*> childWeakRefs;
ContentParent::GetAll(childWeakRefs);
@ -1499,16 +1503,44 @@ nsMemoryReporterManager::StartGettingReports()
s->mTimer.swap(timer);
}
// The parent's report is done; make note of that, and start
// launching child process reports (if any).
EndProcessReport(s->mGeneration, true);
return NS_OK;
}
void
nsMemoryReporterManager::DispatchReporter(
nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize)
{
MOZ_ASSERT(mPendingReportersState);
// Grab refs to everything used in the lambda function.
nsRefPtr<nsMemoryReporterManager> self = this;
nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
[self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize] () {
reporter->CollectReports(handleReport,
handleReportData,
aAnonymize);
if (!aIsAsync) {
self->EndReport();
}
});
NS_DispatchToMainThread(event);
mPendingReportersState->mReportsPending++;
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
bool aAnonymize, FILE* aDMDFile)
bool aAnonymize, FILE* aDMDFile,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData)
{
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
@ -1516,6 +1548,11 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_CRASH();
}
if (NS_WARN_IF(mPendingReportersState)) {
// Report is already in progress.
return NS_ERROR_IN_PROGRESS;
}
#ifdef MOZ_DMD
if (aDMDFile) {
// Clear DMD's reportedness state before running the memory
@ -1526,26 +1563,47 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_ASSERT(!aDMDFile);
#endif
nsCOMArray<nsIMemoryReporter> allReporters;
mPendingReportersState = new PendingReportersState(
aFinishReporting, aFinishReportingData, aDMDFile);
{
mozilla::MutexAutoLock autoLock(mMutex);
for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
allReporters.AppendElement(iter.Key());
DispatchReporter(iter.Key(), iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
}
for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
allReporters.AppendElement(iter.Key());
nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
DispatchReporter(reporter, iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
}
}
for (uint32_t i = 0; i < allReporters.Length(); i++) {
allReporters[i]->CollectReports(aHandleReport, aHandleReportData,
aAnonymize);
}
return NS_OK;
}
NS_IMETHODIMP
nsMemoryReporterManager::EndReport()
{
if (--mPendingReportersState->mReportsPending == 0) {
#ifdef MOZ_DMD
if (aDMDFile) {
return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
}
if (mPendingReportersState->mDMDFile) {
nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
}
#endif
if (mPendingProcessesState) {
// This is the parent process.
EndProcessReport(mPendingProcessesState->mGeneration, true);
} else {
mPendingReportersState->mFinishReporting->Callback(
mPendingReportersState->mFinishReportingData);
}
delete mPendingReportersState;
mPendingReportersState = nullptr;
}
return NS_OK;
}

View File

@ -188,6 +188,11 @@ private:
nsresult StartGettingReports();
nsresult FinishReporting();
void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize);
static void TimeoutCallback(nsITimer* aTimer, void* aData);
// Note: this timeout needs to be long enough to allow for the
// possibility of DMD reports and/or running on a low-end phone.
@ -205,6 +210,9 @@ private:
uint32_t mNextGeneration;
// Used to keep track of state of which processes are currently running and
// waiting to run memory reports. Holds references to parameters needed when
// requesting a memory report and finishing reporting.
struct PendingProcessesState
{
uint32_t mGeneration;
@ -230,11 +238,40 @@ private:
const nsAString& aDMDDumpIdent);
};
// Used to keep track of the state of the asynchronously run memory
// reporters. The callback and file handle used when all memory reporters
// have finished are also stored here.
struct PendingReportersState
{
// Number of memory reporters currently running.
uint32_t mReportsPending;
// Callback for when all memory reporters have completed.
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
nsCOMPtr<nsISupports> mFinishReportingData;
// File handle to write a DMD report to if requested.
FILE* mDMDFile;
PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
FILE* aDMDFile)
: mReportsPending(0)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDFile(aDMDFile)
{
}
};
// When this is non-null, a request is in flight. Note: We use manual
// new/delete for this because its lifetime doesn't match block scope or
// anything like that.
PendingProcessesState* mPendingProcessesState;
// This is reinitialized each time a call to GetReports is initiated.
PendingReportersState* mPendingReportersState;
PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
static bool StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState);