Bug 921502 - Use a JS component to collect telemetry from the startup cache C++ test, instead of using raw JSAPI, r=nfroyd
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 30 Sep 2013 16:09:28 -0400
changeset 163190 8ee9a40710b7b754d32c765581ff9fb82a29272c
parent 163189 3b0e9448a2edb778625adf4d6ed279c07f05ad4b
child 163191 e43f1bac03c150e327352f6a80ce0e30562128ca
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)
reviewersnfroyd
bugs921502
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 921502 - Use a JS component to collect telemetry from the startup cache C++ test, instead of using raw JSAPI, r=nfroyd
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/jsshell.msg
startupcache/test/Makefile.in
startupcache/test/TestStartupCache.cpp
startupcache/test/TestStartupCacheTelemetry.js
startupcache/test/TestStartupCacheTelemetry.manifest
--- a/startupcache/test/Makefile.in
+++ b/startupcache/test/Makefile.in
@@ -1,6 +1,10 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 LIBS += $(MOZ_COMPONENT_LIBS)
+DIST_FILES += \
+  $(srcdir)/TestStartupCacheTelemetry.manifest \
+  $(srcdir)/TestStartupCacheTelemetry.js \
+  $(NULL)
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -15,19 +15,17 @@
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIURI.h"
 #include "nsStringAPI.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
-#include "nsITelemetry.h"
 #include "nsIXPConnect.h"
-#include "jsapi.h"
 #include "prio.h"
 #include "mozilla/Maybe.h"
 
 using namespace JS;
 
 namespace mozilla {
 namespace scache {
 
@@ -395,164 +393,52 @@ TestEarlyShutdown() {
   } else {
     fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE");
     return rv;
   }
  
   return NS_OK;
 }
 
-static bool
-GetHistogramCounts(const char *testmsg, const nsACString &histogram_id,
-                   JSContext *cx, MutableHandle<Value> counts)
-{
-  nsCOMPtr<nsITelemetry> telemetry = do_GetService("@mozilla.org/base/telemetry;1");
-  Rooted<Value> h(cx);
-  nsresult trv = telemetry->GetHistogramById(histogram_id, cx, h.address());
-  if (NS_FAILED(trv)) {
-    fail("%s: couldn't get histogram %s", testmsg, ToNewCString(histogram_id));
-    return false;
-  }
-  passed(testmsg);
-
-  Rooted<Value> snapshot_val(cx);
-  JSFunction *snapshot_fn = nullptr;
-  Rooted<Value> ss(cx);
-  return (JS_GetProperty(cx, JSVAL_TO_OBJECT(h), "snapshot",
-                         &snapshot_val)
-          && (snapshot_fn = JS_ValueToFunction(cx, snapshot_val))
-          && JS::Call(cx, JSVAL_TO_OBJECT(h),
-                      snapshot_fn, 0, nullptr, &ss)
-          && JS_GetProperty(cx, JSVAL_TO_OBJECT(ss), "counts", counts));
-}
-
-nsresult
-CompareCountArrays(JSContext *cx, JSObject *aBefore, JSObject *aAfter)
-{
-  uint32_t before_size, after_size;
-  JS::RootedObject before(cx, aBefore);
-  JS::RootedObject after(cx, aAfter);
-  if (!(JS_GetArrayLength(cx, before, &before_size)
-        && JS_GetArrayLength(cx, after, &after_size))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (before_size != after_size) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  JS::RootedValue before_num(cx), after_num(cx);
-  for (uint32_t i = 0; i < before_size; ++i) {
-    if (!(JS_GetElement(cx, before, i, &before_num)
-          && JS_GetElement(cx, after, i, &after_num))) {
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    bool same = true;
-    if (!JS_LooselyEqual(cx, before_num, after_num, &same)) {
-      return NS_ERROR_UNEXPECTED;
-    } else {
-      if (same) {
-        continue;
-      } else {
-        // Some element of the histograms's count arrays differed.
-        // That's a good thing!
-        return NS_OK;
-      }
-    }
-  }
-
-  // None of the elements of the histograms's count arrays differed.
-  // Not good, we should have recorded something.
-  return NS_ERROR_FAILURE;
-}
-
-nsresult
-TestHistogramValues(const char* type, bool use_js, JSContext *cx,
-                    JSObject *before, JSObject *after)
-{
-  if (!use_js) {
-    fail("couldn't check histogram recording");
-    return NS_ERROR_FAILURE;
-  }
-  nsresult compare = CompareCountArrays(cx, before, after);
-  if (compare == NS_ERROR_UNEXPECTED) {
-    fail("count comparison error");
-    return compare;
-  }
-  if (compare == NS_ERROR_FAILURE) {
-    fail("histogram didn't record %s", type);
-    return compare;
-  }
-  passed("histogram records %s", type);
-  return NS_OK;
-}
-
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("Startup Cache");
   if (xpcom.failed())
     return 1;
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   prefs->SetIntPref("hangmonitor.timeout", 0);
   
   int rv = 0;
-  // nsITelemetry doesn't have a nice C++ interface.
-  JSContext *cx = nullptr;
+  nsresult scrv;
 
-  // XPCOM initialization spins up XPConnect, which spins up a JSRuntime, which
-  // we can only have one of per thread. So we need to get a JSContext out of
-  // XPConnect here, rather than creating our own runtime. XPConnect rules
-  // dictate that we push the context as well, but we're trying to make the
-  // pushing/popping APIs accessible only through nsCxPusher, which isn't
-  // accessible via the external linkage used by this test. We can get away with
-  // using the cx here without triggering a cx stack assert, so just do that
-  // for now. Eventually, the whole notion of pushing and popping will just go
-  // away.
-  nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
-  if (xpc)
-    cx = xpc->GetSafeJSContext();
+  // Register TestStartupCacheTelemetry
+  nsCOMPtr<nsIFile> manifest;
+  scrv = NS_GetSpecialDirectory(NS_GRE_DIR,
+                                getter_AddRefs(manifest));
+  if (NS_FAILED(scrv)) {
+    fail("NS_XPCOM_CURRENT_PROCESS_DIR");
+    return 1;
+  }
 
-  bool use_js = !!cx;
-  JSAutoRequest req(cx);
-  static const JSClass global_class = {
-    "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
-    JS_PropertyStub,  JS_DeletePropertyStub,
-    JS_PropertyStub,  JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub,
-    JS_ConvertStub
-  };
-  JSObject *glob = nullptr;
-  if (use_js)
-    glob = JS_NewGlobalObject(cx, &global_class, nullptr, JS::FireOnNewGlobalHook);
-  if (!glob)
-    use_js = false;
-  mozilla::Maybe<JSAutoCompartment> ac;
-  if (use_js)
-    ac.construct(cx, glob);
-  if (use_js && !JS_InitStandardClasses(cx, glob))
-    use_js = false;
+  manifest->AppendNative(NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
+  XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest);
 
-  NS_NAMED_LITERAL_CSTRING(age_histogram_id, "STARTUP_CACHE_AGE_HOURS");
-  NS_NAMED_LITERAL_CSTRING(invalid_histogram_id, "STARTUP_CACHE_INVALID");
+  nsCOMPtr<nsIObserver> telemetryThing =
+    do_GetService("@mozilla.org/testing/startup-cache-telemetry.js");
+  if (!telemetryThing) {
+    fail("telemetryThing");
+    return 1;
+  }
+  scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
+  if (NS_FAILED(scrv)) {
+    fail("save-initial");
+    rv = 1;
+  }
 
-  Rooted<Value> age_before_counts(cx);
-  if (use_js &&
-      !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test",
-                          age_histogram_id, cx, &age_before_counts))
-    use_js = false;
-  
-  Rooted<Value> invalid_before_counts(cx);
-  if (use_js &&
-      !GetHistogramCounts("STARTUP_CACHE_INVALID histogram before test",
-                          invalid_histogram_id, cx, &invalid_before_counts))
-    use_js = false;
-  
-  nsresult scrv;
   nsCOMPtr<nsIStartupCache> sc 
     = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
   if (NS_FAILED(scrv))
     rv = 1;
   else
     sc->RecordAgesAlways();
   if (NS_FAILED(TestStartupWriteRead()))
     rv = 1;
@@ -561,33 +447,16 @@ int main(int argc, char** argv)
   if (NS_FAILED(TestWriteObject()))
     rv = 1;
   nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory();
   if (NS_FAILED(TestIgnoreDiskCache(profileDir)))
     rv = 1;
   if (NS_FAILED(TestEarlyShutdown()))
     rv = 1;
 
-  Rooted<Value> age_after_counts(cx);
-  if (use_js &&
-      !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram after test",
-                          age_histogram_id, cx, &age_after_counts))
-    use_js = false;
-
-  if (NS_FAILED(TestHistogramValues("age samples", use_js, cx,
-                                    age_before_counts.toObjectOrNull(),
-                                    age_after_counts.toObjectOrNull())))
+  scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
+  if (NS_FAILED(scrv)) {
+    fail("check-final");
     rv = 1;
-                                                    
-  Rooted<Value> invalid_after_counts(cx);
-  if (use_js &&
-      !GetHistogramCounts("STARTUP_CACHE_INVALID histogram after test",
-                          invalid_histogram_id, cx, &invalid_after_counts))
-    use_js = false;
-
-  // STARTUP_CACHE_INVALID should have been triggered by TestIgnoreDiskCache()
-  if (NS_FAILED(TestHistogramValues("invalid disk cache", use_js, cx,
-                                    invalid_before_counts.toObjectOrNull(),
-                                    invalid_after_counts.toObjectOrNull())))
-    rv = 1;
+  }
 
   return rv;
 }
new file mode 100644
--- /dev/null
+++ b/startupcache/test/TestStartupCacheTelemetry.js
@@ -0,0 +1,60 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function shouldHaveChanged(a, b)
+{
+  if (a.length != b.length) {
+    throw Error("TEST-UNEXPECTED-FAIL: telemetry count array size changed");
+  }
+
+  for (let i = 0; i < a.length; ++i) {
+    if (a[i] == b[i]) {
+      continue;
+    }
+    return; // something was different, that's all that matters
+  }
+  throw Error("TEST-UNEXPECTED-FAIL: telemetry data didn't change");
+}
+
+function TestStartupCacheTelemetry() { }
+
+TestStartupCacheTelemetry.prototype = {
+  classID: Components.ID("{73cbeffd-d6c7-42f0-aaf3-f176430dcfc8}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  saveInitial: function() {
+    let t = Services.telemetry;
+    this._age = t.getHistogramById("STARTUP_CACHE_AGE_HOURS").snapshot.counts;
+    this._invalid = t.getHistogramById("STARTUP_CACHE_INVALID").snapshot.counts;
+  },
+
+  checkFinal: function() {
+    let t = Services.telemetry;
+    let newAge = t.getHistogramById("STARTUP_CACHE_AGE_HOURS").snapshot.counts;
+    shouldHaveChanged(this._age, newAge);
+
+    let newInvalid = t.getHistogramById("STARTUP_CACHE_INVALID").snapshot.counts;
+    shouldHaveChanged(this._invalid, newInvalid);
+  },
+
+  observe: function(subject, topic, data) {
+    switch (topic) {
+    case "save-initial":
+      this.saveInitial();
+      break;
+
+    case "check-final":
+      this.checkFinal();
+      break;
+
+    default:
+      throw Error("BADDOG, NO MILKBONE FOR YOU");
+    }
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestStartupCacheTelemetry]);
new file mode 100644
--- /dev/null
+++ b/startupcache/test/TestStartupCacheTelemetry.manifest
@@ -0,0 +1,2 @@
+component {73cbeffd-d6c7-42f0-aaf3-f176430dcfc8} TestStartupCacheTelemetry.js
+contract @mozilla.org/testing/startup-cache-telemetry.js {73cbeffd-d6c7-42f0-aaf3-f176430dcfc8}