ruby-prof 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +500 -482
  3. data/LICENSE +24 -24
  4. data/README.rdoc +487 -485
  5. data/Rakefile +113 -113
  6. data/bin/ruby-prof +345 -345
  7. data/bin/ruby-prof-check-trace +45 -45
  8. data/examples/flat.txt +50 -50
  9. data/examples/graph.dot +84 -84
  10. data/examples/graph.html +823 -823
  11. data/examples/graph.txt +139 -139
  12. data/examples/multi.flat.txt +23 -23
  13. data/examples/multi.graph.html +760 -760
  14. data/examples/multi.grind.dat +114 -114
  15. data/examples/multi.stack.html +547 -547
  16. data/examples/stack.html +547 -547
  17. data/ext/ruby_prof/extconf.rb +68 -68
  18. data/ext/ruby_prof/rp_call_info.c +425 -425
  19. data/ext/ruby_prof/rp_call_info.h +53 -53
  20. data/ext/ruby_prof/rp_measure.c +40 -40
  21. data/ext/ruby_prof/rp_measure.h +45 -45
  22. data/ext/ruby_prof/rp_measure_allocations.c +76 -76
  23. data/ext/ruby_prof/rp_measure_cpu_time.c +136 -136
  24. data/ext/ruby_prof/rp_measure_gc_runs.c +73 -73
  25. data/ext/ruby_prof/rp_measure_gc_time.c +60 -60
  26. data/ext/ruby_prof/rp_measure_memory.c +77 -77
  27. data/ext/ruby_prof/rp_measure_process_time.c +71 -71
  28. data/ext/ruby_prof/rp_measure_wall_time.c +45 -45
  29. data/ext/ruby_prof/rp_method.c +630 -636
  30. data/ext/ruby_prof/rp_method.h +75 -75
  31. data/ext/ruby_prof/rp_stack.c +173 -173
  32. data/ext/ruby_prof/rp_stack.h +63 -63
  33. data/ext/ruby_prof/rp_thread.c +277 -276
  34. data/ext/ruby_prof/rp_thread.h +27 -27
  35. data/ext/ruby_prof/ruby_prof.c +794 -774
  36. data/ext/ruby_prof/ruby_prof.h +60 -59
  37. data/ext/ruby_prof/vc/ruby_prof.sln +20 -21
  38. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +31 -0
  39. data/lib/ruby-prof.rb +68 -68
  40. data/lib/ruby-prof/aggregate_call_info.rb +76 -76
  41. data/lib/ruby-prof/assets/call_stack_printer.css.html +116 -116
  42. data/lib/ruby-prof/assets/call_stack_printer.js.html +384 -384
  43. data/lib/ruby-prof/call_info.rb +115 -115
  44. data/lib/ruby-prof/call_info_visitor.rb +40 -40
  45. data/lib/ruby-prof/compatibility.rb +179 -178
  46. data/lib/ruby-prof/method_info.rb +121 -121
  47. data/lib/ruby-prof/printers/abstract_printer.rb +104 -103
  48. data/lib/ruby-prof/printers/call_info_printer.rb +41 -41
  49. data/lib/ruby-prof/printers/call_stack_printer.rb +265 -265
  50. data/lib/ruby-prof/printers/call_tree_printer.rb +143 -143
  51. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  52. data/lib/ruby-prof/printers/flat_printer.rb +70 -70
  53. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -83
  54. data/lib/ruby-prof/printers/graph_html_printer.rb +249 -249
  55. data/lib/ruby-prof/printers/graph_printer.rb +116 -116
  56. data/lib/ruby-prof/printers/multi_printer.rb +84 -84
  57. data/lib/ruby-prof/profile.rb +26 -26
  58. data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -201
  59. data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -49
  60. data/lib/ruby-prof/rack.rb +174 -174
  61. data/lib/ruby-prof/task.rb +147 -147
  62. data/lib/ruby-prof/thread.rb +35 -35
  63. data/lib/ruby-prof/version.rb +3 -3
  64. data/lib/unprof.rb +10 -10
  65. data/ruby-prof.gemspec +58 -58
  66. data/test/abstract_printer_test.rb +53 -0
  67. data/test/aggregate_test.rb +136 -136
  68. data/test/basic_test.rb +128 -128
  69. data/test/block_test.rb +74 -74
  70. data/test/call_info_test.rb +78 -78
  71. data/test/call_info_visitor_test.rb +31 -31
  72. data/test/duplicate_names_test.rb +32 -32
  73. data/test/dynamic_method_test.rb +55 -55
  74. data/test/enumerable_test.rb +21 -21
  75. data/test/exceptions_test.rb +24 -16
  76. data/test/exclude_methods_test.rb +146 -146
  77. data/test/exclude_threads_test.rb +53 -53
  78. data/test/fiber_test.rb +79 -79
  79. data/test/issue137_test.rb +63 -63
  80. data/test/line_number_test.rb +80 -80
  81. data/test/measure_allocations_test.rb +26 -26
  82. data/test/measure_cpu_time_test.rb +212 -213
  83. data/test/measure_gc_runs_test.rb +32 -32
  84. data/test/measure_gc_time_test.rb +36 -36
  85. data/test/measure_memory_test.rb +33 -33
  86. data/test/measure_process_time_test.rb +61 -63
  87. data/test/measure_wall_time_test.rb +255 -255
  88. data/test/method_elimination_test.rb +84 -84
  89. data/test/module_test.rb +45 -45
  90. data/test/multi_printer_test.rb +104 -104
  91. data/test/no_method_class_test.rb +15 -15
  92. data/test/pause_resume_test.rb +166 -166
  93. data/test/prime.rb +54 -54
  94. data/test/printers_test.rb +275 -275
  95. data/test/printing_recursive_graph_test.rb +127 -127
  96. data/test/rack_test.rb +157 -157
  97. data/test/recursive_test.rb +215 -215
  98. data/test/singleton_test.rb +38 -38
  99. data/test/stack_printer_test.rb +77 -78
  100. data/test/stack_test.rb +138 -138
  101. data/test/start_stop_test.rb +112 -112
  102. data/test/test_helper.rb +267 -275
  103. data/test/thread_test.rb +187 -187
  104. data/test/unique_call_path_test.rb +202 -202
  105. data/test/yarv_test.rb +55 -55
  106. metadata +17 -96
  107. data/doc/LICENSE.html +0 -115
  108. data/doc/README_rdoc.html +0 -637
  109. data/doc/Rack.html +0 -96
  110. data/doc/Rack/RubyProf.html +0 -233
  111. data/doc/Rack/RubyProf/RackProfiler.html +0 -343
  112. data/doc/RubyProf.html +0 -974
  113. data/doc/RubyProf/AbstractPrinter.html +0 -625
  114. data/doc/RubyProf/AggregateCallInfo.html +0 -552
  115. data/doc/RubyProf/CallInfo.html +0 -579
  116. data/doc/RubyProf/CallInfoPrinter.html +0 -121
  117. data/doc/RubyProf/CallInfoVisitor.html +0 -199
  118. data/doc/RubyProf/CallStackPrinter.html +0 -1127
  119. data/doc/RubyProf/CallTreePrinter.html +0 -725
  120. data/doc/RubyProf/Cmd.html +0 -637
  121. data/doc/RubyProf/DeprecationWarnings.html +0 -148
  122. data/doc/RubyProf/DotPrinter.html +0 -258
  123. data/doc/RubyProf/FlatPrinter.html +0 -164
  124. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -210
  125. data/doc/RubyProf/GraphHtmlPrinter.html +0 -558
  126. data/doc/RubyProf/GraphPrinter.html +0 -140
  127. data/doc/RubyProf/MethodInfo.html +0 -676
  128. data/doc/RubyProf/MultiPrinter.html +0 -574
  129. data/doc/RubyProf/Profile.html +0 -908
  130. data/doc/RubyProf/Profile/ExcludeCommonMethods.html +0 -411
  131. data/doc/RubyProf/Profile/LegacyMethodElimination.html +0 -158
  132. data/doc/RubyProf/ProfileTask.html +0 -491
  133. data/doc/RubyProf/Thread.html +0 -275
  134. data/doc/created.rid +0 -33
  135. data/doc/css/fonts.css +0 -167
  136. data/doc/css/rdoc.css +0 -590
  137. data/doc/examples/flat_txt.html +0 -139
  138. data/doc/examples/graph_html.html +0 -910
  139. data/doc/examples/graph_txt.html +0 -248
  140. data/doc/fonts/Lato-Light.ttf +0 -0
  141. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  142. data/doc/fonts/Lato-Regular.ttf +0 -0
  143. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  144. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  145. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  146. data/doc/images/add.png +0 -0
  147. data/doc/images/arrow_up.png +0 -0
  148. data/doc/images/brick.png +0 -0
  149. data/doc/images/brick_link.png +0 -0
  150. data/doc/images/bug.png +0 -0
  151. data/doc/images/bullet_black.png +0 -0
  152. data/doc/images/bullet_toggle_minus.png +0 -0
  153. data/doc/images/bullet_toggle_plus.png +0 -0
  154. data/doc/images/date.png +0 -0
  155. data/doc/images/delete.png +0 -0
  156. data/doc/images/find.png +0 -0
  157. data/doc/images/loadingAnimation.gif +0 -0
  158. data/doc/images/macFFBgHack.png +0 -0
  159. data/doc/images/package.png +0 -0
  160. data/doc/images/page_green.png +0 -0
  161. data/doc/images/page_white_text.png +0 -0
  162. data/doc/images/page_white_width.png +0 -0
  163. data/doc/images/plugin.png +0 -0
  164. data/doc/images/ruby.png +0 -0
  165. data/doc/images/tag_blue.png +0 -0
  166. data/doc/images/tag_green.png +0 -0
  167. data/doc/images/transparent.png +0 -0
  168. data/doc/images/wrench.png +0 -0
  169. data/doc/images/wrench_orange.png +0 -0
  170. data/doc/images/zoom.png +0 -0
  171. data/doc/index.html +0 -666
  172. data/doc/js/darkfish.js +0 -161
  173. data/doc/js/jquery.js +0 -4
  174. data/doc/js/navigation.js +0 -142
  175. data/doc/js/navigation.js.gz +0 -0
  176. data/doc/js/search.js +0 -109
  177. data/doc/js/search_index.js +0 -1
  178. data/doc/js/search_index.js.gz +0 -0
  179. data/doc/js/searcher.js +0 -229
  180. data/doc/js/searcher.js.gz +0 -0
  181. data/doc/table_of_contents.html +0 -1052
  182. data/examples/cachegrind.out.1 +0 -114
  183. data/examples/cachegrind.out.1.32313213 +0 -114
  184. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +0 -108
  185. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +0 -110
@@ -1,265 +1,265 @@
1
- # encoding: utf-8
2
-
3
- require 'erb'
4
- require 'fileutils'
5
- require 'base64'
6
-
7
- module RubyProf
8
- # prints a HTML visualization of the call tree
9
- class CallStackPrinter < AbstractPrinter
10
- include ERB::Util
11
-
12
- # Specify print options.
13
- #
14
- # options - Hash table
15
- # :min_percent - Number 0 to 100 that specifes the minimum
16
- # %self (the methods self time divided by the
17
- # overall total time) that a method must take
18
- # for it to be printed out in the report.
19
- # Default value is 0.
20
- #
21
- # :print_file - True or false. Specifies if a method's source
22
- # file should be printed. Default value if false.
23
- #
24
- # :threshold - a float from 0 to 100 that sets the threshold of
25
- # results displayed.
26
- # Default value is 1.0
27
- #
28
- # :title - a String to overide the default "ruby-prof call tree"
29
- # title of the report.
30
- #
31
- # :expansion - a float from 0 to 100 that sets the threshold of
32
- # results that are expanded, if the percent_total
33
- # exceeds it.
34
- # Default value is 10.0
35
- #
36
- # :application - a String to overide the name of the application,
37
- # as it appears on the report.
38
- #
39
- # :editor_uri - Specifies editor uri scheme used for opening files
40
- # e.g. :atm or :mvim. For OS X default is :txmt.
41
- # Use RUBY_PROF_EDITOR_URI environment variable to overide.
42
- def print(output = STDOUT, options = {})
43
- @output = output
44
- setup_options(options)
45
- @editor = editor_uri
46
- if @graph_html = options.delete(:graph)
47
- @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
48
- end
49
-
50
- print_header
51
-
52
- @overall_threads_time = @result.threads.inject(0) do |val, thread|
53
- val += thread.total_time
54
- end
55
-
56
- @result.threads.each do |thread|
57
- @current_thread_id = thread.fiber_id
58
- @overall_time = thread.total_time
59
- thread_info = String.new("Thread: #{thread.id}")
60
- thread_info << ", Fiber: #{thread.fiber_id}" unless thread.id == thread.fiber_id
61
- thread_info << " (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})"
62
- @output.print "<div class=\"thread\">#{thread_info}</div>"
63
- @output.print "<ul name=\"thread\">"
64
- thread.methods.each do |m|
65
- # $stderr.print m.dump
66
- next unless m.root?
67
- m.call_infos.each do |ci|
68
- next unless ci.root?
69
- print_stack ci, thread.total_time
70
- end
71
- end
72
- @output.print "</ul>"
73
- end
74
-
75
- print_footer
76
-
77
- end
78
-
79
- def print_stack(call_info, parent_time)
80
- total_time = call_info.total_time
81
- percent_parent = (total_time/parent_time)*100
82
- percent_total = (total_time/@overall_time)*100
83
- return unless percent_total > min_percent
84
- color = self.color(percent_total)
85
- kids = call_info.children
86
- visible = percent_total >= threshold
87
- expanded = percent_total >= expansion
88
- display = visible ? "block" : "none"
89
- @output.print "<li class=\"color#{color}\" style=\"display:#{display}\">"
90
- if kids.empty?
91
- @output.print "<a href=\"#\" class=\"toggle empty\" ></a>"
92
- else
93
- visible_children = kids.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
94
- image = visible_children ? (expanded ? "minus" : "plus") : "empty"
95
- @output.print "<a href=\"#\" class=\"toggle #{image}\" ></a>"
96
- end
97
- @output.printf "<span> %4.2f%% (%4.2f%%) %s %s</span>\n", percent_total, percent_parent, link(call_info), graph_link(call_info)
98
- unless kids.empty?
99
- if expanded
100
- @output.print "<ul>"
101
- else
102
- @output.print '<ul style="display:none">'
103
- end
104
- kids.sort_by{|c| -c.total_time}.each do |callinfo|
105
- print_stack callinfo, total_time
106
- end
107
- @output.print "</ul>"
108
- end
109
- @output.print "</li>"
110
- end
111
-
112
- def name(call_info)
113
- method = call_info.target
114
- method.full_name
115
- end
116
-
117
- def link(call_info)
118
- method = call_info.target
119
- file = File.expand_path(method.source_file)
120
- if file =~ /\/ruby_runtime$/
121
- h(name(call_info))
122
- else
123
- if @editor
124
- "<a href=\"#{@editor}://" \
125
- "open?url=file://#{file}&line=#{method.line}\">" \
126
- "#{h(name(call_info))}</a>"
127
- else
128
- "<a href=\"file://#{file}##{method.line}\">#{h(name(call_info))}</a>"
129
- end
130
- end
131
- end
132
-
133
- def graph_link(call_info)
134
- total_calls = call_info.target.call_infos.inject(0){|t, ci| t += ci.called}
135
- href = "#{@graph_html}##{method_href(call_info.target)}"
136
- totals = @graph_html ? "<a href='#{href}'>#{total_calls}</a>" : total_calls.to_s
137
- "[#{call_info.called} calls, #{totals} total]"
138
- end
139
-
140
- def method_href(method)
141
- h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + @current_thread_id.to_s)
142
- end
143
-
144
- def total_time(call_infos)
145
- sum(call_infos.map{|ci| ci.total_time})
146
- end
147
-
148
- def sum(a)
149
- a.inject(0.0){|s,t| s+=t}
150
- end
151
-
152
- def dump(ci)
153
- $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
154
- end
155
-
156
- def color(p)
157
- case i = p.to_i
158
- when 0..5
159
- "01"
160
- when 5..10
161
- "05"
162
- when 100
163
- "9"
164
- else
165
- "#{i/10}"
166
- end
167
- end
168
-
169
- def application
170
- @options[:application] || $PROGRAM_NAME
171
- end
172
-
173
- def arguments
174
- ARGV.join(' ')
175
- end
176
-
177
- def title
178
- @title ||= @options.delete(:title) || "ruby-prof call tree"
179
- end
180
-
181
- def threshold
182
- @options[:threshold] || 1.0
183
- end
184
-
185
- def expansion
186
- @options[:expansion] || 10.0
187
- end
188
-
189
- def print_header
190
- @output.puts "<html><head>"
191
- @output.puts '<meta http-equiv="content-type" content="text/html; charset=utf-8">'
192
- @output.puts "<title>#{h title}</title>"
193
- print_css
194
- print_java_script
195
- @output.puts '</head><body><div style="display: inline-block;">'
196
- print_title_bar
197
- print_commands
198
- print_help
199
- end
200
-
201
- def print_footer
202
- @output.puts '<div id="sentinel"></div></div></body></html>'
203
- end
204
-
205
- def open_asset(file)
206
- path = File.join(File.expand_path('../../assets', __FILE__), file)
207
- File.open(path, 'rb').read
208
- end
209
-
210
- def print_css
211
- html = open_asset('call_stack_printer.css.html')
212
- @output.puts html.gsub('%s', base64_image)
213
- end
214
-
215
- def base64_image
216
- file = open_asset('call_stack_printer.png')
217
- Base64.encode64(file).gsub(/\n/, '')
218
- end
219
-
220
- def print_java_script
221
- html = open_asset('call_stack_printer.js.html')
222
- @output.puts html
223
- end
224
-
225
- def print_title_bar
226
- @output.puts <<-"end_title_bar"
227
- <div id="titlebar">
228
- Call tree for application <b>#{h application} #{h arguments}</b><br/>
229
- Generated on #{Time.now} with options #{h @options.inspect}<br/>
230
- </div>
231
- end_title_bar
232
- end
233
-
234
- def print_commands
235
- @output.puts <<-"end_commands"
236
- <div id=\"commands\">
237
- <span style=\"font-size: 11pt; font-weight: bold;\">Threshold:</span>
238
- <input value=\"#{h threshold}\" size=\"3\" id=\"threshold\" type=\"text\">
239
- <input value=\"Apply\" onclick=\"setThreshold();\" type=\"submit\">
240
- <input value=\"Expand All\" onclick=\"expandAll(event);\" type=\"submit\">
241
- <input value=\"Collapse All\" onclick=\"collapseAll(event);\" type=\"submit\">
242
- <input value=\"Show Help\" onclick=\"toggleHelp(this);\" type=\"submit\">
243
- </div>
244
- end_commands
245
- end
246
-
247
- def print_help
248
- @output.puts <<-'end_help'
249
- <div style="display: none;" id="help">
250
- &#8226; Enter a decimal value <i>d</i> into the threshold field and click "Apply"
251
- to hide all nodes marked with time values lower than <i>d</i>.<br>
252
- &#8226; Click on "Expand All" for full tree expansion.<br>
253
- &#8226; Click on "Collapse All" to show only top level nodes.<br>
254
- &#8226; Use a, s, d, w as in Quake or Urban Terror to navigate the tree.<br>
255
- &#8226; Use f and b to navigate the tree in preorder forward and backwards.<br>
256
- &#8226; Use x to toggle visibility of a subtree.<br>
257
- &#8226; Use * to expand/collapse a whole subtree.<br>
258
- &#8226; Use h to navigate to thread root.<br>
259
- &#8226; Use n and p to navigate between threads.<br>
260
- &#8226; Click on background to move focus to a subtree.<br>
261
- </div>
262
- end_help
263
- end
264
- end
265
- end
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'base64'
6
+
7
+ module RubyProf
8
+ # prints a HTML visualization of the call tree
9
+ class CallStackPrinter < AbstractPrinter
10
+ include ERB::Util
11
+
12
+ # Specify print options.
13
+ #
14
+ # options - Hash table
15
+ # :min_percent - Number 0 to 100 that specifes the minimum
16
+ # %self (the methods self time divided by the
17
+ # overall total time) that a method must take
18
+ # for it to be printed out in the report.
19
+ # Default value is 0.
20
+ #
21
+ # :print_file - True or false. Specifies if a method's source
22
+ # file should be printed. Default value if false.
23
+ #
24
+ # :threshold - a float from 0 to 100 that sets the threshold of
25
+ # results displayed.
26
+ # Default value is 1.0
27
+ #
28
+ # :title - a String to overide the default "ruby-prof call tree"
29
+ # title of the report.
30
+ #
31
+ # :expansion - a float from 0 to 100 that sets the threshold of
32
+ # results that are expanded, if the percent_total
33
+ # exceeds it.
34
+ # Default value is 10.0
35
+ #
36
+ # :application - a String to overide the name of the application,
37
+ # as it appears on the report.
38
+ #
39
+ # :editor_uri - Specifies editor uri scheme used for opening files
40
+ # e.g. :atm or :mvim. For OS X default is :txmt.
41
+ # Use RUBY_PROF_EDITOR_URI environment variable to overide.
42
+ def print(output = STDOUT, options = {})
43
+ @output = output
44
+ setup_options(options)
45
+ @editor = editor_uri
46
+ if @graph_html = options.delete(:graph)
47
+ @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
48
+ end
49
+
50
+ print_header
51
+
52
+ @overall_threads_time = @result.threads.inject(0) do |val, thread|
53
+ val += thread.total_time
54
+ end
55
+
56
+ @result.threads.each do |thread|
57
+ @current_thread_id = thread.fiber_id
58
+ @overall_time = thread.total_time
59
+ thread_info = String.new("Thread: #{thread.id}")
60
+ thread_info << ", Fiber: #{thread.fiber_id}" unless thread.id == thread.fiber_id
61
+ thread_info << " (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})"
62
+ @output.print "<div class=\"thread\">#{thread_info}</div>"
63
+ @output.print "<ul name=\"thread\">"
64
+ thread.methods.each do |m|
65
+ # $stderr.print m.dump
66
+ next unless m.root?
67
+ m.call_infos.each do |ci|
68
+ next unless ci.root?
69
+ print_stack ci, thread.total_time
70
+ end
71
+ end
72
+ @output.print "</ul>"
73
+ end
74
+
75
+ print_footer
76
+
77
+ end
78
+
79
+ def print_stack(call_info, parent_time)
80
+ total_time = call_info.total_time
81
+ percent_parent = (total_time/parent_time)*100
82
+ percent_total = (total_time/@overall_time)*100
83
+ return unless percent_total > min_percent
84
+ color = self.color(percent_total)
85
+ kids = call_info.children
86
+ visible = percent_total >= threshold
87
+ expanded = percent_total >= expansion
88
+ display = visible ? "block" : "none"
89
+ @output.print "<li class=\"color#{color}\" style=\"display:#{display}\">"
90
+ if kids.empty?
91
+ @output.print "<a href=\"#\" class=\"toggle empty\" ></a>"
92
+ else
93
+ visible_children = kids.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
94
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
95
+ @output.print "<a href=\"#\" class=\"toggle #{image}\" ></a>"
96
+ end
97
+ @output.printf "<span> %4.2f%% (%4.2f%%) %s %s</span>\n", percent_total, percent_parent, link(call_info), graph_link(call_info)
98
+ unless kids.empty?
99
+ if expanded
100
+ @output.print "<ul>"
101
+ else
102
+ @output.print '<ul style="display:none">'
103
+ end
104
+ kids.sort_by{|c| -c.total_time}.each do |callinfo|
105
+ print_stack callinfo, total_time
106
+ end
107
+ @output.print "</ul>"
108
+ end
109
+ @output.print "</li>"
110
+ end
111
+
112
+ def name(call_info)
113
+ method = call_info.target
114
+ method.full_name
115
+ end
116
+
117
+ def link(call_info)
118
+ method = call_info.target
119
+ file = File.expand_path(method.source_file)
120
+ if file =~ /\/ruby_runtime$/
121
+ h(name(call_info))
122
+ else
123
+ if @editor
124
+ "<a href=\"#{@editor}://" \
125
+ "open?url=file://#{file}&line=#{method.line}\">" \
126
+ "#{h(name(call_info))}</a>"
127
+ else
128
+ "<a href=\"file://#{file}##{method.line}\">#{h(name(call_info))}</a>"
129
+ end
130
+ end
131
+ end
132
+
133
+ def graph_link(call_info)
134
+ total_calls = call_info.target.call_infos.inject(0){|t, ci| t += ci.called}
135
+ href = "#{@graph_html}##{method_href(call_info.target)}"
136
+ totals = @graph_html ? "<a href='#{href}'>#{total_calls}</a>" : total_calls.to_s
137
+ "[#{call_info.called} calls, #{totals} total]"
138
+ end
139
+
140
+ def method_href(method)
141
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + @current_thread_id.to_s)
142
+ end
143
+
144
+ def total_time(call_infos)
145
+ sum(call_infos.map{|ci| ci.total_time})
146
+ end
147
+
148
+ def sum(a)
149
+ a.inject(0.0){|s,t| s+=t}
150
+ end
151
+
152
+ def dump(ci)
153
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
154
+ end
155
+
156
+ def color(p)
157
+ case i = p.to_i
158
+ when 0..5
159
+ "01"
160
+ when 5..10
161
+ "05"
162
+ when 100
163
+ "9"
164
+ else
165
+ "#{i/10}"
166
+ end
167
+ end
168
+
169
+ def application
170
+ @options[:application] || $PROGRAM_NAME
171
+ end
172
+
173
+ def arguments
174
+ ARGV.join(' ')
175
+ end
176
+
177
+ def title
178
+ @title ||= @options.delete(:title) || "ruby-prof call tree"
179
+ end
180
+
181
+ def threshold
182
+ @options[:threshold] || 1.0
183
+ end
184
+
185
+ def expansion
186
+ @options[:expansion] || 10.0
187
+ end
188
+
189
+ def print_header
190
+ @output.puts "<html><head>"
191
+ @output.puts '<meta http-equiv="content-type" content="text/html; charset=utf-8">'
192
+ @output.puts "<title>#{h title}</title>"
193
+ print_css
194
+ print_java_script
195
+ @output.puts '</head><body><div style="display: inline-block;">'
196
+ print_title_bar
197
+ print_commands
198
+ print_help
199
+ end
200
+
201
+ def print_footer
202
+ @output.puts '<div id="sentinel"></div></div></body></html>'
203
+ end
204
+
205
+ def open_asset(file)
206
+ path = File.join(File.expand_path('../../assets', __FILE__), file)
207
+ File.open(path, 'rb').read
208
+ end
209
+
210
+ def print_css
211
+ html = open_asset('call_stack_printer.css.html')
212
+ @output.puts html.gsub('%s', base64_image)
213
+ end
214
+
215
+ def base64_image
216
+ file = open_asset('call_stack_printer.png')
217
+ Base64.encode64(file).gsub(/\n/, '')
218
+ end
219
+
220
+ def print_java_script
221
+ html = open_asset('call_stack_printer.js.html')
222
+ @output.puts html
223
+ end
224
+
225
+ def print_title_bar
226
+ @output.puts <<-"end_title_bar"
227
+ <div id="titlebar">
228
+ Call tree for application <b>#{h application} #{h arguments}</b><br/>
229
+ Generated on #{Time.now} with options #{h @options.inspect}<br/>
230
+ </div>
231
+ end_title_bar
232
+ end
233
+
234
+ def print_commands
235
+ @output.puts <<-"end_commands"
236
+ <div id=\"commands\">
237
+ <span style=\"font-size: 11pt; font-weight: bold;\">Threshold:</span>
238
+ <input value=\"#{h threshold}\" size=\"3\" id=\"threshold\" type=\"text\">
239
+ <input value=\"Apply\" onclick=\"setThreshold();\" type=\"submit\">
240
+ <input value=\"Expand All\" onclick=\"expandAll(event);\" type=\"submit\">
241
+ <input value=\"Collapse All\" onclick=\"collapseAll(event);\" type=\"submit\">
242
+ <input value=\"Show Help\" onclick=\"toggleHelp(this);\" type=\"submit\">
243
+ </div>
244
+ end_commands
245
+ end
246
+
247
+ def print_help
248
+ @output.puts <<-'end_help'
249
+ <div style="display: none;" id="help">
250
+ &#8226; Enter a decimal value <i>d</i> into the threshold field and click "Apply"
251
+ to hide all nodes marked with time values lower than <i>d</i>.<br>
252
+ &#8226; Click on "Expand All" for full tree expansion.<br>
253
+ &#8226; Click on "Collapse All" to show only top level nodes.<br>
254
+ &#8226; Use a, s, d, w as in Quake or Urban Terror to navigate the tree.<br>
255
+ &#8226; Use f and b to navigate the tree in preorder forward and backwards.<br>
256
+ &#8226; Use x to toggle visibility of a subtree.<br>
257
+ &#8226; Use * to expand/collapse a whole subtree.<br>
258
+ &#8226; Use h to navigate to thread root.<br>
259
+ &#8226; Use n and p to navigate between threads.<br>
260
+ &#8226; Click on background to move focus to a subtree.<br>
261
+ </div>
262
+ end_help
263
+ end
264
+ end
265
+ end