Bug 666075 - Add memory multi-reporters. r=khuey, sr=bz.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -294,52 +294,102 @@ ContentChild::InitXPCOM()
}
PMemoryReportRequestChild*
ContentChild::AllocPMemoryReportRequest()
{
return new MemoryReportRequestChild();
}
+// This is just a wrapper for InfallibleTArray<MemoryReport> that implements
+// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports.
+class MemoryReportsWrapper : public nsISupports {
+public:
+ NS_DECL_ISUPPORTS
+ MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
+ InfallibleTArray<MemoryReport> *mReports;
+};
+NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
+
+class MemoryReportCallback : public nsIMemoryMultiReporterCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ MemoryReportCallback(const nsACString &aProcess)
+ : mProcess(aProcess)
+ {
+ }
+
+ NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
+ PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
+ const nsACString &aDescription,
+ nsISupports *aiWrappedReports)
+ {
+ MemoryReportsWrapper *wrappedReports =
+ static_cast<MemoryReportsWrapper *>(aiWrappedReports);
+
+ MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits,
+ aAmount, nsCString(aDescription));
+ wrappedReports->mReports->AppendElement(memreport);
+ return NS_OK;
+ }
+private:
+ const nsCString mProcess;
+};
+NS_IMPL_ISUPPORTS1(
+ MemoryReportCallback
+, nsIMemoryMultiReporterCallback
+)
+
bool
ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child)
{
- InfallibleTArray<MemoryReport> reports;
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
- nsCOMPtr<nsISimpleEnumerator> r;
- mgr->EnumerateReporters(getter_AddRefs(r));
+
+ InfallibleTArray<MemoryReport> reports;
+ static const int maxLength = 31; // big enough; pid is only a few chars
+ nsPrintfCString process(maxLength, "Content (%d)", getpid());
+
+ // First do the vanilla memory reporters.
+ nsCOMPtr<nsISimpleEnumerator> e;
+ mgr->EnumerateReporters(getter_AddRefs(e));
PRBool more;
- while (NS_SUCCEEDED(r->HasMoreElements(&more)) && more) {
- nsCOMPtr<nsIMemoryReporter> report;
- r->GetNext(getter_AddRefs(report));
+ while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIMemoryReporter> r;
+ e->GetNext(getter_AddRefs(r));
nsCString path;
PRInt32 kind;
PRInt32 units;
+ PRInt64 amount;
nsCString desc;
- PRInt64 amount;
- report->GetPath(getter_Copies(path));
- report->GetKind(&kind);
- report->GetUnits(&units);
- report->GetAmount(&amount);
- report->GetDescription(getter_Copies(desc));
+ r->GetPath(getter_Copies(path));
+ r->GetKind(&kind);
+ r->GetUnits(&units);
+ r->GetAmount(&amount);
+ r->GetDescription(getter_Copies(desc));
+
+ MemoryReport memreport(process, path, kind, units, amount, desc);
+ reports.AppendElement(memreport);
+ }
- static const int maxLength = 31; // big enough; pid is only a few chars
- MemoryReport memreport(nsPrintfCString(maxLength, "Content (%d)",
- getpid()),
- path,
- kind,
- units,
- amount,
- desc);
+ // Then do the memory multi-reporters, by calling CollectReports on each
+ // one, whereupon the callback will turn each measurement into a
+ // MemoryReport.
+ mgr->EnumerateMultiReporters(getter_AddRefs(e));
+ MemoryReportsWrapper wrappedReports(&reports);
+ MemoryReportCallback cb(process);
+ while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIMemoryMultiReporter> r;
+ e->GetNext(getter_AddRefs(r));
- reports.AppendElement(memreport);
-
+ r->CollectReports(&cb, &wrappedReports);
}
child->Send__delete__(child, reports);
return true;
}
bool
ContentChild::DeallocPMemoryReportRequest(PMemoryReportRequestChild* actor)
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -162,40 +162,53 @@ function update()
// After this point we never use the original memory reporter again.
//
// - Note that copying rOrig.amount (which calls a C++ function under the
// IDL covers) to r._amount for every reporter now means that the
// results as consistent as possible -- measurements are made all at
// once before most of the memory required to generate this page is
// allocated.
var reportersByProcess = {};
- var e = mgr.enumerateReporters();
- while (e.hasMoreElements()) {
- var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
- var process = rOrig.process === "" ? "Main" : rOrig.process;
+
+ function addReporter(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
+ {
+ var process = aProcess === "" ? "Main" : aProcess;
var r = {
- _path: rOrig.path,
- _kind: rOrig.kind,
- _units: rOrig.units,
- _amount: rOrig.amount,
- _description: rOrig.description
+ _path: aPath,
+ _kind: aKind,
+ _units: aUnits,
+ _amount: aAmount,
+ _description: aDescription
};
if (!reportersByProcess[process]) {
reportersByProcess[process] = {};
}
var reporters = reportersByProcess[process];
if (reporters[r._path]) {
// Already an entry; must be a duplicated reporter. This can
// happen legitimately. Sum the values.
reporters[r._path]._amount += r._amount;
} else {
reporters[r._path] = r;
}
}
+ // Process vanilla reporters first, then multi-reporters.
+ var e = mgr.enumerateReporters();
+ while (e.hasMoreElements()) {
+ var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
+ addReporter(rOrig.process, rOrig.path, rOrig.kind, rOrig.units,
+ rOrig.amount, rOrig.description);
+ }
+ var e = mgr.enumerateMultiReporters();
+ while (e.hasMoreElements()) {
+ var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+ r.collectReports(addReporter, null);
+ }
+
// Generate output for one process at a time. Always start with the
// Main process.
var text = genProcessText("Main", reportersByProcess["Main"]);
for (var process in reportersByProcess) {
if (process !== "Main") {
text += genProcessText(process, reportersByProcess[process]);
}
}
@@ -237,17 +250,17 @@ function update()
}
// Compare two memory reporter nodes. We want to group together measurements
// with the same units, so sort first by the nodes' _units field, then sort by
// the amount if the units are equal.
function cmp_amount(a, b)
{
if (a._units != b._units)
- return b._units - a._units;
+ return a._units - b._units; // use the enum order from nsIMemoryReporter
else
return b._amount - a._amount;
};
/**
* Generates the text for a single process.
*
* @param aProcess
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -13,27 +13,39 @@
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
const Cc = Components.classes;
const Ci = Components.interfaces;
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
getService(Ci.nsIMemoryReporterManager);
- // Remove all the real reporters; save them to restore at the end.
+ // Remove all the real reporters and multi-reporters; save them to
+ // restore at the end.
var e = mgr.enumerateReporters();
var realReporters = [];
var dummy = 0;
while (e.hasMoreElements()) {
- var mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
- // Get the memoryUsed field, even though we don't use it, just to test
+ var r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
+ // Get the |amount| field, even though we don't use it, just to test
// that the reporter doesn't crash or anything.
- dummy += mr.memoryUsed;
- mgr.unregisterReporter(mr);
- realReporters.push(mr);
+ dummy += r.amount;
+ mgr.unregisterReporter(r);
+ realReporters.push(r);
+ }
+ e = mgr.enumerateMultiReporters();
+ var realMultiReporters = [];
+ var dummy = 0;
+ while (e.hasMoreElements()) {
+ var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+ // Call collectReports, even though we don't use its results, just to
+ // test that the multi-reporter doesn't crash or anything.
+ r.collectReports(function(){}, null);
+ mgr.unregisterMultiReporter(r);
+ realMultiReporters.push(r);
}
// Setup various fake-but-deterministic reporters.
const KB = 1024;
const MB = KB * KB;
const kUnknown = -1;
const MAPPED = Ci.nsIMemoryReporter.KIND_MAPPED;
const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
@@ -219,24 +231,27 @@ 814,743,552 B (100.0%) -- explicit\n\
Other Measurements\n\
0 B -- heap-used [*]\n\
0 B -- other1 [*]\n\
\n\
"
function finish()
{
- // Unregister fake reporters, re-register the real reporters, just in
- // case subsequent tests rely on them.
+ // Unregister fake reporters, re-register the real reporters and
+ // multi-reporters, just in case subsequent tests rely on them.
for (var i = 0; i < fakeReporters.length; i++) {
mgr.unregisterReporter(fakeReporters[i]);
}
for (var i = 0; i < realReporters.length; i++) {
mgr.registerReporter(realReporters[i]);
}
+ for (var i = 0; i < realMultiReporters.length; i++) {
+ mgr.registerMultiReporter(realMultiReporters[i]);
+ }
SimpleTest.finish();
}
// Cut+paste the entire page and check that the cut text matches what we
// expect. This tests the output in general and also that the cutting and
// pasting works as expected.
function test(aFrame, aExpectedText, aNext) {
document.querySelector("#" + aFrame).focus();
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -35,16 +35,26 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsISimpleEnumerator;
+/*
+ * An nsIMemoryReporter reports a single memory measurement as an object.
+ * Use this when it makes sense to gather this measurement without gathering
+ * related measurements at the same time.
+ *
+ * Note that the |amount| field may be implemented as a function, and so
+ * accessing it can trigger significant computation; the other fields can
+ * be accessed without triggering this computation. (Compare and contrast
+ * this with nsIMemoryMultiReporter.)
+ */
[scriptable, uuid(37d18434-9819-4ce1-922f-15d8b63da066)]
interface nsIMemoryReporter : nsISupports
{
/*
* The name of the process containing this reporter. Each reporter initially
* has "" in this field, indicating that it applies to the current process.
* (This is true even for reporters in a child process.) When a reporter
* from a child process is copied into the main process, the copy has its
@@ -128,46 +138,91 @@ interface nsIMemoryReporter : nsISupport
/*
* The units on the reporter's amount. See UNITS_* above.
*/
readonly attribute PRInt32 units;
/*
* The numeric value reported by this memory reporter.
*/
- readonly attribute long long amount;
+ readonly attribute PRInt64 amount;
/*
* A human-readable description of this memory usage report.
*/
readonly attribute string description;
};
-[scriptable, uuid(7c62de18-1edd-40f8-9da2-a8c622763074)]
+[scriptable, function, uuid(5b15f3fa-ba15-443c-8337-7770f5f0ce5d)]
+interface nsIMemoryMultiReporterCallback : nsISupports
+{
+ void callback(in ACString process, in AUTF8String path, in PRInt32 kind,
+ in PRInt32 units, in PRInt64 amount,
+ in AUTF8String description, in nsISupports closure);
+};
+
+/*
+ * An nsIMemoryMultiReporter reports multiple memory measurements via a
+ * callback function which is called once for each measurement. Use this
+ * when you want to gather multiple measurements in a single operation (eg.
+ * a single traversal of a large data structure).
+ *
+ * The arguments to the callback deliberately match the fields in
+ * nsIMemoryReporter, but note that seeing any of these arguments requires
+ * calling collectReports which will trigger all relevant computation.
+ * (Compare and contrast this with nsIMemoryReporter, which allows all
+ * fields except |amount| to be accessed without triggering computation.)
+ */
+[scriptable, uuid(eae277ad-b67d-4389-95f4-03fa11c09d06)]
+interface nsIMemoryMultiReporter : nsISupports
+{
+ void collectReports(in nsIMemoryMultiReporterCallback callback,
+ in nsISupports closure);
+};
+
+[scriptable, uuid(80a93b4c-6fff-4acd-8598-3891074a30ab)]
interface nsIMemoryReporterManager : nsISupports
{
/*
* Return an enumerator of nsIMemoryReporters that are currently registered.
*/
nsISimpleEnumerator enumerateReporters ();
/*
- * Register the given nsIMemoryReporter. It is an error to register
- * more than one reporter with the same path. After a reporter is
- * registered, it will be available via enumerateReporters(). The
- * Manager service will hold a strong reference to the given reporter.
+ * Return an enumerator of nsIMemoryMultiReporters that are currently
+ * registered.
+ */
+ nsISimpleEnumerator enumerateMultiReporters ();
+
+ /*
+ * Register the given nsIMemoryReporter. After a reporter is registered,
+ * it will be available via enumerateReporters(). The Manager service
+ * will hold a strong reference to the given reporter.
*/
void registerReporter (in nsIMemoryReporter reporter);
/*
+ * Register the given nsIMemoryMultiReporter. After a multi-reporter is
+ * registered, it will be available via enumerateMultiReporters(). The
+ * Manager service will hold a strong reference to the given
+ * multi-reporter.
+ */
+ void registerMultiReporter (in nsIMemoryMultiReporter reporter);
+
+ /*
* Unregister the given memory reporter.
*/
void unregisterReporter (in nsIMemoryReporter reporter);
/*
+ * Unregister the given memory multi-reporter.
+ */
+ void unregisterMultiReporter (in nsIMemoryMultiReporter reporter);
+
+ /*
* Initialize.
*/
void init ();
};
%{C++
/*
@@ -184,11 +239,14 @@ interface nsIMemoryReporterManager : nsI
NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _usageFunction(); return NS_OK; } \
NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; } \
}; \
NS_IMPL_ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter)
#define NS_MEMORY_REPORTER_NAME(_classname) MemoryReporter_##_classname
NS_COM nsresult NS_RegisterMemoryReporter (nsIMemoryReporter *reporter);
+NS_COM nsresult NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
+
NS_COM nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter);
+NS_COM nsresult NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
%}
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -433,36 +433,66 @@ nsMemoryReporterManager::EnumerateReport
{
nsresult rv;
mozilla::MutexAutoLock autoLock(mMutex);
rv = NS_NewArrayEnumerator(result, mReporters);
return rv;
}
NS_IMETHODIMP
+nsMemoryReporterManager::EnumerateMultiReporters(nsISimpleEnumerator **result)
+{
+ nsresult rv;
+ mozilla::MutexAutoLock autoLock(mMutex);
+ rv = NS_NewArrayEnumerator(result, mMultiReporters);
+ return rv;
+}
+
+NS_IMETHODIMP
nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
{
mozilla::MutexAutoLock autoLock(mMutex);
if (mReporters.IndexOf(reporter) != -1)
return NS_ERROR_FAILURE;
mReporters.AppendObject(reporter);
return NS_OK;
}
NS_IMETHODIMP
+nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter *reporter)
+{
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (mMultiReporters.IndexOf(reporter) != -1)
+ return NS_ERROR_FAILURE;
+
+ mMultiReporters.AppendObject(reporter);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter)
{
mozilla::MutexAutoLock autoLock(mMutex);
if (!mReporters.RemoveObject(reporter))
return NS_ERROR_FAILURE;
return NS_OK;
}
+NS_IMETHODIMP
+nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporter)
+{
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (!mMultiReporters.RemoveObject(reporter))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
nsMemoryReporter::nsMemoryReporter(nsCString& process,
nsCString& path,
PRInt32 kind,
PRInt32 units,
PRInt64 amount,
nsCString& desc)
@@ -510,27 +540,44 @@ NS_IMETHODIMP nsMemoryReporter::GetAmoun
}
NS_IMETHODIMP nsMemoryReporter::GetDescription(char **aDescription)
{
*aDescription = strdup(mDesc.get());
return NS_OK;
}
-
NS_COM nsresult
NS_RegisterMemoryReporter (nsIMemoryReporter *reporter)
{
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
if (mgr == nsnull)
return NS_ERROR_FAILURE;
return mgr->RegisterReporter(reporter);
}
NS_COM nsresult
+NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
+{
+ nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
+ if (mgr == nsnull)
+ return NS_ERROR_FAILURE;
+ return mgr->RegisterMultiReporter(reporter);
+}
+
+NS_COM nsresult
NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter)
{
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
if (mgr == nsnull)
return NS_ERROR_FAILURE;
return mgr->UnregisterReporter(reporter);
}
+NS_COM nsresult
+NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
+{
+ nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
+ if (mgr == nsnull)
+ return NS_ERROR_FAILURE;
+ return mgr->UnregisterMultiReporter(reporter);
+}
+
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -35,15 +35,16 @@ class nsMemoryReporterManager : public n
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTERMANAGER
nsMemoryReporterManager();
virtual ~nsMemoryReporterManager();
private:
- nsCOMArray<nsIMemoryReporter> mReporters;
- Mutex mMutex;
+ nsCOMArray<nsIMemoryReporter> mReporters;
+ nsCOMArray<nsIMemoryMultiReporter> mMultiReporters;
+ Mutex mMutex;
};
#define NS_MEMORY_REPORTER_MANAGER_CID \
{ 0xfb97e4f5, 0x32dd, 0x497a, \
{ 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }