Commits

Brian Burg  committed c077ca6

checkpoint

  • Participants
  • Parent commits dc7d398

Comments (0)

Files changed (4)

File addon/aboutgc.html

 <!DOCTYPE html>
 <html>
 <head>
-<base href="resource://aboutgc/" />
 <!-- for debugging, change this base document uri to the actual source
 location on your filesystem. This will let you simply refresh the
 about:gc page to reload all parts fresh, instead of having to go
  HTML page itself. If you change the HTML, then you need to reinstall
  the XPI.
 -->
+<base href="file:///Users/burg/repos/mozilla/aboutgc/addon/" />
 <title>About GC</title>
 <script type="text/javascript" src="sample.js" charset="utf-8"></script>
 <script type="text/javascript" src="aboutgc.js"></script>
 </head>
 <h1>GC Timeline</h1>
 <div id="gc-timeline"></div>
-<h1><tt>sfink</tt>'s GC performance</h1>
-<div id="gc-time"></div>
+<h1>GC Pause vs. Reclamation</h1>
+<div id="gc-scatterplot"></div>
 </body>
 </html>

File addon/aboutgc.js

 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");
+}
+
 var mainWindow = wm.getMostRecentWindow("navigator:browser");
 var gBrowser = mainWindow.gBrowser;
 
 
 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 MS_PER_MINUTE = MS_PER_SECOND * 1000;
 const MS_PER_HOUR = MS_PER_MINUTE * 60;
 
+results = [];
+
+function stopProbes() {
+    if (!probes) return;
+
+    while (activeHandlers.length) {
+	probes.removeHandler(activeHandlers.pop());
+    }
+
+    timer.clear();
+    timer = null;
+    probes = null;
+}
+
+function testProbes() {
+    if (!probes) return;
+
+    /* init data stream */
+    probes.asyncQuery("pendingData = [];", function(e){});
+    var cookie;
+
+    cookie = probes.addHandler(probes.GC_DID_START,
+			       "using(env.currentTimeMS);" +
+			       "using(compartment.runtime.gcBytes);",
+			       "current = [env.currentTimeMS, 0, compartment.runtime.gcBytes, 0];");
+    activeHandlers.push(cookie);
+
+    cookie = probes.addHandler(probes.GC_WILL_END,
+			       "using(env.currentTimeMS);" +
+			       "using(compartment.runtime.gcBytes);",
+			       "current[1] = env.currentTimeMS;" +
+			       "current[3] = compartment.runtime.gcBytes;" +
+			       "pendingData.push(current);");
+    activeHandlers.push(cookie);
+
+    var cb = {
+	observe: function(subject, topic, data) {
+	    probes.asyncQuery("while (pendingData.length) { postMessage(pendingData.pop()); }", 
+			      function(e){ results.push(e.value); });
+	},
+    }
+
+    timer.init(cb, 5000, TYPE_REPEATING_SLACK);
+}
+
 function drawGraph() {
     function format_bytes(v, axis) {
 	var decimals = axis.tickDecimals || 3;
     
     function format_elapsed(v, axis) {
 	/* TODO: should be supplied with MS not uS */
-	var rem = v/1000;
+	var rem = v;
 	var str = [];
 	
 	var decimals = ( (axis && axis.tickDecimals) ? axis.tickDecimals : 0 ) || 3;
     function prepareTimelinePlot(dataArr) {
 
 	var heapSizeData = [];
+	var durationSizeData = [];
 	var gcDurationMarks = [];
 
-	for each (rec in dataArr) {
+	var startTime = dataArr[0][0];
+
+	for each (origRec in dataArr) {
+	    rec = origRec;
+	    rec[0] -= startTime;
+	    rec[1] -= startTime;
+
 	    /* generate heap size over time */
 	    heapSizeData.push([rec[0], rec[2]]);
 	    heapSizeData.push([rec[1], rec[3]]);
 
+	    /* generate scatter plot data of gc duration vs. reclaimed memory. */
+	    durationSizeData.push([rec[1]-rec[0], rec[2]-rec[3]]);
+
 	    /* generate gc time markings */
 	    gcDurationMarks.push({ xaxis: { from: rec[0], to: rec[0] }, color: '#dde' });
 	    gcDurationMarks.push({ xaxis: { from: rec[0], to: rec[1] }, color: '#eef' });
 				 },
 			       ],
 			grid: {
-			    markings: gcDurationMarks
+			    markings: gcDurationMarks,
+			    hoverable: true,
 			},
 		      };
 
 			];
 
 	var postPlot = function(plot) {
+	    i = 1;
 	    for each (rec in dataArr) {
 		var pt = plot.pointOffset({ x: (rec[0]+rec[1])/2, y: plot.getAxes().yaxis.max});
 		$('<div>' + format_elapsed(rec[1]-rec[0]) + '</div>')
 		    .css({ 'position': 'absolute',
 			   'top': '0px',
+			   'display': 'none',
 			   '-moz-transform': 'rotate(-90deg) translateY(-1.8em) translateX(-15px)',
 			   'font-family': 'Verdana',
 			   'text-transform': 'capitalize',
-			   'font-size': '10px',
+			   'font-size': '12px',
+			   'font-weight': 'bold',
 			   'left': pt.left + 'px',
 			 })
+		    .attr('id', 'timelineGC_' + i++)
+		    .attr('class', 'timelineGClabel')
 		    .appendTo(plot.getPlaceholder());
 	    }
 	}
 	return [graphdata, options, postPlot];
     }
 
+
+    function prepareScatterPlot(dataArr) {
+
+	var durationSizeData = [];
+
+	for each (rec in dataArr) {
+	    /* generate scatter plot data of gc duration vs. reclaimed memory. */
+	    durationSizeData.push([rec[1]-rec[0], rec[2]-rec[3]]);
+	}
+
+	var options = { yaxes: [ { tickFormatter: format_bytes,
+				   color: 'purple',
+				 },
+			       ],
+			xaxes: [ { tickFormatter: format_elapsed,
+				 },
+			       ],
+			grid: {
+			    clickable: true,
+			},
+			series: {
+			    lines: { show: false },
+			    points: { show: true, radius: 5, fill: true, fillColor: '#666' },
+			},
+		      };
+
+	var graphdata = [ { data: durationSizeData,
+			    clickable: true,
+			  },
+			];
+
+	var postPlot = function(){};
+
+	return [graphdata, options, postPlot];
+    }
+
     function preparePerfPlot(dataArr) {
 
 	var options = { yaxes: [ { 'min': 0,
     }
 
     /* actually plot/draw now */
+    gc1.sort(function(a,b) { return a[0] - b[0]; });
 
     [data, opts, postplot] = prepareTimelinePlot(gc1);
     var timelinePlot = $.plot($("#gc-timeline"), data, opts);
     postplot(timelinePlot);
 
+    $("#gc-timeline").bind("plothover", function (event, pos, item) {
+	if (!item) return;
+
+	var pt = timelinePlot.pointOffset({x: item.datapoint[0], y: item.datapoint[1]});
+
+	var showLabel = $("#timelineGC_" + Math.floor(item.dataIndex/2+1));
+	$(".timelineGClabel").not(showLabel).fadeOut(500);
+	showLabel.stop()
+	    .css({'top': (pt.top-60)+'px', 'left': pt.left+'px'})
+	    .show();
+    });
+
+
+    [data, opts, postplot] = prepareScatterPlot(gc1);
+    var scatterplot = $.plot($("#gc-scatterplot"), data, opts);
+    postplot(scatterplot);
+
+    var active = null;
+    $("#gc-scatterplot").bind("plotclick", function (event, pos, item) {
+	console.log(active);
+	if (active) {
+	    if (!item || active != item.dataIndex) {
+                timelinePlot.unhighlight(0, active*2);
+		scatterplot.unhighlight(0, active);
+	    }
+	}
+	if (item) {
+	    if (active != item.dataIndex) {
+		timelinePlot.highlight(0, item.dataIndex*2);
+		scatterplot.highlight(0, item.dataIndex);
+	    }
+	    active = item.dataIndex;
+	}
+    });
+
+
+/*
     [data, opts] = preparePerfPlot(gcs);
     var perfPlot = $.plot($("#gc-time"), data, opts);
 
         }
     });
 
+*/
+
+    testProbes();
+
 }

File addon/sample.js

 /* legend:
  [ gcStart, gcEnd, heapSizeStart, heapSizeEnd ]+ */
 gc1 = [
+[1316654539954, 1316654539976, 109826048, 109826048],
+[1316654538868, 1316654538890, 109826048, 109826048],
+[1316654542327, 1316654542349, 109834240, 109641728],
+[1316654541228, 1316654541251, 109834240, 109834240],
+[1316654547079, 1316654547101, 89387008, 89366528],
+[1316654555718, 1316654555743, 108904448, 108896256],
+[1316654558004, 1316654558024, 108232704, 108232704],
+[1316654567812, 1316654567879, 96149504, 41865216],
+[1316654566634, 1316654566698, 91062272, 40263680],
+[1316654565493, 1316654565560, 98975744, 44662784],
+[1316654573910, 1316654573934, 108990464, 108990464],
+[1316654572721, 1316654572742, 108990464, 108978176],
+[1316654571530, 1316654571556, 108826624, 108822528],
+[1316654570235, 1316654570303, 96174080, 41897984],
+[1316654569042, 1316654569111, 96157696, 41852928],
+[1316654576330, 1316654576353, 108998656, 108994560],
+[1316654575089, 1316654575110, 108990464, 108986368]
+];
+
+/*
 [2977962, 3113938, 9379840, 8441856],
 [4820817, 5019834, 26009600, 23408640],
 [6476051, 6692174, 5898240, 5308416],
 [47101215, 47504071, 12410880, 11169792],
 [50103114, 50494506, 14213120, 12791808],
 [50727220, 51111553, 12410880, 11169792]
-
-];
+*/
 
 gcs = [
 [ 2977962, 135976, 937984 ],

File addon/stylesheet.css

 /* percentage heights from within addon always seem to be zero */
-#gc-time {
-    width: 100%;
-    height: 700px;
+#gc-timeline {
+    width: 90%;
+    height: 300px;
 }
 
-#gc-timeline {
-    width: 100%;
-    height: 700px;
+#gc-scatterplot {
+    width: 45%;
+    height: 300px;
 }
 
 #tooltip {