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

View File

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

View File

@ -119,6 +119,21 @@
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
// remaining processes are sorted by their |resident| values (starting with
// the largest). Processes without a |resident| memory reporter are saved
@ -552,14 +567,7 @@ End of 5th\n\
SimpleTest.waitForClipboard(
function(aActual) {
mostRecentActual = aActual;
let rslt = aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
return aActual.trim() === aExpected.trim();
},
function() {
synthesizeKey("A", {accelKey: true});

View File

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

View File

@ -44,8 +44,6 @@
const XUL_NS =
"http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul";
SimpleTest.waitForExplicitFinish();
let vsizeAmounts = [];
let residentAmounts = [];
let heapAllocatedAmounts = [];
@ -143,6 +141,21 @@
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
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 = [
"vsize",
"vsizeMaxContiguous",
@ -169,7 +182,7 @@
// aren't available on all platforms. But if the attribute simply
// isn't present, that indicates the distinguished amounts have changed
// and this file hasn't been updated appropriately.
let dummy = mgr[amounts[i]];
dummy = mgr[amounts[i]];
ok(dummy !== undefined,
"accessed an unknown distinguished amount: " + amounts[i]);
} catch (ex) {
@ -191,33 +204,11 @@
domSize, styleSize, otherSize, totalSize,
jsMilliseconds, nonJSMilliseconds);
let asyncSteps = [
getReportsNormal,
getReportsAnonymized,
checkResults,
test_register_strong,
test_register_strong, // Make sure re-registering works
test_register_weak,
SimpleTest.finish
];
mgr.getReportsForThisProcess(handleReportNormal, null,
/* anonymize = */ false);
function runNext() {
setTimeout(asyncSteps.shift(), 0);
}
function getReportsNormal()
{
mgr.getReports(handleReportNormal, null,
runNext, null,
/* anonymize = */ false);
}
function getReportsAnonymized()
{
mgr.getReports(handleReportAnonymized, null,
runNext, null,
/* anonymize = */ true);
}
mgr.getReportsForThisProcess(handleReportAnonymized, null,
/* anonymize = */ true);
function checkSizeReasonable(aName, aAmount)
{
@ -235,50 +226,40 @@
}
}
function checkResults()
{
try {
// 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();
// If mgr.explicit failed, we won't have "heap-allocated" either.
if (haveExplicit) {
checkSpecialReport("heap-allocated", heapAllocatedAmounts);
}
// 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
@ -371,28 +352,27 @@
mgr.registerStrongReporter(reporterAndCallback);
// Check the generated reports.
mgr.getReports(reporterAndCallback, null,
() => {
reporterAndCallback.finish();
window.setTimeout(test_unregister_strong, 0, reporterAndCallback);
}, null,
/* anonymize = */ false);
}
mgr.getReportsForThisProcess(reporterAndCallback, null,
/* anonymize = */ false);
reporterAndCallback.finish();
function test_unregister_strong(aReporterAndCallback)
{
mgr.unregisterStrongReporter(aReporterAndCallback);
// Unregistration works.
mgr.unregisterStrongReporter(reporterAndCallback);
// The reporter was unregistered, hence there shouldn't be any reports from
// the test reporter.
mgr.getReports(aReporterAndCallback, null,
() => {
aReporterAndCallback.finish(0);
runNext();
}, null,
/* anonymize = */ false);
mgr.getReportsForThisProcess(reporterAndCallback, null,
/* anonymize = */ false);
reporterAndCallback.finish(0);
}
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.
function test_register_weak() {
let reporterAndCallback = new MemoryReporterAndCallback();
@ -412,12 +392,9 @@
ok(ex.message.indexOf("NS_ERROR_") >= 0,
"WrappedJS reporter got rejected: " + ex);
}
runNext();
}
// Kick-off the async tests.
runNext();
test_register_weak();
]]>
</script>

View File

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

View File

@ -205,7 +205,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(61de6dc7-ed11-4104-a577-79941f22f434)]
[scriptable, builtinclass, uuid(5e4eaa5a-4808-4b97-8005-e7cdc4d73693)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@ -220,7 +220,6 @@ interface nsIMemoryReporterManager : nsISupports
* unregisterStrongReporter() at any point.
*/
void registerStrongReporter(in nsIMemoryReporter reporter);
void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
/*
* Like registerReporter, but the Manager service will hold a weak reference
@ -230,7 +229,6 @@ interface nsIMemoryReporterManager : nsISupports
* register your JavaScript components with registerStrongReporter().
*/
void registerWeakReporter(in nsIMemoryReporter reporter);
void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
/*
* Unregister the given memory reporter, which must have been registered with
@ -291,6 +289,14 @@ interface nsIMemoryReporterManager : nsISupports
in boolean minimizeMemoryUsage,
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
* write a DMD report to that file and close it.
@ -299,14 +305,7 @@ interface nsIMemoryReporterManager : nsISupports
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize,
in FILE DMDFile,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData);
/*
* Called by an asynchronous memory reporter upon completion.
*/
[noscript] void endReport();
in FILE DMDFile);
/*
* 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
* on them.
*
* Note that these are not reporters and so getReports() does not look at
* them. However, distinguished amounts can be embedded in a reporter.
* Note that these are not reporters and so getReports() and
* 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
* available on all platforms.
@ -325,6 +325,11 @@ interface nsIMemoryReporterManager : nsISupports
* If you add a new distinguished amount, please update
* 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
* taken up.
*
@ -373,6 +378,7 @@ interface nsIMemoryReporterManager : nsISupports
* |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a.
* major) page faults that have occurred since the process started.
*/
readonly attribute int64_t explicit;
readonly attribute int64_t vsize;
readonly attribute int64_t vsizeMaxContiguous;
readonly attribute int64_t resident;
@ -453,12 +459,10 @@ namespace mozilla {
// Register a memory reporter. The manager service will hold a strong
// reference to this reporter.
XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Register a memory reporter. The manager service will hold a weak reference
// to this reporter.
XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Unregister a strong memory reporter.
XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
@ -516,6 +520,15 @@ nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
}
#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)
#error "MOZ_DMD requires MOZ_MEMORY"
#endif

View File

@ -1356,8 +1356,7 @@ nsMemoryReporterManager::nsMemoryReporterManager()
, mSavedStrongReporters(nullptr)
, mSavedWeakReporters(nullptr)
, mNextGeneration(1)
, mPendingProcessesState(nullptr)
, mPendingReportersState(nullptr)
, mGetReportsState(nullptr)
{
}
@ -1415,11 +1414,11 @@ nsMemoryReporterManager::GetReportsExtended(
uint32_t generation = mNextGeneration++;
if (mPendingProcessesState) {
if (mGetReportsState) {
// 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.
MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
generation, mPendingProcessesState->mGeneration);
generation, mGetReportsState->mGeneration);
return NS_OK;
}
@ -1430,15 +1429,16 @@ nsMemoryReporterManager::GetReportsExtended(
if (concurrency < 1) {
concurrency = 1;
}
mPendingProcessesState = new PendingProcessesState(generation,
aAnonymize,
aMinimize,
concurrency,
aHandleReport,
aHandleReportData,
aFinishReporting,
aFinishReportingData,
aDMDDumpIdent);
mGetReportsState = new GetReportsState(generation,
aAnonymize,
aMinimize,
concurrency,
aHandleReport,
aHandleReportData,
aFinishReporting,
aFinishReportingData,
aDMDDumpIdent);
mGetReportsState->mChildrenPending = new nsTArray<nsRefPtr<mozilla::dom::ContentParent>>();
if (aMinimize) {
rv = MinimizeMemoryUsage(NS_NewRunnableMethod(
@ -1452,7 +1452,7 @@ nsMemoryReporterManager::GetReportsExtended(
nsresult
nsMemoryReporterManager::StartGettingReports()
{
PendingProcessesState* s = mPendingProcessesState;
GetReportsState* s = mGetReportsState;
nsresult rv;
// Get reports for this process.
@ -1467,11 +1467,8 @@ nsMemoryReporterManager::StartGettingReports()
}
}
#endif
// This is async.
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mAnonymize, parentDMDFile,
s->mFinishReporting, s->mFinishReportingData);
s->mAnonymize, parentDMDFile);
nsTArray<ContentParent*> childWeakRefs;
ContentParent::GetAll(childWeakRefs);
@ -1482,7 +1479,7 @@ nsMemoryReporterManager::StartGettingReports()
// to be buffered and consume (possibly scarce) memory.
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);
@ -1503,44 +1500,25 @@ 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,
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcess(
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize)
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++;
return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
aAnonymize, nullptr);
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
bool aAnonymize, FILE* aDMDFile,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData)
bool aAnonymize, FILE* aDMDFile)
{
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
@ -1548,11 +1526,6 @@ 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
@ -1563,58 +1536,39 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_ASSERT(!aDMDFile);
#endif
mPendingReportersState = new PendingReportersState(
aFinishReporting, aFinishReportingData, aDMDFile);
nsCOMArray<nsIMemoryReporter> allReporters;
{
mozilla::MutexAutoLock autoLock(mMutex);
for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
DispatchReporter(iter.Key(), iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
nsRefPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
allReporters.AppendElement(entry->GetKey());
}
for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
DispatchReporter(reporter, iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
nsPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
allReporters.AppendElement(entry->GetKey());
}
}
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 (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;
if (aDMDFile) {
return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
}
#endif
return NS_OK;
}
nsMemoryReporterManager::PendingProcessesState*
nsMemoryReporterManager::GetReportsState*
nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
{
// Memory reporting only happens on the main thread.
MOZ_RELEASE_ASSERT(NS_IsMainThread());
PendingProcessesState* s = mPendingProcessesState;
GetReportsState* s = mGetReportsState;
if (!s) {
// If we reach here, then:
@ -1651,7 +1605,7 @@ nsMemoryReporterManager::HandleChildReport(
uint32_t aGeneration,
const dom::MemoryReport& aChildReport)
{
PendingProcessesState* s = GetStateForGeneration(aGeneration);
GetReportsState* s = GetStateForGeneration(aGeneration);
if (!s) {
return;
}
@ -1671,7 +1625,7 @@ nsMemoryReporterManager::HandleChildReport(
/* static */ bool
nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState)
const GetReportsState* aState)
{
#ifdef MOZ_NUWA_PROCESS
if (aChild->IsNuwaProcess()) {
@ -1709,7 +1663,7 @@ nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
void
nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
{
PendingProcessesState* s = GetStateForGeneration(aGeneration);
GetReportsState* s = GetStateForGeneration(aGeneration);
if (!s) {
return;
}
@ -1722,29 +1676,29 @@ nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
aGeneration, s->mNumProcessesCompleted,
aSuccess ? "completed" : "exited during report",
s->mNumProcessesRunning,
static_cast<unsigned>(s->mChildrenPending.Length()));
static_cast<unsigned>(s->mChildrenPending->Length()));
// Start pending children up to the concurrency limit.
while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
!s->mChildrenPending.IsEmpty()) {
!s->mChildrenPending->IsEmpty()) {
// Pop last element from s->mChildrenPending
nsRefPtr<ContentParent> nextChild;
nextChild.swap(s->mChildrenPending.LastElement());
s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1);
nextChild.swap(s->mChildrenPending->LastElement());
s->mChildrenPending->TruncateLength(s->mChildrenPending->Length() - 1);
// Start report (if the child is still alive and not Nuwa).
if (StartChildReport(nextChild, s)) {
++s->mNumProcessesRunning;
MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
" (%u running, %u pending)\n",
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
// the timer (if started) and finish up. Otherwise, just return.
if (s->mNumProcessesRunning == 0) {
MOZ_ASSERT(s->mChildrenPending.IsEmpty());
MOZ_ASSERT(s->mChildrenPending->IsEmpty());
if (s->mTimer) {
s->mTimer->Cancel();
}
@ -1756,15 +1710,15 @@ nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* 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
// crash regardless of DEBUG, and this way the compiler doesn't
// 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",
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
// processes that haven't reported back.
@ -1779,43 +1733,25 @@ nsMemoryReporterManager::FinishReporting()
MOZ_CRASH();
}
MOZ_ASSERT(mPendingProcessesState);
MOZ_ASSERT(mGetReportsState);
MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
mPendingProcessesState->mGeneration,
mPendingProcessesState->mNumProcessesCompleted);
mGetReportsState->mGeneration,
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
// required.
nsresult rv = mPendingProcessesState->mFinishReporting->Callback(
mPendingProcessesState->mFinishReportingData);
nsresult rv = mGetReportsState->mFinishReporting->Callback(
mGetReportsState->mFinishReportingData);
delete mPendingProcessesState;
mPendingProcessesState = nullptr;
delete mGetReportsState;
mGetReportsState = nullptr;
return rv;
}
nsMemoryReporterManager::PendingProcessesState::PendingProcessesState(
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)
nsMemoryReporterManager::GetReportsState::~GetReportsState()
{
delete mChildrenPending;
}
static void
@ -1831,7 +1767,7 @@ CrashIfRefcountIsZero(nsISupports* aObj)
nsresult
nsMemoryReporterManager::RegisterReporterHelper(
nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync)
nsIMemoryReporter* aReporter, bool aForce, bool aStrong)
{
// This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex);
@ -1858,7 +1794,7 @@ nsMemoryReporterManager::RegisterReporterHelper(
//
if (aStrong) {
nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
mStrongReporters->Put(aReporter, aIsAsync);
mStrongReporters->PutEntry(aReporter);
CrashIfRefcountIsZero(aReporter);
} else {
CrashIfRefcountIsZero(aReporter);
@ -1871,7 +1807,7 @@ nsMemoryReporterManager::RegisterReporterHelper(
// CollectReports().
return NS_ERROR_XPC_BAD_CONVERT_JS;
}
mWeakReporters->Put(aReporter, aIsAsync);
mWeakReporters->PutEntry(aReporter);
}
return NS_OK;
@ -1881,32 +1817,14 @@ NS_IMETHODIMP
nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ true,
/* async = */ false);
}
NS_IMETHODIMP
nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ true,
/* async = */ true);
/* strong = */ true);
}
NS_IMETHODIMP
nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ false,
/* async = */ false);
}
NS_IMETHODIMP
nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ false,
/* strong = */ false,
/* async = */ true);
/* strong = */ false);
}
NS_IMETHODIMP
@ -1914,8 +1832,7 @@ nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
nsIMemoryReporter* aReporter)
{
return RegisterReporterHelper(aReporter, /* force = */ true,
/* strong = */ true,
/* async = */ false);
/* strong = */ true);
}
NS_IMETHODIMP
@ -1927,7 +1844,7 @@ nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
if (mStrongReporters->Contains(aReporter)) {
mStrongReporters->Remove(aReporter);
mStrongReporters->RemoveEntry(aReporter);
return NS_OK;
}
@ -1936,7 +1853,7 @@ nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
// references that these reporters aren't expecting (which can keep them
// alive longer than intended).
if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
mSavedStrongReporters->Remove(aReporter);
mSavedStrongReporters->RemoveEntry(aReporter);
return NS_OK;
}
@ -1952,7 +1869,7 @@ nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
if (mWeakReporters->Contains(aReporter)) {
mWeakReporters->Remove(aReporter);
mWeakReporters->RemoveEntry(aReporter);
return NS_OK;
}
@ -1961,7 +1878,7 @@ nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
// references that the old reporters aren't expecting (which can end up as
// dangling pointers that lead to use-after-frees).
if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
mSavedWeakReporters->Remove(aReporter);
mSavedWeakReporters->RemoveEntry(aReporter);
return NS_OK;
}
@ -2010,6 +1927,84 @@ nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
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
nsMemoryReporterManager::GetVsize(int64_t* aVsize)
{
@ -2378,61 +2373,61 @@ nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
namespace mozilla {
#define GET_MEMORY_REPORTER_MANAGER(mgr) \
nsRefPtr<nsMemoryReporterManager> mgr = \
nsMemoryReporterManager::GetOrCreate(); \
if (!mgr) { \
return NS_ERROR_FAILURE; \
}
nsresult
RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
{
// Hold a strong reference to the argument to make sure it gets released if
// we return early below.
nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterStrongReporter(reporter);
}
nsresult
RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter)
{
// Hold a strong reference to the argument to make sure it gets released if
// we return early below.
nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterStrongAsyncReporter(reporter);
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (!mgr) {
return NS_ERROR_FAILURE;
}
return mgr->RegisterStrongReporter(reporter);
}
nsresult
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);
}
nsresult
RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
{
GET_MEMORY_REPORTER_MANAGER(mgr)
return mgr->RegisterWeakAsyncReporter(aReporter);
}
nsresult
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);
}
nsresult
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);
}
#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
// with the memory reporter manager.
#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
@ -2488,3 +2483,45 @@ DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
#undef GET_MEMORY_REPORTER_MANAGER
} // 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());
}
typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
typedef nsTHashtable<nsRefPtrHashKey<nsIMemoryReporter>> StrongReportersTable;
typedef nsTHashtable<nsPtrHashKey<nsIMemoryReporter>> WeakReportersTable;
// Inter-process memory reporting proceeds as follows.
//
// - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
// 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
// reports. Control then returns to the main event loop.
//
@ -108,7 +108,7 @@ public:
// is incomplete.
//
// 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...
//
// - If a process is created after reporting starts, it won't be sent a
@ -184,15 +184,10 @@ public:
private:
nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
bool aForce, bool aStrongRef, bool aIsAsync);
bool aForce, bool aStrongRef);
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.
@ -210,16 +205,16 @@ 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
struct GetReportsState
{
uint32_t mGeneration;
bool mAnonymize;
bool mMinimize;
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 mNumProcessesCompleted;
uint32_t mConcurrencyLimit;
@ -229,52 +224,39 @@ private:
nsCOMPtr<nsISupports> mFinishReportingData;
nsString mDMDDumpIdent;
PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
uint32_t aConcurrencyLimit,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
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)
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(nullptr)
, mNumProcessesRunning(1) // reporting starts with the parent
, mNumProcessesCompleted(0)
, mConcurrencyLimit(aConcurrencyLimit)
, mHandleReport(aHandleReport)
, mHandleReportData(aHandleReportData)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDFile(aDMDFile)
, mDMDDumpIdent(aDMDDumpIdent)
{
}
~GetReportsState();
};
// 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;
GetReportsState* mGetReportsState;
// This is reinitialized each time a call to GetReports is initiated.
PendingReportersState* mPendingReportersState;
PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
GetReportsState* GetStateForGeneration(uint32_t aGeneration);
static bool StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState);
const GetReportsState* aState);
};
#define NS_MEMORY_REPORTER_MANAGER_CID \