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 +4 -4
- data/CHANGELOG +3 -0
- data/Guardfile +7 -0
- data/README.md +27 -0
- data/Rakefile +7 -0
- data/demo/demo.rb +3 -3
- data/demo/rails_startup.rb +8 -0
- data/flamegraph.gemspec +5 -0
- data/lib/flamegraph.rb +11 -10
- data/lib/flamegraph/fast_stack_sampler.rb +7 -0
- data/lib/flamegraph/flamegraph.html +32 -8
- data/lib/flamegraph/sampler.rb +17 -23
- data/lib/flamegraph/version.rb +1 -1
- data/test/test_fast_stack_sampler.rb +23 -0
- data/test/test_helper.rb +4 -0
- data/test/test_renderer.rb +36 -0
- data/test/test_sampler.rb +23 -0
- metadata +73 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d6f147215ccb958640e914e56d3bd1ec6130228
|
4
|
+
data.tar.gz: 4b5ba1debf723c7337b7d124d5d6dad981aaa9b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0003bcb764added6799e674c4f03682c1bd2ed578fd34c3fabd8384dd62133e2b5e8f713289a7d1809d4d57871fd463d444aaef352ee67b9f1f9dc6e627a94bb
|
7
|
+
data.tar.gz: 1219aa97e31f1c2e0243a14846cced5338499bd71321a4d624cc2548a9102e0fa3e9747d7ddbc3f4c66865baa4e68fe7e37b9f449d3acbb92ed0e8f4a5da3631
|
data/CHANGELOG
ADDED
data/Guardfile
ADDED
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
data/demo/demo.rb
CHANGED
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
|
-
|
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
|
-
|
14
|
-
|
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(
|
19
|
+
f.write(rendered)
|
19
20
|
end
|
20
21
|
end
|
21
|
-
|
22
|
+
rendered
|
22
23
|
end
|
23
24
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<script src="
|
5
|
-
<script src="
|
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
|
-
|
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
|
-
|
data/lib/flamegraph/sampler.rb
CHANGED
@@ -1,32 +1,26 @@
|
|
1
1
|
class Flamegraph::Sampler
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@backtraces << t.backtrace_locations
|
7
|
+
thread = Thread.new do
|
8
|
+
until done
|
9
|
+
backtraces << t.backtrace_locations
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
17
|
+
begin
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
done = true
|
21
|
+
thread.join
|
22
|
+
end
|
31
23
|
|
24
|
+
backtraces
|
25
|
+
end
|
32
26
|
end
|
data/lib/flamegraph/version.rb
CHANGED
@@ -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
|
data/test/test_helper.rb
ADDED
@@ -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.
|
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-
|
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.
|
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:
|