ruby-prof-flamegraph 0.2.0

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