Permalink
Browse files

Flame Graph proof of concept

  • Loading branch information...
SamSaffron committed Mar 18, 2013
1 parent 8ff6c70 commit 1a4f58182aa9750b2ec98903ba6573d8c37fa990
@@ -1,52 +1,54 @@
PATH
remote: /home/dyfrgi/MiniProfiler
remote: /home/sam/Source/MiniProfiler
specs:
rack-mini-profiler (0.1.23)
rack (>= 1.1.3)

GEM
remote: http://rubygems.org/
specs:
ZenTest (4.8.1)
activemodel (3.2.6)
activesupport (= 3.2.6)
ZenTest (4.9.0)
activemodel (3.2.12)
activesupport (= 3.2.12)
builder (~> 3.0.0)
activerecord (3.2.6)
activemodel (= 3.2.6)
activesupport (= 3.2.6)
activerecord (3.2.12)
activemodel (= 3.2.12)
activesupport (= 3.2.12)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.6)
activesupport (3.2.12)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
builder (3.0.0)
builder (3.0.4)
commonjs (0.2.6)
dalli (2.1.0)
diff-lcs (1.1.3)
i18n (0.6.0)
less (2.2.1)
dalli (2.6.2)
diff-lcs (1.2.1)
i18n (0.6.4)
less (2.3.1)
commonjs (~> 0.2.6)
libv8 (3.3.10.4)
multi_json (1.3.6)
rack (1.4.1)
rack-test (0.6.1)
libv8 (3.11.8.13)
multi_json (1.6.1)
rack (1.5.2)
rack-test (0.6.2)
rack (>= 1.0)
rake (0.9.2.2)
redis (3.0.1)
rspec (2.11.0)
rspec-core (~> 2.11.0)
rspec-expectations (~> 2.11.0)
rspec-mocks (~> 2.11.0)
rspec-core (2.11.0)
rspec-expectations (2.11.1)
diff-lcs (~> 1.1.3)
rspec-mocks (2.11.1)
therubyracer (0.10.1)
libv8 (~> 3.3.10)
tzinfo (0.3.33)
rake (10.0.3)
redis (3.0.3)
ref (1.0.2)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.0)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
tzinfo (0.3.37)

PLATFORMS
ruby
@@ -0,0 +1,223 @@
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
<!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3.8/sugar.min.js"></script> -->
<meta charset=utf-8 />
<title>Flame Graph of Page</title>
<style>
.info {height: 40px;}
.legend div {
display: block;
float: left;
width: 150px;
margin: 0 8px 8px;
padding: 4px;
height: 50px;
}
</style>
</head>
<body>
<div class="graph"></div>
<div class="info"></div>
<div class="legend"></div>

<script>
var data = /*DATA*/;
var maxX = 0;
var maxY = 0;
$.each(data, function(){
maxX = Math.max(maxX, this.x + this.width);
maxY = Math.max(maxY, this.y);
});
var width = $(window).width();
var height = $(window).height() / 1.2;
$('.graph').width(width).height(height);
var xScale = d3.scale.linear()
.domain([0, maxX])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([0, maxY])
.range([0,height]);
var svg = d3.select(".graph").append("svg")
.attr("width", "100%")
.attr("height", "100%");
var color = function() {
var r = parseInt(205 + Math.random() * 50);
var g = parseInt(Math.random() * 230);
var b = parseInt(Math.random() * 55);
return "rgb(" + r + "," + g + "," + b + ")";
}
var info = {};
// http://stackoverflow.com/questions/1960473/unique-values-in-an-array
Array.prototype.getUnique = function() {
var o = {}, a = []
for (var i = 0; i < this.length; i++) o[this[i]] = 1
for (var e in o) a.push(e)
return a
}
var samplePercent = function(samples){
return "(" + samples +
" sample" + (samples == 1 ? "" : "s") + " - " +
((samples / maxX) * 100).toFixed(2) + "%)";
}
var mouseover = function(d) {
var i = info[d.frame];
$('.info').text( d.frame + " " + samplePercent(i.samples.length));
d3.selectAll(i.nodes)
.attr('opacity',0.5);
};
var mouseout = function(d) {
var i = info[d.frame];
$('.info').text("");
d3.selectAll(i.nodes)
.attr('opacity',1);
};
// http://stackoverflow.com/a/7419630
var rainbow = function(numOfSteps, step) {
// This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
// Adam Cole, 2011-Sept-14
// HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
var r, g, b;
var h = step / numOfSteps;
var i = ~~(h * 6);
var f = h * 6 - i;
var q = 1 - f;
switch(i % 6){
case 0: r = 1, g = f, b = 0; break;
case 1: r = q, g = 1, b = 0; break;
case 2: r = 0, g = 1, b = f; break;
case 3: r = 0, g = q, b = 1; break;
case 4: r = f, g = 0, b = 1; break;
case 5: r = 1, g = 0, b = q; break;
}
var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
return (c);
}
// assign some colors, analyze samples per gem
var gemStats = {}
var guessGem = function(frame)
{
var split = frame.split('/gems/');
if(split.length == 1) {
split = frame.split('/app/');
if(split.length == 1) {
split = frame.split('/lib/');
}
split = split[Math.max(split.length-2,0)].split('/');
return split[split.length-1];
}
else
{
return split[split.length -1].split('/')[0];
}
}
$.each(data, function(){
var gem = guessGem(this.frame);
var stat = gemStats[gem];
if(!stat) {
gemStats[gem] = stat = {samples: [], frames: []};
}
stat.frames.push(this.frame);
for(var j=0; j < this.width; j++){
stat.samples.push(this.x + j);
}
});
var totalGems = 0;
$.each(gemStats, function(){totalGems++;});
var currentIndex = 0;
$.each(gemStats, function(k,stat){
stat.color = rainbow(totalGems, currentIndex);
stat.samples = stat.samples.getUnique();
for(var x=0; x < stat.frames.length; x++) {
info[stat.frames[x]] = {nodes: [], samples: [], color: stat.color};
}
currentIndex += 1;
});
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d) { return xScale(d.x-1); })
.attr("y",function(d) { return yScale(maxY - d.y);})
.attr("width", function(d){return xScale(d.width);})
.attr("height", yScale(1))
.attr("fill", function(d){
var i = info[d.frame];
if(!i) {
info[d.frame] = i = {nodes: [], samples: [], color: color()};
}
i.nodes.push(this);
for(var j=0; j < d.width; j++){
i.samples.push(d.x + j);
}
return i.color;
})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
// Samples may overlap on the same line
for (var r in info) {
if (info[r].samples) {
info[r].samples = info[r].samples.getUnique();
}
};
// render the legend
$.each(gemStats, function(k,v){
var node = $("<div></div>")
.css("background-color", v.color)
.text(k + " " + samplePercent(v.samples.length)) ;
$('.legend').append(node);
});
/*
svg.selectAll("text")
.data($.grep(data, function(d){
return d.width > 2;
}))
.enter()
.append("text")
.text(function(d){ return d.frame; })
.attr("x",function(d) { return xScale(d.x - 0.7); })
.attr("y",function(d) { return yScale(maxY - d.y + 0.7);})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
*/
</script>
</body>
</html>

Oops, something went wrong.

0 comments on commit 1a4f581

Please sign in to comment.