flamegraph 0.0.5 → 0.0.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: 00a8e007306576db9b6d761785ef83a17dafaf42
4
- data.tar.gz: 82efe147b7295c832b85ee2ae74ae8e351f1a701
3
+ metadata.gz: f613760265ad137dae994ad575e5d90397506e1f
4
+ data.tar.gz: b0b4968018697e4308746c72a7f0b9ac88fce97a
5
5
  SHA512:
6
- metadata.gz: 76a760121a2ebec486d6d1f76b4d5414f8d6bf2d1ff7eb82276fdd375c9324ac546d414f353b6d89f0aebfbe5307b6bb49cba4ba38216aa5dddc8065bd2140c0
7
- data.tar.gz: 20e0dee3f38a4860a3cedb1ff0cb836e40519079c85321e5e693c0810467f9b72990e074dd5ef46d05d478b7acb1e43aaf5f5ff94db0d50a336c740b8094c1f7
6
+ metadata.gz: 046ab66a7a510a2f9bd69da94e0b6c610d00e53a73ee3d98e38298075507fc73511fe92d4736c50253e957c8e453da5f9ea174d2577a21bb324a2466db030d03
7
+ data.tar.gz: b8346b54a13bf842a3a584a1f6149a0467b904b1a53d2c72720031c63c42b40268d1b38c4e2c9746c47276fcd5045c98e013c93eae3a27d977f60a7a19155bc4
data/CHANGELOG CHANGED
@@ -17,3 +17,9 @@
17
17
 
18
18
  - 0.0.5
19
19
  - Correct embed
20
+
21
+ 12-Nov-2014
22
+
23
+ - 0.0.6
24
+ - Better analysis with stackprof on ruby 2.1
25
+ - New window displaying the backtrace when clicking on element
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in flamegraph.gemspec
4
4
  gemspec
5
+
6
+ # both are optional, depending on platform
7
+ gem 'fast_stack'
8
+ gem 'stackprof', platform: :mri_21
data/Guardfile CHANGED
@@ -1,6 +1,6 @@
1
1
  guard :minitest do
2
2
  # with Minitest::Unit
3
- watch(%r{^test/(.*)\/?test_(.*)\.rb$})
3
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$}) { |s| s }
4
4
  watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/test_#{m[2]}.rb" }
5
5
  watch(%r{^test/test_helper\.rb$}) { 'test' }
6
6
 
@@ -1,5 +1,21 @@
1
1
  require "json"
2
- require "fast_stack"
2
+
3
+ if RUBY_VERSION >= "2.1.0".freeze
4
+ begin
5
+ require "stackprof"
6
+ require "flamegraph/stackprof_sampler"
7
+ rescue
8
+ STDERR.puts "Please require the stackprof gem falling back to fast_stack"
9
+ require "fast_stack"
10
+ end
11
+ else
12
+ begin
13
+ require "fast_stack"
14
+ rescue
15
+ STDERR.puts "Please require the fast_stack gem, note flamegraph is only supported on Ruby 2.0 and above"
16
+ end
17
+ end
18
+
3
19
  require "flamegraph/version"
4
20
  require "flamegraph/renderer"
5
21
 
@@ -7,9 +23,16 @@ module Flamegraph
7
23
  def self.generate(filename=nil, opts = {})
8
24
  fidelity = opts[:fidelity] || 0.5
9
25
 
10
- backtraces = FastStack.profile(fidelity) do
11
- yield
12
- end
26
+ backtraces =
27
+ if defined? StackProf
28
+ StackProfSampler.collect(fidelity) do
29
+ yield
30
+ end
31
+ else
32
+ FastStack.profile(fidelity) do # , opts[:mode] || :ruby) do
33
+ yield
34
+ end
35
+ end
13
36
 
14
37
  embed_resources = (filename && !opts.key?(:embed_resources)) || opts[:embed_resources]
15
38
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  <title>Flame Graph of Page</title>
9
9
  <style>
10
- .info {height: 40px;}
10
+ .info {min-height: 50px; margin: 10px; }
11
11
  .legend div {
12
12
  display: block;
13
13
  float: left;
@@ -16,13 +16,62 @@
16
16
  padding: 4px;
17
17
  height: 50px;
18
18
  }
19
+ .code {
20
+ font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace;
21
+ }
22
+ #frameinfo table .sample-info {
23
+ width: 200px;
24
+ color: #333;
25
+ font-size: 12px;
26
+ }
27
+ #frameinfo table {
28
+ border-collapse: collapse;
29
+ }
30
+ #frameinfo table tr {
31
+ border-bottom: 1px solid #eee;
32
+ margin-top: 5px;
33
+ }
34
+ #frameinfo td {
35
+ padding: 5px;
36
+ }
37
+ #frameinfo-wrapper {
38
+ position: fixed;
39
+ z-index: 1;
40
+ opacity: 0.7;
41
+ background-color: black;
42
+ width: 100%;
43
+ height: 100%;
44
+ top: 0;
45
+ left: 0;
46
+ display: none;
47
+ }
48
+ #frameinfo {
49
+ position: fixed;
50
+ padding: 10px;
51
+ z-index: 2;
52
+ opacity: 1.0;
53
+ top: 0;
54
+ margin-top: 50px;
55
+ margin-left: 40px;
56
+ width: 80%;
57
+ height: 80%;
58
+ background-color: white;
59
+ border: 2px solid #666;
60
+ display: none;
61
+ overflow: auto;
62
+ }
19
63
  </style>
20
64
  </head>
21
65
  <body>
22
66
  <div class="graph"></div>
23
- <div class="info"></div>
67
+ <div class="info code"></div>
24
68
  <div class="legend"></div>
25
-
69
+ <div id="frameinfo-wrapper"></div>
70
+ <div id="frameinfo">
71
+ <h3>Frame Info</h3>
72
+ <table>
73
+ </table>
74
+ </div>
26
75
  <script>
27
76
 
28
77
  var data = /**DATA**/;
@@ -84,7 +133,12 @@ var guessGem = function(frame)
84
133
  var guessMethod = function(frame) {
85
134
  var split = frame.split('`');
86
135
  if(split.length == 2) {
87
- return split[1].split("'")[0];
136
+ var fullMethod = split[1].split("'")[0];
137
+ split = fullMethod.split("#");
138
+ if(split.length == 2) {
139
+ return split[1];
140
+ }
141
+ return split[0];
88
142
  }
89
143
  return '?';
90
144
  }
@@ -101,7 +155,7 @@ var guessFile = function(frame) {
101
155
  $.each(data, function(){
102
156
  maxX = Math.max(maxX, this.x + this.width);
103
157
  maxY = Math.max(maxY, this.y);
104
- this.shortName = /* guessGem(this.frame) + " " + guessFile(this.frame) + " " */ guessMethod(this.frame);
158
+ this.shortName = guessMethod(this.frame);
105
159
  });
106
160
 
107
161
  var width = $(window).width();
@@ -194,6 +248,87 @@ var mouseout = function(d) {
194
248
  .attr('opacity',1);
195
249
  };
196
250
 
251
+ var backtrace = function(frame){
252
+ for(var i=0; i<data.length; i++){
253
+ if(frame === data[i]){ break; }
254
+ }
255
+
256
+ frames = [frame];
257
+ var depth = frame.y;
258
+
259
+ while(i > 0){
260
+ if(depth == -1) break;
261
+
262
+ if(data[i].y === depth-1) {
263
+ frames.push(data[i]);
264
+ depth--;
265
+ }
266
+
267
+ i--;
268
+ }
269
+
270
+ return frames;
271
+ }
272
+
273
+ $('#frameinfo-wrapper').click(function(d){
274
+ $(this).hide();
275
+ $('#frameinfo').hide();
276
+ });
277
+
278
+ var click = function(d){
279
+ var trace = backtrace(d);
280
+
281
+ var link = function(path, dest){
282
+ return path.replace(/[^\/]+:\d+/, function(x){ return "<a target='_blank' href='"+ dest +"'>" + x + "</a>"})
283
+ };
284
+
285
+ var linkify = function(path){
286
+ var split = path.split("/")[0].split("-");
287
+ if(["activerecord","actionpack","railties","activesupport", "rails"].indexOf(split[0]) > -1) {
288
+ var github = "https://github.com/rails/rails/blob/";
289
+ var file = path.split(":")[0].split("/");
290
+ if(split[0] === "rails") {
291
+ file.shift();
292
+ } else {
293
+ file[0] = split[0];
294
+ }
295
+
296
+ github += (split[1].length < 6 ? "v" : "") + split[1] + "/";
297
+ github += file.join("/");
298
+ github += "#L" + parseInt(path.split(":")[1]);
299
+
300
+ return link(path, github);
301
+ }
302
+ return path;
303
+ }
304
+
305
+ var simplify = function(frame){
306
+ var split = frame.split('/gems/');
307
+ if(split.length > 1){
308
+ var path = linkify(split.pop());
309
+ return "<span class='full-location'>" + split.join('/gems/') + "/</span>" + path;
310
+ } else {
311
+ return frame;
312
+ }
313
+ }
314
+
315
+ var table = trace.map(function(f){
316
+ var i = info[f.frame];
317
+ return "<tr><td class='code'>" + simplify(f.frame) + "</td><td class='sample-info'>" +
318
+ samplePercent(i.samples.length, f.topFrame ? f.topFrame.exclusiveCount : 0) +
319
+ "</td></tr>";
320
+ }).join("\n");
321
+
322
+ var table = $('#frameinfo table').html(table);
323
+ table.find(".full-location").hide().after("<span class='expand'>&hellip; </span>");
324
+ table.find(".expand").css({cursor: "pointer"}).click(function(){
325
+ $(this).hide().parent().find(".full-location").show();
326
+ });
327
+
328
+ $('#frameinfo-wrapper').show();
329
+ $('#frameinfo').show();
330
+ };
331
+
197
332
  // http://stackoverflow.com/a/7419630
198
333
  var rainbow = function(numOfSteps, step) {
199
334
  // 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.
@@ -291,7 +426,7 @@ function fontSize(d,i) {
291
426
  var height = yScale(1);
292
427
  var length = 0;
293
428
  d3.select(this).style("font-size", size + "px").text(word);
294
- while(((this.getBBox().width >= width) || (this.getBBox().height >= height)) && (size > 12))
429
+ while((size > 12.1) && ((this.getBBox().width >= width) || (this.getBBox().height >= height)))
295
430
  {
296
431
  size -= 0.1;
297
432
  d3.select(this).style("font-size", size + "px");
@@ -323,7 +458,9 @@ svg.selectAll("g")
323
458
  return i.color;
324
459
  })
325
460
  .on("mouseover", mouseover)
326
- .on("mouseout", mouseout);
461
+ .on("mouseout", mouseout)
462
+ .on("click", click)
463
+ .attr("cursor", "pointer");
327
464
 
328
465
  d3.select(this)
329
466
  .append("text")
@@ -331,7 +468,9 @@ svg.selectAll("g")
331
468
  .attr("y",function(d) { return yScale(maxY - d.y);})
332
469
  .on("mouseover", mouseover)
333
470
  .on("mouseout", mouseout)
471
+ .on("click", click)
334
472
  .each(fontSize)
473
+ .attr("cursor", "pointer")
335
474
  .attr("display", "none");
336
475
 
337
476
  });
@@ -22,13 +22,14 @@ class Flamegraph::Renderer
22
22
  end
23
23
 
24
24
  def graph_data
25
- height = 0
26
-
27
25
  table = []
28
26
  prev = []
29
27
 
30
28
  # a 2d array makes collapsing easy
31
29
  @stacks.each_with_index do |stack, pos|
30
+
31
+ next unless stack
32
+
32
33
  col = []
33
34
 
34
35
  stack.reverse.map{|r| r.to_s}.each_with_index do |frame, i|
@@ -79,7 +80,7 @@ class Flamegraph::Renderer
79
80
  end
80
81
 
81
82
  def read(file)
82
- body = IO.read(::File.expand_path(file, ::File.dirname(__FILE__)))
83
+ IO.read(::File.expand_path(file, ::File.dirname(__FILE__)))
83
84
  end
84
85
 
85
86
  end
@@ -0,0 +1,42 @@
1
+ class Flamegraph::StackProfSampler
2
+ def self.collect(fidelity=0.5)
3
+
4
+ result = StackProf.run(mode: :wall,
5
+ raw: true,
6
+ aggregate: false,
7
+ interval: (fidelity * 1000).to_i) do
8
+ yield
9
+ end
10
+
11
+
12
+ stacks = []
13
+ stack = []
14
+
15
+ return [] unless result[:raw]
16
+
17
+ length = nil
18
+ result[:raw].each do |i|
19
+ if length.nil?
20
+ length = i
21
+ next
22
+ end
23
+
24
+ if length > 0
25
+ frame = result[:frames][i]
26
+ frame = "#{frame[:file]}:#{frame[:line]}:in `#{frame[:name]}'"
27
+ stack << frame.to_s
28
+ length -= 1
29
+ next
30
+ end
31
+
32
+ i.times do
33
+ stacks << stack.reverse
34
+ end
35
+
36
+ stack = []
37
+ length = nil
38
+ end
39
+
40
+ stacks
41
+ end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module Flamegraph
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -0,0 +1,36 @@
1
+ if RUBY_VERSION >= "2.1.0"
2
+
3
+ require 'stackprof'
4
+ require 'test_helper'
5
+ require 'flamegraph/stackprof_sampler'
6
+
7
+ class TestStackprofSampler < Minitest::Test
8
+
9
+ def idle(duration)
10
+ r, w = IO.pipe
11
+ IO.select([r], nil, nil, duration)
12
+ ensure
13
+ r.close
14
+ w.close
15
+ end
16
+
17
+ def test_sample_collection
18
+
19
+ samples = Flamegraph::StackProfSampler.collect do
20
+ idle 0.005
21
+ end
22
+
23
+ assert(samples.count > 3, "Should get more than 3 samples in 5 millisecs")
24
+ end
25
+
26
+ def test_fidelity
27
+ samples = Flamegraph::StackProfSampler.collect(10) do
28
+ idle 0.005
29
+ end
30
+
31
+ assert(samples.count <= 1, "Should get a max of 1 sample got #{samples.count}")
32
+ end
33
+
34
+ end
35
+
36
+ end
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flamegraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-05 00:00:00.000000000 Z
11
+ date: 2014-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_stack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: guard-minitest
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  description: Flamegraph support for arbitrary ruby apps
@@ -101,7 +101,7 @@ executables: []
101
101
  extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
- - .gitignore
104
+ - ".gitignore"
105
105
  - CHANGELOG
106
106
  - Gemfile
107
107
  - Guardfile
@@ -119,11 +119,13 @@ files:
119
119
  - lib/flamegraph/lodash.min.js
120
120
  - lib/flamegraph/renderer.rb
121
121
  - lib/flamegraph/sampler.rb
122
+ - lib/flamegraph/stackprof_sampler.rb
122
123
  - lib/flamegraph/version.rb
123
124
  - test/test_fast_stack_sampler.rb
124
125
  - test/test_helper.rb
125
126
  - test/test_renderer.rb
126
127
  - test/test_sampler.rb
128
+ - test/test_stackprof_sampler.rb
127
129
  homepage: ''
128
130
  licenses:
129
131
  - MIT
@@ -134,17 +136,17 @@ require_paths:
134
136
  - lib
135
137
  required_ruby_version: !ruby/object:Gem::Requirement
136
138
  requirements:
137
- - - '>='
139
+ - - ">="
138
140
  - !ruby/object:Gem::Version
139
141
  version: '0'
140
142
  required_rubygems_version: !ruby/object:Gem::Requirement
141
143
  requirements:
142
- - - '>='
144
+ - - ">="
143
145
  - !ruby/object:Gem::Version
144
146
  version: '0'
145
147
  requirements: []
146
148
  rubyforge_project:
147
- rubygems_version: 2.0.14
149
+ rubygems_version: 2.2.2
148
150
  signing_key:
149
151
  specification_version: 4
150
152
  summary: Flamegraph support for arbitrary ruby apps
@@ -153,3 +155,4 @@ test_files:
153
155
  - test/test_helper.rb
154
156
  - test/test_renderer.rb
155
157
  - test/test_sampler.rb
158
+ - test/test_stackprof_sampler.rb