ruby-prof-flamegraph 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8bbc733bda4ede9cd81cec28badf3403953e8eab
4
+ data.tar.gz: 54665c4c1659e1bb9f0ee39dea833c059d8eeb02
5
+ SHA512:
6
+ metadata.gz: 29a651045811ed6ed69c2985843945f53fddaca69a008c1c3a0194e26714380e0079426c10a78b313a1ad5d601d179a388c57a3e5fefe4060e393f1a738a241a
7
+ data.tar.gz: ee3b2bf7cdbaabaa0ed7b526895f74792b1e5c7eca131e23968daf6b1876c307ac787f5b7d42d62c35d5d57a60adb4433c90bf6d87c1788031547834cee5a909
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby-prof-flamegraph.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Thai Pangsakulyanont
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ ruby-prof-flamegraph
2
+ ====================
3
+
4
+ A [ruby-prof][] printer that outputs a fold stack file that's compatible with [FlameGraph][].
5
+ It is created based on `RubyProf::CallStackPrinter`.
6
+
7
+
8
+ Awesomeness
9
+ -----------
10
+
11
+ [FlameGraph][] is a way to visualize stack trace,
12
+ making it very obvious where in the program takes the longest time.
13
+ It is a Perl script takes a "fold stack" file and generates a nice, interactive SVG.
14
+ The fold stack is usually generated from DTrace or Prof data using [stackcollapse.pl][FlameGraph], which is included with FlameGraph.
15
+
16
+ I created this gem because I want to find out where the bottleneck is in [SlimWiki][]'s specs,
17
+ but I don't know DTrace and just want the result quick.
18
+
19
+ I did not expect this,
20
+ but generating a company name from Faker causes 44 YAML files to be parsed,
21
+ taking 28 seconds.
22
+
23
+ (TODO include image)
24
+
25
+
26
+ To learn more about Flame Graphs, check these out:
27
+
28
+ - [Official Flame Graphs Website](http://www.brendangregg.com/flamegraphs.html) by [Brendan Gregg](http://www.brendangregg.com/)
29
+ - [Node.js in Flames](http://techblog.netflix.com/2014/11/nodejs-in-flames.html), which is the article that introduced me to flame graph (via [Node Weekly Issue #62](http://nodeweekly.com/issues/62))
30
+ - [Blazing Performance with Flame Graphs](https://www.usenix.org/conference/lisa13/technical-sessions/plenary/gregg) talk at USENIX/LISA13 ([slideshare](http://www.slideshare.net/brendangregg/blazing-performance-with-flame-graphs?ref=http://www.brendangregg.com/flamegraphs.html)) ([video](http://www.youtube.com/watch?v=nZfNehCzGdw))
31
+
32
+
33
+
34
+ [ruby-prof]: https://github.com/ruby-prof/ruby-prof
35
+ [FlameGraph]: https://github.com/brendangregg/FlameGraph
36
+ [SlimWiki]: https://slimwiki.com/
37
+
38
+
39
+ ## Installation
40
+
41
+ ```ruby
42
+ gem 'ruby-prof-flamegraph'
43
+ ```
44
+
45
+
46
+ ## Usage
47
+
48
+ Just `require 'ruby-prof-flamegraph` and use `RubyProf::FlameGraphPrinter` as your printer for ruby-prof.
49
+ For vanilla ruby-prof, see [example.rb](example.rb).
50
+
51
+ For rspec-prof, `RSpecProf.printer_class = RubyProf::FlameGraphPrinter`
52
+
53
+ [rspec-prof]: https://github.com/sinisterchipmunk/rspec-prof
54
+
55
+
56
+
57
+ ## Example
58
+
59
+ See the result in [example.svg][]
60
+
61
+ [example.svg]: example.svg
62
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/example.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'ruby-prof'
2
+ require 'ruby-prof-flamegraph'
3
+
4
+ rubyprof_dir = Gem::Specification.find_by_name('ruby-prof').gem_dir
5
+ require "#{rubyprof_dir}/test/prime"
6
+
7
+ # Profile the code
8
+ result = RubyProf.profile do
9
+ run_primes(200)
10
+ end
11
+
12
+ # Print a graph profile to text
13
+ printer = RubyProf::FlameGraphPrinter.new(result)
14
+ printer.print(STDOUT, {})
data/example.svg ADDED
@@ -0,0 +1,249 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" width="1200" height="210" onload="init(evt)" viewBox="0 0 1200 210" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
4
+ <defs >
5
+ <linearGradient id="background" y1="0" y2="1" x1="0" x2="0" >
6
+ <stop stop-color="#eeeeee" offset="5%" />
7
+ <stop stop-color="#eeeeb0" offset="95%" />
8
+ </linearGradient>
9
+ </defs>
10
+ <style type="text/css">
11
+ .func_g:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
12
+ </style>
13
+ <script type="text/ecmascript">
14
+ <![CDATA[
15
+ var details, svg;
16
+ function init(evt) {
17
+ details = document.getElementById("details").firstChild;
18
+ svg = document.getElementsByTagName("svg")[0];
19
+ }
20
+ function s(info) { details.nodeValue = "Function: " + info; }
21
+ function c() { details.nodeValue = ' '; }
22
+ function find_child(parent, name, attr) {
23
+ var children = parent.childNodes;
24
+ for (var i=0; i<children.length;i++) {
25
+ if (children[i].tagName == name)
26
+ return (attr != undefined) ? children[i].attributes[attr].value : children[i];
27
+ }
28
+ return;
29
+ }
30
+ function orig_save(e, attr, val) {
31
+ if (e.attributes["_orig_"+attr] != undefined) return;
32
+ if (e.attributes[attr] == undefined) return;
33
+ if (val == undefined) val = e.attributes[attr].value;
34
+ e.setAttribute("_orig_"+attr, val);
35
+ }
36
+ function orig_load(e, attr) {
37
+ if (e.attributes["_orig_"+attr] == undefined) return;
38
+ e.attributes[attr].value = e.attributes["_orig_"+attr].value;
39
+ e.removeAttribute("_orig_"+attr);
40
+ }
41
+ function update_text(e) {
42
+ var r = find_child(e, "rect");
43
+ var t = find_child(e, "text");
44
+ var w = parseFloat(r.attributes["width"].value) -3;
45
+ var txt = find_child(e, "title").textContent.replace(/\([^(]*\)/,"");
46
+ t.attributes["x"].value = parseFloat(r.attributes["x"].value) +3;
47
+
48
+ // Smaller than this size won't fit anything
49
+ if (w < 2*12*0.59) {
50
+ t.textContent = "";
51
+ return;
52
+ }
53
+
54
+ t.textContent = txt;
55
+ // Fit in full text width
56
+ if (/^ *$/.test(txt) || t.getSubStringLength(0, txt.length) < w)
57
+ return;
58
+
59
+ for (var x=txt.length-2; x>0; x--) {
60
+ if (t.getSubStringLength(0, x+2) <= w) {
61
+ t.textContent = txt.substring(0,x) + "..";
62
+ return;
63
+ }
64
+ }
65
+ t.textContent = "";
66
+ }
67
+ function zoom_reset(e) {
68
+ if (e.attributes != undefined) {
69
+ orig_load(e, "x");
70
+ orig_load(e, "width");
71
+ }
72
+ if (e.childNodes == undefined) return;
73
+ for(var i=0, c=e.childNodes; i<c.length; i++) {
74
+ zoom_reset(c[i]);
75
+ }
76
+ }
77
+ function zoom_child(e, x, ratio) {
78
+ if (e.attributes != undefined) {
79
+ if (e.attributes["x"] != undefined) {
80
+ orig_save(e, "x");
81
+ e.attributes["x"].value = (parseFloat(e.attributes["x"].value) - x - 10) * ratio + 10;
82
+ if(e.tagName == "text") e.attributes["x"].value = find_child(e.parentNode, "rect", "x") + 3;
83
+ }
84
+ if (e.attributes["width"] != undefined) {
85
+ orig_save(e, "width");
86
+ e.attributes["width"].value = parseFloat(e.attributes["width"].value) * ratio;
87
+ }
88
+ }
89
+
90
+ if (e.childNodes == undefined) return;
91
+ for(var i=0, c=e.childNodes; i<c.length; i++) {
92
+ zoom_child(c[i], x-10, ratio);
93
+ }
94
+ }
95
+ function zoom_parent(e) {
96
+ if (e.attributes) {
97
+ if (e.attributes["x"] != undefined) {
98
+ orig_save(e, "x");
99
+ e.attributes["x"].value = 10;
100
+ }
101
+ if (e.attributes["width"] != undefined) {
102
+ orig_save(e, "width");
103
+ e.attributes["width"].value = parseInt(svg.width.baseVal.value) - (10*2);
104
+ }
105
+ }
106
+ if (e.childNodes == undefined) return;
107
+ for(var i=0, c=e.childNodes; i<c.length; i++) {
108
+ zoom_parent(c[i]);
109
+ }
110
+ }
111
+ function zoom(node) {
112
+ var attr = find_child(node, "rect").attributes;
113
+ var width = parseFloat(attr["width"].value);
114
+ var xmin = parseFloat(attr["x"].value);
115
+ var xmax = parseFloat(xmin + width);
116
+ var ymin = parseFloat(attr["y"].value);
117
+ var ratio = (svg.width.baseVal.value - 2*10) / width;
118
+
119
+ // XXX: Workaround for JavaScript float issues (fix me)
120
+ var fudge = 0.0001;
121
+
122
+ var unzoombtn = document.getElementById("unzoom");
123
+ unzoombtn.style["opacity"] = "1.0";
124
+
125
+ var el = document.getElementsByTagName("g");
126
+ for(var i=0;i<el.length;i++){
127
+ var e = el[i];
128
+ var a = find_child(e, "rect").attributes;
129
+ var ex = parseFloat(a["x"].value);
130
+ var ew = parseFloat(a["width"].value);
131
+ // Is it an ancestor
132
+ if (0 == 0) {
133
+ var upstack = parseFloat(a["y"].value) > ymin;
134
+ } else {
135
+ var upstack = parseFloat(a["y"].value) < ymin;
136
+ }
137
+ if (upstack) {
138
+ // Direct ancestor
139
+ if (ex <= xmin && (ex+ew+fudge) >= xmax) {
140
+ e.style["opacity"] = "0.5";
141
+ zoom_parent(e);
142
+ e.onclick = function(e){unzoom(); zoom(this);};
143
+ update_text(e);
144
+ }
145
+ // not in current path
146
+ else
147
+ e.style["display"] = "none";
148
+ }
149
+ // Children maybe
150
+ else {
151
+ // no common path
152
+ if (ex < xmin || ex + fudge >= xmax) {
153
+ e.style["display"] = "none";
154
+ }
155
+ else {
156
+ zoom_child(e, xmin, ratio);
157
+ e.onclick = function(e){zoom(this);};
158
+ update_text(e);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ function unzoom() {
164
+ var unzoombtn = document.getElementById("unzoom");
165
+ unzoombtn.style["opacity"] = "0.0";
166
+
167
+ var el = document.getElementsByTagName("g");
168
+ for(i=0;i<el.length;i++) {
169
+ el[i].style["display"] = "block";
170
+ el[i].style["opacity"] = "1";
171
+ zoom_reset(el[i]);
172
+ update_text(el[i]);
173
+ }
174
+ }
175
+ ]]>
176
+ </script>
177
+ <rect x="0.0" y="0" width="1200.0" height="210.0" fill="url(#background)" />
178
+ <text text-anchor="middle" x="600.00" y="24" font-size="17" font-family="Verdana" fill="rgb(0,0,0)" >Flame Graph</text>
179
+ <text text-anchor="" x="10.00" y="193" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" id="details" > </text>
180
+ <text text-anchor="" x="10.00" y="24" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" id="unzoom" onclick="unzoom()" style="opacity:0.0;cursor:pointer" >Reset Zoom</text>
181
+ <g class="func_g" onmouseover="s('Fiber:70303624221020 (27 ms, 100.13%)')" onmouseout="c()" onclick="zoom(this)">
182
+ <title>Fiber:70303624221020 (27 ms, 100.13%)</title><rect x="10.0" y="129" width="1180.0" height="15.0" fill="rgb(214,125,8)" rx="2" ry="2" />
183
+ <text text-anchor="" x="13.00" y="139.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Fiber:70303624221020</text>
184
+ </g>
185
+ <g class="func_g" onmouseover="s('Object#run_primes (1) (27 ms, 100.13%)')" onmouseout="c()" onclick="zoom(this)">
186
+ <title>Object#run_primes (1) (27 ms, 100.13%)</title><rect x="11.4" y="97" width="1178.6" height="15.0" fill="rgb(209,210,36)" rx="2" ry="2" />
187
+ <text text-anchor="" x="14.40" y="107.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Object#run_primes (1)</text>
188
+ </g>
189
+ <g class="func_g" onmouseover="s('Thread:70303615441860 (27 ms, 100.13%)')" onmouseout="c()" onclick="zoom(this)">
190
+ <title>Thread:70303615441860 (27 ms, 100.13%)</title><rect x="10.0" y="145" width="1180.0" height="15.0" fill="rgb(248,190,45)" rx="2" ry="2" />
191
+ <text text-anchor="" x="13.00" y="155.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Thread:70303615441860</text>
192
+ </g>
193
+ <g class="func_g" onmouseover="s('Object#find_largest (1) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
194
+ <title>Object#find_largest (1) (0 ms, 0.00%)</title><rect x="11.8" y="81" width="3.0" height="15.0" fill="rgb(250,61,52)" rx="2" ry="2" />
195
+ <text text-anchor="" x="14.84" y="91.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
196
+ </g>
197
+ <g class="func_g" onmouseover="s('Kernel#respond_to_missing? (200) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
198
+ <title>Kernel#respond_to_missing? (200) (0 ms, 0.00%)</title><rect x="1182.6" y="33" width="7.2" height="15.0" fill="rgb(227,7,25)" rx="2" ry="2" />
199
+ <text text-anchor="" x="1185.60" y="43.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
200
+ </g>
201
+ <g class="func_g" onmouseover="s('Array#first (1) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
202
+ <title>Array#first (1) (0 ms, 0.00%)</title><rect x="12.4" y="65" width="0.1" height="15.0" fill="rgb(241,32,35)" rx="2" ry="2" />
203
+ <text text-anchor="" x="15.36" y="75.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
204
+ </g>
205
+ <g class="func_g" onmouseover="s('Kernel#rand (200) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
206
+ <title>Kernel#rand (200) (0 ms, 0.00%)</title><rect x="1170.4" y="49" width="19.4" height="15.0" fill="rgb(230,24,35)" rx="2" ry="2" />
207
+ <text text-anchor="" x="1173.40" y="59.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
208
+ </g>
209
+ <g class="func_g" onmouseover="s('Object#find_primes (1) (26 ms, 96.42%)')" onmouseout="c()" onclick="zoom(this)">
210
+ <title>Object#find_primes (1) (26 ms, 96.42%)</title><rect x="14.8" y="81" width="1142.5" height="15.0" fill="rgb(251,108,5)" rx="2" ry="2" />
211
+ <text text-anchor="" x="17.77" y="91.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Object#find_primes (1)</text>
212
+ </g>
213
+ <g class="func_g" onmouseover="s('Array#select (1) (26 ms, 96.42%)')" onmouseout="c()" onclick="zoom(this)">
214
+ <title>Array#select (1) (26 ms, 96.42%)</title><rect x="15.0" y="65" width="1142.3" height="15.0" fill="rgb(224,170,33)" rx="2" ry="2" />
215
+ <text text-anchor="" x="18.03" y="75.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Array#select (1)</text>
216
+ </g>
217
+ <g class="func_g" onmouseover="s('Array#each_index (1) (1 ms, 3.71%)')" onmouseout="c()" onclick="zoom(this)">
218
+ <title>Array#each_index (1) (1 ms, 3.71%)</title><rect x="1157.5" y="65" width="32.3" height="15.0" fill="rgb(223,147,10)" rx="2" ry="2" />
219
+ <text text-anchor="" x="1160.49" y="75.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Ar..</text>
220
+ </g>
221
+ <g class="func_g" onmouseover="s('Class#new (1) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
222
+ <title>Class#new (1) (0 ms, 0.00%)</title><rect x="1189.8" y="65" width="0.2" height="15.0" fill="rgb(214,129,37)" rx="2" ry="2" />
223
+ <text text-anchor="" x="1192.78" y="75.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
224
+ </g>
225
+ <g class="func_g" onmouseover="s('Object#make_random_array (1) (1 ms, 3.71%)')" onmouseout="c()" onclick="zoom(this)">
226
+ <title>Object#make_random_array (1) (1 ms, 3.71%)</title><rect x="1157.3" y="81" width="32.7" height="15.0" fill="rgb(238,46,41)" rx="2" ry="2" />
227
+ <text text-anchor="" x="1160.27" y="91.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Ob..</text>
228
+ </g>
229
+ <g class="func_g" onmouseover="s('Integer#upto (200) (25 ms, 92.71%)')" onmouseout="c()" onclick="zoom(this)">
230
+ <title>Integer#upto (200) (25 ms, 92.71%)</title><rect x="46.9" y="33" width="1110.4" height="15.0" fill="rgb(213,32,29)" rx="2" ry="2" />
231
+ <text text-anchor="" x="49.89" y="43.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Integer#upto (200)</text>
232
+ </g>
233
+ <g class="func_g" onmouseover="s('Global#[No method] (1) (27 ms, 100.13%)')" onmouseout="c()" onclick="zoom(this)">
234
+ <title>Global#[No method] (1) (27 ms, 100.13%)</title><rect x="10.0" y="113" width="1180.0" height="15.0" fill="rgb(213,132,43)" rx="2" ry="2" />
235
+ <text text-anchor="" x="13.00" y="123.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Global#[No method] (1)</text>
236
+ </g>
237
+ <g class="func_g" onmouseover="s('all (27 ms, 100%)')" onmouseout="c()" onclick="zoom(this)">
238
+ <title>all (27 ms, 100%)</title><rect x="10.0" y="161" width="1180.0" height="15.0" fill="rgb(232,90,54)" rx="2" ry="2" />
239
+ <text text-anchor="" x="13.00" y="171.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
240
+ </g>
241
+ <g class="func_g" onmouseover="s('Integer#upto (1) (0 ms, 0.00%)')" onmouseout="c()" onclick="zoom(this)">
242
+ <title>Integer#upto (1) (0 ms, 0.00%)</title><rect x="12.5" y="65" width="2.3" height="15.0" fill="rgb(243,8,16)" rx="2" ry="2" />
243
+ <text text-anchor="" x="15.49" y="75.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" ></text>
244
+ </g>
245
+ <g class="func_g" onmouseover="s('Object#is_prime (200) (26 ms, 96.42%)')" onmouseout="c()" onclick="zoom(this)">
246
+ <title>Object#is_prime (200) (26 ms, 96.42%)</title><rect x="26.2" y="49" width="1131.1" height="15.0" fill="rgb(243,54,53)" rx="2" ry="2" />
247
+ <text text-anchor="" x="29.24" y="59.5" font-size="12" font-family="Verdana" fill="rgb(0,0,0)" >Object#is_prime (200)</text>
248
+ </g>
249
+ </svg>
@@ -0,0 +1,2 @@
1
+
2
+ require 'ruby-prof/printers/flame_graph_printer'
@@ -0,0 +1,67 @@
1
+
2
+ require 'ruby-prof'
3
+
4
+ module RubyProf
5
+
6
+ # wow much flame graph many stack wow!!
7
+ #
8
+ class FlameGraphPrinter < AbstractPrinter
9
+
10
+ VERSION = '0.2.0'
11
+
12
+ def print(output = STDOUT, options = {})
13
+ @output = output
14
+ setup_options(options)
15
+
16
+ @overall_threads_time = @result.threads.reduce(0) { |a, thread| a + thread.total_time }
17
+
18
+ @result.threads.each do |thread|
19
+ @current_thread_id = thread.fiber_id
20
+ @overall_time = thread.total_time
21
+ start = []
22
+ start << "Thread:#{thread.id}"
23
+ start << "Fiber:#{thread.fiber_id}" unless thread.id == thread.fiber_id
24
+ thread.methods.each do |m|
25
+ next unless m.root?
26
+ m.call_infos.each do |ci|
27
+ next unless ci.root?
28
+ print_stack start, ci
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def min_time
35
+ 0
36
+ end
37
+
38
+ def print_stack(prefix, call_info)
39
+
40
+ total_time = call_info.total_time
41
+ percent_total = (total_time/@overall_time)*100
42
+ return unless percent_total > min_percent
43
+ return unless total_time >= min_time
44
+
45
+ kids = call_info.children
46
+ current = prefix + [name(call_info)]
47
+ @output.puts "#{current.join(';')} #{number call_info.self_time * 1e3}"
48
+
49
+ kids.each do |child|
50
+ print_stack current, child
51
+ end
52
+
53
+ end
54
+
55
+ def name(call_info)
56
+ method = call_info.target
57
+ "#{method.full_name} (#{call_info.called})"
58
+ end
59
+
60
+ def number(x)
61
+ ("%.6f" % x).sub(/\.0*$/, '').sub(/(\..*?)0+$/, '\1')
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ruby-prof-flamegraph'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby-prof-flamegraph"
8
+ spec.version = RubyProf::FlameGraphPrinter::VERSION
9
+ spec.authors = ["Thai Pangsakulyanont"]
10
+ spec.email = ["org.yi.dttvb@gmail.com"]
11
+ spec.summary = %q{ruby-prof printer that exports to fold stacks compatible with FlameGraph}
12
+ spec.homepage = "http://github.com/dtinth/ruby-prof-flamegraph"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.7"
20
+ spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_runtime_dependency "ruby-prof", "~> 0.15"
22
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-prof-flamegraph
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Thai Pangsakulyanont
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-prof
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.15'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.15'
55
+ description:
56
+ email:
57
+ - org.yi.dttvb@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - example.rb
68
+ - example.svg
69
+ - lib/ruby-prof-flamegraph.rb
70
+ - lib/ruby-prof/printers/flame_graph_printer.rb
71
+ - ruby-prof-flamegraph.gemspec
72
+ homepage: http://github.com/dtinth/ruby-prof-flamegraph
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.2.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: ruby-prof printer that exports to fold stacks compatible with FlameGraph
96
+ test_files: []