Bug 928476 - Add telemetry to measure cross-global adopts. r=mrbkap,nfroyd
authorBobby Holley <bobbyholley@gmail.com>
Wed, 23 Oct 2013 14:02:42 +0200
changeset 151849 a00ba6f64d80a50c9e5a0abf88a3a8a2b59b5f6b
parent 151848 9687708309c56eb4e7a82404cc9fbb4e5d65c3e1
child 151850 d64bea16611b69c3533ec238577c5139a02b3f1e
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmrbkap, nfroyd
bugs928476
milestone27.0a1
Bug 928476 - Add telemetry to measure cross-global adopts. r=mrbkap,nfroyd
dom/bindings/BindingUtils.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
toolkit/components/telemetry/Histograms.json
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1654,24 +1654,29 @@ ReparentWrapper(JSContext* aCx, JS::Hand
   JS::Rooted<JSObject*> aObj(aCx, aObjArg);
   const DOMClass* domClass = GetDOMClass(aObj);
 
   JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
   JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj));
 
   JSAutoCompartment oldAc(aCx, oldParent);
 
-  if (js::GetObjectCompartment(oldParent) ==
-      js::GetObjectCompartment(newParent)) {
+  JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
+  JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
+  if (oldCompartment == newCompartment) {
     if (!JS_SetParent(aCx, aObj, newParent)) {
       MOZ_CRASH();
     }
     return NS_OK;
   }
 
+  // Telemetry.
+  xpc::RecordDonatedNode(oldCompartment);
+  xpc::RecordAdoptedNode(newCompartment);
+
   nsISupports* native = UnwrapDOMObjectToISupports(aObj);
   if (!native) {
     return NS_OK;
   }
 
   // Before proceeding, eagerly create any same-compartment security wrappers
   // that the object might have. This forces us to take the 'WithWrapper' path
   // while transplanting that handles this stuff correctly.
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -256,19 +256,42 @@ public:
 
 public:
   bool mContinuation;
   bool mActive;
 };
 
 namespace xpc {
 
+static uint32_t kLivingAdopters = 0;
+
+void
+RecordAdoptedNode(JSCompartment *c)
+{
+    CompartmentPrivate *priv = EnsureCompartmentPrivate(c);
+    if (!priv->adoptedNode) {
+        priv->adoptedNode = true;
+        ++kLivingAdopters;
+    }
+}
+
+void
+RecordDonatedNode(JSCompartment *c)
+{
+    EnsureCompartmentPrivate(c)->donatedNode = true;
+}
+
 CompartmentPrivate::~CompartmentPrivate()
 {
     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
+
+    Telemetry::Accumulate(Telemetry::COMPARTMENT_ADOPTED_NODE, adoptedNode);
+    Telemetry::Accumulate(Telemetry::COMPARTMENT_DONATED_NODE, donatedNode);
+    if (adoptedNode)
+        --kLivingAdopters;
 }
 
 bool CompartmentPrivate::TryParseLocationURI()
 {
     // Already tried parsing the location before
     if (locationWasParsed)
       return false;
     locationWasParsed = true;
@@ -648,16 +671,23 @@ XPCJSRuntime::GCSliceCallback(JSRuntime 
 
     if (self->mPrevGCSliceCallback)
         (*self->mPrevGCSliceCallback)(rt, progress, desc);
 }
 
 void
 XPCJSRuntime::CustomGCCallback(JSGCStatus status)
 {
+    // Record kLivingAdopters once per GC to approximate time sampling during
+    // periods of activity.
+    if (status == JSGC_BEGIN) {
+        Telemetry::Accumulate(Telemetry::COMPARTMENT_LIVING_ADOPTERS,
+                              kLivingAdopters);
+    }
+
     nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
     for (uint32_t i = 0; i < callbacks.Length(); ++i)
         callbacks[i](status);
 }
 
 /* static */ void
 XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC)
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3739,32 +3739,38 @@ GetRTIdByIndex(JSContext *cx, unsigned i
 namespace xpc {
 
 class CompartmentPrivate
 {
 public:
     CompartmentPrivate()
         : wantXrays(false)
         , universalXPConnectEnabled(false)
+        , adoptedNode(false)
+        , donatedNode(false)
         , scope(nullptr)
         , locationWasParsed(false)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     }
 
     ~CompartmentPrivate();
 
     bool wantXrays;
 
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It's intended as a temporary stopgap measure until we can finish ripping out
     // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
     // the old scoping rules of enablePrivilege). Using it is inherently unsafe.
     bool universalXPConnectEnabled;
 
+    // for telemetry. See bug 928476.
+    bool adoptedNode;
+    bool donatedNode;
+
     // Our XPCWrappedNativeScope. This is non-null if and only if this is an
     // XPConnect compartment.
     XPCWrappedNativeScope *scope;
 
     const nsACString& GetLocation() {
         if (location.IsEmpty() && locationURI) {
             if (NS_FAILED(locationURI->GetSpec(location)))
                 location = NS_LITERAL_CSTRING("<unknown location>");
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -354,16 +354,22 @@ SystemErrorReporter(JSContext *cx, const
 // to fail to compile.
 NS_EXPORT_(void)
 SystemErrorReporterExternal(JSContext *cx, const char *message,
                             JSErrorReport *rep);
 
 NS_EXPORT_(void)
 SimulateActivityCallback(bool aActive);
 
+void
+RecordAdoptedNode(JSCompartment *c);
+
+void
+RecordDonatedNode(JSCompartment *c);
+
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 
 typedef JSObject*
 (*DefineInterface)(JSContext *cx, JS::Handle<JSObject*> global,
                    JS::Handle<jsid> id, bool defineOnGlobal);
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -23,16 +23,29 @@
     "extended_statistics_ok": true,
     "description": "time spent updating accessibility (ms)"
   },
   "BACKGROUNDFILESAVER_THREAD_COUNT": {
     "kind": "enumerated",
     "n_values": 21,
     "description": "Maximum number of concurrent threads reached during a given download session"
   },
+  "COMPARTMENT_DONATED_NODE": {
+    "kind": "boolean",
+    "description": "When a compartment is destroyed, we record whether one of its nodes was ever adopted into another compartment"
+  },
+  "COMPARTMENT_ADOPTED_NODE": {
+    "kind": "boolean",
+    "description": "When a compartment is destroyed, we record whether it had ever adopted a node from another compartment"
+  },
+  "COMPARTMENT_LIVING_ADOPTERS": {
+    "kind": "enumerated",
+    "n_values": 10,
+    "description": "The number of living compartments for which COMPARTMENT_ADOPTED_NODE is true"
+  },
   "CYCLE_COLLECTOR": {
     "kind": "exponential",
     "high": "10000",
     "n_buckets": 50,
     "description": "Time spent on one cycle collection (ms)"
   },
   "CYCLE_COLLECTOR_WORKER": {
     "kind": "exponential",