Bug 670059 - Add some JS engine telemetry counters to measure occurrences of: E4X, __iterator__, mutable __proto__ (r=taras,waldo)
authorLuke Wagner <luke@mozilla.com>
Thu, 07 Jul 2011 15:40:33 -0700
changeset 73896 ba19e1cd3f918d684ad8d71551254631c8d23870
parent 73895 1d186a5f3a96e938a253726ac48ecaf00e80c712
child 73897 6181622382cfe47637cac70bff1a09e80b6cc2cb
push id20923
push usermak77@bonardo.net
push dateFri, 05 Aug 2011 15:10:34 +0000
treeherdermozilla-central@c7931e07dd4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstaras, waldo
bugs670059
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 670059 - Add some JS engine telemetry counters to measure occurrences of: E4X, __iterator__, mutable __proto__ (r=taras,waldo)
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsxml.cpp
js/src/xpconnect/idl/Makefile.in
js/src/xpconnect/idl/nsIJSEngineTelemetryStats.idl
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
toolkit/components/telemetry/TelemetryPing.js
--- 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,