ruby-prof 0.8.2 → 0.9.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 (74) hide show
  1. data/CHANGES +23 -13
  2. data/{README → README.rdoc} +118 -111
  3. data/Rakefile +14 -26
  4. data/bin/ruby-prof +16 -4
  5. data/examples/empty.png +0 -0
  6. data/examples/graph.dot +106 -0
  7. data/examples/graph.png +0 -0
  8. data/examples/minus.png +0 -0
  9. data/examples/multi.flat.txt +23 -0
  10. data/examples/multi.graph.html +906 -0
  11. data/examples/multi.grind.dat +194 -0
  12. data/examples/multi.stack.html +573 -0
  13. data/examples/plus.png +0 -0
  14. data/examples/stack.html +573 -0
  15. data/ext/ruby_prof/extconf.rb +0 -1
  16. data/ext/ruby_prof/measure_allocations.h +6 -6
  17. data/ext/ruby_prof/measure_cpu_time.h +5 -5
  18. data/ext/ruby_prof/measure_gc_runs.h +1 -1
  19. data/ext/ruby_prof/measure_gc_time.h +1 -1
  20. data/ext/ruby_prof/measure_memory.h +4 -4
  21. data/ext/ruby_prof/measure_process_time.h +1 -1
  22. data/ext/ruby_prof/measure_wall_time.h +1 -1
  23. data/ext/ruby_prof/ruby_prof.c +240 -167
  24. data/ext/ruby_prof/ruby_prof.h +12 -10
  25. data/ext/ruby_prof/version.h +3 -3
  26. data/lib/ruby-prof.rb +7 -1
  27. data/lib/ruby-prof/abstract_printer.rb +5 -5
  28. data/lib/ruby-prof/aggregate_call_info.rb +9 -3
  29. data/lib/ruby-prof/call_info.rb +66 -1
  30. data/lib/ruby-prof/call_stack_printer.rb +751 -0
  31. data/lib/ruby-prof/call_tree_printer.rb +2 -2
  32. data/lib/ruby-prof/dot_printer.rb +151 -0
  33. data/lib/ruby-prof/empty.png +0 -0
  34. data/lib/ruby-prof/flat_printer.rb +16 -16
  35. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +13 -13
  36. data/lib/ruby-prof/graph_html_printer.rb +58 -36
  37. data/lib/ruby-prof/graph_printer.rb +30 -30
  38. data/lib/ruby-prof/method_info.rb +24 -4
  39. data/lib/ruby-prof/minus.png +0 -0
  40. data/lib/ruby-prof/multi_printer.rb +54 -0
  41. data/lib/ruby-prof/plus.png +0 -0
  42. data/lib/ruby-prof/rack.rb +28 -0
  43. data/lib/ruby-prof/result.rb +70 -0
  44. data/lib/ruby-prof/symbol_to_proc.rb +1 -1
  45. data/lib/ruby-prof/task.rb +19 -19
  46. data/lib/ruby-prof/test.rb +3 -3
  47. data/lib/ruby_prof.so +0 -0
  48. data/rails/environment/profile.rb +2 -2
  49. data/rails/example/example_test.rb +2 -2
  50. data/rails/profile_test_helper.rb +1 -1
  51. data/test/aggregate_test.rb +21 -6
  52. data/test/basic_test.rb +3 -3
  53. data/test/duplicate_names_test.rb +2 -2
  54. data/test/enumerable_test.rb +2 -2
  55. data/test/exceptions_test.rb +2 -2
  56. data/test/exclude_threads_test.rb +45 -45
  57. data/test/exec_test.rb +2 -2
  58. data/test/line_number_test.rb +11 -11
  59. data/test/measurement_test.rb +4 -3
  60. data/test/method_elimination_test.rb +74 -0
  61. data/test/module_test.rb +7 -7
  62. data/test/multi_printer_test.rb +81 -0
  63. data/test/no_method_class_test.rb +2 -2
  64. data/test/prime.rb +7 -10
  65. data/test/printers_test.rb +57 -47
  66. data/test/recursive_test.rb +23 -62
  67. data/test/singleton_test.rb +3 -2
  68. data/test/stack_printer_test.rb +74 -0
  69. data/test/stack_test.rb +1 -1
  70. data/test/start_stop_test.rb +2 -2
  71. data/test/test_suite.rb +9 -0
  72. data/test/thread_test.rb +17 -17
  73. data/test/unique_call_path_test.rb +4 -4
  74. metadata +29 -8
@@ -15,7 +15,7 @@ module RubyProf
15
15
  def output_dir
16
16
  PROFILE_OPTIONS[:output_dir]
17
17
  end
18
-
18
+
19
19
  def run(result)
20
20
  return if @method_name.to_s == "default_test"
21
21
 
@@ -108,7 +108,7 @@ module RubyProf
108
108
  def report_profile(data, measure_mode)
109
109
  PROFILE_OPTIONS[:printers].each do |printer_klass|
110
110
  printer = printer_klass.new(data)
111
-
111
+
112
112
  # Makes sure the output directory exits
113
113
  FileUtils.mkdir_p(output_dir)
114
114
 
@@ -145,4 +145,4 @@ module RubyProf
145
145
  end
146
146
  end
147
147
  end
148
- end
148
+ end
Binary file
@@ -5,7 +5,7 @@
5
5
  # definitely not use the production databse!
6
6
 
7
7
 
8
- # Cache classes - otherwise your code
8
+ # Cache classes - otherwise your code
9
9
  # will run approximately 5 times slower and the
10
10
  # profiling results will be overwhelmed by Rails
11
11
  # dependency loading mechanism
@@ -21,4 +21,4 @@ config.action_view.cache_template_loading = true
21
21
  config.action_controller.perform_caching = false
22
22
 
23
23
  # Turn off most logging
24
- config.log_level = :info
24
+ config.log_level = :info
@@ -2,8 +2,8 @@ require File.dirname(__FILE__) + '../profile_test_helper'
2
2
 
3
3
  class ExampleTest < Test::Unit::TestCase
4
4
  include RubyProf::Test
5
-
5
+
6
6
  def test_stuff
7
7
  puts "Test method"
8
8
  end
9
- end
9
+ end
@@ -17,5 +17,5 @@ ENV["RAILS_ENV"] = env
17
17
  require 'ruby-prof'
18
18
 
19
19
  # Setup output directory to Rails tmp directory
20
- RubyProf::Test::PROFILE_OPTIONS[:output_dir] =
20
+ RubyProf::Test::PROFILE_OPTIONS[:output_dir] =
21
21
  File.expand_path(File.join(RAILS_ROOT, 'tmp', 'profile'))
@@ -14,7 +14,7 @@ class AggClass
14
14
  def z
15
15
  sleep 1
16
16
  end
17
-
17
+
18
18
  def a
19
19
  z
20
20
  end
@@ -24,7 +24,7 @@ class AggClass
24
24
  end
25
25
 
26
26
  def c
27
- a
27
+ a
28
28
  end
29
29
  end
30
30
 
@@ -34,6 +34,21 @@ class AggregateTest < Test::Unit::TestCase
34
34
  RubyProf::measure_mode = RubyProf::WALL_TIME
35
35
  end
36
36
 
37
+ def test_all_call_infos_are_minimal_as_there_is_no_recursion
38
+ c1 = AggClass.new
39
+ result = RubyProf.profile do
40
+ c1.a
41
+ c1.b
42
+ c1.c
43
+ end
44
+ methods = result.threads.values.first.sort.reverse
45
+ methods.each do |m|
46
+ m.call_infos.each do |ci|
47
+ assert ci.minimal?, "#{ci.call_sequence} should be minimal in the call tree"
48
+ end
49
+ end
50
+ end
51
+
37
52
  def test_call_infos
38
53
  c1 = AggClass.new
39
54
  result = RubyProf.profile do
@@ -43,7 +58,7 @@ class AggregateTest < Test::Unit::TestCase
43
58
  end
44
59
 
45
60
  methods = result.threads.values.first.sort.reverse
46
- method = methods.find {|method| method.full_name == 'AggClass#z'}
61
+ method = methods.find {|meth| meth.full_name == 'AggClass#z'}
47
62
 
48
63
  # Check AggClass#z
49
64
  assert_equal('AggClass#z', method.full_name)
@@ -76,7 +91,7 @@ class AggregateTest < Test::Unit::TestCase
76
91
  end
77
92
 
78
93
  methods = result.threads.values.first.sort.reverse
79
- method = methods.find {|method| method.full_name == 'AggClass#z'}
94
+ method = methods.find {|meth| meth.full_name == 'AggClass#z'}
80
95
 
81
96
  # Check AggClass#z
82
97
  assert_equal('AggClass#z', method.full_name)
@@ -102,7 +117,7 @@ class AggregateTest < Test::Unit::TestCase
102
117
  end
103
118
 
104
119
  methods = result.threads.values.first.sort.reverse
105
- method = methods.find {|method| method.full_name == 'AggClass#a'}
120
+ method = methods.find {|meth| meth.full_name == 'AggClass#a'}
106
121
 
107
122
  # Check AggClass#a
108
123
  assert_equal('AggClass#a', method.full_name)
@@ -118,4 +133,4 @@ class AggregateTest < Test::Unit::TestCase
118
133
  assert_in_delta(3, call_info.children_time, 0.05)
119
134
  assert_equal(3, call_info.called)
120
135
  end
121
- end
136
+ end
@@ -7,7 +7,7 @@ class C1
7
7
  def C1.hello
8
8
  sleep(0.1)
9
9
  end
10
-
10
+
11
11
  def hello
12
12
  sleep(0.2)
13
13
  end
@@ -115,7 +115,7 @@ class BasicTest < Test::Unit::TestCase
115
115
  assert_in_delta(0, methods[2].wait_time, 0.01)
116
116
  assert_in_delta(0.1, methods[2].self_time, 0.01)
117
117
  end
118
-
118
+
119
119
  if RUBY_VERSION < '1.9'
120
120
  PARENT = Object
121
121
  else
@@ -225,7 +225,7 @@ class BasicTest < Test::Unit::TestCase
225
225
  assert_equal('Class#new', names[3])
226
226
  assert(names.include?("<Class::#{PARENT}>#allocate"))
227
227
  assert(names.include?("#{PARENT}#initialize"))
228
-
228
+
229
229
  # Check times
230
230
  assert_in_delta(0.3, methods[0].total_time, 0.1)
231
231
  assert_in_delta(0, methods[0].wait_time, 0.1)
@@ -19,10 +19,10 @@ class DuplicateNames < Test::Unit::TestCase
19
19
  eval str
20
20
  Foo::Bar.new.foo
21
21
  end
22
-
22
+
23
23
  # There should be 3 foo methods
24
24
  methods = result.threads.values.first.sort.reverse
25
-
25
+
26
26
  methods = methods.select do |method|
27
27
  method.full_name == 'DuplicateNames::Foo::Bar#foo'
28
28
  end
@@ -11,6 +11,6 @@ class EnumerableTest < Test::Unit::TestCase
11
11
  result = RubyProf.profile do
12
12
  3.times { [1,2,3].any? {|n| n} }
13
13
  end
14
- assert result.threads.to_a.first[1].length == 4
14
+ assert result.threads.to_a.first[1].length == 4
15
15
  end
16
- end
16
+ end
@@ -5,11 +5,11 @@ require 'ruby-prof'
5
5
  class ExceptionsTest < Test::Unit::TestCase
6
6
  def test_profile
7
7
  result = begin
8
- RubyProf.profile do
8
+ RubyProf.profile do
9
9
  raise(RuntimeError, 'Test error')
10
10
  end
11
11
  rescue => e
12
- end
12
+ end
13
13
  assert_not_nil(result)
14
14
  end
15
15
  end
@@ -6,49 +6,49 @@ require 'ruby-prof'
6
6
 
7
7
  # -- Tests ----
8
8
  class ExcludeThreadsTest < Test::Unit::TestCase
9
- def test_exclude_threads
10
-
11
- def thread1_proc
12
- sleep(0.5)
13
- sleep(2)
14
- end
15
-
16
- def thread2_proc
17
- sleep(0.5)
18
- sleep(2)
19
- end
20
-
21
- thread1 = Thread.new do
22
- thread1_proc
23
- end
24
-
25
- thread2 = Thread.new do
26
- thread2_proc
27
- end
28
-
29
- RubyProf::exclude_threads = [ thread2 ]
30
-
31
- RubyProf.start
32
-
33
- thread1.join
34
- thread2.join
35
-
36
- result = RubyProf.stop
37
-
38
- RubyProf::exclude_threads = nil
39
-
40
- assert_equal(2, result.threads.length)
41
-
42
- output = Array.new
43
- result.threads.each do | thread_id, methods |
44
- methods.each do | m |
45
- if m.full_name.index("ExcludeThreadsTest#thread") == 0
46
- output.push(m.full_name)
47
- end
48
- end
49
- end
50
-
51
- assert_equal(1, output.length)
52
- assert_equal("ExcludeThreadsTest#thread1_proc", output[0])
9
+ def test_exclude_threads
10
+
11
+ def thread1_proc
12
+ sleep(0.5)
13
+ sleep(2)
14
+ end
15
+
16
+ def thread2_proc
17
+ sleep(0.5)
18
+ sleep(2)
19
+ end
20
+
21
+ thread1 = Thread.new do
22
+ thread1_proc
23
+ end
24
+
25
+ thread2 = Thread.new do
26
+ thread2_proc
27
+ end
28
+
29
+ RubyProf::exclude_threads = [ thread2 ]
30
+
31
+ RubyProf.start
32
+
33
+ thread1.join
34
+ thread2.join
35
+
36
+ result = RubyProf.stop
37
+
38
+ RubyProf::exclude_threads = nil
39
+
40
+ assert_equal(2, result.threads.length)
41
+
42
+ output = Array.new
43
+ result.threads.each do | thread_id, methods |
44
+ methods.each do | m |
45
+ if m.full_name.index("ExcludeThreadsTest#thread") == 0
46
+ output.push(m.full_name)
47
+ end
48
+ end
49
+ end
50
+
51
+ assert_equal(1, output.length)
52
+ assert_equal("ExcludeThreadsTest#thread1_proc", output[0])
53
53
  end
54
- end
54
+ end
@@ -9,6 +9,6 @@ class EnumerableTest < Test::Unit::TestCase
9
9
  def test_being_able_to_run_its_binary
10
10
  Dir.chdir(File.dirname(__FILE__)) do
11
11
  assert system(OS.ruby_bin + " ruby-prof-bin do_nothing.rb")
12
- end
12
+ end
13
13
  end
14
- end
14
+ end
@@ -7,12 +7,12 @@ class LineNumbers
7
7
  def method1
8
8
  a = 3
9
9
  end
10
-
10
+
11
11
  def method2
12
12
  a = 3
13
13
  method1
14
14
  end
15
-
15
+
16
16
  def method3
17
17
  sleep(1)
18
18
  end
@@ -22,30 +22,30 @@ end
22
22
  class LineNumbersTest < Test::Unit::TestCase
23
23
  def test_function_line_no
24
24
  numbers = LineNumbers.new
25
-
25
+
26
26
  result = RubyProf.profile do
27
27
  numbers.method2
28
28
  end
29
29
 
30
30
  methods = result.threads.values.first.sort.reverse
31
31
  assert_equal(3, methods.length)
32
-
32
+
33
33
  method = methods[0]
34
34
  assert_equal('LineNumbersTest#test_function_line_no', method.full_name)
35
35
  assert_equal(27, method.line)
36
-
36
+
37
37
  method = methods[1]
38
38
  assert_equal('LineNumbers#method2', method.full_name)
39
39
  assert_equal(11, method.line)
40
-
40
+
41
41
  method = methods[2]
42
42
  assert_equal('LineNumbers#method1', method.full_name)
43
43
  assert_equal(7, method.line)
44
44
  end
45
-
45
+
46
46
  def test_c_function
47
47
  numbers = LineNumbers.new
48
-
48
+
49
49
  result = RubyProf.profile do
50
50
  numbers.method3
51
51
  end
@@ -61,13 +61,13 @@ class LineNumbersTest < Test::Unit::TestCase
61
61
  method = methods[0]
62
62
  assert_equal('Kernel#sleep', method.full_name)
63
63
  assert_equal(0, method.line)
64
-
64
+
65
65
  method = methods[1]
66
66
  assert_equal('LineNumbers#method3', method.full_name)
67
67
  assert_equal(16, method.line)
68
-
68
+
69
69
  method = methods[2]
70
70
  assert_equal('LineNumbersTest#test_c_function', method.full_name)
71
71
  assert_equal(50, method.line)
72
72
  end
73
- end
73
+ end
@@ -42,7 +42,7 @@ class MeasurementTest < Test::Unit::TestCase
42
42
  RubyProf::measure_mode = RubyProf::CPU_TIME
43
43
  assert_equal(RubyProf::CPU_TIME, RubyProf::measure_mode)
44
44
  end
45
-
45
+
46
46
  def test_cpu_time
47
47
  RubyProf.cpu_frequency = 2.33e9
48
48
 
@@ -82,8 +82,9 @@ class MeasurementTest < Test::Unit::TestCase
82
82
  u = RubyProf.measure_memory
83
83
  assert(u >= t, [t, u].inspect)
84
84
 
85
+ RubyProf::measure_mode = RubyProf::MEMORY
85
86
  result = RubyProf.profile {Array.new}
86
- total = result.threads.values.first.methods.inject(0) { |sum, m| sum + m.total_time }
87
+ total = result.threads.values.first.inject(0) { |sum, m| sum + m.total_time }
87
88
 
88
89
  assert(total > 0, 'Should measure more than zero kilobytes of memory usage')
89
90
  assert_not_equal(0, total % 1, 'Should not truncate fractional kilobyte measurements')
@@ -118,4 +119,4 @@ class MeasurementTest < Test::Unit::TestCase
118
119
  assert u > t, [t, u].inspect
119
120
  end
120
121
  end
121
- end
122
+ end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'ruby-prof'
5
+ require 'tmpdir'
6
+
7
+ # Test data
8
+ # A
9
+ # / \
10
+ # B C
11
+ # \
12
+ # B
13
+
14
+ class ESTPT
15
+ def a
16
+ 100.times{b}
17
+ 300.times{c}
18
+ c;c;c
19
+ end
20
+
21
+ def b
22
+ sleep 0
23
+ end
24
+
25
+ def c
26
+ 5.times{b}
27
+ end
28
+ end
29
+
30
+ class MethodEliminationTest < Test::Unit::TestCase
31
+ def setup
32
+ # Need to use wall time for this test due to the sleep calls
33
+ RubyProf::measure_mode = RubyProf::WALL_TIME
34
+ end
35
+
36
+ def test_setting_parent
37
+ result = RubyProf.profile do
38
+ 1000.times { 1+1 }
39
+ end
40
+ method_infos = result.threads.values.first
41
+ assert(m1 = method_infos[0])
42
+ assert(c1 = m1.call_infos.first)
43
+ assert_equal(c1, c1.parent = c1)
44
+ assert_equal c1, c1.parent
45
+ end
46
+
47
+ def test_methods_can_be_eliminated
48
+ RubyProf.start
49
+ 5.times{ESTPT.new.a}
50
+ result = RubyProf.stop
51
+ # result.dump
52
+ eliminated = result.eliminate_methods!([/Integer#times/])
53
+ # puts eliminated.inspect
54
+ # result.dump
55
+ eliminated.each do |m|
56
+ assert_method_has_been_eliminated(result, m)
57
+ end
58
+ end
59
+
60
+ private
61
+ def assert_method_has_been_eliminated(result, eliminated_method)
62
+ result.threads.each do |thread_id, methods|
63
+ methods.each do |method|
64
+ method.call_infos.each do |ci|
65
+ assert(ci.target != eliminated_method, "broken self")
66
+ assert(ci.parent.target != eliminated_method, "broken parent") if ci.parent
67
+ ci.children.each do |callee|
68
+ assert(callee.target != eliminated_method, "broken kid")
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end