ruby-prof 0.17.0 → 0.18.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.
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