ruby-prof 1.4.4-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +608 -0
  3. data/LICENSE +25 -0
  4. data/README.md +5 -0
  5. data/Rakefile +98 -0
  6. data/bin/ruby-prof +328 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +22 -0
  9. data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
  10. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  11. data/ext/ruby_prof/rp_allocation.c +287 -0
  12. data/ext/ruby_prof/rp_allocation.h +31 -0
  13. data/ext/ruby_prof/rp_call_tree.c +367 -0
  14. data/ext/ruby_prof/rp_call_tree.h +43 -0
  15. data/ext/ruby_prof/rp_call_trees.c +288 -0
  16. data/ext/ruby_prof/rp_call_trees.h +28 -0
  17. data/ext/ruby_prof/rp_measure_allocations.c +47 -0
  18. data/ext/ruby_prof/rp_measure_memory.c +46 -0
  19. data/ext/ruby_prof/rp_measure_process_time.c +66 -0
  20. data/ext/ruby_prof/rp_measure_wall_time.c +64 -0
  21. data/ext/ruby_prof/rp_measurement.c +237 -0
  22. data/ext/ruby_prof/rp_measurement.h +50 -0
  23. data/ext/ruby_prof/rp_method.c +491 -0
  24. data/ext/ruby_prof/rp_method.h +62 -0
  25. data/ext/ruby_prof/rp_profile.c +915 -0
  26. data/ext/ruby_prof/rp_profile.h +35 -0
  27. data/ext/ruby_prof/rp_stack.c +212 -0
  28. data/ext/ruby_prof/rp_stack.h +53 -0
  29. data/ext/ruby_prof/rp_thread.c +362 -0
  30. data/ext/ruby_prof/rp_thread.h +39 -0
  31. data/ext/ruby_prof/ruby_prof.c +52 -0
  32. data/ext/ruby_prof/ruby_prof.h +26 -0
  33. data/ext/ruby_prof/vc/ruby_prof.sln +39 -0
  34. data/ext/ruby_prof/vc/ruby_prof.vcxproj +160 -0
  35. data/lib/3.1/ruby_prof.so +0 -0
  36. data/lib/ruby-prof/assets/call_stack_printer.html.erb +711 -0
  37. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  38. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  39. data/lib/ruby-prof/call_tree.rb +57 -0
  40. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  41. data/lib/ruby-prof/compatibility.rb +99 -0
  42. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  43. data/lib/ruby-prof/measurement.rb +17 -0
  44. data/lib/ruby-prof/method_info.rb +78 -0
  45. data/lib/ruby-prof/printers/abstract_printer.rb +137 -0
  46. data/lib/ruby-prof/printers/call_info_printer.rb +53 -0
  47. data/lib/ruby-prof/printers/call_stack_printer.rb +180 -0
  48. data/lib/ruby-prof/printers/call_tree_printer.rb +147 -0
  49. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  50. data/lib/ruby-prof/printers/flat_printer.rb +53 -0
  51. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -0
  52. data/lib/ruby-prof/printers/graph_printer.rb +113 -0
  53. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  54. data/lib/ruby-prof/profile.rb +37 -0
  55. data/lib/ruby-prof/rack.rb +95 -0
  56. data/lib/ruby-prof/task.rb +147 -0
  57. data/lib/ruby-prof/thread.rb +20 -0
  58. data/lib/ruby-prof/version.rb +3 -0
  59. data/lib/ruby-prof.rb +52 -0
  60. data/lib/unprof.rb +10 -0
  61. data/ruby-prof.gemspec +64 -0
  62. data/test/abstract_printer_test.rb +26 -0
  63. data/test/alias_test.rb +122 -0
  64. data/test/basic_test.rb +43 -0
  65. data/test/call_tree_visitor_test.rb +32 -0
  66. data/test/call_trees_test.rb +66 -0
  67. data/test/duplicate_names_test.rb +32 -0
  68. data/test/dynamic_method_test.rb +67 -0
  69. data/test/enumerable_test.rb +21 -0
  70. data/test/exceptions_test.rb +24 -0
  71. data/test/exclude_methods_test.rb +151 -0
  72. data/test/exclude_threads_test.rb +53 -0
  73. data/test/fiber_test.rb +129 -0
  74. data/test/gc_test.rb +100 -0
  75. data/test/inverse_call_tree_test.rb +175 -0
  76. data/test/line_number_test.rb +158 -0
  77. data/test/marshal_test.rb +145 -0
  78. data/test/measure_allocations.rb +26 -0
  79. data/test/measure_allocations_test.rb +333 -0
  80. data/test/measure_memory_test.rb +688 -0
  81. data/test/measure_process_time_test.rb +1614 -0
  82. data/test/measure_times.rb +56 -0
  83. data/test/measure_wall_time_test.rb +426 -0
  84. data/test/multi_printer_test.rb +71 -0
  85. data/test/no_method_class_test.rb +15 -0
  86. data/test/pause_resume_test.rb +175 -0
  87. data/test/prime.rb +54 -0
  88. data/test/prime_script.rb +6 -0
  89. data/test/printer_call_stack_test.rb +27 -0
  90. data/test/printer_call_tree_test.rb +30 -0
  91. data/test/printer_flat_test.rb +99 -0
  92. data/test/printer_graph_html_test.rb +59 -0
  93. data/test/printer_graph_test.rb +40 -0
  94. data/test/printers_test.rb +141 -0
  95. data/test/printing_recursive_graph_test.rb +81 -0
  96. data/test/profile_test.rb +16 -0
  97. data/test/rack_test.rb +93 -0
  98. data/test/recursive_test.rb +430 -0
  99. data/test/singleton_test.rb +38 -0
  100. data/test/stack_printer_test.rb +64 -0
  101. data/test/start_stop_test.rb +109 -0
  102. data/test/test_helper.rb +13 -0
  103. data/test/thread_test.rb +144 -0
  104. data/test/unique_call_path_test.rb +136 -0
  105. data/test/yarv_test.rb +60 -0
  106. metadata +187 -0
@@ -0,0 +1,198 @@
1
+ require 'set'
2
+
3
+ # :enddoc:
4
+ module RubyProf
5
+ module ExcludeCommonMethods
6
+ ENUMERABLE_NAMES = Enumerable.instance_methods(false)
7
+
8
+ def self.apply!(profile)
9
+ ##
10
+ # Kernel Methods
11
+ ##
12
+
13
+ exclude_methods(profile, Kernel, [
14
+ :dup,
15
+ :initialize_dup,
16
+ :tap,
17
+ :send,
18
+ :public_send,
19
+ ])
20
+
21
+ ##
22
+ # Fundamental Types
23
+ ##
24
+
25
+ exclude_methods(profile, BasicObject, :"!=")
26
+ exclude_methods(profile, Method, :"[]")
27
+ exclude_methods(profile, Module, :new)
28
+ exclude_methods(profile, Class, :new)
29
+ exclude_methods(profile, Proc, :call, :yield)
30
+ exclude_methods(profile, Range, :each)
31
+ exclude_methods(profile, Integer, :times)
32
+
33
+ ##
34
+ # Value Types
35
+ ##
36
+
37
+ exclude_methods(profile, String, [
38
+ :sub,
39
+ :sub!,
40
+ :gsub,
41
+ :gsub!,
42
+ ])
43
+
44
+ ##
45
+ # Emumerables
46
+ ##
47
+
48
+ exclude_enumerable(profile, Enumerable)
49
+ exclude_enumerable(profile, Enumerator)
50
+
51
+ ##
52
+ # Collections
53
+ ##
54
+
55
+ exclude_enumerable(profile, Array, [
56
+ :each_index,
57
+ :map!,
58
+ :select!,
59
+ :reject!,
60
+ :collect!,
61
+ :sort!,
62
+ :sort_by!,
63
+ :index,
64
+ :delete_if,
65
+ :keep_if,
66
+ :drop_while,
67
+ :uniq,
68
+ :uniq!,
69
+ :"==",
70
+ :eql?,
71
+ :hash,
72
+ :to_json,
73
+ :as_json,
74
+ :encode_json,
75
+ ])
76
+
77
+ exclude_enumerable(profile, Hash, [
78
+ :dup,
79
+ :initialize_dup,
80
+ :fetch,
81
+ :"[]",
82
+ :"[]=",
83
+ :each_key,
84
+ :each_value,
85
+ :each_pair,
86
+ :map!,
87
+ :select!,
88
+ :reject!,
89
+ :collect!,
90
+ :delete_if,
91
+ :keep_if,
92
+ :slice,
93
+ :slice!,
94
+ :except,
95
+ :except!,
96
+ :"==",
97
+ :eql?,
98
+ :hash,
99
+ :to_json,
100
+ :as_json,
101
+ :encode_json,
102
+ ])
103
+
104
+ exclude_enumerable(profile, Set, [
105
+ :map!,
106
+ :select!,
107
+ :reject!,
108
+ :collect!,
109
+ :classify,
110
+ :delete_if,
111
+ :keep_if,
112
+ :divide,
113
+ :"==",
114
+ :eql?,
115
+ :hash,
116
+ :to_json,
117
+ :as_json,
118
+ :encode_json,
119
+ ])
120
+
121
+ ##
122
+ # Garbage Collection
123
+ ##
124
+
125
+ exclude_singleton_methods(profile, GC, [
126
+ :start
127
+ ])
128
+
129
+ ##
130
+ # Unicorn
131
+ ##
132
+
133
+ if defined?(Unicorn)
134
+ exclude_methods(profile, Unicorn::HttpServer, :process_client)
135
+ end
136
+
137
+ if defined?(Unicorn::OobGC)
138
+ exclude_methods(profile, Unicorn::OobGC, :process_client)
139
+ end
140
+
141
+ ##
142
+ # New Relic
143
+ ##
144
+
145
+ if defined?(NewRelic::Agent)
146
+ if defined?(NewRelic::Agent::Instrumentation::MiddlewareTracing)
147
+ exclude_methods(profile, NewRelic::Agent::Instrumentation::MiddlewareTracing, [
148
+ :call
149
+ ])
150
+ end
151
+
152
+ if defined?(NewRelic::Agent::MethodTracerHelpers)
153
+ exclude_methods(profile, NewRelic::Agent::MethodTracerHelpers, [
154
+ :trace_execution_scoped,
155
+ :log_errors,
156
+ ])
157
+
158
+ exclude_singleton_methods(profile, NewRelic::Agent::MethodTracerHelpers, [
159
+ :trace_execution_scoped,
160
+ :log_errors,
161
+ ])
162
+ end
163
+
164
+ if defined?(NewRelic::Agent::MethodTracer)
165
+ exclude_methods(profile, NewRelic::Agent::MethodTracer, [
166
+ :trace_execution_scoped,
167
+ :trace_execution_unscoped,
168
+ ])
169
+ end
170
+ end
171
+
172
+ ##
173
+ # Miscellaneous Methods
174
+ ##
175
+
176
+ if defined?(Mustache)
177
+ exclude_methods(profile, Mustache::Context, [
178
+ :fetch
179
+ ])
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def self.exclude_enumerable(profile, mod, *method_or_methods)
186
+ exclude_methods(profile, mod, [:each, *method_or_methods])
187
+ exclude_methods(profile, mod, ENUMERABLE_NAMES)
188
+ end
189
+
190
+ def self.exclude_methods(profile, mod, *method_or_methods)
191
+ profile.exclude_methods!(mod, method_or_methods)
192
+ end
193
+
194
+ def self.exclude_singleton_methods(profile, mod, *method_or_methods)
195
+ profile.exclude_singleton_methods!(mod, method_or_methods)
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,17 @@
1
+ module RubyProf
2
+ # The Measurement class is a helper class used by RubyProf::MethodInfo to store information about the method.
3
+ # You cannot create a CallTree object directly, they are generated while running a profile.
4
+ class Measurement
5
+ def children_time
6
+ self.total_time - self.self_time - self.wait_time
7
+ end
8
+
9
+ def to_s
10
+ "c: #{called}, tt: #{total_time}, st: #{self_time}"
11
+ end
12
+
13
+ def inspect
14
+ super + "(#{self.to_s})"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # The MethodInfo class is used to track information about each method that is profiled.
5
+ # You cannot create a MethodInfo object directly, they are generated while running a profile.
6
+ class MethodInfo
7
+ include Comparable
8
+
9
+ # Returns the full name of a class. The interpretation of method names is:
10
+ #
11
+ # * MyObject#test - An method defined in a class
12
+ # * <Class:MyObject>#test - A method defined in a singleton class.
13
+ # * <Module:MyObject>#test - A method defined in a singleton module.
14
+ # * <Object:MyObject>#test - A method defined in a singleton object.
15
+ def full_name
16
+ decorated_class_name = case self.klass_flags
17
+ when 0x2
18
+ "<Class::#{klass_name}>"
19
+ when 0x4
20
+ "<Module::#{klass_name}>"
21
+ when 0x8
22
+ "<Object::#{klass_name}>"
23
+ else
24
+ klass_name
25
+ end
26
+
27
+ "#{decorated_class_name}##{method_name}"
28
+ end
29
+
30
+ # The number of times this method was called
31
+ def called
32
+ self.measurement.called
33
+ end
34
+
35
+ # The total time this method took - includes self time + wait time + child time
36
+ def total_time
37
+ self.measurement.total_time
38
+ end
39
+
40
+ # The time this method took to execute
41
+ def self_time
42
+ self.measurement.self_time
43
+ end
44
+
45
+ # The time this method waited for other fibers/threads to execute
46
+ def wait_time
47
+ self.measurement.wait_time
48
+ end
49
+
50
+ # The time this method's children took to execute
51
+ def children_time
52
+ self.total_time - self.self_time - self.wait_time
53
+ end
54
+
55
+ # :enddoc:
56
+ def <=>(other)
57
+ if other.nil?
58
+ -1
59
+ elsif self.full_name == other.full_name
60
+ 0
61
+ elsif self.total_time < other.total_time
62
+ -1
63
+ elsif self.total_time > other.total_time
64
+ 1
65
+ elsif self.call_trees.min_depth < other.call_trees.min_depth
66
+ 1
67
+ elsif self.call_trees.min_depth > other.call_trees.min_depth
68
+ -1
69
+ else
70
+ self.full_name <=> other.full_name
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ "#{self.full_name} (c: #{self.called}, tt: #{self.total_time}, st: #{self.self_time}, wt: #{wait_time}, ct: #{self.children_time})"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,137 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # This is the base class for all Printers. It is never used directly.
5
+ class AbstractPrinter
6
+ # :stopdoc:
7
+ def self.needs_dir?
8
+ false
9
+ end
10
+ # :startdoc:
11
+
12
+ # Create a new printer.
13
+ #
14
+ # result should be the output generated from a profiling run
15
+ def initialize(result)
16
+ @result = result
17
+ @output = nil
18
+ end
19
+
20
+ # Returns the min_percent of time a method must take to be included in a profiling report
21
+ def min_percent
22
+ @options[:min_percent] || 0
23
+ end
24
+
25
+ # Returns the max_percent of time a method can take to be included in a profiling report
26
+ def max_percent
27
+ @options[:max_percent] || 100
28
+ end
29
+
30
+ # Returns the method to filter methods by (when using min_percent and max_percent)
31
+ def filter_by
32
+ @options[:filter_by] || :self_time
33
+ end
34
+
35
+ # Returns the time format used to show when a profile was run
36
+ def time_format
37
+ '%A, %B %-d at %l:%M:%S %p (%Z)'
38
+ end
39
+
40
+ # Returns how profile data should be sorted
41
+ def sort_method
42
+ @options[:sort_method]
43
+ end
44
+
45
+ # Prints a report to the provided output.
46
+ #
47
+ # output - Any IO object, including STDOUT or a file.
48
+ # The default value is STDOUT.
49
+ #
50
+ # options - Hash of print options. Note that each printer can
51
+ # define its own set of options.
52
+ #
53
+ # :min_percent - Number 0 to 100 that specifes the minimum
54
+ # %self (the methods self time divided by the
55
+ # overall total time) that a method must take
56
+ # for it to be printed out in the report.
57
+ # Default value is 0.
58
+ #
59
+ # :sort_method - Specifies method used for sorting method infos.
60
+ # Available values are :total_time, :self_time,
61
+ # :wait_time, :children_time
62
+ # Default value is :total_time
63
+ def print(output = STDOUT, options = {})
64
+ @output = output
65
+ setup_options(options)
66
+ print_threads
67
+ end
68
+
69
+ # :nodoc:
70
+ def setup_options(options = {})
71
+ @options = options
72
+ end
73
+
74
+ def method_location(method)
75
+ if method.source_file
76
+ "#{method.source_file}:#{method.line}"
77
+ end
78
+ end
79
+
80
+ def method_href(thread, method)
81
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread.fiber_id.to_s)
82
+ end
83
+
84
+ def open_asset(file)
85
+ path = File.join(File.expand_path('../../assets', __FILE__), file)
86
+ File.open(path, 'rb').read
87
+ end
88
+
89
+ def print_threads
90
+ @result.threads.each do |thread|
91
+ print_thread(thread)
92
+ end
93
+ end
94
+
95
+ def print_thread(thread)
96
+ print_header(thread)
97
+ print_methods(thread)
98
+ print_footer(thread)
99
+ end
100
+
101
+ def print_header(thread)
102
+ @output << "Measure Mode: %s\n" % @result.measure_mode_string
103
+ @output << "Thread ID: %d\n" % thread.id
104
+ @output << "Fiber ID: %d\n" % thread.fiber_id unless thread.id == thread.fiber_id
105
+ @output << "Total: %0.6f\n" % thread.total_time
106
+ @output << "Sort by: #{sort_method}\n"
107
+ @output << "\n"
108
+ print_column_headers
109
+ end
110
+
111
+ def print_column_headers
112
+ end
113
+
114
+ def print_footer(thread)
115
+ @output << <<~EOT
116
+
117
+ * recursively called methods
118
+
119
+ Columns are:
120
+
121
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
122
+ total - The time spent in this method and its children.
123
+ self - The time spent in this method.
124
+ wait - The amount of time this method waited for other threads.
125
+ child - The time spent in this method's children.
126
+ calls - The number of times this method was called.
127
+ name - The name of the method.
128
+ location - The location of the method.
129
+
130
+ The interpretation of method names is:
131
+
132
+ * MyObject#test - An instance method "test" of the class "MyObject"
133
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
134
+ EOT
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # Prints out the call graph based on CallTree instances. This
5
+ # is mainly for debugging purposes as it provides access into
6
+ # into RubyProf's internals.
7
+ #
8
+ # To use the printer:
9
+ #
10
+ # result = RubyProf.profile do
11
+ # [code to profile]
12
+ # end
13
+ #
14
+ # printer = RubyProf::CallInfoPrinter.new(result)
15
+ # printer.print(STDOUT)
16
+ class CallInfoPrinter < AbstractPrinter
17
+ TIME_WIDTH = 0
18
+
19
+ private
20
+
21
+ def print_header(thread)
22
+ @output << "----------------------------------------------------\n"
23
+ @output << "Thread ID: #{thread.id}\n"
24
+ @output << "Fiber ID: #{thread.fiber_id}\n"
25
+ @output << "Total Time: #{thread.total_time}\n"
26
+ @output << "Sort by: #{sort_method}\n"
27
+ @output << "\n"
28
+ end
29
+
30
+ def print_methods(thread)
31
+ visitor = CallTreeVisitor.new(thread.call_tree)
32
+
33
+ visitor.visit do |call_tree, event|
34
+ if event == :enter
35
+ @output << " " * call_tree.depth
36
+ @output << call_tree.target.full_name
37
+ @output << " ("
38
+ @output << "tt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.total_time)}, "
39
+ @output << "st:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.self_time)}, "
40
+ @output << "wt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.wait_time)}, "
41
+ @output << "ct:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.children_time)}, "
42
+ @output << "call:#{call_tree.called}, "
43
+ @output << ")"
44
+ @output << "\n"
45
+ end
46
+ end
47
+ end
48
+
49
+ def print_footer(thread)
50
+ @output << "\n" << "\n"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'base64'
6
+ require 'set'
7
+ require 'stringio'
8
+
9
+ module RubyProf
10
+ # Prints a HTML visualization of the call tree.
11
+ #
12
+ # To use the printer:
13
+ #
14
+ # result = RubyProf.profile do
15
+ # [code to profile]
16
+ # end
17
+ #
18
+ # printer = RubyProf::CallStackPrinter.new(result)
19
+ # printer.print(STDOUT)
20
+
21
+ class CallStackPrinter < AbstractPrinter
22
+ include ERB::Util
23
+
24
+ # Specify print options.
25
+ #
26
+ # options - Hash table
27
+ # :min_percent - Number 0 to 100 that specifes the minimum
28
+ # %self (the methods self time divided by the
29
+ # overall total time) that a method must take
30
+ # for it to be printed out in the report.
31
+ # Default value is 0.
32
+ #
33
+ # :threshold - a float from 0 to 100 that sets the threshold of
34
+ # results displayed.
35
+ # Default value is 1.0
36
+ #
37
+ # :title - a String to overide the default "ruby-prof call tree"
38
+ # title of the report.
39
+ #
40
+ # :expansion - a float from 0 to 100 that sets the threshold of
41
+ # results that are expanded, if the percent_total
42
+ # exceeds it.
43
+ # Default value is 10.0
44
+ #
45
+ # :application - a String to overide the name of the application,
46
+ # as it appears on the report.
47
+ def print(output = STDOUT, options = {})
48
+ setup_options(options)
49
+ output << @erb.result(binding)
50
+ end
51
+
52
+ # :enddoc:
53
+ def setup_options(options)
54
+ super(options)
55
+ @erb = ERB.new(self.template)
56
+ end
57
+
58
+ def print_stack(output, visited, call_tree, parent_time)
59
+ total_time = call_tree.total_time
60
+ percent_parent = (total_time/parent_time)*100
61
+ percent_total = (total_time/@overall_time)*100
62
+ return unless percent_total > min_percent
63
+ color = self.color(percent_total)
64
+ visible = percent_total >= threshold
65
+ expanded = percent_total >= expansion
66
+ display = visible ? "block" : "none"
67
+
68
+ output << "<li class=\"color#{color}\" style=\"display:#{display}\">" << "\n"
69
+
70
+ if visited.include?(call_tree)
71
+ output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
72
+ output << "<span>%s %s</span>" % [link(call_tree.target, true), graph_link(call_tree)] << "\n"
73
+ else
74
+ visited << call_tree
75
+
76
+ if call_tree.children.empty?
77
+ output << "<a href=\"#\" class=\"toggle empty\" ></a>" << "\n"
78
+ else
79
+ visible_children = call_tree.children.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
80
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
81
+ output << "<a href=\"#\" class=\"toggle #{image}\" ></a>" << "\n"
82
+ end
83
+ output << "<span>%4.2f%% (%4.2f%%) %s %s</span>" % [percent_total, percent_parent,
84
+ link(call_tree.target, false), graph_link(call_tree)] << "\n"
85
+
86
+ unless call_tree.children.empty?
87
+ output << (expanded ? '<ul>' : '<ul style="display:none">') << "\n"
88
+ call_tree.children.sort_by{|c| -c.total_time}.each do |child_call_tree|
89
+ print_stack(output, visited, child_call_tree, total_time)
90
+ end
91
+ output << '</ul>' << "\n"
92
+ end
93
+
94
+ visited.delete(call_tree)
95
+ end
96
+ output << '</li>' << "\n"
97
+ end
98
+
99
+ def name(call_tree)
100
+ method = call_tree.target
101
+ method.full_name
102
+ end
103
+
104
+ def link(method, recursive)
105
+ method_name = "#{recursive ? '*' : ''}#{method.full_name}"
106
+ if method.source_file.nil?
107
+ h method_name
108
+ else
109
+ file = File.expand_path(method.source_file)
110
+ "<a href=\"file://#{file}##{method.line}\">#{h method_name}</a>"
111
+ end
112
+ end
113
+
114
+ def graph_link(call_tree)
115
+ total_calls = call_tree.target.called
116
+ totals = total_calls.to_s
117
+ "[#{call_tree.called} calls, #{totals} total]"
118
+ end
119
+
120
+ def method_href(method)
121
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_"))
122
+ end
123
+
124
+ def total_time(call_trees)
125
+ sum(call_trees.map{|ci| ci.total_time})
126
+ end
127
+
128
+ def sum(a)
129
+ a.inject(0.0){|s,t| s+=t}
130
+ end
131
+
132
+ def dump(ci)
133
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
134
+ end
135
+
136
+ def color(p)
137
+ case i = p.to_i
138
+ when 0..5
139
+ "01"
140
+ when 5..10
141
+ "05"
142
+ when 100
143
+ "9"
144
+ else
145
+ "#{i/10}"
146
+ end
147
+ end
148
+
149
+ def application
150
+ @options[:application] || $PROGRAM_NAME
151
+ end
152
+
153
+ def arguments
154
+ ARGV.join(' ')
155
+ end
156
+
157
+ def title
158
+ @title ||= @options.delete(:title) || "ruby-prof call tree"
159
+ end
160
+
161
+ def threshold
162
+ @options[:threshold] || 1.0
163
+ end
164
+
165
+ def expansion
166
+ @options[:expansion] || 10.0
167
+ end
168
+
169
+ def base64_image
170
+ @data ||= begin
171
+ file = open_asset('call_stack_printer.png')
172
+ Base64.encode64(file).gsub(/\n/, '')
173
+ end
174
+ end
175
+
176
+ def template
177
+ open_asset('call_stack_printer.html.erb')
178
+ end
179
+ end
180
+ end