Permalink
Browse files

initial extension attempt; this is all derived from about:gc

Our upstream about:gc was originally https://bitbucket.org/burg/aboutgc
  • Loading branch information...
0 parents commit d34ac5844f35f109b98a95452dae70322fb549d1 @asutherland committed Feb 11, 2012
Showing with 333 additions and 0 deletions.
  1. +7 −0 Makefile
  2. +21 −0 README.md
  3. +14 −0 about-jsprobes.html
  4. +189 −0 about-jsprobes.js
  5. +67 −0 bootstrap.js
  6. +35 −0 install.rdf
  7. 0 stylesheet.css
@@ -0,0 +1,7 @@
+FILES := bootstrap.js install.rdf stylesheet.css about-jsprobes.html about-jsprobes.js lib/*.js
+
+about-jsprobes.xpi: $(FILES)
+ zip -9 $@ $+
+
+clean:
+ rm -f about-jsprobes.xpi *[~#]
@@ -0,0 +1,21 @@
+This is intended to be a super simple playground for JSProbes. It is a simple
+restartless add-on that adds an "about:jsprobes" URL that displays/runs
+about-jsprobes.html and about-jsprobes.js with chrome privileges. The probes
+will not be active until you create the page. The probes will ideally stop
+once you close the page.
+
+Your stock Firefox does not include jsprobes support. To get said support, you
+need to apply the patch queue from: https://bitbucket.org/asuth/jsprobes-patches
+and modify your .mozconfig to include:
+
+ ac_add_options --enable-jsprobes
+
+You can also try and use a build spun by someone else.
+
+This add-on is derived from about:gc by Steve Fink (and later amended by Brian
+Burg to support JSProbes.)
+
+If you want to learn about JSProbes, you want to see:
+
+- http://brrian.tumblr.com/post/10571624125/jsprobes
+- http://blog.mozilla.com/sfink/2011/09/21/js-probes/
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<base href="resource://aboutjsprobes/" />
+<title>About JSProbes</title>
+<script type="text/javascript" src="about-jsprobes.js"></script>
+<link rel="stylesheet" href="stylesheet.css" />
+</head>
+<body>
+<h2>Probe Results:</h2>
+<pre id="oot">
+</pre>
+</body>
+</html>
@@ -0,0 +1,189 @@
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+const wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator);
+
+try {
+ const probes = Cc["@mozilla.org/base/probes;1"]
+ .getService(Ci.nsIProbeService);
+} catch(e) {
+ console.error("Could not load nsIProbeService");
+ throw new Error("I can't go on without my probes.");
+}
+
+var mainWindow = wm.getMostRecentWindow("navigator:browser");
+var gBrowser = mainWindow.gBrowser;
+
+var urlListener = {
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ },
+
+ onLocationChange: function(aProgress, aRequest, aURI) {
+ window.console.log("new URI: " + aURI.spec);
+ }
+};
+
+window.console.log("added listener");
+
+window.addEventListener('unload', function() {
+ gBrowser.removeProgressListener(urlListener);
+ stopProbes();
+});
+
+gBrowser.addProgressListener(urlListener);
+
+// If scrolling through real-time data, advance this whenever dropping off
+// data points.
+var firstGCIndex = 0;
+var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+const TYPE_REPEATING_SLACK = Ci.nsITimer.TYPE_REPEATING_SLACK;
+var activeHandlers = [];
+
+const BYTES_PER_KB = Math.pow(2, 10);
+const BYTES_PER_MB = Math.pow(2, 20);
+const BYTES_PER_GB = Math.pow(2, 30);
+
+const MS_PER_SECOND = 1000;
+const MS_PER_MINUTE = MS_PER_SECOND * 1000;
+const MS_PER_HOUR = MS_PER_MINUTE * 60;
+
+results = [];
+
+timerCb = {};
+
+function stopProbes() {
+ if (!probes) return;
+
+ while (activeHandlers.length) {
+ probes.removeHandler(activeHandlers.pop());
+ }
+
+ timer.cancel();
+ timer = null;
+ probes = null;
+}
+
+function execOnProbeThread(func, callback) {
+ var execStr = func.toString();
+ execStr = execStr.substring(execStr.indexOf("{") + 1,
+ execStr.lastIndexOf("}"));
+ probes.asyncQuery(execStr, callback);
+}
+
+function registerProbe(probepoint, captureArgs, func) {
+ var usingStr = "using(" + captureArgs.join(");using(") + ");";
+ var execStr = func.toString();
+ execStr = execStr.substring(execStr.indexOf("{") + 1,
+ execStr.lastIndexOf("}"));
+ var cookie = probes.addHandler(probepoint, usingStr, execStr);
+ activeHandlers.push(cookie);
+}
+
+function gatherDataFromProbeThreadPeriodically(intervalMS,
+ probeThreadFunc,
+ thisThreadProcessFunc) {
+ timerCb = {
+ observe: function(subject, topic, data) {
+ execOnProbeThread(probeThreadFunc, thisThreadProcessFunc);
+ }
+ };
+
+ timer.init(timerCb, intervalMS, TYPE_REPEATING_SLACK);
+}
+
+var outputDomNode = document.getElementById("oot");
+function prettyPrint(obj) {
+ var s = JSON.stringify(obj, 0, 2),
+ tn = document.createTextNode(s);
+ outputDomNode.appendChild(tn);
+}
+
+/**
+ * These are the GC probes from about:gc, re-written to use the registerProbe
+ * idiom above that scrapes source out of functions and tries to look pretty.
+ *
+ * Their general goal is to produce a list of GC info where the items look like:
+ * [GC start timestamp, GC end timestamp, before bytes, after bytes]. There
+ * are also heap resize events of the form [timestamp, old bytes, new bytes].
+ * These all end up living in tagged objects, and compartment and global GCs
+ * are distinguished from each other.
+ */
+function activateGCProbes() {
+ execOnProbeThread(function() {
+ var pendingData = [],
+ HEAP_RESIZE_INTERVAL = 500.0, // minimum MS between posted events
+ lastRecTime = 0,
+ current;
+ });
+
+ registerProbe(
+ probes.COMPARTMENT_GC_DID_START,
+ ["env.currentTimeMS", "runtime.gcBytes"],
+ function() {
+ current = {
+ type: 'GC_COMPARTMENT',
+ data: [env.currentTimeMS, 0, runtime.gcBytes, 0],
+ sortValue: env.currentTimeMS };
+ });
+
+ registerProbe(
+ probes.GLOBAL_GC_DID_START,
+ ["env.currentTimeMS", "runtime.gcBytes"],
+ function() {
+ current = {
+ type: 'GC_GLOBAL',
+ data: [env.currentTimeMS, 0, runtime.gcBytes, 0],
+ sortValue: env.currentTimeMS
+ };
+ });
+
+ registerProbe(
+ probes.JS_WILL_RESIZE_HEAP,
+ ["env.currentTimeMS", "oldSize", "newSize"],
+ function() {
+ if ((env.currentTimeMS - lastRecTime) > HEAP_RESIZE_INTERVAL) {
+ lastRecTime = env.currentTimeMS;
+ pendingData.push({
+ type: 'HEAP_RESIZE',
+ sortValue: env.currentTimeMS,
+ data: [env.currentTimeMS, oldSize, newSize]
+ });
+ }
+ });
+
+ registerProbe(
+ probes.COMPARTMENT_GC_WILL_END,
+ ["env.currentTimeMS", "runtime.gcBytes"],
+ function() {
+ current.data[1] = env.currentTimeMS;
+ current.data[3] = runtime.gcBytes;
+ pendingData.push(current);
+ });
+
+ registerProbe(
+ probes.GLOBAL_GC_WILL_END,
+ ["env.currentTimeMS", "runtime.gcBytes"],
+ function() {
+ current.data[1] = env.currentTimeMS;
+ current.data[3] = runtime.gcBytes;
+ pendingData.push(current);
+ });
+
+ gatherDataFromProbeThreadPeriodically(
+ 1000,
+ function onProbeThread() {
+ postMessage(pendingData);
+ pendingData = [];
+ },
+ function onOurThread(e) {
+ prettyPrint(e.value);
+ });
+}
+
+activateGCProbes();
@@ -0,0 +1,67 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cm = Components.manager;
+
+var testing = false;
+
+Cm.QueryInterface(Ci.nsIComponentRegistrar);
+
+Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
+Components.utils.import('resource://gre/modules/Services.jsm');
+
+function AboutJSProbes() {}
+
+AboutJSProbes.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+ classDescription: 'about:jsprobes',
+ classID: Components.ID('{ced45ce8-a0ee-4dbd-8099-1a27f60e83c6}'),
+ contractID: '@mozilla.org/network/protocol/about;1?what=jsprobes',
+
+ newChannel: function(uri)
+ {
+ var channel = Services.io.newChannel(
+ 'resource://aboutjsprobes/about-jsprobes.html', null, null);
+ var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
+ .getService(Ci.nsIScriptSecurityManager);
+ var principal = securityManager.getSystemPrincipal(uri);
+ channel.originalURI = uri;
+ channel.owner = principal;
+
+ // var c= Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
+ // c.logStringMessage("uri = " + uri.toString());
+
+ return channel;
+ },
+
+ getURIFlags: function(uri)
+ {
+ return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ Ci.nsIAboutModule.ALLOW_SCRIPT;
+ }
+};
+
+const AboutJSProbesFactory =
+ XPCOMUtils.generateNSGetFactory([AboutJSProbes])(
+ AboutJSProbes.prototype.classID);
+
+const APP_STARTUP = 1;
+const ADDON_ENABLE = 3;
+const ADDON_UPGRADE = 7;
+
+function startup(aData, aReason) {
+ Cm.registerFactory(AboutJSProbes.prototype.classID,
+ AboutJSProbes.prototype.classDescription,
+ AboutJSProbes.prototype.contractID,
+ AboutJSProbesFactory);
+ var fileuri = Services.io.newFileURI(aData.installPath);
+ if (!aData.installPath.isDirectory())
+ fileuri = Services.io.newURI('jar:' + fileuri.spec + '!/', null, null);
+ Services.io.getProtocolHandler('resource').QueryInterface(Ci.nsIResProtocolHandler).setSubstitution('aboutjsprobes', fileuri);
+}
+
+function shutdown(aData, aReason) {
+ Services.io.getProtocolHandler('resource').QueryInterface(Ci.nsIResProtocolHandler).setSubstitution('aboutjsprobes', null);
+ Cm.unregisterFactory(AboutJSProbes.prototype.classID, AboutJSProbesFactory);
+}
+function install(aData, aReason) { }
+function uninstall(aData, aReason) { }
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>about-jsprobes@mozilla.org</em:id>
+ <em:version>0.1</em:version>
+ <em:type>2</em:type>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Firefox -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>12.0</em:minVersion>
+ <em:maxVersion>14.0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Fennec -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</em:id>
+ <em:minVersion>12.0</em:minVersion>
+ <em:maxVersion>14.0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>About JSProbes</em:name>
+ <em:description>JS Probes Experimentation Template</em:description>
+ <em:creator>Team Mozilla</em:creator>
+ <em:iconURL/>
+
+ <em:optionsURL/>
+
+ </Description>
+</RDF>
No changes.

0 comments on commit d34ac58

Please sign in to comment.