Bug 670059 - Add some JS engine telemetry counters to measure occurrences of: E4X, __iterator__, mutable __proto__ (r=taras,waldo)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1429,16 +1429,22 @@ JSContext::generatorFor(StackFrame *fp)
for (size_t i = 0; i < genStack.length(); ++i) {
if (genStack[i]->liveFrame() == fp)
return genStack[i];
}
JS_NOT_REACHED("no matching generator");
return NULL;
}
+bool
+JSContext::runningWithTrustedPrincipals() const
+{
+ return !compartment || compartment->principals == runtime->trustedPrincipals();
+}
+
JS_FRIEND_API(void)
JSRuntime::onTooMuchMalloc()
{
#ifdef JS_THREADSAFE
AutoLockGC lock(this);
/*
* We can be called outside a request and can race against a GC that
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1291,16 +1291,22 @@ struct JSContext
#ifdef DEBUG
/*
* Controls whether a quadratic-complexity assertion is performed during
* stack iteration, defaults to true.
*/
bool stackIterAssertionEnabled;
#endif
+ /*
+ * See JS_SetTrustedPrincipals in jsapi.h.
+ * Note: !cx->compartment is treated as trusted.
+ */
+ bool runningWithTrustedPrincipals() const;
+
private:
/*
* The allocation code calls the function to indicate either OOM failure
* when p is null or that a memory pressure counter has reached some
* threshold when p is not null. The function takes the pointer and not
* a boolean flag to minimize the amount of code in its inlined callers.
*/
JS_FRIEND_API(void) checkMallocGCPressure(void *p);
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -75,8 +75,37 @@ JS_UnwrapObject(JSObject *obj)
return obj->unwrap();
}
JS_FRIEND_API(JSObject *)
JS_GetFrameScopeChainRaw(JSStackFrame *fp)
{
return &Valueify(fp)->scopeChain();
}
+
+/*
+ * The below code is for temporary telemetry use. It can be removed when
+ * sufficient data has been harvested.
+ */
+
+extern size_t sE4XObjectsCreated;
+
+JS_FRIEND_API(size_t)
+JS_GetE4XObjectsCreated(JSContext *)
+{
+ return sE4XObjectsCreated;
+}
+
+extern size_t sSetProtoCalled;
+
+JS_FRIEND_API(size_t)
+JS_SetProtoCalled(JSContext *)
+{
+ return sSetProtoCalled;
+}
+
+extern size_t sCustomIteratorCount;
+
+JS_FRIEND_API(size_t)
+JS_GetCustomIteratorCount(JSContext *cx)
+{
+ return sCustomIteratorCount;
+}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -52,11 +52,20 @@ extern JS_FRIEND_API(JSObject *)
JS_FindCompilationScope(JSContext *cx, JSObject *obj);
extern JS_FRIEND_API(JSObject *)
JS_UnwrapObject(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
JS_GetFrameScopeChainRaw(JSStackFrame *fp);
+extern JS_FRIEND_API(size_t)
+JS_GetE4XObjectsCreated(JSContext *cx);
+
+extern JS_FRIEND_API(size_t)
+JS_SetProtoCalled(JSContext *cx);
+
+extern JS_FRIEND_API(size_t)
+JS_GetCustomIteratorCount(JSContext *cx);
+
JS_END_EXTERN_C
#endif /* jsfriendapi_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2903,8 +2903,21 @@ RunDebugGC(JSContext *cx)
RunLastDitchGC(cx);
}
#endif
}
} /* namespace gc */
} /* namespace js */
+
+#if JS_HAS_XML_SUPPORT
+extern size_t sE4XObjectsCreated;
+
+JSXML *
+js_NewGCXML(JSContext *cx)
+{
+ if (!cx->runningWithTrustedPrincipals())
+ ++sE4XObjectsCreated;
+
+ return NewGCThing<JSXML>(cx, js::gc::FINALIZE_XML, sizeof(JSXML));
+}
+#endif
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -251,17 +251,13 @@ js_NewGCFunction(JSContext *cx)
inline js::Shape *
js_NewGCShape(JSContext *cx)
{
return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
}
#if JS_HAS_XML_SUPPORT
-inline JSXML *
-js_NewGCXML(JSContext *cx)
-{
- return NewGCThing<JSXML>(cx, js::gc::FINALIZE_XML, sizeof(JSXML));
-}
+extern JSXML *
+js_NewGCXML(JSContext *cx);
#endif
-
#endif /* jsgcinlines_h___ */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -333,30 +333,35 @@ VectorToIdArray(JSContext *cx, AutoIdVec
JS_FRIEND_API(bool)
GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
{
return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
}
}
+size_t sCustomIteratorCount = 0;
+
static inline bool
GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
{
/* Check whether we have a valid __iterator__ method. */
JSAtom *atom = cx->runtime->atomState.iteratorAtom;
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
return false;
/* If there is no custom __iterator__ method, we are done here. */
if (!vp->isObject()) {
vp->setUndefined();
return true;
}
+ if (!cx->runningWithTrustedPrincipals())
+ ++sCustomIteratorCount;
+
/* Otherwise call it and return that object. */
LeaveTrace(cx);
Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
if (!ExternalInvoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
return false;
if (vp->isPrimitive()) {
/*
* We are always coming from js_ValueToIterator, and we are no longer on
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -160,19 +160,24 @@ static JSBool
obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
/* Let CheckAccess get the slot's value, based on the access mode. */
uintN attrs;
id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
}
+size_t sSetProtoCalled = 0;
+
static JSBool
obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
+ if (!cx->runningWithTrustedPrincipals())
+ ++sSetProtoCalled;
+
/* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
if (!obj->isExtensible()) {
obj->reportNotExtensible(cx);
return false;
}
if (!vp->isObjectOrNull())
return JS_TRUE;
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -159,24 +159,29 @@ IsDeclared(const JSObject *obj)
static JSBool
xml_isXMLName(JSContext *cx, uintN argc, jsval *vp)
{
*vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
return JS_TRUE;
}
+size_t sE4XObjectsCreated = 0;
+
/*
* This wrapper is needed because NewBuiltinClassInstance doesn't
* call the constructor, and we need a place to set the
* HAS_EQUALITY bit.
*/
static inline JSObject *
NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
{
+ if (!cx->runningWithTrustedPrincipals())
+ ++sE4XObjectsCreated;
+
JSObject *obj = NewBuiltinClassInstance(cx, clasp);
if (obj)
obj->syncSpecialEquality();
return obj;
}
#define DEFINE_GETTER(name,code) \
static JSBool \
@@ -7181,16 +7186,22 @@ js_InitXMLClass(JSContext *cx, JSObject
if (!xmlProto)
return NULL;
JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
if (!xml)
return NULL;
xmlProto->setPrivate(xml);
xml->object = xmlProto;
+ /* Don't count this as a real content-created XML object. */
+ if (!cx->runningWithTrustedPrincipals()) {
+ JS_ASSERT(sE4XObjectsCreated > 0);
+ --sE4XObjectsCreated;
+ }
+
const uintN XML_CTOR_LENGTH = 1;
JSFunction *ctor = global->createConstructor(cx, XML, &js_XMLClass, CLASS_ATOM(cx, XML),
XML_CTOR_LENGTH);
if (!ctor)
return NULL;
if (!LinkConstructorAndPrototype(cx, ctor, xmlProto))
return NULL;
--- a/js/src/xpconnect/idl/Makefile.in
+++ b/js/src/xpconnect/idl/Makefile.in
@@ -60,12 +60,13 @@ XPIDLSRCS = \
nsIXPConnect.idl \
nsIXPCSecurityManager.idl \
nsIXPCScriptable.idl \
nsIScriptError.idl \
nsIXPCScriptNotify.idl \
nsIScriptableInterfaces.idl \
xpcIJSWeakReference.idl \
xpcIJSGetFactory.idl \
+ nsIJSEngineTelemetryStats.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/idl/nsIJSEngineTelemetryStats.idl
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Firefox.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation <http://www.mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+/**
+ * Telemetry uses this interface on the @mozilla.org/js/xpc/XPConnect;1 service
+ * to extract JS engine stats.
+ */
+[scriptable, uuid(5a6ea52b-4e23-402f-93e3-59f29b2f1a88)]
+interface nsIJSEngineTelemetryStats : nsISupports
+{
+ /**
+ * The value returned by this attribute is included as the 'js' property of
+ * the telemetry ping JSON blob.
+ */
+ [implicit_jscontext]
+ readonly attribute jsval telemetryValue;
+};
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -43,16 +43,17 @@
/* High level class and public functions implementation. */
#include "xpcprivate.h"
#include "XPCWrapper.h"
#include "nsBaseHashtable.h"
#include "nsHashKeys.h"
#include "jsatom.h"
#include "jsobj.h"
+#include "jsfriendapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsscript.h"
#include "nsThreadUtilsInternal.h"
#include "dom_quickstubs.h"
#include "nsNullPrincipal.h"
#include "nsIURI.h"
#include "nsJSEnvironment.h"
@@ -61,23 +62,24 @@
#include "XrayWrapper.h"
#include "WrapperFactory.h"
#include "AccessCheck.h"
#include "jsdIDebuggerService.h"
#include "xpcquickstubs.h"
-NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect,
+NS_IMPL_THREADSAFE_ISUPPORTS7(nsXPConnect,
nsIXPConnect,
nsISupportsWeakReference,
nsIThreadObserver,
nsIJSRuntimeService,
nsIJSContextStack,
- nsIThreadJSContextStack)
+ nsIThreadJSContextStack,
+ nsIJSEngineTelemetryStats)
nsXPConnect* nsXPConnect::gSelf = nsnull;
JSBool nsXPConnect::gOnceAliveNowDead = JS_FALSE;
PRUint32 nsXPConnect::gReportAllJSExceptions = 0;
JSBool nsXPConnect::gDebugMode = JS_FALSE;
JSBool nsXPConnect::gDesiredDebugMode = JS_FALSE;
// Global cache of the default script security manager (QI'd to
@@ -2898,16 +2900,44 @@ NS_IMETHODIMP
nsXPConnect::SetDebugModeWhenPossible(PRBool mode)
{
gDesiredDebugMode = mode;
if (!mode)
CheckForDebugMode(mRuntime->GetJSRuntime());
return NS_OK;
}
+NS_IMETHODIMP
+nsXPConnect::GetTelemetryValue(JSContext *cx, jsval *rval)
+{
+ JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
+ if (!obj)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uintN attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+
+ size_t i = JS_GetE4XObjectsCreated(cx);
+ jsval v = DOUBLE_TO_JSVAL(i);
+ if (!JS_DefineProperty(cx, obj, "e4x", v, NULL, NULL, attrs))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ i = JS_SetProtoCalled(cx);
+ v = DOUBLE_TO_JSVAL(i);
+ if (!JS_DefineProperty(cx, obj, "setProto", v, NULL, NULL, attrs))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ i = JS_GetCustomIteratorCount(cx);
+ v = DOUBLE_TO_JSVAL(i);
+ if (!JS_DefineProperty(cx, obj, "customIter", v, NULL, NULL, attrs))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *rval = OBJECT_TO_JSVAL(obj);
+ return NS_OK;
+}
+
/* These are here to be callable from a debugger */
JS_BEGIN_EXTERN_C
JS_EXPORT_API(void) DumpJSStack()
{
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
if(NS_SUCCEEDED(rv) && xpc)
xpc->DebugDumpJSStack(PR_TRUE, PR_TRUE, PR_FALSE);
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -98,16 +98,17 @@
#include "nsAutoJSValHolder.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h"
#include "nsDataHashtable.h"
#include "nsThreadUtils.h"
#include "nsIJSContextStack.h"
+#include "nsIJSEngineTelemetryStats.h"
#include "nsDeque.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsIExceptionService.h"
#include "nsVariant.h"
#include "nsIPropertyBag.h"
@@ -456,26 +457,28 @@ const PRBool OBJ_IS_GLOBAL = PR_TRUE;
const PRBool OBJ_IS_NOT_GLOBAL = PR_FALSE;
class nsXPConnect : public nsIXPConnect,
public nsIThreadObserver,
public nsSupportsWeakReference,
public nsCycleCollectionJSRuntime,
public nsCycleCollectionParticipant,
public nsIJSRuntimeService,
- public nsIThreadJSContextStack
+ public nsIThreadJSContextStack,
+ public nsIJSEngineTelemetryStats
{
public:
// all the interface method declarations...
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCONNECT
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIJSRUNTIMESERVICE
NS_DECL_NSIJSCONTEXTSTACK
NS_DECL_NSITHREADJSCONTEXTSTACK
+ NS_DECL_NSIJSENGINETELEMETRYSTATS
// non-interface implementation
public:
// These get non-addref'd pointers
static nsXPConnect* GetXPConnect();
static nsXPConnect* FastGetXPConnect() { return gSelf ? gSelf : GetXPConnect(); }
static XPCJSRuntime* GetRuntimeInstance();
XPCJSRuntime* GetRuntime() {return mRuntime;}
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -181,23 +181,29 @@ function getMetadata(reason) {
function getSimpleMeasurements() {
let si = Cc["@mozilla.org/toolkit/app-startup;1"].
getService(Ci.nsIAppStartup).getStartupInfo();
var ret = {
// uptime in minutes
uptime: Math.round((new Date() - si.process) / 60000)
}
+
if (si.process) {
for each (let field in ["main", "firstPaint", "sessionRestored"]) {
if (!(field in si))
continue;
ret[field] = si[field] - si.process
}
}
+
+ ret.js = Cc["@mozilla.org/js/xpc/XPConnect;1"]
+ .getService(Ci.nsIJSEngineTelemetryStats)
+ .telemetryValue;
+
return ret;
}
function TelemetryPing() {}
TelemetryPing.prototype = {
_histograms: {},
_initialized: false,