Skip to content

Instantly share code, notes, and snippets.

@nmaier
Created May 26, 2012 01:45
Show Gist options
  • Save nmaier/2791682 to your computer and use it in GitHub Desktop.
Save nmaier/2791682 to your computer and use it in GitHub Desktop.
Patch v0, Avoid a massive number of XPConnect calls when using memory reporters
# HG changeset patch
# Parent 1987beeb0038e84eddd39d3026388ba361dc0cf2
Avoid a massive number of XPConnect calls when using memory reporters
diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -149,17 +149,13 @@ function minimizeMemoryUsage3x(fAfter)
*
* @param aMgr
* The memory reporter manager.
- * @param aIgnoreSingle
- * Function that indicates if we should skip a single reporter, based
- * on its path.
- * @param aIgnoreMulti
- * Function that indicates if we should skip a multi-reporter, based on
- * its name.
+ * @param aIgnore
+ * Function that indicates if we should skip a reporter, based on its
+ * path.
* @param aHandleReport
* The function that's called for each report.
*/
-function processMemoryReporters(aMgr, aIgnoreSingle, aIgnoreMulti,
- aHandleReport)
+function processMemoryReporters(aMgr, aIgnore, aHandleReport)
{
// Process each memory reporter with aHandleReport.
//
@@ -171,54 +167,19 @@ function processMemoryReporters(aMgr, aI
//
// - After this point we never use the original memory report again.
- function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
- aDescription)
- {
- checkReport(aUnsafePath, aKind, aUnits, aAmount, aDescription);
- aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription);
- }
-
- let e = aMgr.enumerateReporters();
- while (e.hasMoreElements()) {
- let rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
- let unsafePath;
+ let reports = aMgr.collectAllReports();
+ for (let i = reports.length; ~--i;) {
+ let {
+ process, path, kind, units, amount, description
+ } = reports[i];
try {
- unsafePath = rOrig.path;
- if (!aIgnoreSingle(unsafePath)) {
- handleReport(rOrig.process, unsafePath, rOrig.kind, rOrig.units,
- rOrig.amount, rOrig.description);
+ if (!aIgnore(path)) {
+ checkReport(path, kind, units, amount, description);
+ aHandleReport(process, path, kind, units, amount, description);
}
}
catch (ex) {
- debug("Exception thrown by memory reporter: " + unsafePath + ": " + ex);
- }
- }
-
- let e = aMgr.enumerateMultiReporters();
- while (e.hasMoreElements()) {
- let mr = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
- let name = mr.name;
- try {
- if (!aIgnoreMulti(name)) {
- mr.collectReports(handleReport, null);
- }
- }
- catch (ex) {
- // There are two exception cases that must be distinguished here.
- //
- // - We want to halt proceedings on exceptions thrown within this file
- // (i.e. assertion failures in handleReport); such exceptions contain
- // gAssertionFailureMsgPrefix in their string representation.
- //
- // - We want to continue on when faced with exceptions thrown outside
- // this file (i.e. when measuring an amount in collectReports).
- let str = ex.toString();
- if (str.search(gAssertionFailureMsgPrefix) >= 0) {
- throw(ex);
- } else {
- debug("Exception thrown within memory multi-reporter: " + name + ": " +
- ex);
- }
+ debug("Exception thrown by memory reporter: " + path + ": " + ex);
}
}
}
@@ -503,20 +464,14 @@ function getReportsByProcess(aMgr)
// that reports from these multi-reporters can reach here as single reports
// if they were in the child process.)
- function ignoreSingle(aUnsafePath)
+ function ignore(aUnsafePath)
{
return (isSmapsPath(aUnsafePath) && !gVerbose) ||
+ aUnsafePath.startsWith("smaps/") ||
aUnsafePath.startsWith("compartments/") ||
aUnsafePath.startsWith("ghost-windows/");
}
- function ignoreMulti(aMRName)
- {
- return (aMRName === "smaps" && !gVerbose) ||
- aMRName === "compartments" ||
- aMRName === "ghost-windows";
- }
-
let reportsByProcess = {};
function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
@@ -538,7 +493,7 @@ function getReportsByProcess(aMgr)
}
}
- processMemoryReporters(aMgr, ignoreSingle, ignoreMulti, handleReport);
+ processMemoryReporters(aMgr, ignore, handleReport);
return reportsByProcess;
}
@@ -1531,16 +1486,11 @@ function getCompartmentsByProcess(aMgr)
// (Note that some such reports can reach here as single reports if they were
// in the child process.)
- function ignoreSingle(aUnsafePath)
+ function ignore(aUnsafePath)
{
return !aUnsafePath.startsWith("compartments/");
}
- function ignoreMulti(aMRName)
- {
- return aMRName !== "compartments";
- }
-
let compartmentsByProcess = {};
function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
@@ -1584,7 +1534,7 @@ function getCompartmentsByProcess(aMgr)
}
}
- processMemoryReporters(aMgr, ignoreSingle, ignoreMulti, handleReport);
+ processMemoryReporters(aMgr, ignore, handleReport);
return compartmentsByProcess;
}
@@ -1606,16 +1556,11 @@ GhostWindow.prototype = {
function getGhostWindowsByProcess(aMgr)
{
- function ignoreSingle(aUnsafePath)
+ function ignore(aUnsafePath)
{
return !aUnsafePath.startsWith('ghost-windows/')
}
- function ignoreMulti(aName)
- {
- return aName !== "ghost-windows";
- }
-
let ghostWindowsByProcess = {};
function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
@@ -1641,7 +1586,7 @@ function getGhostWindowsByProcess(aMgr)
}
}
- processMemoryReporters(aMgr, ignoreSingle, ignoreMulti, handleReport);
+ processMemoryReporters(aMgr, ignore, handleReport);
return ghostWindowsByProcess;
}
diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -237,7 +237,7 @@ interface nsIMemoryMultiReporter : nsISu
readonly attribute PRInt64 explicitNonHeap;
};
-[scriptable, uuid(4527b1d8-a81f-4af3-9623-80e4120392c7)]
+[scriptable, uuid(203e5380-a6c1-11e1-b3dd-0800200c9a66)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@@ -303,6 +303,14 @@ interface nsIMemoryReporterManager : nsI
* This attribute indicates if moz_malloc_usable_size() works.
*/
readonly attribute boolean hasMozMallocUsableSize;
+
+ /*
+ * Collect all reports in a Javascript array. This will avoid a lot of
+ * XPConnect calls and also simplifies the JS consumers, such as
+ * about:memory.
+ * This function should only be called from Javascript.
+ */
+ [implicit_jscontext] jsval collectAllReports();
};
%{C++
diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -12,6 +12,8 @@
#include "nsISimpleEnumerator.h"
#include "mozilla/Telemetry.h"
+#include "jsapi.h"
+
using namespace mozilla;
#if defined(MOZ_MEMORY)
@@ -838,6 +840,178 @@ nsMemoryReporterManager::GetHasMozMalloc
return NS_OK;
}
+class CollectAllReportersCallback: public nsIMemoryMultiReporterCallback
+{
+private:
+ static void finalize(const JSStringFinalizer *fin, jschar *chars) {
+ nsMemory::Free(chars);
+ }
+ static const JSStringFinalizer finalizer;
+
+ JSContext *cx;
+ JSObject *array;
+
+ inline
+ bool setString(JSObject *obj, const char *name, const nsACString &str)
+ {
+ if (str.IsVoid()) {
+ return JS_DefineProperty(cx, obj, name, JSVAL_NULL, NULL, NULL, JSPROP_ENUMERATE);
+ }
+ JSString *jsstr = JS_NewStringCopyN(cx, str.BeginReading(), str.Length());
+ if (!jsstr) {
+ return false;
+ }
+ return JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(jsstr), NULL, NULL, JSPROP_ENUMERATE);
+ }
+ inline
+ bool setUtf8String(JSObject *obj, const char *name, const nsACString &str)
+ {
+ if (str.IsVoid()) {
+ return JS_DefineProperty(cx, obj, name, JSVAL_NULL, NULL, NULL, JSPROP_ENUMERATE);
+ }
+ PRUint32 len;
+ jschar *chars = (jschar *)UTF8ToNewUnicode(str, &len);
+ if (!chars) {
+ return false;
+ }
+ JSString *jsstr = JS_NewExternalString(cx, chars, len, &finalizer);
+ if (!jsstr) {
+ finalize(NULL, chars);
+ return false;
+ }
+ return JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(jsstr), NULL, NULL, JSPROP_ENUMERATE);
+ }
+ inline
+ bool setPRInt32(JSObject *obj, const char *name, const PRInt32 value)
+ {
+ return JS_DefineProperty(cx, obj, name, INT_TO_JSVAL((int32_t)value), NULL, NULL, JSPROP_ENUMERATE);
+ }
+ inline
+ bool setPRInt64(JSObject *obj, const char *name, const PRInt64 value)
+ {
+ return JS_DefineProperty(cx, obj, name, DOUBLE_TO_JSVAL(double(value)), NULL, NULL, JSPROP_ENUMERATE);
+ }
+
+public:
+ CollectAllReportersCallback(JSContext *aCx, JSObject *aArray)
+ : cx(aCx), array(aArray)
+ {}
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
+ PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
+ const nsACString &aDescription, nsISupports *aData);
+};
+const JSStringFinalizer CollectAllReportersCallback::finalizer =
+ { CollectAllReportersCallback::finalize };
+
+NS_IMPL_ISUPPORTS1(CollectAllReportersCallback, nsIMemoryMultiReporterCallback)
+
+NS_IMETHODIMP CollectAllReportersCallback::Callback(const nsACString &aProcess,
+ const nsACString &aPath,
+ PRInt32 aKind,
+ PRInt32 aUnits,
+ PRInt64 aAmount,
+ const nsACString &aDescription,
+ nsISupports *aData)
+{
+ JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
+ if (!obj) {
+ return NS_ERROR_FAILURE;
+ }
+ JS::AutoObjectRooter rd(cx, obj);
+
+ if (!setString(obj, "process", aProcess)
+ || !setUtf8String(obj, "path", aPath)
+ || !setPRInt32(obj, "kind", aKind)
+ || !setPRInt32(obj, "units", aUnits)
+ || !setPRInt64(obj, "amount", aAmount)
+ || !setUtf8String(obj, "description", aDescription)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ jsval objval = OBJECT_TO_JSVAL(obj);
+ uint32_t length;
+ if (!JS_GetArrayLength(cx, array, &length)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!JS_SetElement(cx, array, length, &objval)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::CollectAllReports(JSContext *cx, jsval *aRetVal)
+{
+ nsresult rv;
+ JSObject *array = JS_NewArrayObject(cx, 0, NULL);
+ if (!array) {
+ return NS_ERROR_FAILURE;
+ }
+ JS::AutoObjectRooter rd(cx, array);
+
+ nsRefPtr<CollectAllReportersCallback> collector =
+ new CollectAllReportersCallback(cx, array);
+
+ // Process regular reporters
+ bool more;
+ nsCOMPtr<nsISimpleEnumerator> eSingle;
+ if (NS_FAILED(rv = EnumerateReporters(getter_AddRefs(eSingle)))) {
+ return rv;
+ }
+ nsCString process, path, description;
+ PRInt32 kind, units;
+ PRInt64 amount;
+ nsCOMPtr<nsIMemoryReporter> reporter;
+ while (NS_SUCCEEDED(eSingle->HasMoreElements(&more)) && more) {
+ if (NS_FAILED(rv = eSingle->GetNext(getter_AddRefs(reporter)))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetProcess(process))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetPath(path))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetDescription(description))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetKind(&kind))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetUnits(&units))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = reporter->GetAmount(&amount))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = collector->Callback(process, path, kind, units,
+ amount, description, nsnull))) {
+ return rv;
+ }
+ }
+
+ // Process multi reporters
+ nsCOMPtr<nsISimpleEnumerator> eMulti;
+ if (NS_FAILED(rv = EnumerateMultiReporters(getter_AddRefs(eMulti)))) {
+ return rv;
+ }
+ nsCOMPtr<nsIMemoryMultiReporter> multiReporter;
+ while (NS_SUCCEEDED(eMulti->HasMoreElements(&more)) && more) {
+ if (NS_FAILED(rv = eMulti->GetNext(getter_AddRefs(multiReporter)))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = multiReporter->CollectReports(collector, nsnull))) {
+ return rv;
+ }
+ }
+
+ *aRetVal = OBJECT_TO_JSVAL(array);
+ return NS_OK;
+}
+
NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
nsMemoryReporter::nsMemoryReporter(nsACString& process,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment