stackprof 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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