flamegraph 0.0.1 → 0.0.2

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: 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: