ruby-prof 0.15.3 → 0.15.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +9 -0
  3. data/README.rdoc +24 -39
  4. data/bin/ruby-prof +1 -2
  5. data/doc/created.rid +8 -8
  6. data/doc/js/search_index.js +1 -1
  7. data/doc/js/search_index.js.gz +0 -0
  8. data/ext/ruby_prof/rp_measure.c +20 -28
  9. data/ext/ruby_prof/rp_measure.h +6 -6
  10. data/ext/ruby_prof/rp_measure_allocations.c +9 -2
  11. data/ext/ruby_prof/rp_measure_memory.c +4 -0
  12. data/lib/ruby-prof.rb +9 -20
  13. data/lib/ruby-prof/compatibility.rb +12 -4
  14. data/lib/ruby-prof/printers/flat_printer.rb +1 -0
  15. data/lib/ruby-prof/version.rb +1 -1
  16. data/test/thread_test.rb +7 -4
  17. metadata +2 -54
  18. data/doc/LICENSE.html +0 -114
  19. data/doc/README_rdoc.html +0 -607
  20. data/doc/Rack.html +0 -95
  21. data/doc/Rack/RubyProf.html +0 -264
  22. data/doc/RubyProf.html +0 -965
  23. data/doc/RubyProf/AbstractPrinter.html +0 -546
  24. data/doc/RubyProf/AggregateCallInfo.html +0 -537
  25. data/doc/RubyProf/CallInfo.html +0 -468
  26. data/doc/RubyProf/CallInfoPrinter.html +0 -120
  27. data/doc/RubyProf/CallInfoVisitor.html +0 -200
  28. data/doc/RubyProf/CallStackPrinter.html +0 -1604
  29. data/doc/RubyProf/CallTreePrinter.html +0 -359
  30. data/doc/RubyProf/Cmd.html +0 -624
  31. data/doc/RubyProf/DotPrinter.html +0 -257
  32. data/doc/RubyProf/FlatPrinter.html +0 -163
  33. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -201
  34. data/doc/RubyProf/GraphHtmlPrinter.html +0 -575
  35. data/doc/RubyProf/GraphPrinter.html +0 -139
  36. data/doc/RubyProf/MethodInfo.html +0 -685
  37. data/doc/RubyProf/MultiPrinter.html +0 -358
  38. data/doc/RubyProf/Profile.html +0 -764
  39. data/doc/RubyProf/ProfileTask.html +0 -490
  40. data/doc/RubyProf/Thread.html +0 -199
  41. data/doc/examples/flat_txt.html +0 -149
  42. data/doc/examples/graph_html.html +0 -850
  43. data/doc/examples/graph_txt.html +0 -274
  44. data/doc/images/add.png +0 -0
  45. data/doc/images/arrow_up.png +0 -0
  46. data/doc/images/brick.png +0 -0
  47. data/doc/images/brick_link.png +0 -0
  48. data/doc/images/bug.png +0 -0
  49. data/doc/images/bullet_black.png +0 -0
  50. data/doc/images/bullet_toggle_minus.png +0 -0
  51. data/doc/images/bullet_toggle_plus.png +0 -0
  52. data/doc/images/date.png +0 -0
  53. data/doc/images/delete.png +0 -0
  54. data/doc/images/find.png +0 -0
  55. data/doc/images/macFFBgHack.png +0 -0
  56. data/doc/images/package.png +0 -0
  57. data/doc/images/page_green.png +0 -0
  58. data/doc/images/page_white_text.png +0 -0
  59. data/doc/images/page_white_width.png +0 -0
  60. data/doc/images/plugin.png +0 -0
  61. data/doc/images/ruby.png +0 -0
  62. data/doc/images/tag_blue.png +0 -0
  63. data/doc/images/tag_green.png +0 -0
  64. data/doc/images/transparent.png +0 -0
  65. data/doc/images/wrench.png +0 -0
  66. data/doc/images/wrench_orange.png +0 -0
  67. data/doc/images/zoom.png +0 -0
  68. data/doc/index.html +0 -633
  69. data/doc/table_of_contents.html +0 -859
@@ -1,274 +0,0 @@
1
- <!DOCTYPE html>
2
-
3
- <html>
4
- <head>
5
- <meta charset="UTF-8">
6
-
7
- <title>graph - ruby-prof</title>
8
-
9
- <script type="text/javascript">
10
- var rdoc_rel_prefix = "../";
11
- </script>
12
-
13
- <script src="../js/jquery.js"></script>
14
- <script src="../js/darkfish.js"></script>
15
-
16
- <link href="../css/fonts.css" rel="stylesheet">
17
- <link href="../css/rdoc.css" rel="stylesheet">
18
-
19
-
20
-
21
- <body id="top" role="document" class="file">
22
- <nav role="navigation">
23
- <div id="project-navigation">
24
- <div id="home-section" role="region" title="Quick navigation" class="nav-section">
25
- <h2>
26
- <a href="../index.html" rel="home">Home</a>
27
- </h2>
28
-
29
- <div id="table-of-contents-navigation">
30
- <a href="../table_of_contents.html#pages">Pages</a>
31
- <a href="../table_of_contents.html#classes">Classes</a>
32
- <a href="../table_of_contents.html#methods">Methods</a>
33
- </div>
34
- </div>
35
-
36
- <div id="search-section" role="search" class="project-section initially-hidden">
37
- <form action="#" method="get" accept-charset="utf-8">
38
- <div id="search-field-wrapper">
39
- <input id="search-field" role="combobox" aria-label="Search"
40
- aria-autocomplete="list" aria-controls="search-results"
41
- type="text" name="search" placeholder="Search" spellcheck="false"
42
- title="Type to search, Up and Down to navigate, Enter to load">
43
- </div>
44
-
45
- <ul id="search-results" aria-label="Search Results"
46
- aria-busy="false" aria-expanded="false"
47
- aria-atomic="false" class="initially-hidden"></ul>
48
- </form>
49
- </div>
50
-
51
- </div>
52
-
53
-
54
- <div class="nav-section">
55
- <h3>Table of Contents</h3>
56
-
57
- <ul class="link-list" role="directory">
58
- <li><a href="#label-Graph+Profiles">Graph Profiles</a>
59
- <li><a href="#label-Overview">Overview</a>
60
- <li><a href="#label-Parents">Parents</a>
61
- <li><a href="#label-Children">Children</a>
62
- </ul>
63
- </div>
64
-
65
-
66
- <div id="project-metadata">
67
- <div id="fileindex-section" class="nav-section">
68
- <h3>Pages</h3>
69
-
70
- <ul class="link-list">
71
-
72
- <li><a href="../LICENSE.html">LICENSE</a>
73
-
74
- <li><a href="../README_rdoc.html">README</a>
75
-
76
- <li><a href="../examples/flat_txt.html">flat</a>
77
-
78
- <li><a href="../examples/graph_html.html">graph.html</a>
79
-
80
- <li><a href="../examples/graph_txt.html">graph</a>
81
-
82
- </ul>
83
- </div>
84
-
85
- </div>
86
- </nav>
87
-
88
- <main role="main" aria-label="Page examples/graph.txt">
89
-
90
- <h1 id="label-Graph+Profiles">Graph Profiles<span><a href="#label-Graph+Profiles">&para;</a> <a href="#top">&uarr;</a></span></h1>
91
-
92
- <p>Graph profiles show how long each method runs, which methods call it and
93
- which methods it calls.</p>
94
-
95
- <p>As an example, here is the output from running printers_test.rb:</p>
96
-
97
- <p>Thread ID: 21277412</p>
98
-
99
- <pre> %total %self total self children calls Name
100
- --------------------------------------------------------------------------------
101
- 100.00% 0.00% 8.77 0.00 8.77 1 #toplevel
102
- 8.77 0.00 8.77 1/1 Object#run_primes
103
- --------------------------------------------------------------------------------
104
- 8.77 0.00 8.77 1/1 #toplevel
105
- 100.00% 0.00% 8.77 0.00 8.77 1 Object#run_primes
106
- 0.02 0.00 0.02 1/1 Object#make_random_array
107
- 2.09 0.00 2.09 1/1 Object#find_largest
108
- 6.66 0.00 6.66 1/1 Object#find_primes
109
- --------------------------------------------------------------------------------
110
- 6.63 4.06 2.56 500/501 Object#is_prime
111
- 2.09 0.00 2.09 1/501 Object#find_largest
112
- 99.48% 46.34% 8.72 4.06 4.66 501 Integer#upto
113
- 0.00 0.00 0.00 61/61 Array#[]
114
- 0.00 0.00 0.00 61/61 Fixnum#&gt;
115
- 2.09 2.09 0.00 61/61 Kernel.sleep
116
- 1.24 1.24 0.00 250862/250862 Fixnum#==
117
- 1.33 1.33 0.00 250862/250862 Fixnum#%
118
- --------------------------------------------------------------------------------
119
- 6.66 0.01 6.64 1/1 Object#find_primes
120
- 75.93% 0.17% 6.66 0.01 6.64 1 Array#select
121
- 6.64 0.01 6.63 500/500 Object#is_prime
122
- --------------------------------------------------------------------------------
123
- 6.66 0.00 6.66 1/1 Object#run_primes
124
- 75.93% 0.00% 6.66 0.00 6.66 1 Object#find_primes
125
- 6.66 0.01 6.64 1/1 Array#select
126
- --------------------------------------------------------------------------------
127
- 6.64 0.01 6.63 500/500 Array#select
128
- 75.76% 0.17% 6.64 0.01 6.63 500 Object#is_prime
129
- 0.00 0.00 0.00 500/501 Fixnum#-
130
- 6.63 4.06 2.56 500/501 Integer#upto
131
- --------------------------------------------------------------------------------
132
- 2.09 0.00 2.09 1/1 Object#run_primes
133
- 23.89% 0.00% 2.09 0.00 2.09 1 Object#find_largest
134
- 0.00 0.00 0.00 1/501 Fixnum#-
135
- 2.09 0.00 2.09 1/501 Integer#upto
136
- 0.00 0.00 0.00 1/1 Array#first
137
- 0.00 0.00 0.00 1/1 Array#length
138
- --------------------------------------------------------------------------------
139
- 2.09 2.09 0.00 61/61 Integer#upto
140
- 23.89% 23.89% 2.09 2.09 0.00 61 Kernel.sleep
141
- --------------------------------------------------------------------------------
142
- 1.33 1.33 0.00 250862/250862 Integer#upto
143
- 15.12% 15.12% 1.33 1.33 0.00 250862 Fixnum#%
144
- --------------------------------------------------------------------------------
145
- 1.24 1.24 0.00 250862/250862 Integer#upto
146
- 14.13% 14.13% 1.24 1.24 0.00 250862 Fixnum#==
147
- --------------------------------------------------------------------------------
148
- 0.02 0.00 0.02 1/1 Object#run_primes
149
- 0.18% 0.00% 0.02 0.00 0.02 1 Object#make_random_array
150
- 0.02 0.02 0.00 1/1 Array#each_index
151
- 0.00 0.00 0.00 1/1 Class#new
152
- --------------------------------------------------------------------------------
153
- 0.02 0.02 0.00 1/1 Object#make_random_array
154
- 0.18% 0.18% 0.02 0.02 0.00 1 Array#each_index
155
- 0.00 0.00 0.00 500/500 Kernel.rand
156
- 0.00 0.00 0.00 500/500 Array#[]=
157
- --------------------------------------------------------------------------------
158
- 0.00 0.00 0.00 500/501 Object#is_prime
159
- 0.00 0.00 0.00 1/501 Object#find_largest
160
- 0.00% 0.00% 0.00 0.00 0.00 501 Fixnum#-
161
- --------------------------------------------------------------------------------
162
- 0.00 0.00 0.00 1/1 Kernel.rand
163
- 0.00% 0.00% 0.00 0.00 0.00 1 Integer#to_int
164
- --------------------------------------------------------------------------------
165
- 0.00 0.00 0.00 1/1 Object#find_largest
166
- 0.00% 0.00% 0.00 0.00 0.00 1 Array#first
167
- --------------------------------------------------------------------------------
168
- 0.00 0.00 0.00 1/1 Class#new
169
- 0.00% 0.00% 0.00 0.00 0.00 1 Array#initialize
170
- --------------------------------------------------------------------------------
171
- 0.00 0.00 0.00 1/1 Object#find_largest
172
- 0.00% 0.00% 0.00 0.00 0.00 1 Array#length
173
- --------------------------------------------------------------------------------
174
- 0.00 0.00 0.00 1/1 Object#make_random_array
175
- 0.00% 0.00% 0.00 0.00 0.00 1 Class#new
176
- 0.00 0.00 0.00 1/1 Array#initialize
177
- --------------------------------------------------------------------------------
178
- 0.00 0.00 0.00 61/61 Integer#upto
179
- 0.00% 0.00% 0.00 0.00 0.00 61 Fixnum#&gt;
180
- --------------------------------------------------------------------------------
181
- 0.00 0.00 0.00 61/61 Integer#upto
182
- 0.00% 0.00% 0.00 0.00 0.00 61 Array#[]
183
- --------------------------------------------------------------------------------
184
- 0.00 0.00 0.00 500/500 Array#each_index
185
- 0.00% 0.00% 0.00 0.00 0.00 500 Array#[]=
186
- --------------------------------------------------------------------------------
187
- 0.00 0.00 0.00 500/500 Array#each_index
188
- 0.00% 0.00% 0.00 0.00 0.00 500 Kernel.rand
189
- 0.00 0.00 0.00 1/1 Integer#to_int</pre>
190
-
191
- <h2 id="label-Overview">Overview<span><a href="#label-Overview">&para;</a> <a href="#top">&uarr;</a></span></h2>
192
-
193
- <p>Dashed lines divide the report into entries, with one entry per method.
194
- Entries are sorted by total time which is the time spent in the method
195
- plus its children.</p>
196
-
197
- <p>Each entry has a primary line demarked by values in the %total and %self
198
- columns. The primary line represents the method being profiled. Lines
199
- above it are the methods that called this method (parents) while the lines
200
- below it are the methods it called (children).</p>
201
-
202
- <p>All values are in seconds. For the primary line, the columns represent:</p>
203
-
204
- <pre>%total - The percentage of time spent in this method and its children
205
- %self - The percentage of time spent in this method
206
- total - The time spent in this method and its children.
207
- self - The time spent in this method.
208
- children - The time spent in this method&#39;s children.
209
- calls - The number of times this method was called.
210
- name - The name of the method.</pre>
211
-
212
- <p>The interpretation of method names is:</p>
213
- <ul><li>
214
- <p>toplevel - The root method that calls all other methods</p>
215
- </li><li>
216
- <p>MyObject#test - An instance method “test” of the class “MyObject”</p>
217
- </li><li>
218
- <p>&lt;Object:MyObject&gt;#test - The &lt;&gt; characters indicate a singleton
219
- method on a singleton class.</p>
220
- </li></ul>
221
-
222
- <p>For example, we see that 99.48% of the time was spent in Integer#upto and
223
- its children. Of that time, 4.06 seconds was spent in Integer#upto itself
224
- and 4.66 in its children. Overall, Integer#upto was called 501 times.</p>
225
-
226
- <h2 id="label-Parents">Parents<span><a href="#label-Parents">&para;</a> <a href="#top">&uarr;</a></span></h2>
227
-
228
- <p>In each entry, the lines above the primary line are the methods that
229
- called the current method. If the current method is a root method then no
230
- parents are shown.</p>
231
-
232
- <p>For parent lines, the columns represent:</p>
233
-
234
- <pre>total - The time spent in the current method and it children on behalf of the parent method.
235
- self - The time spent in this method on behalf of the parent method.
236
- children - The time spent in this method&#39;s children on behalf of the parent.
237
- calls - The number of times the parent method called this child</pre>
238
-
239
- <p>Looking at Integer#upto again, we see that it was called 500 times from
240
- Object#is_prime and 1 time from find_largest. Of the 8.72 total seconds
241
- spent in Integer#upto, 6.63 were done for Object#is_prime and 2.09 for
242
- Object#find_largest.</p>
243
-
244
- <h2 id="label-Children">Children<span><a href="#label-Children">&para;</a> <a href="#top">&uarr;</a></span></h2>
245
-
246
- <p>In each entry, the lines below the primary line are the methods that the
247
- current method called. If the current method is a leaf method then no
248
- children are shown.</p>
249
-
250
- <p>For children lines, the columns represent:</p>
251
-
252
- <pre>total - The time spent in the child, and its children, on behalf of the current method
253
- self - The time spent in the child on behalf of the current method.
254
- children - The time spent in the child&#39;s children (ie, granchildren) in behalf of the current method
255
- calls - The number of times the child method was called by the current method.</pre>
256
-
257
- <p>Taking our example of Integer#upto, we see that it called five other
258
- methods - Array#[], Fixnum#&gt;, Kernel.sleep, Fixnum#= and Fixnum#%.
259
- Looking at Kernel.sleep, we see that its spent 2.09 seconds working for
260
- Integer#upto and its children spent 0 time working for Integer#upto. To
261
- see the overall time Kernel.sleep took we would have to look up its entry
262
- in the graph table.</p>
263
-
264
- <p></p>
265
- </main>
266
-
267
-
268
-
269
- <footer id="validator-badges" role="contentinfo">
270
- <p><a href="http://validator.w3.org/check/referer">Validate</a>
271
- <p>Generated by <a href="http://docs.seattlerb.org/rdoc/">RDoc</a> 4.2.0.
272
- <p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
273
- </footer>
274
-
data/doc/images/add.png DELETED
Binary file
Binary file
data/doc/images/brick.png DELETED
Binary file
Binary file
data/doc/images/bug.png DELETED
Binary file
Binary file
Binary file
Binary file
data/doc/images/date.png DELETED
Binary file
Binary file
data/doc/images/find.png DELETED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/doc/images/ruby.png DELETED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/doc/images/zoom.png DELETED
Binary file
data/doc/index.html DELETED
@@ -1,633 +0,0 @@
1
- <!DOCTYPE html>
2
-
3
- <html>
4
- <head>
5
- <meta charset="UTF-8">
6
-
7
- <title>ruby-prof</title>
8
-
9
- <script type="text/javascript">
10
- var rdoc_rel_prefix = "./";
11
- </script>
12
-
13
- <script src="./js/jquery.js"></script>
14
- <script src="./js/darkfish.js"></script>
15
-
16
- <link href="./css/fonts.css" rel="stylesheet">
17
- <link href="./css/rdoc.css" rel="stylesheet">
18
-
19
-
20
-
21
- <body id="top" role="document" class="file">
22
- <nav role="navigation">
23
- <div id="project-navigation">
24
- <div id="home-section" role="region" title="Quick navigation" class="nav-section">
25
- <h2>
26
- <a href="./index.html" rel="home">Home</a>
27
- </h2>
28
-
29
- <div id="table-of-contents-navigation">
30
- <a href="./table_of_contents.html#pages">Pages</a>
31
- <a href="./table_of_contents.html#classes">Classes</a>
32
- <a href="./table_of_contents.html#methods">Methods</a>
33
- </div>
34
- </div>
35
-
36
-
37
- <div id="search-section" role="search" class="project-section initially-hidden">
38
- <form action="#" method="get" accept-charset="utf-8">
39
- <div id="search-field-wrapper">
40
- <input id="search-field" role="combobox" aria-label="Search"
41
- aria-autocomplete="list" aria-controls="search-results"
42
- type="text" name="search" placeholder="Search" spellcheck="false"
43
- title="Type to search, Up and Down to navigate, Enter to load">
44
- </div>
45
-
46
- <ul id="search-results" aria-label="Search Results"
47
- aria-busy="false" aria-expanded="false"
48
- aria-atomic="false" class="initially-hidden"></ul>
49
- </form>
50
- </div>
51
-
52
- </div>
53
-
54
- <div id="project-metadata">
55
- <div id="fileindex-section" class="nav-section">
56
- <h3>Pages</h3>
57
-
58
- <ul class="link-list">
59
-
60
- <li><a href="./LICENSE.html">LICENSE</a>
61
-
62
- <li><a href="./README_rdoc.html">README</a>
63
-
64
- <li><a href="./examples/flat_txt.html">flat</a>
65
-
66
- <li><a href="./examples/graph_html.html">graph.html</a>
67
-
68
- <li><a href="./examples/graph_txt.html">graph</a>
69
-
70
- </ul>
71
- </div>
72
-
73
- <div id="classindex-section" class="nav-section">
74
- <h3>Class and Module Index</h3>
75
-
76
- <ul class="link-list">
77
-
78
- <li><a href="./Rack.html">Rack</a>
79
-
80
- <li><a href="./Rack/RubyProf.html">Rack::RubyProf</a>
81
-
82
- <li><a href="./RubyProf.html">RubyProf</a>
83
-
84
- <li><a href="./RubyProf/AbstractPrinter.html">RubyProf::AbstractPrinter</a>
85
-
86
- <li><a href="./RubyProf/AggregateCallInfo.html">RubyProf::AggregateCallInfo</a>
87
-
88
- <li><a href="./RubyProf/CallInfo.html">RubyProf::CallInfo</a>
89
-
90
- <li><a href="./RubyProf/CallInfoPrinter.html">RubyProf::CallInfoPrinter</a>
91
-
92
- <li><a href="./RubyProf/CallInfoVisitor.html">RubyProf::CallInfoVisitor</a>
93
-
94
- <li><a href="./RubyProf/CallStackPrinter.html">RubyProf::CallStackPrinter</a>
95
-
96
- <li><a href="./RubyProf/CallTreePrinter.html">RubyProf::CallTreePrinter</a>
97
-
98
- <li><a href="./RubyProf/Cmd.html">RubyProf::Cmd</a>
99
-
100
- <li><a href="./RubyProf/DotPrinter.html">RubyProf::DotPrinter</a>
101
-
102
- <li><a href="./RubyProf/FlatPrinter.html">RubyProf::FlatPrinter</a>
103
-
104
- <li><a href="./RubyProf/FlatPrinterWithLineNumbers.html">RubyProf::FlatPrinterWithLineNumbers</a>
105
-
106
- <li><a href="./RubyProf/GraphHtmlPrinter.html">RubyProf::GraphHtmlPrinter</a>
107
-
108
- <li><a href="./RubyProf/GraphPrinter.html">RubyProf::GraphPrinter</a>
109
-
110
- <li><a href="./RubyProf/MethodInfo.html">RubyProf::MethodInfo</a>
111
-
112
- <li><a href="./RubyProf/MultiPrinter.html">RubyProf::MultiPrinter</a>
113
-
114
- <li><a href="./RubyProf/Profile.html">RubyProf::Profile</a>
115
-
116
- <li><a href="./RubyProf/ProfileTask.html">RubyProf::ProfileTask</a>
117
-
118
- <li><a href="./RubyProf/Thread.html">RubyProf::Thread</a>
119
-
120
- </ul>
121
- </div>
122
-
123
- </div>
124
- </nav>
125
-
126
- <main role="main">
127
-
128
-
129
- <h1 id="label-ruby-prof">ruby-prof<span><a href="#label-ruby-prof">&para;</a> <a href="#top">&uarr;</a></span></h1>
130
-
131
- <p><a href="https://travis-ci.org/ruby-prof/ruby-prof"><img
132
- src="https://travis-ci.org/ruby-prof/ruby-prof.png?branch=master"
133
- alt="Build Status" /></a></p>
134
-
135
- <h2 id="label-Overview">Overview<span><a href="#label-Overview">&para;</a> <a href="#top">&uarr;</a></span></h2>
136
-
137
- <p>ruby-prof is a fast code profiler for Ruby. Its features include:</p>
138
- <ul><li>
139
- <p>Speed - it is a C extension and therefore many times faster than the
140
- standard Ruby profiler.</p>
141
- </li><li>
142
- <p>Modes - Ruby prof can measure a number of different parameters, including
143
- call times, memory usage and object allocations.</p>
144
- </li><li>
145
- <p>Reports - can generate text and cross-referenced html reports</p>
146
- <ul><li>
147
- <p>Flat Profiles - similar to the reports generated by the standard Ruby
148
- profiler</p>
149
- </li><li>
150
- <p>Graph profiles - similar to GProf, these show how long a method runs, which
151
- methods call it and which methods it calls.</p>
152
- </li><li>
153
- <p>Call tree profiles - outputs results in the calltree format suitable for
154
- the KCacheGrind profiling tool.</p>
155
- </li><li>
156
- <p>Many more – see reports section of this <a
157
- href="README_rdoc.html">README</a>.</p>
158
- </li></ul>
159
- </li><li>
160
- <p>Threads - supports profiling multiple threads simultaneously</p>
161
- </li></ul>
162
-
163
- <h2 id="label-Requirements">Requirements<span><a href="#label-Requirements">&para;</a> <a href="#top">&uarr;</a></span></h2>
164
-
165
- <p>ruby-prof requires Ruby 1.9.3 or higher.</p>
166
-
167
- <p>If you are running Linux or Unix you&#39;ll need a C compiler so the
168
- extension can be compiled when it is installed.</p>
169
-
170
- <p>If you are running Windows, then you may need to install the Windows
171
- specific RubyGem which includes an already built extension (see Install
172
- section).</p>
173
-
174
- <h2 id="label-Install">Install<span><a href="#label-Install">&para;</a> <a href="#top">&uarr;</a></span></h2>
175
-
176
- <p>The easiest way to install ruby-prof is by using Ruby Gems. To install:</p>
177
-
178
- <pre class="ruby"><span class="ruby-identifier">gem</span> <span class="ruby-identifier">install</span> <span class="ruby-identifier">ruby</span><span class="ruby-operator">-</span><span class="ruby-identifier">prof</span>
179
- </pre>
180
-
181
- <p>If you&#39;re on windows then please install the devkit first so that it
182
- can compile.</p>
183
-
184
- <h2 id="label-Usage">Usage<span><a href="#label-Usage">&para;</a> <a href="#top">&uarr;</a></span></h2>
185
-
186
- <p>There are two ways of running ruby-prof, via the command line or via its
187
- API.</p>
188
-
189
- <h3 id="label-ruby-prof+executable">ruby-prof executable<span><a href="#label-ruby-prof+executable">&para;</a> <a href="#top">&uarr;</a></span></h3>
190
-
191
- <p>The first is to use ruby-prof to run the Ruby program you want to profile.
192
- For more information refer to the documentation of the ruby-prof command.</p>
193
-
194
- <h3 id="label-ruby-prof+API">ruby-prof API<span><a href="#label-ruby-prof+API">&para;</a> <a href="#top">&uarr;</a></span></h3>
195
-
196
- <p>The second way is to use the ruby-prof API to profile particular segments
197
- of code.</p>
198
-
199
- <pre>require &#39;ruby-prof&#39;
200
-
201
- # Profile the code
202
- RubyProf.start
203
- ...
204
- [code to profile]
205
- ...
206
- result = RubyProf.stop
207
-
208
- # Print a flat profile to text
209
- printer = RubyProf::FlatPrinter.new(result)
210
- printer.print(STDOUT)</pre>
211
-
212
- <p>Alternatively, you can use a block to tell ruby-prof what to profile:</p>
213
-
214
- <pre>require &#39;ruby-prof&#39;
215
-
216
- # Profile the code
217
- result = RubyProf.profile do
218
- ...
219
- [code to profile]
220
- ...
221
- end
222
-
223
- # Print a graph profile to text
224
- printer = RubyProf::GraphPrinter.new(result)
225
- printer.print(STDOUT, {})</pre>
226
-
227
- <p>ruby-prof also supports pausing and resuming profiling runs.</p>
228
-
229
- <pre>require &#39;ruby-prof&#39;
230
-
231
- # Profile the code
232
- RubyProf.start
233
- [code to profile]
234
- RubyProf.pause
235
- [other code]
236
- RubyProf.resume
237
- [code to profile]
238
- result = RubyProf.stop</pre>
239
-
240
- <p>Note that resume will automatically call start if a profiling run has not
241
- yet started. In addition, resume can also take a block:</p>
242
-
243
- <pre>require &#39;ruby-prof&#39;
244
-
245
- # Profile the code
246
- RubyProf.resume do
247
- [code to profile]
248
- end
249
-
250
- data = RubyProf.stop</pre>
251
-
252
- <p>With this usage, resume will automatically call pause at the end of the
253
- block.</p>
254
-
255
- <h2 id="label-Method+and+Thread+Elimination">Method and Thread Elimination<span><a href="#label-Method+and+Thread+Elimination">&para;</a> <a href="#top">&uarr;</a></span></h2>
256
-
257
- <p>ruby-prof supports eliminating specific methods and threads from profiling
258
- results. This is useful for reducing connectivity in the call graph, making
259
- it easier to identify the source of performance problems when using a graph
260
- printer.</p>
261
-
262
- <p>For example, consider Integer#times: it&#39;s hardly ever useful to know
263
- how much time is spent in the method itself. We&#39;re much more interested
264
- in how much the passed in block contributes to the time spent in the method
265
- which contains the Integer#times call.</p>
266
-
267
- <p>Methods are eliminated from the collected data by calling
268
- `eliminate_methods!` on the profiling result, before submitting it to a
269
- printer.</p>
270
-
271
- <pre class="ruby"><span class="ruby-identifier">result</span> = <span class="ruby-constant">RubyProf</span>.<span class="ruby-identifier">stop</span>
272
- <span class="ruby-identifier">result</span>.<span class="ruby-identifier">eliminate_methods!</span>([<span class="ruby-node">/Integer#times/</span>])
273
- </pre>
274
-
275
- <p>The argument given to `eliminate_methods!` is either an array of regular
276
- expressions, or the name of a file containing a list of regular expressions
277
- (line separated text).</p>
278
-
279
- <p>After eliminating methods the resulting profile will appear exactly as if
280
- those methods had been inlined at their call sites.</p>
281
-
282
- <p>In a similar manner, threads can be excluded so they are not profiled at
283
- all. To do this, pass an array of threads to exclude to ruby-prof:</p>
284
-
285
- <pre class="ruby"><span class="ruby-constant">RubyProf</span><span class="ruby-operator">::</span><span class="ruby-identifier">exclude_threads</span> = [ <span class="ruby-identifier">thread2</span> ]
286
- <span class="ruby-constant">RubyProf</span>.<span class="ruby-identifier">start</span>
287
- </pre>
288
-
289
- <p>Note that the excluded threads must be specified <strong>before</strong>
290
- profiling.</p>
291
-
292
- <h2 id="label-Benchmarking+full+load+time+including+rubygems+startup+cost">Benchmarking full load time including rubygems startup cost<span><a href="#label-Benchmarking+full+load+time+including+rubygems+startup+cost">&para;</a> <a href="#top">&uarr;</a></span></h2>
293
-
294
- <p>If you want to get a more accurate measurement of what takes all of a
295
- gem&#39;s bin/xxx command to load, you may want to also measure
296
- rubygems&#39; startup penalty. You can do this by calling into
297
- bin/ruby-prof directly, ex:</p>
298
-
299
- <p>$ gem which ruby-prof</p>
300
-
301
- <pre>g:/192/lib/ruby/gems/1.9.1/gems/ruby-prof-0.10.2/lib/ruby-prof.rb</pre>
302
-
303
- <p>now run it thus (substitute lib/ruby-prof.rb with bin/ruby-prof):</p>
304
-
305
- <p>$ ruby g:/192/lib/ruby/gems/1.9.1/gems/ruby-prof-0.10.2/bin/ruby-prof
306
- g:192binsome_installed_gem_command</p>
307
-
308
- <p>or</p>
309
-
310
- <p>$ ruby g:/192/lib/ruby/gems/1.9.1/gems/ruby-prof-0.10.2/bin/ruby-prof
311
- ./some_file_that_does_a_require_rubygems_at_the_beginning.rb</p>
312
-
313
- <h2 id="label-Profiling+Rails">Profiling Rails<span><a href="#label-Profiling+Rails">&para;</a> <a href="#top">&uarr;</a></span></h2>
314
-
315
- <p>To profile a Rails application it is vital to run it using production like
316
- settings (cache classes, cache view lookups, etc.). Otherwise, Rail&#39;s
317
- dependency loading code will overwhelm any time spent in the application
318
- itself (our tests show that Rails dependency loading causes a roughly 6x
319
- slowdown). The best way to do this is create a new Rails environment,
320
- profile.rb.</p>
321
-
322
- <p>So to profile Rails:</p>
323
- <ol><li>
324
- <p>Create a new profile.rb environment. Make sure to turn on cache_classes
325
- and cache_template_loading. Otherwise your profiling results will be
326
- overwhelemed by the time Rails spends loading required files. You should
327
- likely turn off caching.</p>
328
- </li><li>
329
- <p>Add the ruby-prof to your gemfile:</p>
330
-
331
- <pre class="ruby"><span class="ruby-identifier">group</span> :<span class="ruby-identifier">profile</span> <span class="ruby-keyword">do</span>
332
- <span class="ruby-identifier">gem</span> <span class="ruby-string">&#39;ruby-prof&#39;</span>
333
- <span class="ruby-keyword">end</span>
334
- </pre>
335
- </li><li>
336
- <p>Add the ruby prof rack adapter to your middleware stack. One way to do
337
- this is by adding the following code to config.ru:</p>
338
-
339
- <pre class="ruby"><span class="ruby-keyword">if</span> <span class="ruby-constant">Rails</span>.<span class="ruby-identifier">env</span>.<span class="ruby-identifier">profile?</span>
340
- <span class="ruby-identifier">use</span> <span class="ruby-constant">Rack</span><span class="ruby-operator">::</span><span class="ruby-constant">RubyProf</span>, :<span class="ruby-identifier">path</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-string">&#39;/temp/profile&#39;</span>
341
- <span class="ruby-keyword">end</span>
342
- </pre>
343
-
344
- <p>The path is where you want profiling results to be stored. By default the
345
- rack adapter will generate a html call graph report and flat text report.</p>
346
- </li><li>
347
- <p>Now make a request to your running server. New profiling information will
348
- be generated for each request. Note that each request will overwrite the
349
- profiling reports created by the previous request!</p>
350
- </li></ol>
351
-
352
- <h2 id="label-Reports">Reports<span><a href="#label-Reports">&para;</a> <a href="#top">&uarr;</a></span></h2>
353
-
354
- <p>ruby-prof can generate a number of different reports:</p>
355
- <ul><li>
356
- <p>Flat Reports</p>
357
- </li><li>
358
- <p>Graph Reports</p>
359
- </li><li>
360
- <p>HTML Graph Reports</p>
361
- </li><li>
362
- <p>Call graphs</p>
363
- </li><li>
364
- <p>Call stack reports</p>
365
- </li><li>
366
- <p>More!</p>
367
- </li></ul>
368
-
369
- <p>Flat profiles show the overall time spent in each method. They are a good
370
- of quickly identifying which methods take the most time. An example of a
371
- flat profile and an explanation can be found in <a
372
- href="http://github.com/ruby-prof/ruby-prof/tree/master/examples/flat.txt">examples/flat.txt</a>.</p>
373
-
374
- <p>There are several varieties of these – run $ ruby-prof –help</p>
375
-
376
- <p>Graph profiles also show the overall time spent in each method. In
377
- addition, they also show which methods call the current method and which
378
- methods its calls. Thus they are good for understanding how methods gets
379
- called and provide insight into the flow of your program. An example text
380
- graph profile is located at <a
381
- href="http://github.com/ruby-prof/ruby-prof/tree/master/examples/graph.txt">examples/graph.txt</a>.</p>
382
-
383
- <p>HTML Graph profiles are the same as graph profiles, except output is
384
- generated in hyper-linked HTML. Since graph profiles can be quite large,
385
- the embedded links make it much easier to navigate the results. An example
386
- html graph profile is located at <a
387
- href="http://github.com/ruby-prof/ruby-prof/tree/master/examples/graph.html">examples/graph.html</a>.</p>
388
-
389
- <p>Call graphs output results in the calltree profile format which is used by
390
- KCachegrind. Call graph support was generously donated by Carl Shimer. More
391
- information about the format can be found at the <a
392
- href="http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat">KCachegrind</a>
393
- site.</p>
394
-
395
- <p>Call stack reports produce a HTML visualization of the time spent in each
396
- execution path of the profiled code. An example can be found at <a
397
- href="http://github.com/ruby-prof/ruby-prof/tree/master/examples/call_stack.html">examples/stack.html</a>.</p>
398
-
399
- <p>Another good example: [<a
400
- href="http://twitpic.com/28z94a">twitpic.com/28z94a</a>]</p>
401
-
402
- <p>Finally, there&#39;s a so called MultiPrinter which can generate several
403
- reports in one profiling run. See <a
404
- href="http://github.com/ruby-prof/ruby-prof/tree/master/examples/multi.stack.html">examples/multi.stack.html</a>.</p>
405
-
406
- <p>There is also a graphviz .dot visualiser.</p>
407
-
408
- <h2 id="label-Printers">Printers<span><a href="#label-Printers">&para;</a> <a href="#top">&uarr;</a></span></h2>
409
-
410
- <p>Reports are created by printers. Supported printers include:</p>
411
- <ul><li>
412
- <p><a href="RubyProf/FlatPrinter.html">RubyProf::FlatPrinter</a> - Creates a
413
- flat report in text format</p>
414
- </li><li>
415
- <p><a
416
- href="RubyProf/FlatPrinterWithLineNumbers.html">RubyProf::FlatPrinterWithLineNumbers</a>
417
- - same as above but more verbose</p>
418
- </li><li>
419
- <p><a href="RubyProf/GraphPrinter.html">RubyProf::GraphPrinter</a> - Creates a
420
- call graph report in text format</p>
421
- </li><li>
422
- <p><a href="RubyProf/GraphHtmlPrinter.html">RubyProf::GraphHtmlPrinter</a> -
423
- Creates a call graph report in HTML (separate files per thread)</p>
424
- </li><li>
425
- <p><a href="RubyProf/DotPrinter.html">RubyProf::DotPrinter</a> - Creates a
426
- call graph report in GraphViz&#39;s DOT format which can be converted to an
427
- image</p>
428
- </li><li>
429
- <p><a href="RubyProf/CallTreePrinter.html">RubyProf::CallTreePrinter</a> -
430
- Creates a call tree report compatible with KCachegrind.</p>
431
- </li><li>
432
- <p><a href="RubyProf/CallStackPrinter.html">RubyProf::CallStackPrinter</a> -
433
- Creates a HTML visualization of the Ruby stack</p>
434
- </li><li>
435
- <p><a href="RubyProf/MultiPrinter.html">RubyProf::MultiPrinter</a> - Uses the
436
- other printers to create several reports in one profiling run</p>
437
- </li><li>
438
- <p>More!</p>
439
- </li></ul>
440
-
441
- <p>To use a printer:</p>
442
-
443
- <pre>...
444
- result = RubyProf.stop
445
- printer = RubyProf::GraphPrinter.new(result)
446
- printer.print(STDOUT, :min_percent =&gt; 2)</pre>
447
-
448
- <p>The first parameter is any writable IO object such as STDOUT or a file. The
449
- second parameter, specifies the minimum percentage a method must take to be
450
- printed. Percentages should be specified as integers in the range 0 to
451
- 100. For more information please see the documentation for the different
452
- printers.</p>
453
-
454
- <p>The other option is :print_file =&gt; true (default false), which adds the
455
- filename to the output (GraphPrinter only).</p>
456
-
457
- <p>The MultiPrinter differs from the other printers in that it requires a
458
- directory path and a basename for the files it produces.</p>
459
-
460
- <pre class="ruby"><span class="ruby-identifier">printer</span> = <span class="ruby-constant">RubyProf</span><span class="ruby-operator">::</span><span class="ruby-constant">MultiPrinter</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">result</span>)
461
- <span class="ruby-identifier">printer</span>.<span class="ruby-identifier">print</span>(:<span class="ruby-identifier">path</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-string">&quot;.&quot;</span>, :<span class="ruby-identifier">profile</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-string">&quot;profile&quot;</span>)
462
- </pre>
463
-
464
- <h2 id="label-Measurements">Measurements<span><a href="#label-Measurements">&para;</a> <a href="#top">&uarr;</a></span></h2>
465
-
466
- <p>Depending on the mode and platform, ruby-prof can measure various aspects
467
- of a Ruby program. Supported measurements include:</p>
468
- <ul><li>
469
- <p>process time (RubyProf::PROCESS_TIME)</p>
470
- </li><li>
471
- <p>wall time (RubyProf::WALL_TIME)</p>
472
- </li><li>
473
- <p>cpu time (RubyProf::CPU_TIME)</p>
474
- </li><li>
475
- <p>object allocations (RubyProf::ALLOCATIONS)</p>
476
- </li><li>
477
- <p>memory usage (RubyProf::MEMORY)</p>
478
- </li><li>
479
- <p>garbage collections runs (RubyProf::GC_RUNS)</p>
480
- </li><li>
481
- <p>garbage collection time (RubyProf::GC_TIME)</p>
482
- </li></ul>
483
-
484
- <p>Process time measures the time used by a process between any two moments.
485
- It is unaffected by other processes concurrently running on the system.
486
- Note that Windows does not support measuring process times - therefore,
487
- measurements on Windows defaults to wall time.</p>
488
-
489
- <p>Wall time measures the real-world time elapsed between any two moments. If
490
- there are other processes concurrently running on the system that use
491
- significant CPU or disk time during a profiling run then the reported
492
- results will be too large.</p>
493
-
494
- <p>CPU time uses the CPU clock counter to measure time. The returned values
495
- are dependent on the correctly setting the CPU&#39;s frequency. This mode
496
- is only supported on Pentium or PowerPC platforms (linux only).</p>
497
-
498
- <p>Object allocation reports show how many objects each method in a program
499
- allocates. This support was added by Sylvain Joyeux and requires a patched
500
- Ruby interpreter. See below.</p>
501
-
502
- <p>Memory usage reports show how much memory each method in a program uses.
503
- This support was added by Alexander Dymo and requires a patched Ruby
504
- interpreter. See below.</p>
505
-
506
- <p>Garbage collection runs report how many times Ruby&#39;s garbage collector
507
- is invoked during a profiling session. This support was added by Jeremy
508
- Kemper and requires a patched Ruby interpreter. See below.</p>
509
-
510
- <p>Garbage collection time reports how much time is spent in Ruby&#39;s
511
- garbage collector during a profiling session. This support was added by
512
- Jeremy Kemper and requires a patched Ruby interpreter. See below.</p>
513
-
514
- <p>Ruby patches: all of the patches to Ruby are included in the railsexpress
515
- patchsets for rvm, see <a
516
- href="https://github.com/skaes/rvm-patchsets">github.com/skaes/rvm-patchsets</a></p>
517
-
518
- <p>To set the measurement:</p>
519
- <ul><li>
520
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
521
- RubyProf::PROCESS_TIME</p>
522
- </li><li>
523
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
524
- RubyProf::WALL_TIME</p>
525
- </li><li>
526
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
527
- RubyProf::CPU_TIME</p>
528
- </li><li>
529
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
530
- RubyProf::ALLOCATIONS</p>
531
- </li><li>
532
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
533
- RubyProf::MEMORY</p>
534
- </li><li>
535
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
536
- RubyProf::GC_RUNS</p>
537
- </li><li>
538
- <p><a href="RubyProf.html#method-c-measure_mode">RubyProf.measure_mode</a> =
539
- RubyProf::GC_TIME</p>
540
- </li></ul>
541
-
542
- <p>The default value is RubyProf::PROCESS_TIME.</p>
543
-
544
- <p>You may also specify the measure_mode by using the RUBY_PROF_MEASURE_MODE
545
- environment variable:</p>
546
- <ul><li>
547
- <p>export RUBY_PROF_MEASURE_MODE=process</p>
548
- </li><li>
549
- <p>export RUBY_PROF_MEASURE_MODE=wall</p>
550
- </li><li>
551
- <p>export RUBY_PROF_MEASURE_MODE=cpu</p>
552
- </li><li>
553
- <p>export RUBY_PROF_MEASURE_MODE=allocations</p>
554
- </li><li>
555
- <p>export RUBY_PROF_MEASURE_MODE=memory</p>
556
- </li><li>
557
- <p>export RUBY_PROF_MEASURE_MODE=gc_runs</p>
558
- </li><li>
559
- <p>export RUBY_PROF_MEASURE_MODE=gc_time</p>
560
- </li></ul>
561
-
562
- <p>On Linux, process time is measured using the clock method provided by the C
563
- runtime library. Note that the clock method does not report time spent in
564
- the kernel or child processes and therefore does not measure time spent in
565
- methods such as Kernel.sleep method. If you need to measure these values,
566
- then use wall time. Wall time is measured using the gettimeofday kernel
567
- method.</p>
568
-
569
- <p>On Windows, timings default to wall times. If you set the clock mode to
570
- PROCESS_TIME, then timing are read using the clock method provided by the C
571
- runtime library. Note though, these values are wall times on Windows and
572
- not process times like on Linux. Wall time is measured using the
573
- GetLocalTime API.</p>
574
-
575
- <p>If you use wall time, the results will be affected by other processes
576
- running on your computer, network delays, disk access, etc. As result, for
577
- the best results, try to make sure your computer is only performing your
578
- profiling run and is otherwise quiescent.</p>
579
-
580
- <p>On both platforms, cpu time is measured using the RDTSC assembly function
581
- provided by the Pentium and PowerPC platforms. CPU time is dependent on the
582
- cpu&#39;s frequency. On Linux, ruby-prof attempts to read this value from
583
- “/proc/cpuinfo.” On Windows, you must manually specify the clock
584
- frequency. This can be done using the RUBY_PROF_CPU_FREQUENCY environment
585
- variable:</p>
586
-
587
- <pre>export RUBY_PROF_CPU_FREQUENCY=&lt;value&gt;</pre>
588
-
589
- <p>You can also directly set the cpu frequency by calling:</p>
590
-
591
- <pre>RubyProf.cpu_frequency = &lt;value&gt;</pre>
592
-
593
- <h2 id="label-Multi-threaded+Applications">Multi-threaded Applications<span><a href="#label-Multi-threaded+Applications">&para;</a> <a href="#top">&uarr;</a></span></h2>
594
-
595
- <p>Unfortunately, Ruby does not provide an internal api for detecting thread
596
- context switches in 1.8. As a result, the timings ruby-prof reports for
597
- each thread may be slightly inaccurate. In particular, this will happen
598
- for newly spawned threads that go to sleep immediately (their first call).
599
- For instance, if you use Ruby&#39;s timeout library to wait for 2 seconds,
600
- the 2 seconds will be assigned to the foreground thread and not the newly
601
- created background thread. These errors can largely be avoided if the
602
- background thread performs any operation before going to sleep.</p>
603
-
604
- <h2 id="label-Performance">Performance<span><a href="#label-Performance">&para;</a> <a href="#top">&uarr;</a></span></h2>
605
-
606
- <p>Significant effort has been put into reducing ruby-prof&#39;s overhead as
607
- much as possible. Our tests show that the overhead associated with
608
- profiling code varies considerably with the code being profiled. Most
609
- programs will run approximately twice as slow while highly recursive
610
- programs (like the fibonacci series test) will run three times slower.</p>
611
-
612
- <h2 id="label-License">License<span><a href="#label-License">&para;</a> <a href="#top">&uarr;</a></span></h2>
613
-
614
- <p>See <a href="LICENSE.html">LICENSE</a> for license information.</p>
615
-
616
- <h2 id="label-Development">Development<span><a href="#label-Development">&para;</a> <a href="#top">&uarr;</a></span></h2>
617
-
618
- <p>Code is located at <a
619
- href="https://github.com/ruby-prof/ruby-prof">github.com/ruby-prof/ruby-prof</a></p>
620
-
621
- <p>Google group/mailing list: <a
622
- href="http://groups.google.com/group/ruby-optimization">groups.google.com/group/ruby-optimization</a>
623
- or start a github issue.</p>
624
- </main>
625
-
626
-
627
-
628
- <footer id="validator-badges" role="contentinfo">
629
- <p><a href="http://validator.w3.org/check/referer">Validate</a>
630
- <p>Generated by <a href="http://docs.seattlerb.org/rdoc/">RDoc</a> 4.2.0.
631
- <p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
632
- </footer>
633
-