mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1194555 - Part 6: Run reporters asynchronously. r=njn,jld,ted
This commit is contained in:
parent
e93f0f86a3
commit
80fca20565
@ -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
|
||||
|
@ -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});
|
||||
|
@ -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});
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user