Backed out 7 changesets (bug 1194555) for xpcshell failures

Backed out changeset e77be333f4fb (bug 1194555)
Backed out changeset 11951462a37c (bug 1194555)
Backed out changeset 99b27aa952c5 (bug 1194555)
Backed out changeset 70a8ed3b6a45 (bug 1194555)
Backed out changeset 748bfebe81e7 (bug 1194555)
Backed out changeset cbaac05a2934 (bug 1194555)
Backed out changeset fcbfd1379fcd (bug 1194555)
This commit is contained in:
Wes Kocher 2015-10-06 14:40:51 -07:00
parent 8fa86d0fe0
commit 46d77c938b
9 changed files with 379 additions and 400 deletions

View File

@ -895,32 +895,6 @@ NS_IMPL_ISUPPORTS(
, nsIMemoryReporterCallback , 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 bool
ContentChild::RecvPMemoryReportRequestConstructor( ContentChild::RecvPMemoryReportRequestConstructor(
PMemoryReportRequestChild* aChild, PMemoryReportRequestChild* aChild,
@ -957,12 +931,11 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
// MemoryReport. // MemoryReport.
nsRefPtr<MemoryReportCallback> cb = nsRefPtr<MemoryReportCallback> cb =
new MemoryReportCallback(this, process); new MemoryReportCallback(this, process);
nsRefPtr<MemoryReportFinishedCallback> finished = mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
new MemoryReportFinishedCallback(this); FileDescriptorToFILE(mDMDFile, "wb"));
return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize, bool sent = Send__delete__(this);
FileDescriptorToFILE(mDMDFile, "wb"), return sent ? NS_OK : NS_ERROR_FAILURE;
finished, nullptr);
} }
bool bool

View File

@ -30,13 +30,12 @@ window.onload = function() {
amount += aAmount; amount += aAmount;
} }
var finished = function() { mgr.getReportsForThisProcess(handleReport, null, /* anonymize = */ false);
ok(amount > 0, "we should be using a nonzero amount of memory");
ok(true, "yay, didn't crash!");
SimpleTest.finish();
}
mgr.getReports(handleReport, null, finished, null, /* anonymize = */ false); ok(amount > 0, "we should be using a nonzero amount of memory");
ok(true, "yay, didn't crash!");
SimpleTest.finish();
} }
</script> </script>

View File

@ -119,6 +119,21 @@
mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]); mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
} }
// mgr.explicit sums "heap-allocated" and all the appropriate NONHEAP ones:
// - "explicit/c", "explicit/cc" x 2, "explicit/d", "explicit/e"
// - but *not* "explicit/c/d" x 2
// Check explicit now before we add the fake reporters for the fake 2nd
// and subsequent processes.
//
// Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
// --disable-jemalloc build. Allow for that exception, but *only* that
// exception.
try {
is(mgr.explicit, 500*MB + (100 + 13 + 10)*MB + 599*KB, "mgr.explicit");
} catch (ex) {
is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
}
// The main process always comes first when we display about:memory. The // The main process always comes first when we display about:memory. The
// remaining processes are sorted by their |resident| values (starting with // remaining processes are sorted by their |resident| values (starting with
// the largest). Processes without a |resident| memory reporter are saved // the largest). Processes without a |resident| memory reporter are saved
@ -552,14 +567,7 @@ End of 5th\n\
SimpleTest.waitForClipboard( SimpleTest.waitForClipboard(
function(aActual) { function(aActual) {
mostRecentActual = aActual; mostRecentActual = aActual;
let rslt = aActual.trim() === aExpected.trim(); return aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
}, },
function() { function() {
synthesizeKey("A", {accelKey: true}); synthesizeKey("A", {accelKey: true});

View File

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

View File

@ -44,8 +44,6 @@
const XUL_NS = const XUL_NS =
"http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul"; "http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul";
SimpleTest.waitForExplicitFinish();
let vsizeAmounts = []; let vsizeAmounts = [];
let residentAmounts = []; let residentAmounts = [];
let heapAllocatedAmounts = []; let heapAllocatedAmounts = [];
@ -143,6 +141,21 @@
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
getService(Ci.nsIMemoryReporterManager); getService(Ci.nsIMemoryReporterManager);
// Access the distinguished amounts (mgr.explicit et al.) just to make sure
// they don't crash. We can't check their actual values because they're
// non-deterministic.
//
// Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
// --disable-jemalloc build. Allow for that exception, but *only* that
// exception.
let dummy;
let haveExplicit = true;
try {
dummy = mgr.explicit;
} catch (ex) {
is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
haveExplicit = false;
}
let amounts = [ let amounts = [
"vsize", "vsize",
"vsizeMaxContiguous", "vsizeMaxContiguous",
@ -169,7 +182,7 @@
// aren't available on all platforms. But if the attribute simply // aren't available on all platforms. But if the attribute simply
// isn't present, that indicates the distinguished amounts have changed // isn't present, that indicates the distinguished amounts have changed
// and this file hasn't been updated appropriately. // and this file hasn't been updated appropriately.
let dummy = mgr[amounts[i]]; dummy = mgr[amounts[i]];
ok(dummy !== undefined, ok(dummy !== undefined,
"accessed an unknown distinguished amount: " + amounts[i]); "accessed an unknown distinguished amount: " + amounts[i]);
} catch (ex) { } catch (ex) {
@ -191,33 +204,11 @@
domSize, styleSize, otherSize, totalSize, domSize, styleSize, otherSize, totalSize,
jsMilliseconds, nonJSMilliseconds); jsMilliseconds, nonJSMilliseconds);
let asyncSteps = [ mgr.getReportsForThisProcess(handleReportNormal, null,
getReportsNormal, /* anonymize = */ false);
getReportsAnonymized,
checkResults,
test_register_strong,
test_register_strong, // Make sure re-registering works
test_register_weak,
SimpleTest.finish
];
function runNext() { mgr.getReportsForThisProcess(handleReportAnonymized, null,
setTimeout(asyncSteps.shift(), 0); /* anonymize = */ true);
}
function getReportsNormal()
{
mgr.getReports(handleReportNormal, null,
runNext, null,
/* anonymize = */ false);
}
function getReportsAnonymized()
{
mgr.getReports(handleReportAnonymized, null,
runNext, null,
/* anonymize = */ true);
}
function checkSizeReasonable(aName, aAmount) function checkSizeReasonable(aName, aAmount)
{ {
@ -235,50 +226,40 @@
} }
} }
function checkResults() // If mgr.explicit failed, we won't have "heap-allocated" either.
{ if (haveExplicit) {
try { checkSpecialReport("heap-allocated", heapAllocatedAmounts);
// Nb: mgr.heapAllocated will throw NS_ERROR_NOT_AVAILABLE if this is a
// --disable-jemalloc build. Allow for skipping this test on that
// exception, but *only* that exception.
let dummy = mgr.heapAllocated;
checkSpecialReport("heap-allocated", heapAllocatedAmounts);
} catch (ex) {
is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.heapAllocated exception");
}
// vsize may be unreasonable if ASAN is enabled
checkSpecialReport("vsize", vsizeAmounts, /*canBeUnreasonable*/true);
checkSpecialReport("resident", residentAmounts);
for (var reporter in jsGcHeapUsedGcThings) {
ok(jsGcHeapUsedGcThings[reporter] == 1);
}
checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
jsGcHeapUsedGcThingsTotal);
ok(present.jsNonWindowCompartments, "js-non-window compartments are present");
ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
ok(present.places, "places is present");
ok(present.images, "images is present");
ok(present.xptiWorkingSet, "xpti-working-set is present");
ok(present.atomTablesMain, "atom-tables/main is present");
ok(present.sandboxLocation, "sandbox locations are present");
ok(present.bigString, "large string is present");
ok(present.smallString1, "small string 1 is present");
ok(present.smallString2, "small string 2 is present");
ok(!present.anonymizedWhenUnnecessary,
"anonymized paths are not present when unnecessary. Failed case: " +
present.anonymizedWhenUnnecessary);
ok(!present.httpWhenAnonymized,
"http URLs are anonymized when necessary. Failed case: " +
present.httpWhenAnonymized);
ok(!present.unanonymizedFilePathWhenAnonymized,
"file URLs are anonymized when necessary. Failed case: " +
present.unanonymizedFilePathWhenAnonymized);
runNext();
} }
// vsize may be unreasonable if ASAN is enabled
checkSpecialReport("vsize", vsizeAmounts, /*canBeUnreasonable*/true);
checkSpecialReport("resident", residentAmounts);
for (var reporter in jsGcHeapUsedGcThings) {
ok(jsGcHeapUsedGcThings[reporter] == 1);
}
checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
jsGcHeapUsedGcThingsTotal);
ok(present.jsNonWindowCompartments, "js-non-window compartments are present");
ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
ok(present.places, "places is present");
ok(present.images, "images is present");
ok(present.xptiWorkingSet, "xpti-working-set is present");
ok(present.atomTablesMain, "atom-tables/main is present");
ok(present.sandboxLocation, "sandbox locations are present");
ok(present.bigString, "large string is present");
ok(present.smallString1, "small string 1 is present");
ok(present.smallString2, "small string 2 is present");
ok(!present.anonymizedWhenUnnecessary,
"anonymized paths are not present when unnecessary. Failed case: " +
present.anonymizedWhenUnnecessary);
ok(!present.httpWhenAnonymized,
"http URLs are anonymized when necessary. Failed case: " +
present.httpWhenAnonymized);
ok(!present.unanonymizedFilePathWhenAnonymized,
"file URLs are anonymized when necessary. Failed case: " +
present.unanonymizedFilePathWhenAnonymized);
// Reporter registration tests // Reporter registration tests
@ -371,28 +352,27 @@
mgr.registerStrongReporter(reporterAndCallback); mgr.registerStrongReporter(reporterAndCallback);
// Check the generated reports. // Check the generated reports.
mgr.getReports(reporterAndCallback, null, mgr.getReportsForThisProcess(reporterAndCallback, null,
() => { /* anonymize = */ false);
reporterAndCallback.finish(); reporterAndCallback.finish();
window.setTimeout(test_unregister_strong, 0, reporterAndCallback);
}, null,
/* anonymize = */ false);
}
function test_unregister_strong(aReporterAndCallback) // Unregistration works.
{ mgr.unregisterStrongReporter(reporterAndCallback);
mgr.unregisterStrongReporter(aReporterAndCallback);
// The reporter was unregistered, hence there shouldn't be any reports from // The reporter was unregistered, hence there shouldn't be any reports from
// the test reporter. // the test reporter.
mgr.getReports(aReporterAndCallback, null, mgr.getReportsForThisProcess(reporterAndCallback, null,
() => { /* anonymize = */ false);
aReporterAndCallback.finish(0); reporterAndCallback.finish(0);
runNext();
}, null,
/* anonymize = */ false);
} }
test_register_strong();
// Check strong reporters a second time, to make sure a reporter can be
// re-registered.
test_register_strong();
// Check that you cannot register JS components as weak reporters. // Check that you cannot register JS components as weak reporters.
function test_register_weak() { function test_register_weak() {
let reporterAndCallback = new MemoryReporterAndCallback(); let reporterAndCallback = new MemoryReporterAndCallback();
@ -412,12 +392,9 @@
ok(ex.message.indexOf("NS_ERROR_") >= 0, ok(ex.message.indexOf("NS_ERROR_") >= 0,
"WrappedJS reporter got rejected: " + ex); "WrappedJS reporter got rejected: " + ex);
} }
runNext();
} }
// Kick-off the async tests. test_register_weak();
runNext();
]]> ]]>
</script> </script>

View File

@ -24,8 +24,6 @@
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
SimpleTest.waitForExplicitFinish();
// Make a fake DB file. // Make a fake DB file.
let file = Cc["@mozilla.org/file/directory_service;1"]. let file = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties). getService(Ci.nsIProperties).
@ -42,12 +40,11 @@
// them. It shouldn't crash. // them. It shouldn't crash.
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
getService(Ci.nsIMemoryReporterManager); getService(Ci.nsIMemoryReporterManager);
mgr.getReports(function(){}, null, mgr.getReportsForThisProcess(function(){}, null, /* anonymize = */ false);
() => {
ok(true, "didn't crash"); // If we haven't crashed, we've passed, but the test harness requires that
SimpleTest.finish(); // we explicitly check something.
}, null, ok(true, "didn't crash");
/* anonymize = */ false);
]]> ]]>
</script> </script>

View File

@ -205,7 +205,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data); void callback(in nsISupports data);
}; };
[scriptable, builtinclass, uuid(61de6dc7-ed11-4104-a577-79941f22f434)] [scriptable, builtinclass, uuid(5e4eaa5a-4808-4b97-8005-e7cdc4d73693)]
interface nsIMemoryReporterManager : nsISupports interface nsIMemoryReporterManager : nsISupports
{ {
/* /*
@ -220,7 +220,6 @@ interface nsIMemoryReporterManager : nsISupports
* unregisterStrongReporter() at any point. * unregisterStrongReporter() at any point.
*/ */
void registerStrongReporter(in nsIMemoryReporter reporter); void registerStrongReporter(in nsIMemoryReporter reporter);
void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
/* /*
* Like registerReporter, but the Manager service will hold a weak reference * Like registerReporter, but the Manager service will hold a weak reference
@ -230,7 +229,6 @@ interface nsIMemoryReporterManager : nsISupports
* register your JavaScript components with registerStrongReporter(). * register your JavaScript components with registerStrongReporter().
*/ */
void registerWeakReporter(in nsIMemoryReporter reporter); void registerWeakReporter(in nsIMemoryReporter reporter);
void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
/* /*
* Unregister the given memory reporter, which must have been registered with * Unregister the given memory reporter, which must have been registered with
@ -291,6 +289,14 @@ interface nsIMemoryReporterManager : nsISupports
in boolean minimizeMemoryUsage, in boolean minimizeMemoryUsage,
in AString DMDDumpIdent); in AString DMDDumpIdent);
/*
* Get memory reports in the current process only. |handleReport| is called
* for each report.
*/
void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize);
/* /*
* As above, but if DMD is enabled and |DMDFile| is non-null then * As above, but if DMD is enabled and |DMDFile| is non-null then
* write a DMD report to that file and close it. * write a DMD report to that file and close it.
@ -299,14 +305,7 @@ interface nsIMemoryReporterManager : nsISupports
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport, getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData, in nsISupports handleReportData,
in boolean anonymize, 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 * The memory reporter manager, for the most part, treats reporters
@ -316,8 +315,9 @@ interface nsIMemoryReporterManager : nsISupports
* interesting that we want external code (e.g. telemetry) to be able to rely * interesting that we want external code (e.g. telemetry) to be able to rely
* on them. * on them.
* *
* Note that these are not reporters and so getReports() does not look at * Note that these are not reporters and so getReports() and
* them. However, distinguished amounts can be embedded in a reporter. * getReportsForThisProcess() do not look at them. However, distinguished
* amounts can be embedded in a reporter.
* *
* Access to these attributes can fail. In particular, some of them are not * Access to these attributes can fail. In particular, some of them are not
* available on all platforms. * available on all platforms.
@ -325,6 +325,11 @@ interface nsIMemoryReporterManager : nsISupports
* If you add a new distinguished amount, please update * If you add a new distinguished amount, please update
* toolkit/components/aboutmemory/tests/test_memoryReporters.xul. * toolkit/components/aboutmemory/tests/test_memoryReporters.xul.
* *
* |explicit| (UNITS_BYTES) The total size of explicit memory allocations,
* both at the OS-level (eg. via mmap, VirtualAlloc) and at the heap level
* (eg. via malloc, calloc, operator new). It covers all heap allocations,
* but will miss any OS-level ones not covered by memory reporters.
*
* |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space * |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space
* taken up. * taken up.
* *
@ -373,6 +378,7 @@ interface nsIMemoryReporterManager : nsISupports
* |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a. * |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a.
* major) page faults that have occurred since the process started. * major) page faults that have occurred since the process started.
*/ */
readonly attribute int64_t explicit;
readonly attribute int64_t vsize; readonly attribute int64_t vsize;
readonly attribute int64_t vsizeMaxContiguous; readonly attribute int64_t vsizeMaxContiguous;
readonly attribute int64_t resident; readonly attribute int64_t resident;
@ -453,12 +459,10 @@ namespace mozilla {
// Register a memory reporter. The manager service will hold a strong // Register a memory reporter. The manager service will hold a strong
// reference to this reporter. // reference to this reporter.
XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter); XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Register a memory reporter. The manager service will hold a weak reference // Register a memory reporter. The manager service will hold a weak reference
// to this reporter. // to this reporter.
XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter); XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Unregister a strong memory reporter. // Unregister a strong memory reporter.
XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter); XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
@ -516,6 +520,15 @@ nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
} }
#if defined(MOZ_DMD) && !defined(MOZILLA_XPCOMRT_API) #if defined(MOZ_DMD) && !defined(MOZILLA_XPCOMRT_API)
namespace mozilla {
namespace dmd {
// This runs all the memory reporters in the current process but does nothing
// with the results; i.e. it does the minimal amount of work possible for DMD
// to do its thing. It does nothing with child processes.
void RunReportersForThisProcess();
}
}
#if !defined(MOZ_MEMORY) #if !defined(MOZ_MEMORY)
#error "MOZ_DMD requires MOZ_MEMORY" #error "MOZ_DMD requires MOZ_MEMORY"
#endif #endif

View File

@ -1356,8 +1356,7 @@ nsMemoryReporterManager::nsMemoryReporterManager()
, mSavedStrongReporters(nullptr) , mSavedStrongReporters(nullptr)
, mSavedWeakReporters(nullptr) , mSavedWeakReporters(nullptr)
, mNextGeneration(1) , mNextGeneration(1)
, mPendingProcessesState(nullptr) , mGetReportsState(nullptr)
, mPendingReportersState(nullptr)
{ {
} }
@ -1415,11 +1414,11 @@ nsMemoryReporterManager::GetReportsExtended(
uint32_t generation = mNextGeneration++; uint32_t generation = mNextGeneration++;
if (mPendingProcessesState) { if (mGetReportsState) {
// A request is in flight. Don't start another one. And don't report // A request is in flight. Don't start another one. And don't report
// an error; just ignore it, and let the in-flight request finish. // an error; just ignore it, and let the in-flight request finish.
MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n", MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
generation, mPendingProcessesState->mGeneration); generation, mGetReportsState->mGeneration);
return NS_OK; return NS_OK;
} }
@ -1430,15 +1429,16 @@ nsMemoryReporterManager::GetReportsExtended(
if (concurrency < 1) { if (concurrency < 1) {
concurrency = 1; concurrency = 1;
} }
mPendingProcessesState = new PendingProcessesState(generation, mGetReportsState = new GetReportsState(generation,
aAnonymize, aAnonymize,
aMinimize, aMinimize,
concurrency, concurrency,
aHandleReport, aHandleReport,
aHandleReportData, aHandleReportData,
aFinishReporting, aFinishReporting,
aFinishReportingData, aFinishReportingData,
aDMDDumpIdent); aDMDDumpIdent);
mGetReportsState->mChildrenPending = new nsTArray<nsRefPtr<mozilla::dom::ContentParent>>();
if (aMinimize) { if (aMinimize) {
rv = MinimizeMemoryUsage(NS_NewRunnableMethod( rv = MinimizeMemoryUsage(NS_NewRunnableMethod(
@ -1452,7 +1452,7 @@ nsMemoryReporterManager::GetReportsExtended(
nsresult nsresult
nsMemoryReporterManager::StartGettingReports() nsMemoryReporterManager::StartGettingReports()
{ {
PendingProcessesState* s = mPendingProcessesState; GetReportsState* s = mGetReportsState;
nsresult rv; nsresult rv;
// Get reports for this process. // Get reports for this process.
@ -1467,11 +1467,8 @@ nsMemoryReporterManager::StartGettingReports()
} }
} }
#endif #endif
// This is async.
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData, GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mAnonymize, parentDMDFile, s->mAnonymize, parentDMDFile);
s->mFinishReporting, s->mFinishReportingData);
nsTArray<ContentParent*> childWeakRefs; nsTArray<ContentParent*> childWeakRefs;
ContentParent::GetAll(childWeakRefs); ContentParent::GetAll(childWeakRefs);
@ -1482,7 +1479,7 @@ nsMemoryReporterManager::StartGettingReports()
// to be buffered and consume (possibly scarce) memory. // to be buffered and consume (possibly scarce) memory.
for (size_t i = 0; i < childWeakRefs.Length(); ++i) { for (size_t i = 0; i < childWeakRefs.Length(); ++i) {
s->mChildrenPending.AppendElement(childWeakRefs[i]); s->mChildrenPending->AppendElement(childWeakRefs[i]);
} }
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID); nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
@ -1503,44 +1500,25 @@ nsMemoryReporterManager::StartGettingReports()
s->mTimer.swap(timer); 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; return NS_OK;
} }
void NS_IMETHODIMP
nsMemoryReporterManager::DispatchReporter( nsMemoryReporterManager::GetReportsForThisProcess(
nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport, nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData, nsISupports* aHandleReportData, bool aAnonymize)
bool aAnonymize)
{ {
MOZ_ASSERT(mPendingReportersState); return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
aAnonymize, nullptr);
// 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 NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended( nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData, 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 // Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread. // be called from the main thread.
@ -1548,11 +1526,6 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_CRASH(); MOZ_CRASH();
} }
if (NS_WARN_IF(mPendingReportersState)) {
// Report is already in progress.
return NS_ERROR_IN_PROGRESS;
}
#ifdef MOZ_DMD #ifdef MOZ_DMD
if (aDMDFile) { if (aDMDFile) {
// Clear DMD's reportedness state before running the memory // Clear DMD's reportedness state before running the memory
@ -1563,58 +1536,39 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_ASSERT(!aDMDFile); MOZ_ASSERT(!aDMDFile);
#endif #endif
mPendingReportersState = new PendingReportersState( nsCOMArray<nsIMemoryReporter> allReporters;
aFinishReporting, aFinishReportingData, aDMDFile);
{ {
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) { for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
DispatchReporter(iter.Key(), iter.Data(), nsRefPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
aHandleReport, aHandleReportData, aAnonymize); allReporters.AppendElement(entry->GetKey());
} }
for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) { for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMemoryReporter> reporter = iter.Key(); nsPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
DispatchReporter(reporter, iter.Data(), allReporters.AppendElement(entry->GetKey());
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 #ifdef MOZ_DMD
if (mPendingReportersState->mDMDFile) { if (aDMDFile) {
nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile); return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
}
#endif
if (mPendingProcessesState) {
// This is the parent process.
EndProcessReport(mPendingProcessesState->mGeneration, true);
} else {
mPendingReportersState->mFinishReporting->Callback(
mPendingReportersState->mFinishReportingData);
}
delete mPendingReportersState;
mPendingReportersState = nullptr;
} }
#endif
return NS_OK; return NS_OK;
} }
nsMemoryReporterManager::PendingProcessesState* nsMemoryReporterManager::GetReportsState*
nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration) nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
{ {
// Memory reporting only happens on the main thread. // Memory reporting only happens on the main thread.
MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(NS_IsMainThread());
PendingProcessesState* s = mPendingProcessesState; GetReportsState* s = mGetReportsState;
if (!s) { if (!s) {
// If we reach here, then: // If we reach here, then:
@ -1651,7 +1605,7 @@ nsMemoryReporterManager::HandleChildReport(
uint32_t aGeneration, uint32_t aGeneration,
const dom::MemoryReport& aChildReport) const dom::MemoryReport& aChildReport)
{ {
PendingProcessesState* s = GetStateForGeneration(aGeneration); GetReportsState* s = GetStateForGeneration(aGeneration);
if (!s) { if (!s) {
return; return;
} }
@ -1671,7 +1625,7 @@ nsMemoryReporterManager::HandleChildReport(
/* static */ bool /* static */ bool
nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild, nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState) const GetReportsState* aState)
{ {
#ifdef MOZ_NUWA_PROCESS #ifdef MOZ_NUWA_PROCESS
if (aChild->IsNuwaProcess()) { if (aChild->IsNuwaProcess()) {
@ -1709,7 +1663,7 @@ nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
void void
nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess) nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
{ {
PendingProcessesState* s = GetStateForGeneration(aGeneration); GetReportsState* s = GetStateForGeneration(aGeneration);
if (!s) { if (!s) {
return; return;
} }
@ -1722,29 +1676,29 @@ nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
aGeneration, s->mNumProcessesCompleted, aGeneration, s->mNumProcessesCompleted,
aSuccess ? "completed" : "exited during report", aSuccess ? "completed" : "exited during report",
s->mNumProcessesRunning, s->mNumProcessesRunning,
static_cast<unsigned>(s->mChildrenPending.Length())); static_cast<unsigned>(s->mChildrenPending->Length()));
// Start pending children up to the concurrency limit. // Start pending children up to the concurrency limit.
while (s->mNumProcessesRunning < s->mConcurrencyLimit && while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
!s->mChildrenPending.IsEmpty()) { !s->mChildrenPending->IsEmpty()) {
// Pop last element from s->mChildrenPending // Pop last element from s->mChildrenPending
nsRefPtr<ContentParent> nextChild; nsRefPtr<ContentParent> nextChild;
nextChild.swap(s->mChildrenPending.LastElement()); nextChild.swap(s->mChildrenPending->LastElement());
s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1); s->mChildrenPending->TruncateLength(s->mChildrenPending->Length() - 1);
// Start report (if the child is still alive and not Nuwa). // Start report (if the child is still alive and not Nuwa).
if (StartChildReport(nextChild, s)) { if (StartChildReport(nextChild, s)) {
++s->mNumProcessesRunning; ++s->mNumProcessesRunning;
MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report" MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
" (%u running, %u pending)\n", " (%u running, %u pending)\n",
aGeneration, s->mNumProcessesRunning, aGeneration, s->mNumProcessesRunning,
static_cast<unsigned>(s->mChildrenPending.Length())); static_cast<unsigned>(s->mChildrenPending->Length()));
} }
} }
// If all the child processes (if any) have reported, we can cancel // If all the child processes (if any) have reported, we can cancel
// the timer (if started) and finish up. Otherwise, just return. // the timer (if started) and finish up. Otherwise, just return.
if (s->mNumProcessesRunning == 0) { if (s->mNumProcessesRunning == 0) {
MOZ_ASSERT(s->mChildrenPending.IsEmpty()); MOZ_ASSERT(s->mChildrenPending->IsEmpty());
if (s->mTimer) { if (s->mTimer) {
s->mTimer->Cancel(); s->mTimer->Cancel();
} }
@ -1756,15 +1710,15 @@ nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData) nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
{ {
nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData); nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
PendingProcessesState* s = mgr->mPendingProcessesState; GetReportsState* s = mgr->mGetReportsState;
// Release assert because: if the pointer is null we're about to // Release assert because: if the pointer is null we're about to
// crash regardless of DEBUG, and this way the compiler doesn't // crash regardless of DEBUG, and this way the compiler doesn't
// complain about unused variables. // complain about unused variables.
MOZ_RELEASE_ASSERT(s, "mgr->mPendingProcessesState"); MOZ_RELEASE_ASSERT(s, "mgr->mGetReportsState");
MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n", MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n",
s->mGeneration, s->mNumProcessesRunning, s->mGeneration, s->mNumProcessesRunning,
static_cast<unsigned>(s->mChildrenPending.Length())); static_cast<unsigned>(s->mChildrenPending->Length()));
// We don't bother sending any kind of cancellation message to the child // We don't bother sending any kind of cancellation message to the child
// processes that haven't reported back. // processes that haven't reported back.
@ -1779,43 +1733,25 @@ nsMemoryReporterManager::FinishReporting()
MOZ_CRASH(); MOZ_CRASH();
} }
MOZ_ASSERT(mPendingProcessesState); MOZ_ASSERT(mGetReportsState);
MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n", MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
mPendingProcessesState->mGeneration, mGetReportsState->mGeneration,
mPendingProcessesState->mNumProcessesCompleted); mGetReportsState->mNumProcessesCompleted);
// Call this before deleting |mPendingProcessesState|. That way, if // Call this before deleting |mGetReportsState|. That way, if
// |mFinishReportData| calls GetReports(), it will silently abort, as // |mFinishReportData| calls GetReports(), it will silently abort, as
// required. // required.
nsresult rv = mPendingProcessesState->mFinishReporting->Callback( nsresult rv = mGetReportsState->mFinishReporting->Callback(
mPendingProcessesState->mFinishReportingData); mGetReportsState->mFinishReportingData);
delete mPendingProcessesState; delete mGetReportsState;
mPendingProcessesState = nullptr; mGetReportsState = nullptr;
return rv; return rv;
} }
nsMemoryReporterManager::PendingProcessesState::PendingProcessesState( nsMemoryReporterManager::GetReportsState::~GetReportsState()
uint32_t aGeneration, bool aAnonymize, bool aMinimize,
uint32_t aConcurrencyLimit,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
const nsAString& aDMDDumpIdent)
: mGeneration(aGeneration)
, mAnonymize(aAnonymize)
, mMinimize(aMinimize)
, mChildrenPending()
, mNumProcessesRunning(1) // reporting starts with the parent
, mNumProcessesCompleted(0)
, mConcurrencyLimit(aConcurrencyLimit)
, mHandleReport(aHandleReport)
, mHandleReportData(aHandleReportData)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDDumpIdent(aDMDDumpIdent)
{ {
delete mChildrenPending;
} }
static void static void
@ -1831,7 +1767,7 @@ CrashIfRefcountIsZero(nsISupports* aObj)
nsresult nsresult
nsMemoryReporterManager::RegisterReporterHelper( nsMemoryReporterManager::RegisterReporterHelper(
nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync) nsIMemoryReporter* aReporter, bool aForce, bool aStrong)
{ {
// This method is thread-safe. // This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
@ -1858,7 +1794,7 @@ nsMemoryReporterManager::RegisterReporterHelper(
// //
if (aStrong) { if (aStrong) {
nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter; nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
mStrongReporters->Put(aReporter, aIsAsync); mStrongReporters->PutEntry(aReporter);
CrashIfRefcountIsZero(aReporter); CrashIfRefcountIsZero(aReporter);
} else { } else {
CrashIfRefcountIsZero(aReporter); CrashIfRefcountIsZero(aReporter);
@ -1871,7 +1807,7 @@ nsMemoryReporterManager::RegisterReporterHelper(
// CollectReports(). // CollectReports().
return NS_ERROR_XPC_BAD_CONVERT_JS; return NS_ERROR_XPC_BAD_CONVERT_JS;
} }
mWeakReporters->Put(aReporter, aIsAsync); mWeakReporters->PutEntry(aReporter);
} }
return NS_OK; return NS_OK;
@ -1881,32 +1817,14 @@ NS_IMETHODIMP
nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter) nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
{ {
return RegisterReporterHelper(aReporter, /* force = */ false, return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ true, /* strong = */ true);
/* async = */ false);
}
NS_IMETHODIMP
nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ true,
/* async = */ true);
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter) nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
{ {
return RegisterReporterHelper(aReporter, /* force = */ false, return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ false, /* strong = */ false);
/* async = */ false);
}
NS_IMETHODIMP
nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ false,
/* async = */ true);
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -1914,8 +1832,7 @@ nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
nsIMemoryReporter* aReporter) nsIMemoryReporter* aReporter)
{ {
return RegisterReporterHelper(aReporter, /* force = */ true, return RegisterReporterHelper(aReporter, /* force = */ true,
/* strong = */ true, /* strong = */ true);
/* async = */ false);
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -1927,7 +1844,7 @@ nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
MOZ_ASSERT(!mWeakReporters->Contains(aReporter)); MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
if (mStrongReporters->Contains(aReporter)) { if (mStrongReporters->Contains(aReporter)) {
mStrongReporters->Remove(aReporter); mStrongReporters->RemoveEntry(aReporter);
return NS_OK; return NS_OK;
} }
@ -1936,7 +1853,7 @@ nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
// references that these reporters aren't expecting (which can keep them // references that these reporters aren't expecting (which can keep them
// alive longer than intended). // alive longer than intended).
if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) { if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
mSavedStrongReporters->Remove(aReporter); mSavedStrongReporters->RemoveEntry(aReporter);
return NS_OK; return NS_OK;
} }
@ -1952,7 +1869,7 @@ nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
MOZ_ASSERT(!mStrongReporters->Contains(aReporter)); MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
if (mWeakReporters->Contains(aReporter)) { if (mWeakReporters->Contains(aReporter)) {
mWeakReporters->Remove(aReporter); mWeakReporters->RemoveEntry(aReporter);
return NS_OK; return NS_OK;
} }
@ -1961,7 +1878,7 @@ nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
// references that the old reporters aren't expecting (which can end up as // references that the old reporters aren't expecting (which can end up as
// dangling pointers that lead to use-after-frees). // dangling pointers that lead to use-after-frees).
if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) { if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
mSavedWeakReporters->Remove(aReporter); mSavedWeakReporters->RemoveEntry(aReporter);
return NS_OK; return NS_OK;
} }
@ -2010,6 +1927,84 @@ nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
return NS_OK; return NS_OK;
} }
// This is just a wrapper for int64_t that implements nsISupports, so it can be
// passed to nsIMemoryReporter::CollectReports.
class Int64Wrapper final : public nsISupports
{
~Int64Wrapper() {}
public:
NS_DECL_ISUPPORTS
Int64Wrapper() : mValue(0)
{
}
int64_t mValue;
};
NS_IMPL_ISUPPORTS0(Int64Wrapper)
class ExplicitCallback final : public nsIHandleReportCallback
{
~ExplicitCallback() {}
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
int32_t aKind, int32_t aUnits, int64_t aAmount,
const nsACString& aDescription,
nsISupports* aWrappedExplicit) override
{
// Using the "heap-allocated" reporter here instead of
// nsMemoryReporterManager.heapAllocated goes against the usual
// pattern. But it's for a good reason: in tests, we can easily
// create artificial (i.e. deterministic) reporters -- which allows us
// to precisely test nsMemoryReporterManager.explicit -- but we can't
// do that for distinguished amounts.
if (aPath.EqualsLiteral("heap-allocated") ||
(aKind == nsIMemoryReporter::KIND_NONHEAP &&
PromiseFlatCString(aPath).Find("explicit") == 0)) {
Int64Wrapper* wrappedInt64 = static_cast<Int64Wrapper*>(aWrappedExplicit);
wrappedInt64->mValue += aAmount;
}
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(ExplicitCallback, nsIHandleReportCallback)
NS_IMETHODIMP
nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
{
if (NS_WARN_IF(!aAmount)) {
return NS_ERROR_INVALID_ARG;
}
*aAmount = 0;
#ifndef HAVE_JEMALLOC_STATS
return NS_ERROR_NOT_AVAILABLE;
#else
// For each reporter we call CollectReports and filter out the
// non-explicit, non-NONHEAP measurements (except for "heap-allocated").
// That's lots of wasted work, and we used to have a GetExplicitNonHeap()
// method which did this more efficiently, but it ended up being more
// trouble than it was worth.
nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback();
nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
// Anonymization doesn't matter here, because we're only summing all the
// reported values. Enable it anyway because it's slightly faster, since it
// doesn't have to get URLs, find notable strings, etc.
GetReportsForThisProcess(handleReport, wrappedExplicitSize,
/* anonymize = */ true);
*aAmount = wrappedExplicitSize->mValue;
return NS_OK;
#endif // HAVE_JEMALLOC_STATS
}
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::GetVsize(int64_t* aVsize) nsMemoryReporterManager::GetVsize(int64_t* aVsize)
{ {
@ -2378,61 +2373,61 @@ nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
namespace mozilla { namespace mozilla {
#define GET_MEMORY_REPORTER_MANAGER(mgr) \
nsRefPtr<nsMemoryReporterManager> mgr = \
nsMemoryReporterManager::GetOrCreate(); \
if (!mgr) { \
return NS_ERROR_FAILURE; \
}
nsresult nsresult
RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
{ {
// Hold a strong reference to the argument to make sure it gets released if // Hold a strong reference to the argument to make sure it gets released if
// we return early below. // we return early below.
nsCOMPtr<nsIMemoryReporter> reporter = aReporter; nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterStrongReporter(reporter);
}
nsresult nsCOMPtr<nsIMemoryReporterManager> mgr =
RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter) do_GetService("@mozilla.org/memory-reporter-manager;1");
{ if (!mgr) {
// Hold a strong reference to the argument to make sure it gets released if return NS_ERROR_FAILURE;
// we return early below. }
nsCOMPtr<nsIMemoryReporter> reporter = aReporter; return mgr->RegisterStrongReporter(reporter);
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterStrongAsyncReporter(reporter);
} }
nsresult nsresult
RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
{ {
GET_MEMORY_REPORTER_MANAGER(mgr) nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (!mgr) {
return NS_ERROR_FAILURE;
}
return mgr->RegisterWeakReporter(aReporter); return mgr->RegisterWeakReporter(aReporter);
} }
nsresult
RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
{
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterWeakAsyncReporter(aReporter);
}
nsresult nsresult
UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
{ {
GET_MEMORY_REPORTER_MANAGER(mgr) nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (!mgr) {
return NS_ERROR_FAILURE;
}
return mgr->UnregisterStrongReporter(aReporter); return mgr->UnregisterStrongReporter(aReporter);
} }
nsresult nsresult
UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
{ {
GET_MEMORY_REPORTER_MANAGER(mgr) nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (!mgr) {
return NS_ERROR_FAILURE;
}
return mgr->UnregisterWeakReporter(aReporter); return mgr->UnregisterWeakReporter(aReporter);
} }
#define GET_MEMORY_REPORTER_MANAGER(mgr) \
nsRefPtr<nsMemoryReporterManager> mgr = \
nsMemoryReporterManager::GetOrCreate(); \
if (!mgr) { \
return NS_ERROR_FAILURE; \
}
// Macro for generating functions that register distinguished amount functions // Macro for generating functions that register distinguished amount functions
// with the memory reporter manager. // with the memory reporter manager.
#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \ #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
@ -2488,3 +2483,45 @@ DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
#undef GET_MEMORY_REPORTER_MANAGER #undef GET_MEMORY_REPORTER_MANAGER
} // namespace mozilla } // namespace mozilla
#if defined(MOZ_DMD)
namespace mozilla {
namespace dmd {
class DoNothingCallback final : public nsIHandleReportCallback
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
int32_t aKind, int32_t aUnits, int64_t aAmount,
const nsACString& aDescription,
nsISupports* aData) override
{
// Do nothing; the reporter has already reported to DMD.
return NS_OK;
}
private:
~DoNothingCallback() {}
};
NS_IMPL_ISUPPORTS(DoNothingCallback, nsIHandleReportCallback)
void
RunReportersForThisProcess()
{
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
mgr->GetReportsForThisProcess(doNothing, nullptr, /* anonymize = */ false);
}
} // namespace dmd
} // namespace mozilla
#endif // defined(MOZ_DMD)

View File

@ -41,14 +41,14 @@ public:
return static_cast<nsMemoryReporterManager*>(imgr.get()); return static_cast<nsMemoryReporterManager*>(imgr.get());
} }
typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable; typedef nsTHashtable<nsRefPtrHashKey<nsIMemoryReporter>> StrongReportersTable;
typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable; typedef nsTHashtable<nsPtrHashKey<nsIMemoryReporter>> WeakReportersTable;
// Inter-process memory reporting proceeds as follows. // Inter-process memory reporting proceeds as follows.
// //
// - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER) // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
// synchronously gets memory reports for the current process, sets up some // synchronously gets memory reports for the current process, sets up some
// state (mPendingProcessesState) for when child processes report back -- // state (mGetReportsState) for when child processes report back --
// including a timer -- and starts telling child processes to get memory // including a timer -- and starts telling child processes to get memory
// reports. Control then returns to the main event loop. // reports. Control then returns to the main event loop.
// //
@ -108,7 +108,7 @@ public:
// is incomplete. // is incomplete.
// //
// Now, what what happens if a child process is created/destroyed in the // Now, what what happens if a child process is created/destroyed in the
// middle of a request? Well, PendingProcessesState is initialized with an array // middle of a request? Well, GetReportsState is initialized with an array
// of child process actors as of when the report started. So... // of child process actors as of when the report started. So...
// //
// - If a process is created after reporting starts, it won't be sent a // - If a process is created after reporting starts, it won't be sent a
@ -184,15 +184,10 @@ public:
private: private:
nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter, nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
bool aForce, bool aStrongRef, bool aIsAsync); bool aForce, bool aStrongRef);
nsresult StartGettingReports(); nsresult StartGettingReports();
nsresult FinishReporting(); nsresult FinishReporting();
void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize);
static void TimeoutCallback(nsITimer* aTimer, void* aData); static void TimeoutCallback(nsITimer* aTimer, void* aData);
// Note: this timeout needs to be long enough to allow for the // Note: this timeout needs to be long enough to allow for the
// possibility of DMD reports and/or running on a low-end phone. // possibility of DMD reports and/or running on a low-end phone.
@ -210,16 +205,16 @@ private:
uint32_t mNextGeneration; uint32_t mNextGeneration;
// Used to keep track of state of which processes are currently running and struct GetReportsState
// waiting to run memory reports. Holds references to parameters needed when
// requesting a memory report and finishing reporting.
struct PendingProcessesState
{ {
uint32_t mGeneration; uint32_t mGeneration;
bool mAnonymize; bool mAnonymize;
bool mMinimize; bool mMinimize;
nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsITimer> mTimer;
nsTArray<nsRefPtr<mozilla::dom::ContentParent>> mChildrenPending; // This is a pointer to an nsTArray because otherwise C++ is
// unhappy unless this header includes ContentParent.h, which not
// everything that includes this header knows how to find.
nsTArray<nsRefPtr<mozilla::dom::ContentParent>>* mChildrenPending;
uint32_t mNumProcessesRunning; uint32_t mNumProcessesRunning;
uint32_t mNumProcessesCompleted; uint32_t mNumProcessesCompleted;
uint32_t mConcurrencyLimit; uint32_t mConcurrencyLimit;
@ -229,52 +224,39 @@ private:
nsCOMPtr<nsISupports> mFinishReportingData; nsCOMPtr<nsISupports> mFinishReportingData;
nsString mDMDDumpIdent; nsString mDMDDumpIdent;
PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize, GetReportsState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
uint32_t aConcurrencyLimit, uint32_t aConcurrencyLimit,
nsIHandleReportCallback* aHandleReport, nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData, nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting, nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData, nsISupports* aFinishReportingData,
const nsAString& aDMDDumpIdent); const nsAString& aDMDDumpIdent)
}; : mGeneration(aGeneration)
, mAnonymize(aAnonymize)
// Used to keep track of the state of the asynchronously run memory , mMinimize(aMinimize)
// reporters. The callback and file handle used when all memory reporters , mChildrenPending(nullptr)
// have finished are also stored here. , mNumProcessesRunning(1) // reporting starts with the parent
struct PendingReportersState , mNumProcessesCompleted(0)
{ , mConcurrencyLimit(aConcurrencyLimit)
// Number of memory reporters currently running. , mHandleReport(aHandleReport)
uint32_t mReportsPending; , mHandleReportData(aHandleReportData)
// 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) , mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData) , mFinishReportingData(aFinishReportingData)
, mDMDFile(aDMDFile) , mDMDDumpIdent(aDMDDumpIdent)
{ {
} }
~GetReportsState();
}; };
// When this is non-null, a request is in flight. Note: We use manual // 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 // new/delete for this because its lifetime doesn't match block scope or
// anything like that. // anything like that.
PendingProcessesState* mPendingProcessesState; GetReportsState* mGetReportsState;
// This is reinitialized each time a call to GetReports is initiated. GetReportsState* GetStateForGeneration(uint32_t aGeneration);
PendingReportersState* mPendingReportersState;
PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
static bool StartChildReport(mozilla::dom::ContentParent* aChild, static bool StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState); const GetReportsState* aState);
}; };
#define NS_MEMORY_REPORTER_MANAGER_CID \ #define NS_MEMORY_REPORTER_MANAGER_CID \