stackprof 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70371feb6d35b951507adaecd3d86f23cca8b7a7
4
- data.tar.gz: d61e93ca25ce3378b6a52429bd5f7f3f0173a180
3
+ metadata.gz: e783faab7c8c97296d52c602c971eddb3e4c85eb
4
+ data.tar.gz: 4952c5aec78540e9a2509e37f6e2195f56568712
5
5
  SHA512:
6
- metadata.gz: 34c3b3a5814257b73d923dc73a1965aa637436dac1f6a7785e8d17b59ea02e2b83104ec9f0341a11dc96ce03b1bdb87ebb9b0f6d9a4dd9c58fceaebb103084f6
7
- data.tar.gz: 016dba5b810895fd1f14aa29eaa294442e05095c4fa1d4bb79d1b5c54edd0936c30140a2e1ed719b02ccde7abc657b464874fa673f67ea48c3188550d466fea8
6
+ metadata.gz: 3a0f36b8202aefb65836dbe71b359fbcea3f7ecf4252fce52ec127f47db0ea9995f177f808c631d6803f5b40e1697d2e065028a15ceb17387351b2d3fa7af0e7
7
+ data.tar.gz: 685c58d929c5681693c64a1a63e939382171420ecc83856cabc9efeb2d0f7e65c25802d6f0c5c47e0b6def8addda2f0b78985048c7829f6b23e7ccb636be79ff
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stackprof (0.2.4)
4
+ stackprof (0.2.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -20,7 +20,11 @@ parser = OptionParser.new(ARGV) do |o|
20
20
  o.on('--callgrind', 'Callgrind output (use with kcachegrind, stackprof-gprof2dot.py)'){ options[:format] = :callgrind }
21
21
  o.on('--graphviz', "Graphviz output (use with dot)"){ options[:format] = :graphviz }
22
22
  o.on('--stackcollapse', 'stackcollapse.pl compatible output (use with stackprof-flamegraph.pl)'){ options[:format] = :stackcollapse }
23
- o.on('--flamegraph', "timeline-flamegraph output (js)\n\n"){ options[:format] = :flamegraph }
23
+ o.on('--flamegraph', "timeline-flamegraph output (js)"){ options[:format] = :flamegraph }
24
+ o.on('--flamegraph-viewer [f.js]', String, "open html viewer for flamegraph output\n\n"){ |file|
25
+ puts("open file://#{File.expand_path('../../lib/stackprof/flamegraph/viewer.html', __FILE__)}?data=#{File.expand_path(file)}")
26
+ exit
27
+ }
24
28
  o.on('--dump', 'Print marshaled profile dump (combine multiple profiles)'){ options[:format] = :dump }
25
29
  o.on('--debug', 'Pretty print raw profile data'){ options[:format] = :debug }
26
30
  end
@@ -346,7 +346,7 @@ stackprof_record_sample()
346
346
  VALUE prev_frame = Qnil;
347
347
 
348
348
  _stackprof.overall_samples++;
349
- num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer), _stackprof.frames_buffer, _stackprof.lines_buffer);
349
+ num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer) / sizeof(VALUE), _stackprof.frames_buffer, _stackprof.lines_buffer);
350
350
 
351
351
  if (_stackprof.raw) {
352
352
  int found = 0;
@@ -0,0 +1,357 @@
1
+ var guessGem = function(frame) {
2
+ var split = frame.split('/gems/');
3
+ if(split.length == 1) {
4
+ split = frame.split('/app/');
5
+ if(split.length == 1) {
6
+ split = frame.split('/lib/');
7
+ } else {
8
+ return split[split.length-1].split('/')[0]
9
+ }
10
+
11
+ split = split[Math.max(split.length-2,0)].split('/');
12
+ return split[split.length-1].split(':')[0];
13
+ }
14
+ else
15
+ {
16
+ return split[split.length -1].split('/')[0].split('-', 2)[0];
17
+ }
18
+ }
19
+
20
+ var color = function() {
21
+ var r = parseInt(205 + Math.random() * 50);
22
+ var g = parseInt(Math.random() * 230);
23
+ var b = parseInt(Math.random() * 55);
24
+ return "rgb(" + r + "," + g + "," + b + ")";
25
+ }
26
+
27
+ // http://stackoverflow.com/a/7419630
28
+ var rainbow = function(numOfSteps, step) {
29
+ // 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.
30
+ // Adam Cole, 2011-Sept-14
31
+ // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
32
+ var r, g, b;
33
+ var h = step / numOfSteps;
34
+ var i = ~~(h * 6);
35
+ var f = h * 6 - i;
36
+ var q = 1 - f;
37
+ switch(i % 6){
38
+ case 0: r = 1, g = f, b = 0; break;
39
+ case 1: r = q, g = 1, b = 0; break;
40
+ case 2: r = 0, g = 1, b = f; break;
41
+ case 3: r = 0, g = q, b = 1; break;
42
+ case 4: r = f, g = 0, b = 1; break;
43
+ case 5: r = 1, g = 0, b = q; break;
44
+ }
45
+ var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
46
+ return (c);
47
+ }
48
+
49
+ // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
50
+ var getUnique = function(orig) {
51
+ var o = {}, a = []
52
+ for (var i = 0; i < orig.length; i++) o[orig[i]] = 1
53
+ for (var e in o) a.push(e)
54
+ return a
55
+ }
56
+
57
+ function flamegraph(data) {
58
+ var maxX = 0;
59
+ var maxY = 0;
60
+ var minY = 10000;
61
+ $.each(data, function(){
62
+ maxX = Math.max(maxX, this.x + this.width);
63
+ maxY = Math.max(maxY, this.y);
64
+ minY = Math.min(minY, this.y);
65
+ });
66
+
67
+ // normalize Y
68
+ if (minY > 0) {
69
+ $.each(data, function(){
70
+ this.y -= minY
71
+ })
72
+ maxY -= minY
73
+ minY = 0
74
+ }
75
+
76
+ var margin = {top: 10, right: 10, bottom: 10, left: 10}
77
+ var width = $(window).width() - 200 - margin.left - margin.right;
78
+ var height = $(window).height() * 0.70 - margin.top - margin.bottom;
79
+ var height2 = $(window).height() * 0.30 - 60 - margin.top - margin.bottom;
80
+
81
+ $('.flamegraph').width(width + margin.left + margin.right).height(height + margin.top + margin.bottom);
82
+ $('.zoom').width(width + margin.left + margin.right).height(height2 + margin.top + margin.bottom);
83
+
84
+ var xScale = d3.scale.linear()
85
+ .domain([0, maxX])
86
+ .range([0, width]);
87
+
88
+ var xScale2 = d3.scale.linear()
89
+ .domain([0, maxX])
90
+ .range([0, width])
91
+
92
+ var yScale = d3.scale.linear()
93
+ .domain([0, maxY])
94
+ .range([0,height]);
95
+
96
+ var yScale2 = d3.scale.linear()
97
+ .domain([0, maxY])
98
+ .range([0,height2]);
99
+
100
+ var zoomXRatio = 1
101
+ var zoomed = function() {
102
+ svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + (zoomXRatio*d3.event.scale) + "," + d3.event.scale + ")");
103
+
104
+ var x = xScale.domain(), y = yScale.domain()
105
+ brush.extent([ [x[0]/zoomXRatio, y[0]], [x[1]/zoomXRatio, y[1]] ])
106
+ if (x[1] == maxX && y[1] == maxY)
107
+ brush.clear()
108
+ svg2.select('g.brush').call(brush)
109
+ }
110
+
111
+ var zoom = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 14]).on('zoom', zoomed)
112
+
113
+ var svg2 = d3.select('.zoom').append('svg').attr('width', '100%').attr('height', '100%').append('svg:g')
114
+ .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
115
+ .append('g').attr('class', 'graph')
116
+
117
+ var svg = d3.select(".flamegraph")
118
+ .append("svg")
119
+ .attr("width", "100%")
120
+ .attr("height", "100%")
121
+ .attr("pointer-events", "all")
122
+ .append('svg:g')
123
+ .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
124
+ .call(zoom)
125
+ .append('svg:g').attr('class', 'graph');
126
+
127
+ // so zoom works everywhere
128
+ svg.append("rect")
129
+ .attr("x",function(d) { return xScale(0); })
130
+ .attr("y",function(d) { return yScale(0);})
131
+ .attr("width", function(d){return xScale(maxX);})
132
+ .attr("height", yScale(maxY))
133
+ .attr("fill", "white");
134
+
135
+ var samplePercentRaw = function(samples, exclusive) {
136
+ var ret = [samples, ((samples / maxX) * 100).toFixed(2)]
137
+ if (exclusive)
138
+ ret = ret.concat([exclusive, ((exclusive / maxX) * 100).toFixed(2)])
139
+ return ret;
140
+ }
141
+
142
+ var samplePercent = function(samples, exclusive) {
143
+ var info = samplePercentRaw(samples, exclusive)
144
+ var samplesPct = info[1], exclusivePct = info[3]
145
+ var ret = " (" + samples + " sample" + (samples == 1 ? "" : "s") + " - " + samplesPct + "%) ";
146
+ if (exclusive)
147
+ ret += " (" + exclusive + " exclusive - " + exclusivePct + "%) ";
148
+ return ret;
149
+ }
150
+
151
+ var info = {};
152
+
153
+ var mouseover = function(d) {
154
+ var i = info[d.frame_id];
155
+ var shortFile = d.file.replace(/^.+\/(gems|app|lib|config|jobs)/, '$1')
156
+ var data = samplePercentRaw(i.samples.length, d.topFrame ? d.topFrame.exclusiveCount : 0)
157
+
158
+ $('.info')
159
+ .css('background-color', i.color)
160
+ .find('.frame').text(d.frame).end()
161
+ .find('.file').text(shortFile).end()
162
+ .find('.samples').text(data[0] + ' samples ('+data[1]+'%)').end()
163
+ .find('.exclusive').text('')
164
+
165
+ if (data[3])
166
+ $('.info .exclusive').text(data[2] + ' exclusive ('+data[3]+'%)')
167
+
168
+ d3.selectAll(i.nodes)
169
+ .attr('opacity',0.5);
170
+ };
171
+
172
+ var mouseout = function(d) {
173
+ var i = info[d.frame_id];
174
+ $('.info').css('background-color', 'none').find('.frame, .file, .samples, .exclusive').text('')
175
+
176
+ d3.selectAll(i.nodes)
177
+ .attr('opacity',1);
178
+ };
179
+
180
+ // assign some colors, analyze samples per gem
181
+ var gemStats = {}
182
+ var topFrames = {}
183
+ var lastFrame = {frame: 'd52e04d-df28-41ed-a215-b6ec840a8ea5', x: -1}
184
+
185
+ $.each(data, function(){
186
+ var gem = guessGem(this.file);
187
+ var stat = gemStats[gem];
188
+ this.gemName = gem
189
+
190
+ if(!stat) {
191
+ gemStats[gem] = stat = {name: gem, samples: [], frames: [], nodes:[]};
192
+ }
193
+
194
+ stat.frames.push(this.frame_id);
195
+ for(var j=0; j < this.width; j++){
196
+ stat.samples.push(this.x + j);
197
+ }
198
+ // This assumes the traversal is in order
199
+ if (lastFrame.x != this.x) {
200
+ var topFrame = topFrames[lastFrame.frame_id]
201
+ if (!topFrame) {
202
+ topFrames[lastFrame.frame_id] = topFrame = {exclusiveCount: 0}
203
+ }
204
+ topFrame.exclusiveCount += 1;
205
+ lastFrame.topFrame = topFrame;
206
+ }
207
+ lastFrame = this;
208
+
209
+ });
210
+
211
+ var topFrame = topFrames[lastFrame.frame_id]
212
+ if (!topFrame) {
213
+ topFrames[lastFrame.frame_id] = topFrame = {exclusiveCount: 0}
214
+ }
215
+ topFrame.exclusiveCount += 1;
216
+ lastFrame.topFrame = topFrame;
217
+
218
+ var totalGems = 0;
219
+ $.each(gemStats, function(k,stat){
220
+ totalGems++;
221
+ stat.samples = getUnique(stat.samples);
222
+ });
223
+
224
+ var gemsSorted = $.map(gemStats, function(v, k){ return v })
225
+ gemsSorted.sort(function(a, b){ return b.samples.length - a.samples.length })
226
+
227
+ var currentIndex = 0;
228
+ $.each(gemsSorted, function(k,stat){
229
+ stat.color = rainbow(totalGems, currentIndex);
230
+ currentIndex += 1;
231
+
232
+ for(var x=0; x < stat.frames.length; x++) {
233
+ info[stat.frames[x]] = {nodes: [], samples: [], color: stat.color};
234
+ }
235
+ });
236
+
237
+ function drawData(svg, data, xScale, yScale, mini) {
238
+ svg.selectAll("g.flames")
239
+ .data(data)
240
+ .enter()
241
+ .append("g")
242
+ .attr('class', 'flames')
243
+ .each(function(d){
244
+ gemStats[d.gemName].nodes.push(this)
245
+
246
+ var r = d3.select(this)
247
+ .append("rect")
248
+ .attr("x",function(d) { return xScale(d.x); })
249
+ .attr("y",function(d) { return yScale(maxY - d.y);})
250
+ .attr("width", function(d){return xScale(d.width);})
251
+ .attr("height", yScale(1))
252
+ .attr("fill", function(d){
253
+ var i = info[d.frame_id];
254
+ if(!i) {
255
+ info[d.frame_id] = i = {nodes: [], samples: [], color: color()};
256
+ }
257
+ i.nodes.push(this);
258
+ if (!mini)
259
+ for(var j=0; j < d.width; j++){
260
+ i.samples.push(d.x + j);
261
+ }
262
+ return i.color;
263
+ })
264
+
265
+ if (!mini)
266
+ r
267
+ .on("mouseover", mouseover)
268
+ .on("mouseout", mouseout);
269
+
270
+ if (!mini)
271
+ d3.select(this)
272
+ .append('foreignObject')
273
+ .classed('label-body', true)
274
+ .attr("x",function(d) { return xScale(d.x); })
275
+ .attr("y",function(d) { return yScale(maxY - d.y);})
276
+ .attr("width", function(d){return xScale(d.width);})
277
+ .attr("height", yScale(1))
278
+ .attr("line-height", yScale(1))
279
+ .attr("font-size", yScale(0.42) + 'px')
280
+ .attr('pointer-events', 'none')
281
+ .append('xhtml:span')
282
+ .style("height", yScale(1))
283
+ .classed('label', true)
284
+ .text(function(d){ return d.frame })
285
+ });
286
+ }
287
+
288
+ drawData(svg, data, xScale, yScale, 0)
289
+ drawData(svg2, data, xScale2, yScale2, 1)
290
+
291
+ var brushed = function(){
292
+ if (brush.empty()) {
293
+ svg.attr('transform', '')
294
+ zoomXRatio = 1
295
+ zoom.scale(1).translate([0,0])
296
+ svg.selectAll('.label-body')
297
+ .attr('transform', 'scale(1,1)')
298
+ .attr("x",function(d) { return xScale(d.x)*zoomXRatio; })
299
+ .attr("width", function(d){return xScale(d.width)*zoomXRatio;})
300
+ } else {
301
+ var e = brush.extent()
302
+ var x = [e[0][0],e[1][0]], y = [e[0][1],e[1][1]]
303
+
304
+ xScale.domain([0, maxX])
305
+ yScale.domain([0, maxY])
306
+
307
+ var w = width, h = height2
308
+ var dx = xScale2(1.0*x[1]-x[0]), dy = yScale2(1.0*y[1]-y[0])
309
+ var sx = w/dx, sy = h/dy
310
+ var trlx = -xScale(x[0])*sx, trly = -yScale(y[0])*sy
311
+ var transform = "translate(" + trlx + ',' + trly + ")" + " scale(" + sx + ',' + sy + ")"
312
+
313
+ zoomXRatio = sx/sy
314
+
315
+ svg.selectAll('.label-body')
316
+ .attr("x",function(d) { return xScale(d.x)*zoomXRatio; })
317
+ .attr("width", function(d){return xScale(d.width)*zoomXRatio;})
318
+ .attr('transform', function(d){
319
+ var x = xScale(d.x)
320
+ return "scale("+(1.0/zoomXRatio)+",1)"
321
+ })
322
+
323
+ svg.attr("transform", transform)
324
+ zoom.translate([trlx, trly]).scale(sy)
325
+ }
326
+ }
327
+
328
+ var brush = d3.svg.brush()
329
+ .x(xScale2)
330
+ .y(yScale2)
331
+ .on("brush", brushed);
332
+
333
+ svg2.append("g")
334
+ .attr("class", "brush")
335
+ .call(brush)
336
+
337
+ // Samples may overlap on the same line
338
+ for (var r in info) {
339
+ if (info[r].samples) {
340
+ info[r].samples = getUnique(info[r].samples);
341
+ }
342
+ };
343
+
344
+ // render the legend
345
+ $.each(gemsSorted, function(k,gem){
346
+ var data = samplePercentRaw(gem.samples.length)
347
+ var node = $("<div class='"+gem.name+"'></div>")
348
+ .css("background-color", gem.color)
349
+ .html("<span style='float: right'>" + data[0] + 'x<br>' + data[1] + '%' + '</span>' + '<div class="name">'+gem.name+'<br>&nbsp;</div>');
350
+
351
+ node.on('mouseenter mouseleave', function(e){
352
+ d3.selectAll(gemStats[gem.name].nodes).classed('highlighted', e.type == 'mouseenter')
353
+ })
354
+
355
+ $('.legend').append(node);
356
+ });
357
+ }
@@ -0,0 +1,85 @@
1
+ <html>
2
+ <head>
3
+ <title>flamegraph</title>
4
+ <style>
5
+ body {
6
+ margin: 0;
7
+ padding: 0;
8
+ font-family: Monaco, "Liberation Mono", Courier, monospace;
9
+ font-size: 10pt;
10
+ }
11
+ .info {
12
+ display: block;
13
+ height: 40px;
14
+ margin: 3px 6px;
15
+ margin-right: 206px;
16
+ padding: 3px 6px;
17
+ line-height: 18px;
18
+ }
19
+ .legend {
20
+ display: block;
21
+ float: right;
22
+ width: 195px;
23
+ max-height: 100%;
24
+ overflow-y: scroll;
25
+ }
26
+ .legend > div {
27
+ padding: 6px;
28
+ clear: right;
29
+ }
30
+ .legend > div span {
31
+ opacity: 0.75;
32
+ display: block;
33
+ text-align: right;
34
+ }
35
+ .legend > div .name {
36
+ max-width: 70%;
37
+ word-wrap: break-word;
38
+ }
39
+ .legend:hover + .flamegraph .flames:not(.highlighted) {
40
+ opacity: 0.25;
41
+ }
42
+ .legend:hover ~ .zoom .flames:not(.highlighted) {
43
+ opacity: 0.25;
44
+ }
45
+ .brush .extent {
46
+ stroke: #999;
47
+ fill-opacity: .125;
48
+ shape-rendering: crispEdges;
49
+ }
50
+ .label {
51
+ white-space: nowrap;
52
+ display: inline-flex;
53
+ align-items: center;
54
+ vertical-align: middle;
55
+ padding-left: 1px;
56
+ }
57
+ </style>
58
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
59
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
60
+ <script src="flamegraph.js"></script>
61
+ </head>
62
+ <body>
63
+ <div class="legend"></div>
64
+ <div class="flamegraph"></div>
65
+ <div class="info">
66
+ <div style="float: right; text-align: right">
67
+ <div class="samples"></div>
68
+ <div class="exclusive"></div>
69
+ </div>
70
+ <div class="frame"></div>
71
+ <div class="file"></div>
72
+ </div>
73
+ <div class="zoom"></div>
74
+ <script type="text/javascript">
75
+ var queryDict = {}
76
+ location.search.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = decodeURIComponent(item.split("=")[1])})
77
+
78
+ if (queryDict.data) {
79
+ s = document.createElement('script')
80
+ s.setAttribute('src', queryDict.data)
81
+ document.body.appendChild(s)
82
+ }
83
+ </script>
84
+ </body>
85
+ </html>
@@ -83,12 +83,14 @@ module StackProf
83
83
  raise "profile does not include raw samples" unless raw = data[:raw]
84
84
 
85
85
  stacks = []
86
+ max_x = 0
86
87
  max_y = 0
87
88
  while len = raw.shift
88
89
  max_y = len if len > max_y
89
- stacks << raw.slice!(0, len+1)
90
+ stack = raw.slice!(0, len+1)
91
+ stacks << stack
92
+ max_x += stack.last
90
93
  end
91
- max_x = stacks.inject(0){ |sum, (*stack, weight)| sum + weight }
92
94
 
93
95
  f.puts 'flamegraph(['
94
96
  max_y.times do |y|
@@ -96,8 +98,9 @@ module StackProf
96
98
  row_width = 0
97
99
  x = 0
98
100
 
99
- stacks.each do |*stack, weight|
100
- cell = stack[y]
101
+ stacks.each do |stack|
102
+ weight = stack.last
103
+ cell = stack[y] unless y == stack.length-1
101
104
 
102
105
  if cell.nil?
103
106
  if row_prev
@@ -219,7 +222,7 @@ module StackProf
219
222
  f.puts "#{line} #{weight.is_a?(Array) ? weight[1] : weight}"
220
223
  end if frame[:lines]
221
224
  frame[:edges].each do |edge, weight|
222
- oframe = list[edge.to_s]
225
+ oframe = list[edge]
223
226
  f.puts "cfl=#{oframe[:file]}" unless oframe[:file] == frame[:file]
224
227
  f.puts "cfn=#{oframe[:name]}"
225
228
  f.puts "calls=#{weight} #{frame[:line] || 0}\n#{oframe[:line] || 0} #{weight}"
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'stackprof'
3
- s.version = '0.2.5'
3
+ s.version = '0.2.6'
4
4
  s.homepage = 'http://github.com/tmm1/stackprof'
5
5
 
6
6
  s.authors = 'Aman Gupta'
@@ -61,6 +61,16 @@ class StackProfTest < Test::Unit::TestCase
61
61
  assert_equal "block in StackProfTest#math", frame[:name]
62
62
  end
63
63
 
64
+ def test_cputime_bmethod
65
+ profile = StackProf.run(mode: :cpu, interval: 500) do
66
+ bmath
67
+ end
68
+
69
+ assert_operator profile[:samples], :>, 1
70
+ frame = profile[:frames].values.first
71
+ assert_equal "block in StackProfTest#math", frame[:name]
72
+ end
73
+
64
74
  def test_walltime
65
75
  profile = StackProf.run(mode: :wall) do
66
76
  idle
@@ -148,4 +158,10 @@ class StackProfTest < Test::Unit::TestCase
148
158
  r.close
149
159
  w.close
150
160
  end
161
+
162
+ define_method(:bmath) do
163
+ 250_000.times do
164
+ 2 ** 10
165
+ end
166
+ end
151
167
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-25 00:00:00.000000000 Z
11
+ date: 2014-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -59,6 +59,8 @@ files:
59
59
  - bin/stackprof-gprof2dot.py
60
60
  - ext/extconf.rb
61
61
  - ext/stackprof.c
62
+ - lib/stackprof/flamegraph/flamegraph.js
63
+ - lib/stackprof/flamegraph/viewer.html
62
64
  - lib/stackprof/middleware.rb
63
65
  - lib/stackprof/report.rb
64
66
  - sample.rb