ruby-prof 0.11.0.rc1 → 0.11.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGES +20 -5
  2. data/README.rdoc +10 -3
  3. data/ext/ruby_prof/rp_call_info.c +108 -79
  4. data/ext/ruby_prof/rp_call_info.h +1 -0
  5. data/ext/ruby_prof/rp_measure_cpu_time.c +111 -111
  6. data/ext/ruby_prof/rp_measure_gc_runs.c +1 -1
  7. data/ext/ruby_prof/rp_measure_memory.c +1 -1
  8. data/ext/ruby_prof/rp_measure_process_time.c +71 -71
  9. data/ext/ruby_prof/rp_measure_wall_time.c +1 -1
  10. data/ext/ruby_prof/rp_method.c +143 -73
  11. data/ext/ruby_prof/rp_method.h +7 -4
  12. data/ext/ruby_prof/rp_stack.c +16 -1
  13. data/ext/ruby_prof/rp_stack.h +4 -1
  14. data/ext/ruby_prof/rp_thread.c +165 -35
  15. data/ext/ruby_prof/rp_thread.h +8 -2
  16. data/ext/ruby_prof/ruby_prof.c +164 -171
  17. data/ext/ruby_prof/ruby_prof.h +53 -54
  18. data/ext/ruby_prof/vc/ruby_prof.sln +26 -0
  19. data/ext/ruby_prof/vc/ruby_prof.vcxproj +109 -0
  20. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +105 -0
  21. data/lib/ruby-prof/aggregate_call_info.rb +9 -7
  22. data/lib/ruby-prof/call_info.rb +2 -27
  23. data/lib/ruby-prof/call_info_visitor.rb +42 -0
  24. data/lib/ruby-prof/{empty.png → images/empty.png} +0 -0
  25. data/lib/ruby-prof/{minus.png → images/minus.png} +0 -0
  26. data/lib/ruby-prof/{plus.png → images/plus.png} +0 -0
  27. data/lib/ruby-prof/method_info.rb +13 -15
  28. data/lib/ruby-prof/{abstract_printer.rb → printers/abstract_printer.rb} +36 -2
  29. data/lib/ruby-prof/printers/call_info_printer.rb +40 -0
  30. data/lib/ruby-prof/{call_stack_printer.rb → printers/call_stack_printer.rb} +11 -16
  31. data/lib/ruby-prof/{call_tree_printer.rb → printers/call_tree_printer.rb} +4 -4
  32. data/lib/ruby-prof/{dot_printer.rb → printers/dot_printer.rb} +11 -31
  33. data/lib/ruby-prof/{flat_printer.rb → printers/flat_printer.rb} +26 -35
  34. data/lib/ruby-prof/{flat_printer_with_line_numbers.rb → printers/flat_printer_with_line_numbers.rb} +14 -25
  35. data/lib/ruby-prof/printers/graph_html_printer.rb +248 -0
  36. data/lib/ruby-prof/{graph_printer.rb → printers/graph_printer.rb} +31 -73
  37. data/lib/ruby-prof/{multi_printer.rb → printers/multi_printer.rb} +0 -0
  38. data/lib/ruby-prof/profile.rb +27 -22
  39. data/lib/ruby-prof/rack.rb +22 -12
  40. data/ruby-prof.gemspec +58 -0
  41. data/test/aggregate_test.rb +6 -6
  42. data/test/call_info_visitor_test.rb +31 -0
  43. data/test/duplicate_names_test.rb +1 -1
  44. data/test/dynamic_method_test.rb +1 -1
  45. data/test/enumerable_test.rb +1 -1
  46. data/test/exclude_threads_test.rb +2 -2
  47. data/test/gc_test.rb +35 -0
  48. data/test/line_number_test.rb +2 -2
  49. data/test/measure_cpu_time_test.rb +5 -5
  50. data/test/measure_process_time_test.rb +5 -5
  51. data/test/measure_wall_time_test.rb +5 -5
  52. data/test/method_elimination_test.rb +3 -3
  53. data/test/module_test.rb +1 -1
  54. data/test/no_method_class_test.rb +1 -1
  55. data/test/printers_test.rb +16 -8
  56. data/test/recursive_test.rb +115 -91
  57. data/test/stack_test.rb +1 -1
  58. data/test/start_stop_test.rb +13 -13
  59. data/test/summarize_test.rb +48 -0
  60. data/test/test_suite.rb +1 -0
  61. data/test/thread_test.rb +16 -12
  62. data/test/unique_call_path_test.rb +10 -10
  63. metadata +64 -85
  64. data/lib/1.8/ruby_prof.so +0 -0
  65. data/lib/1.9/ruby_prof.exp +0 -0
  66. data/lib/1.9/ruby_prof.ilk +0 -0
  67. data/lib/1.9/ruby_prof.lib +0 -0
  68. data/lib/1.9/ruby_prof.pdb +0 -0
  69. data/lib/1.9/ruby_prof.so +0 -0
  70. data/lib/ruby-prof.rb +0 -70
  71. data/lib/ruby-prof/graph_html_printer.rb +0 -286
  72. data/lib/ruby-prof/symbol_to_proc.rb +0 -10
  73. data/lib/ruby_prof.exp +0 -0
  74. data/lib/ruby_prof.ilk +0 -0
  75. data/lib/ruby_prof.lib +0 -0
  76. data/lib/ruby_prof.pdb +0 -0
  77. data/lib/ruby_prof.so +0 -0
  78. data/lib/unprof.rb +0 -10
  79. data/test/do_nothing.rb +0 -0
@@ -0,0 +1,42 @@
1
+ # The call info visitor class does a depth-first traversal
2
+ # across a thread's call stack. At each call_info node,
3
+ # the visitor executes the block provided in the
4
+ # #visit method. The block is passed two parameters, the
5
+ # event and the call_info instance. Event will be
6
+ # either :enter or :exit.
7
+ #
8
+ # visitor = RubyProf::CallInfoVisitor.new(result.threads.first)
9
+ #
10
+ # method_names = Array.new
11
+ #
12
+ # visitor.visit do |call_info, event|
13
+ # method_names << call_info.target.full_name if event == :enter
14
+ # end
15
+ #
16
+ # puts method_names
17
+
18
+ module RubyProf
19
+ class CallInfoVisitor
20
+ attr_reader :block, :thread
21
+
22
+ def initialize(thread)
23
+ @thread = thread
24
+ end
25
+
26
+ def visit(&block)
27
+ @block = block
28
+
29
+ self.thread.top_method.call_infos.each do |call_info|
30
+ self.visit_call_info(call_info)
31
+ end
32
+ end
33
+
34
+ def visit_call_info(call_info)
35
+ self.block.call(call_info, :enter)
36
+ call_info.children.each do |child|
37
+ visit_call_info(child)
38
+ end
39
+ self.block.call(call_info, :exit)
40
+ end
41
+ end
42
+ end
File without changes
File without changes
File without changes
@@ -29,7 +29,7 @@ module RubyProf
29
29
  def total_time
30
30
  @total_time ||= begin
31
31
  call_infos.inject(0) do |sum, call_info|
32
- sum += call_info.total_time if call_info.minimal?
32
+ sum += call_info.total_time unless call_info.recursive
33
33
  sum
34
34
  end
35
35
  end
@@ -38,7 +38,8 @@ module RubyProf
38
38
  def self_time
39
39
  @self_time ||= begin
40
40
  call_infos.inject(0) do |sum, call_info|
41
- sum += call_info.self_time
41
+ sum += call_info.self_time unless call_info.recursive
42
+ sum
42
43
  end
43
44
  end
44
45
  end
@@ -46,7 +47,8 @@ module RubyProf
46
47
  def wait_time
47
48
  @wait_time ||= begin
48
49
  call_infos.inject(0) do |sum, call_info|
49
- sum += call_info.wait_time
50
+ sum += call_info.wait_time unless call_info.recursive
51
+ sum
50
52
  end
51
53
  end
52
54
  end
@@ -54,7 +56,7 @@ module RubyProf
54
56
  def children_time
55
57
  @children_time ||= begin
56
58
  call_infos.inject(0) do |sum, call_info|
57
- sum += call_info.children_time if call_info.minimal?
59
+ sum += call_info.children_time unless call_info.recursive
58
60
  sum
59
61
  end
60
62
  end
@@ -74,6 +76,12 @@ module RubyProf
74
76
  end
75
77
  end
76
78
 
79
+ def recursive?
80
+ call_infos.detect do |call_info|
81
+ call_info.recursive
82
+ end
83
+ end
84
+
77
85
  def children
78
86
  @children ||= begin
79
87
  call_infos.map do |call_info|
@@ -109,17 +117,7 @@ module RubyProf
109
117
  end
110
118
 
111
119
  def to_s
112
- full_name
113
- end
114
-
115
- def dump
116
- res = ""
117
- res << "MINFO: #{klass_name}##{method_name} total_time: #{total_time} (#{full_name})\n"
118
- call_infos.each do |ci|
119
- pinfo = ci.root? ? "TOPLEVEL" : (p=ci.parent.target; "#{p.klass_name}##{p.method_name} (#{ci.parent.object_id}) (#{p.full_name})")
120
- res << "CINFO[#{ci.object_id}] called #{ci.called} times from #{pinfo}\n"
121
- end
122
- res
120
+ "#{self.full_name} (c: #{self.called}, tt: #{self.total_time}, st: #{self.self_time}, ct: #{self.children_time})"
123
121
  end
124
122
 
125
123
  # remove method from the call graph. should not be called directly.
@@ -2,10 +2,12 @@
2
2
 
3
3
  module RubyProf
4
4
  class AbstractPrinter
5
+ # Create a new printer.
6
+ #
7
+ # result should be the output generated from a profiling run
5
8
  def initialize(result)
6
9
  @result = result
7
10
  @output = nil
8
- @options = {}
9
11
  end
10
12
 
11
13
  # Specify print options.
@@ -47,5 +49,37 @@ module RubyProf
47
49
  end
48
50
  name
49
51
  end
52
+
53
+ # Print a profiling report to the provided output.
54
+ #
55
+ # output - Any IO object, including STDOUT or a file.
56
+ # The default value is STDOUT.
57
+ #
58
+ # options - Hash of print options. See #setup_options
59
+ # for more information. Note that each printer can
60
+ # define its own set of options.
61
+ def print(output = STDOUT, options = {})
62
+ @output = output
63
+ setup_options(options)
64
+ print_threads
65
+ end
66
+
67
+ def print_threads
68
+ @result.threads.each do |thread|
69
+ print_thread(thread)
70
+ end
71
+ end
72
+
73
+ def print_thread(thread)
74
+ print_header(thread)
75
+ print_methods(thread)
76
+ print_footer(thread)
77
+ end
78
+
79
+ def print_header(thread)
80
+ end
81
+
82
+ def print_footer(thread)
83
+ end
50
84
  end
51
- end
85
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # Prints out the call graph based on CallInfo instances. This
5
+ # is mainly for debugging purposes as it provides access into
6
+ # into RubyProf's internals.
7
+
8
+ class CallInfoPrinter < AbstractPrinter
9
+ TIME_WIDTH = 0
10
+
11
+ private
12
+
13
+ def print_header(thread)
14
+ @output << "Thread ID: #{thread.id}\n"
15
+ @output << "Total Time: #{thread.top_method.total_time}\n"
16
+ @output << "Sort by: #{sort_method}\n"
17
+ @output << "\n"
18
+ end
19
+
20
+ def print_methods(thread)
21
+ visitor = CallInfoVisitor.new(thread)
22
+
23
+ visitor.visit do |call_info, event|
24
+ if event == :enter
25
+ @output << " " * call_info.depth
26
+ @output << call_info.target.full_name
27
+ @output << " ("
28
+ @output << "tt:#{sprintf("%#{TIME_WIDTH}.2f", call_info.total_time)}, "
29
+ @output << "st:#{sprintf("%#{TIME_WIDTH}.2f", call_info.self_time)}, "
30
+ @output << "wt:#{sprintf("%#{TIME_WIDTH}.2f", call_info.wait_time)}, "
31
+ @output << "ct:#{sprintf("%#{TIME_WIDTH}.2f", call_info.children_time)}, "
32
+ @output << "call:#{call_info.called}, "
33
+ @output << "rec:#{call_info.recursive}"
34
+ @output << ")"
35
+ @output << "\n"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -42,28 +42,23 @@ module RubyProf
42
42
  @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
43
43
  end
44
44
 
45
- @overall_threads_time = 0.0
46
- @threads_totals = Hash.new
47
- @result.threads.each do |thread_id, methods|
48
- roots = methods.select{|m| m.root?}
49
- thread_total_time = sum(roots.map{|r| self.total_time(r.call_infos)})
50
- @overall_threads_time += thread_total_time
51
- @threads_totals[thread_id] = thread_total_time
52
- end
53
-
54
45
  print_header
55
46
 
56
- @result.threads.keys.sort.each do |thread_id|
57
- @current_thread_id = thread_id
58
- @overall_time = @threads_totals[thread_id]
59
- @output.print "<div class=\"thread\">Thread: #{thread_id} (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})</div>"
47
+ @overall_threads_time = @result.threads.inject(0) do |val, thread|
48
+ val += thread.top_method.total_time
49
+ end
50
+
51
+ @result.threads.each do |thread|
52
+ @current_thread_id = thread.id
53
+ @overall_time = thread.top_method.total_time
54
+ @output.print "<div class=\"thread\">Thread: #{thread.id} (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})</div>"
60
55
  @output.print "<ul name=\"thread\">"
61
- @result.threads[thread_id].each do |m|
56
+ thread.methods.each do |m|
62
57
  # $stderr.print m.dump
63
58
  next unless m.root?
64
59
  m.call_infos.each do |ci|
65
60
  next unless ci.root?
66
- print_stack ci, @threads_totals[thread_id]
61
+ print_stack ci, thread.top_method.total_time
67
62
  end
68
63
  end
69
64
  @output.print "</ul>"
@@ -185,7 +180,7 @@ module RubyProf
185
180
  def copy_image_files
186
181
  if @output.is_a?(File)
187
182
  target_dir = File.dirname(@output.path)
188
- image_dir = File.dirname(__FILE__)
183
+ image_dir = File.join(File.dirname(__FILE__), '..', 'images')
189
184
  %w(empty plus minus).each do |img|
190
185
  source_file = "#{image_dir}/#{img}.png"
191
186
  target_file = "#{target_dir}/#{img}.png"
@@ -54,8 +54,8 @@ module RubyProf
54
54
  end
55
55
 
56
56
  def print_threads
57
- @result.threads.each do |thread_id, methods|
58
- print_methods(thread_id, methods)
57
+ @result.threads.each do |thread|
58
+ print_thread(thread)
59
59
  end
60
60
  end
61
61
 
@@ -67,8 +67,8 @@ module RubyProf
67
67
  File.expand_path(method.source_file)
68
68
  end
69
69
 
70
- def print_methods(thread_id, methods)
71
- methods.reverse_each do |method|
70
+ def print_thread(thread)
71
+ thread.methods.reverse_each do |method|
72
72
  # Print out the file and method name
73
73
  @output << "fl=#{file(method)}\n"
74
74
  @output << "fn=#{method_name(method)}\n"
@@ -45,10 +45,8 @@ module RubyProf
45
45
  @output = output
46
46
  setup_options(options)
47
47
 
48
- total_time = thread_times.values.inject{|a,b| a+b}
49
-
50
48
  puts 'digraph "Profile" {'
51
- puts "label=\"#{mode_name} >=#{min_percent}%\\nTotal: #{total_time}\";"
49
+ #puts "label=\"#{mode_name} >=#{min_percent}%\\nTotal: #{total_time}\";"
52
50
  puts "labelloc=t;"
53
51
  puts "labeljust=l;"
54
52
  print_threads
@@ -64,32 +62,14 @@ module RubyProf
64
62
  RubyProf.constants.find{|c| RubyProf.const_get(c) == RubyProf.measure_mode}
65
63
  end
66
64
 
67
- # Computes the total time per thread:
68
- def thread_times
69
- @thread_times ||= begin
70
- times = {}
71
- @result.threads.each do |thread_id, methods|
72
- toplevel = methods.sort.last
73
-
74
- total_time = toplevel.total_time
75
- # This looks like a hack for very small times... from GraphPrinter
76
- total_time = 0.01 if total_time == 0
77
- times[thread_id] = total_time
78
- end
79
-
80
- times
81
- end
82
- end
83
-
84
65
  def print_threads
85
- # sort assumes that spawned threads have higher object_ids
86
- @result.threads.sort.each do |thread_id, methods|
87
- puts "subgraph \"Thread #{thread_id}\" {"
88
-
89
- print_methods(thread_id, methods)
66
+ @result.threads.each do |thread|
67
+ puts "subgraph \"Thread #{thread.id}\" {"
68
+
69
+ print_thread(thread)
90
70
  puts "}"
91
71
 
92
- print_classes(thread_id, methods)
72
+ print_classes(thread)
93
73
  end
94
74
  end
95
75
 
@@ -98,9 +78,9 @@ module RubyProf
98
78
  subject.object_id
99
79
  end
100
80
 
101
- def print_methods(thread_id, methods)
102
- total_time = thread_times[thread_id]
103
- methods.sort_by(&sort_method).reverse_each do |method|
81
+ def print_thread(thread)
82
+ total_time = thread.top_method.total_time
83
+ thread.methods.sort_by(&sort_method).reverse_each do |method|
104
84
  total_percentage = (method.total_time/total_time) * 100
105
85
 
106
86
  next if total_percentage < min_percent
@@ -111,9 +91,9 @@ module RubyProf
111
91
  end
112
92
  end
113
93
 
114
- def print_classes(thread_id, methods)
94
+ def print_classes(thread)
115
95
  grouped = {}
116
- methods.each{|m| grouped[m.klass_name] ||= []; grouped[m.klass_name] << m}
96
+ thread.methods.each{|m| grouped[m.klass_name] ||= []; grouped[m.klass_name] << m}
117
97
  grouped.each do |cls, methods2|
118
98
  # Filter down to just seen methods
119
99
  big_methods, small_methods = methods2.partition{|m| @seen_methods.include? m}
@@ -12,46 +12,31 @@ module RubyProf
12
12
  # printer.print(STDOUT, {})
13
13
  #
14
14
  class FlatPrinter < AbstractPrinter
15
- # Print a flat profile report to the provided output.
16
- #
17
- # output - Any IO object, including STDOUT or a file.
18
- # The default value is STDOUT.
19
- #
20
- # options - Hash of print options. See #setup_options
21
- # for more information.
22
- #
23
- def print(output = STDOUT, options = {})
24
- @output = output
25
- # Now sort methods by largest self time by default,
26
- # not total time like in other printouts
27
- options[:sort_method] ||= :self_time
28
- setup_options(options)
29
- print_threads
15
+ # Override for this printer to sort by self time by default
16
+ def sort_method
17
+ @options[:sort_method] || :self_time
30
18
  end
31
19
 
32
20
  private
33
21
 
34
- def print_threads
35
- @result.threads.each do |thread_id, methods|
36
- print_methods(thread_id, methods)
37
- @output << "\n" * 2
38
- end
39
- end
40
-
41
- def print_methods(thread_id, methods)
42
- # Get total time
43
- toplevel = methods.max
44
- total_time = toplevel.total_time
45
- if total_time == 0
46
- total_time = 0.01
47
- end
48
-
49
- methods = methods.sort_by(&sort_method).reverse
22
+ #def print_threads
23
+ # @result.threads.each do |thread|
24
+ # print_thread(thread)
25
+ # @output << "\n" * 2
26
+ # end
27
+ #end
50
28
 
51
- @output << "Thread ID: %d\n" % thread_id
52
- @output << "Total: %0.6f\n" % total_time
29
+ def print_header(thread)
30
+ @output << "Thread ID: %d\n" % thread.id
31
+ @output << "Total: %0.6f\n" % thread.top_method.total_time
32
+ @output << "Sort by: #{sort_method}\n"
53
33
  @output << "\n"
54
- @output << " %self total self wait child calls name\n"
34
+ @output << " %self total self wait child calls name\n"
35
+ end
36
+
37
+ def print_methods(thread)
38
+ total_time = thread.top_method.total_time
39
+ methods = thread.methods.sort_by(&sort_method).reverse
55
40
 
56
41
  sum = 0
57
42
  methods.each do |method|
@@ -62,16 +47,22 @@ module RubyProf
62
47
  #self_time_called = method.called > 0 ? method.self_time/method.called : 0
63
48
  #total_time_called = method.called > 0? method.total_time/method.called : 0
64
49
 
65
- @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s\n" % [
50
+ @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s%s \n" % [
66
51
  method.self_time / total_time * 100, # %self
67
52
  method.total_time, # total
68
53
  method.self_time, # self
69
54
  method.wait_time, # wait
70
55
  method.children_time, # children
71
56
  method.called, # calls
57
+ method.recursive? ? "*" : " ", # cycle
72
58
  method_name(method) # name
73
59
  ]
74
60
  end
75
61
  end
62
+
63
+ def print_footer(thread)
64
+ @output << "\n"
65
+ @output << "* in front of method name means it is recursively called\n"
66
+ end
76
67
  end
77
68
  end