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 165664 a00ba6f64d80a50c9e5a0abf88a3a8a2b59b5f6b
parent 165663 9687708309c56eb4e7a82404cc9fbb4e5d65c3e1
child 165665 d64bea16611b69c3533ec238577c5139a02b3f1e
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, nfroyd
bugs928476
milestone27.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 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",