flamegraph 0.0.1 → 0.0.2

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: d5843c5d2ae0e8009e59a7b2ae1be5866801f7f2
4
- data.tar.gz: 2c8783c5494d36e030917705e56469f22bb7ad33
3
+ metadata.gz: 1d6f147215ccb958640e914e56d3bd1ec6130228
4
+ data.tar.gz: 4b5ba1debf723c7337b7d124d5d6dad981aaa9b6
5
5
  SHA512:
6
- metadata.gz: 5eb5c07f7b53389bad117144637dced3282e0db7d8b16fec95b293e3f7d30ea53fe3908fa27cf27fa487099c19745d858ac51fb0a53a60577063cc9ba25e1ccf
7
- data.tar.gz: 23c5aff03a944b3658dd7ec3beba84c9e5ce3439d1012a5e26c68e796b30800276e38143e07e7bc47e1f0201f8c54602e68f2991858cce45728fba708ff35346
6
+ metadata.gz: 0003bcb764added6799e674c4f03682c1bd2ed578fd34c3fabd8384dd62133e2b5e8f713289a7d1809d4d57871fd463d444aaef352ee67b9f1f9dc6e627a94bb
7
+ data.tar.gz: 1219aa97e31f1c2e0243a14846cced5338499bd71321a4d624cc2548a9102e0fa3e9747d7ddbc3f4c66865baa4e68fe7e37b9f449d3acbb92ed0e8f4a5da3631
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ 3-Sep-2013
2
+
3
+ - Added fast_stack for more accurate graphing
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard :minitest do
2
+ # with Minitest::Unit
3
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
4
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/test_#{m[2]}.rb" }
5
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
6
+
7
+ end
data/README.md CHANGED
@@ -20,6 +20,33 @@ Or install it yourself as:
20
20
 
21
21
  Note: Only supported on Ruby 2.0. Gathering stack traces is too slow on earlier versions of Ruby or JRuby
22
22
 
23
+ ```ruby
24
+
25
+ require 'flamegraph'
26
+ html = Flamegraph.generate do
27
+ # your work here
28
+ end
29
+
30
+ # or
31
+
32
+
33
+ Flamegraph.generate(filename) do
34
+ # your work here
35
+ end
36
+
37
+ ```
38
+
39
+ ## Demo
40
+
41
+ Demo of: https://github.com/SamSaffron/flamegraph/blob/master/demo/demo.rb
42
+
43
+ http://samsaffron.github.io/flamegraph/demo.html
44
+
45
+ Demo of Discourse startup using: https://github.com/SamSaffron/flamegraph/blob/master/demo/rails_startup.rb
46
+
47
+ **WARNING VERY SLOW, MAY CRASH BROWSER TAB**
48
+ http://samsaffron.github.io/flamegraph/rails-startup.html
49
+
23
50
  ## Contributing
24
51
 
25
52
  1. Fork it
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/*_test.rb"
6
+ end
7
+
8
+
data/demo/demo.rb CHANGED
@@ -11,9 +11,9 @@ def ack(m, n)
11
11
  end
12
12
  end
13
13
 
14
- graph = Flamegraph.generate do
15
- ack(3,7)
16
- end
14
+ # graph = Flamegraph.generate do
15
+ # ack(3,7)
16
+ # end
17
17
 
18
18
  Flamegraph.generate("graph.html") do
19
19
  ack(3,7)
@@ -0,0 +1,8 @@
1
+ require 'flamegraph'
2
+
3
+ Flamegraph.generate("graph.html") do
4
+ ENV['RAILS_ENV'] = 'profile'
5
+ require File.expand_path(File.dirname(__FILE__) + "/config/environment")
6
+
7
+ I18n.t("test")
8
+ end
data/flamegraph.gemspec CHANGED
@@ -18,6 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "fast_stack"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.3"
22
24
  spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "minitest"
26
+ spec.add_development_dependency "guard"
27
+ spec.add_development_dependency "guard-minitest"
23
28
  end
data/lib/flamegraph.rb CHANGED
@@ -1,23 +1,24 @@
1
1
  require "json"
2
+ require "fast_stack"
2
3
  require "flamegraph/version"
3
- require "flamegraph/sampler"
4
4
  require "flamegraph/renderer"
5
5
 
6
6
  module Flamegraph
7
- def self.generate(filename=nil)
8
- sampler = Flamegraph::Sampler.new
9
- sampler.start
10
- yield
11
- results = sampler.finish
7
+ def self.generate(filename=nil, opts = {})
8
+ fidelity = opts[:fidelity] || 0.5
12
9
 
13
- renderer = Flamegraph::Renderer.new(results)
14
- result = renderer.graph_html
10
+ backtraces = FastStack.profile(fidelity) do
11
+ yield
12
+ end
13
+
14
+ renderer = Flamegraph::Renderer.new(backtraces)
15
+ rendered = renderer.graph_html
15
16
 
16
17
  if filename
17
18
  File.open(filename,"w") do |f|
18
- f.write(result)
19
+ f.write(rendered)
19
20
  end
20
21
  end
21
- result
22
+ rendered
22
23
  end
23
24
  end
@@ -0,0 +1,7 @@
1
+ class Flamegraph::FastStackSampler
2
+ def self.collect(fidelity=0.5)
3
+ FastStack.profile(fidelity) do
4
+ yield
5
+ end
6
+ end
7
+ end
@@ -1,8 +1,8 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
5
- <script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
4
+ <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
5
+ <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
6
6
  <meta charset=utf-8 />
7
7
  <title>Flame Graph of Page</title>
8
8
  <style>
@@ -167,15 +167,21 @@ Array.prototype.getUnique = function() {
167
167
  return a
168
168
  }
169
169
 
170
- var samplePercent = function(samples){
171
- return "(" + samples +
170
+ var samplePercent = function(samples, exclusive){
171
+ var info = " (" + samples +
172
172
  " sample" + (samples == 1 ? "" : "s") + " - " +
173
- ((samples / maxX) * 100).toFixed(2) + "%)";
173
+ ((samples / maxX) * 100).toFixed(2) + "%) ";
174
+ if (exclusive) {
175
+ info += " (" + exclusive +
176
+ " exclusive - " +
177
+ ((exclusive / maxX) * 100).toFixed(2) + "%) ";
178
+ }
179
+ return info;
174
180
  }
175
181
 
176
182
  var mouseover = function(d) {
177
183
  var i = info[d.frame];
178
- $('.info').text( d.frame + " " + samplePercent(i.samples.length));
184
+ $('.info').text( d.frame + " " + samplePercent(i.samples.length, d.topFrame ? d.topFrame.exclusiveCount : 0));
179
185
  d3.selectAll(i.nodes)
180
186
  .attr('opacity',0.5);
181
187
  };
@@ -211,6 +217,8 @@ var rainbow = function(numOfSteps, step) {
211
217
 
212
218
  // assign some colors, analyze samples per gem
213
219
  var gemStats = {}
220
+ var topFrames = {}
221
+ var lastFrame = {frame: 'd52e04d-df28-41ed-a215-b6ec840a8ea5', x: -1}
214
222
 
215
223
  $.each(data, function(){
216
224
 
@@ -225,8 +233,26 @@ $.each(data, function(){
225
233
  for(var j=0; j < this.width; j++){
226
234
  stat.samples.push(this.x + j);
227
235
  }
236
+ // This assumes the traversal is in order
237
+ if (lastFrame.x != this.x) {
238
+ var topFrame = topFrames[lastFrame.frame]
239
+ if (!topFrame) {
240
+ topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
241
+ }
242
+ topFrame.exclusiveCount += 1;
243
+ lastFrame.topFrame = topFrame;
244
+ }
245
+ lastFrame = this;
246
+
228
247
  });
229
248
 
249
+ var topFrame = topFrames[lastFrame.frame]
250
+ if (!topFrame) {
251
+ topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
252
+ }
253
+ topFrame.exclusiveCount += 1;
254
+ lastFrame.topFrame = topFrame;
255
+
230
256
  var totalGems = 0;
231
257
  $.each(gemStats, function(){totalGems++;});
232
258
 
@@ -321,5 +347,3 @@ $.each(gemStats, function(k,v){
321
347
  </script>
322
348
  </body>
323
349
  </html>
324
-
325
-
@@ -1,32 +1,26 @@
1
1
  class Flamegraph::Sampler
2
-
3
- def initialize
4
- @backtraces = []
5
- @done_samplint = false
6
- end
7
-
8
- def start
9
- @backtraces = []
10
- @done_samplint = false
2
+ def self.collect(fidelity=0.5)
3
+ backtraces = []
4
+ done = false
11
5
 
12
6
  t = Thread.current
13
- @thread = Thread.new do
14
- begin
15
- while !@done_sampling
16
- @backtraces << t.backtrace_locations
7
+ thread = Thread.new do
8
+ until done
9
+ backtraces << t.backtrace_locations
17
10
 
18
- # On my machine using Ruby 2.0 this give me excellent fidelity of stack trace per 1.2ms
19
- # with this fidelity analysis becomes very powerful
20
- sleep 0.0005
21
- end
11
+ # On my machine using Ruby 2.0 this give me excellent fidelity of stack trace per 1.2ms
12
+ # with this fidelity analysis becomes very powerful
13
+ sleep (fidelity / 1000.0)
22
14
  end
23
15
  end
24
- end
25
16
 
26
- def finish
27
- @done_sampling = true
28
- @thread.join
29
- @backtraces
30
- end
17
+ begin
18
+ yield
19
+ ensure
20
+ done = true
21
+ thread.join
22
+ end
31
23
 
24
+ backtraces
25
+ end
32
26
  end
@@ -1,3 +1,3 @@
1
1
  module Flamegraph
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+ require 'fast_stack'
3
+ require 'flamegraph/fast_stack_sampler'
4
+
5
+ class TestFastStackSampler < Minitest::Test
6
+
7
+ def test_sample_collection
8
+ samples = Flamegraph::FastStackSampler.collect do
9
+ sleep 0.005
10
+ end
11
+
12
+ assert(samples.count > 3, "Should get more than 3 samples in 5 millisecs")
13
+ end
14
+
15
+ def test_fidelity
16
+ samples = Flamegraph::FastStackSampler.collect(10) do
17
+ sleep 0.005
18
+ end
19
+
20
+ assert(samples.count <= 1, "Should get a max of 1 sample got #{samples.count}")
21
+ end
22
+
23
+ end
@@ -0,0 +1,4 @@
1
+ require 'flamegraph'
2
+ require 'minitest/pride'
3
+
4
+
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class TestRenderer < Minitest::Test
4
+
5
+ def test_builds_table_correctly
6
+ stacks = [["3","2","1"],["4","1"],["4","5"]]
7
+
8
+ g = Flamegraph::Renderer.new(stacks)
9
+ assert_equal([
10
+ {:x => 1, :y => 1, :frame => "1", :width => 2},
11
+ {:x => 1, :y => 2, :frame => "2", :width => 1},
12
+ {:x => 1, :y => 3, :frame => "3", :width => 1},
13
+ {:x => 2, :y => 2, :frame => "4", :width => 2},
14
+ {:x => 3, :y => 1, :frame => "5", :width => 1}
15
+ ], g.graph_data)
16
+
17
+ end
18
+
19
+ def test_avoids_bridges
20
+ stacks = [["3","2","1"],["4","1"],["4","5"]]
21
+
22
+ g = Flamegraph::Renderer.new(stacks)
23
+
24
+ assert_equal([
25
+ {:x => 1, :y => 1, :frame => "1", :width => 2},
26
+ {:x => 1, :y => 2, :frame => "2", :width => 1},
27
+ {:x => 1, :y => 3, :frame => "3", :width => 1},
28
+ {:x => 2, :y => 2, :frame => "4", :width => 2},
29
+ {:x => 3, :y => 1, :frame => "5", :width => 1}
30
+ ], g.graph_data)
31
+
32
+
33
+ end
34
+
35
+ end
36
+
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+ require 'flamegraph/sampler'
3
+
4
+ class TestSampler < Minitest::Test
5
+
6
+ def test_sample_collection
7
+ samples = Flamegraph::Sampler.collect do
8
+ sleep 0.005
9
+ end
10
+
11
+ assert(samples.count > 3, "Should get more than 3 samples in 5 millisecs")
12
+ end
13
+
14
+ def test_fidelity
15
+ samples = Flamegraph::Sampler.collect(10) do
16
+ sleep 0.005
17
+ end
18
+
19
+ assert(samples.count <= 1, "Should get a max of 1 sample got #{samples.count}")
20
+ end
21
+
22
+ end
23
+
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flamegraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
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-05-10 00:00:00.000000000 Z
11
+ date: 2013-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fast_stack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,48 @@ dependencies:
38
52
  - - '>='
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
41
97
  description: Flamegraph support for arbitrary ruby apps
42
98
  email:
43
99
  - sam.saffron@gmail.com
@@ -46,17 +102,25 @@ extensions: []
46
102
  extra_rdoc_files: []
47
103
  files:
48
104
  - .gitignore
105
+ - CHANGELOG
49
106
  - Gemfile
107
+ - Guardfile
50
108
  - LICENSE.txt
51
109
  - README.md
52
110
  - Rakefile
53
111
  - demo/demo.rb
112
+ - demo/rails_startup.rb
54
113
  - flamegraph.gemspec
55
114
  - lib/flamegraph.rb
115
+ - lib/flamegraph/fast_stack_sampler.rb
56
116
  - lib/flamegraph/flamegraph.html
57
117
  - lib/flamegraph/renderer.rb
58
118
  - lib/flamegraph/sampler.rb
59
119
  - lib/flamegraph/version.rb
120
+ - test/test_fast_stack_sampler.rb
121
+ - test/test_helper.rb
122
+ - test/test_renderer.rb
123
+ - test/test_sampler.rb
60
124
  homepage: ''
61
125
  licenses:
62
126
  - MIT
@@ -77,8 +141,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
141
  version: '0'
78
142
  requirements: []
79
143
  rubyforge_project:
80
- rubygems_version: 2.0.0
144
+ rubygems_version: 2.0.3
81
145
  signing_key:
82
146
  specification_version: 4
83
147
  summary: Flamegraph support for arbitrary ruby apps
84
- test_files: []
148
+ test_files:
149
+ - test/test_fast_stack_sampler.rb
150
+ - test/test_helper.rb
151
+ - test/test_renderer.rb
152
+ - test/test_sampler.rb
153
+ has_rdoc: