Created
May 26, 2012 01:45
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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