ruby-prof 0.11.0.rc1-x86-mingw32 → 0.11.0.rc2-x86-mingw32

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 (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/1.8/ruby_prof.so +0 -0
  22. data/lib/1.9/ruby_prof.so +0 -0
  23. data/lib/ruby-prof/aggregate_call_info.rb +9 -7
  24. data/lib/ruby-prof/call_info.rb +2 -27
  25. data/lib/ruby-prof/call_info_visitor.rb +42 -0
  26. data/lib/ruby-prof/{empty.png → images/empty.png} +0 -0
  27. data/lib/ruby-prof/{minus.png → images/minus.png} +0 -0
  28. data/lib/ruby-prof/{plus.png → images/plus.png} +0 -0
  29. data/lib/ruby-prof/method_info.rb +13 -15
  30. data/lib/ruby-prof/{abstract_printer.rb → printers/abstract_printer.rb} +36 -2
  31. data/lib/ruby-prof/printers/call_info_printer.rb +40 -0
  32. data/lib/ruby-prof/{call_stack_printer.rb → printers/call_stack_printer.rb} +11 -16
  33. data/lib/ruby-prof/{call_tree_printer.rb → printers/call_tree_printer.rb} +4 -4
  34. data/lib/ruby-prof/{dot_printer.rb → printers/dot_printer.rb} +11 -31
  35. data/lib/ruby-prof/{flat_printer.rb → printers/flat_printer.rb} +26 -35
  36. data/lib/ruby-prof/{flat_printer_with_line_numbers.rb → printers/flat_printer_with_line_numbers.rb} +14 -25
  37. data/lib/ruby-prof/printers/graph_html_printer.rb +248 -0
  38. data/lib/ruby-prof/{graph_printer.rb → printers/graph_printer.rb} +31 -73
  39. data/lib/ruby-prof/{multi_printer.rb → printers/multi_printer.rb} +0 -0
  40. data/lib/ruby-prof/profile.rb +27 -22
  41. data/lib/ruby-prof/rack.rb +22 -12
  42. data/ruby-prof.gemspec +58 -0
  43. data/test/aggregate_test.rb +6 -6
  44. data/test/call_info_visitor_test.rb +31 -0
  45. data/test/duplicate_names_test.rb +1 -1
  46. data/test/dynamic_method_test.rb +1 -1
  47. data/test/enumerable_test.rb +1 -1
  48. data/test/exclude_threads_test.rb +2 -2
  49. data/test/gc_test.rb +35 -0
  50. data/test/line_number_test.rb +2 -2
  51. data/test/measure_cpu_time_test.rb +5 -5
  52. data/test/measure_process_time_test.rb +5 -5
  53. data/test/measure_wall_time_test.rb +5 -5
  54. data/test/method_elimination_test.rb +3 -3
  55. data/test/module_test.rb +1 -1
  56. data/test/no_method_class_test.rb +1 -1
  57. data/test/printers_test.rb +16 -8
  58. data/test/recursive_test.rb +115 -91
  59. data/test/stack_test.rb +1 -1
  60. data/test/start_stop_test.rb +13 -13
  61. data/test/summarize_test.rb +48 -0
  62. data/test/test_suite.rb +1 -0
  63. data/test/thread_test.rb +16 -12
  64. data/test/unique_call_path_test.rb +10 -10
  65. metadata +65 -85
  66. data/lib/1.9/ruby_prof.exp +0 -0
  67. data/lib/1.9/ruby_prof.ilk +0 -0
  68. data/lib/1.9/ruby_prof.lib +0 -0
  69. data/lib/1.9/ruby_prof.pdb +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
@@ -3,16 +3,31 @@
3
3
  require 'set'
4
4
  module RubyProf
5
5
  class Profile
6
- # this method gets called internally when profiling is stopped.
7
- # it determines for each call_info whether it is minimal: a
8
- # call_info is minimal in a call tree if the call_info is not a
9
- # descendant of a call_info of the same method
10
- def compute_minimality
11
- threads.each do |threadid, method_infos|
12
- root_methods = method_infos.select{|mi| mi.root?}
13
- root_methods.each do |mi|
14
- mi.call_infos.select{|ci| ci.root?}.each do |call_info_root|
15
- call_info_root.compute_minimality(Set.new)
6
+ # This method gets called once profiling has been completed
7
+ # but before results are returned to the user. Thus it provides
8
+ # a hook to do any necessary post-processing on the call graph.
9
+ def post_process
10
+ self.threads.each do |thread|
11
+ detect_recursion(thread)
12
+ end
13
+ end
14
+
15
+ # This method detect recursive calls in the call graph.
16
+ def detect_recursion(thread)
17
+ visited_methods = Hash.new do |hash, key|
18
+ hash[key] = 0
19
+ end
20
+
21
+ visitor = CallInfoVisitor.new(thread)
22
+ visitor.visit do |call_info, event|
23
+ case event
24
+ when :enter
25
+ visited_methods[call_info.target] += 1
26
+ call_info.recursive = (visited_methods[call_info.target] > 1)
27
+ when :exit
28
+ visited_methods[call_info.target] -= 1
29
+ if visited_methods[call_info.target] == 0
30
+ visited_methods.delete(call_info.target)
16
31
  end
17
32
  end
18
33
  end
@@ -23,22 +38,12 @@ module RubyProf
23
38
  def eliminate_methods!(matchers)
24
39
  matchers = read_regexps_from_file(matchers) if matchers.is_a?(String)
25
40
  eliminated = []
26
- threads.each do |thread_id, methods|
27
- matchers.each{ |matcher| eliminated.concat(eliminate_methods(methods, matcher)) }
41
+ threads.each do |thread|
42
+ matchers.each{ |matcher| eliminated.concat(eliminate_methods(thread.methods, matcher)) }
28
43
  end
29
- compute_minimality # is this really necessary?
30
44
  eliminated
31
45
  end
32
46
 
33
- def dump
34
- threads.each do |thread_id, methods|
35
- $stderr.puts "Call Info Dump for thread id #{thread_id}"
36
- methods.each do |method_info|
37
- $stderr.puts method_info.dump
38
- end
39
- end
40
- end
41
-
42
47
  private
43
48
 
44
49
  # read regexps from file
@@ -1,29 +1,39 @@
1
1
  # encoding: utf-8
2
+ require 'tmpdir'
2
3
 
3
4
  module Rack
4
5
  class RubyProf
5
- def initialize(app)
6
+ def initialize(app, options = {})
6
7
  @app = app
8
+ @options = options
9
+ @options[:min_percent] ||= 1
10
+ @tmpdir = options[:path] || Dir.tmpdir
11
+ @printer_klasses = {::RubyProf::FlatPrinter => 'flat.txt',
12
+ ::RubyProf::GraphPrinter => 'graph.txt',
13
+ ::RubyProf::GraphHtmlPrinter => 'graph.html',
14
+ ::RubyProf::CallStackPrinter => 'call_stack.html'}
7
15
  end
8
16
 
9
17
  def call(env)
10
- ::RubyProf.start
11
- result = @app.call(env)
12
- data = ::RubyProf.stop
18
+ result = nil
19
+ data = ::RubyProf::Profile.profile do
20
+ result = @app.call(env)
21
+ end
22
+
23
+ request = Rack::Request.new(env)
24
+ path = request.path.gsub('/', '-')
25
+ path.slice!(0)
13
26
 
14
- print(data)
27
+ print(data, path)
15
28
  result
16
29
  end
17
30
 
18
- def print(data)
19
- require 'tmpdir' # late require so we load on demand only
20
- printers = {::RubyProf::FlatPrinter => ::File.join(Dir.tmpdir, 'profile.txt'),
21
- ::RubyProf::GraphHtmlPrinter => ::File.join(Dir.tmpdir, 'profile.html')}
22
-
23
- printers.each do |printer_klass, file_name|
31
+ def print(data, path)
32
+ @printer_klasses.each do |printer_klass, base_name|
24
33
  printer = printer_klass.new(data)
34
+ file_name = ::File.join(@tmpdir, "#{path}-#{base_name}")
25
35
  ::File.open(file_name, 'wb') do |file|
26
- printer.print(file, :min_percent => 0.00000001 )
36
+ printer.print(file, @options)
27
37
  end
28
38
  end
29
39
  end
data/ruby-prof.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # Read version from header file
4
+ version_header = File.read(File.expand_path('../ext/ruby_prof/version.h', __FILE__))
5
+ #match = version_header.match(/RUBY_PROF_VERSION\s*"(\.+)"/)
6
+ match = version_header.match(/RUBY_PROF_VERSION\s*"([^"]+)"/)
7
+ raise(RuntimeError, "Could not determine RUBY_PROF_VERSION") if not match
8
+
9
+ # For now make this an rc1
10
+ RUBY_PROF_VERSION = "#{match[1]}.rc2"
11
+
12
+ Gem::Specification.new do |spec|
13
+ spec.name = "ruby-prof"
14
+
15
+ spec.homepage = "http://rubyforge.org/projects/ruby-prof/"
16
+ spec.summary = "Fast Ruby profiler"
17
+ spec.description = <<-EOF
18
+ ruby-prof is a fast code profiler for Ruby. It is a C extension and
19
+ therefore is many times faster than the standard Ruby profiler. It
20
+ supports both flat and graph profiles. For each method, graph profiles
21
+ show how long the method ran, which methods called it and which
22
+ methods it called. RubyProf generate both text and html and can output
23
+ it to standard out or to a file.
24
+ EOF
25
+
26
+ spec.version = RUBY_PROF_VERSION
27
+
28
+ spec.author = "Shugo Maeda, Charlie Savage, Roger Pack, Stefan Kaes"
29
+ spec.email = "shugo@ruby-lang.org, cfis@savagexi.com, rogerdpack@gmail.com, skaes@railsexpress.de"
30
+ spec.platform = Gem::Platform::RUBY
31
+ spec.require_path = "lib"
32
+ spec.bindir = "bin"
33
+ spec.executables = ["ruby-prof"]
34
+ spec.extensions = ["ext/ruby_prof/extconf.rb"]
35
+ spec.files = Dir['CHANGES',
36
+ 'LICENSE',
37
+ 'Rakefile',
38
+ 'README.rdoc',
39
+ 'ruby-prof.gemspec',
40
+ 'bin/ruby-prof',
41
+ 'doc/**/*',
42
+ 'examples/*',
43
+ 'ext/ruby_prof/extconf.rb',
44
+ 'ext/ruby_prof/*.c',
45
+ 'ext/ruby_prof/*.h',
46
+ 'ext/ruby_prof/vc/*.sln',
47
+ 'ext/ruby_prof/vc/*.vcxproj',
48
+ 'lib/ruby-prof/*.rb',
49
+ 'lib/ruby-prof/images/*.png',
50
+ 'lib/ruby-prof/printers/*.rb',
51
+ 'test/*.rb']
52
+
53
+ spec.test_files = Dir["test/test_*.rb"]
54
+ spec.required_ruby_version = '>= 1.8.7'
55
+ spec.date = DateTime.now
56
+ spec.homepage = 'https://github.com/rdp/ruby-prof'
57
+ spec.add_development_dependency 'rake-compiler'
58
+ end
@@ -34,17 +34,17 @@ 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
37
+ def test_all_call_infos_are_not_recursive
38
38
  c1 = AggClass.new
39
39
  result = RubyProf.profile do
40
40
  c1.a
41
41
  c1.b
42
42
  c1.c
43
43
  end
44
- methods = result.threads.values.first.sort.reverse
44
+ methods = result.threads.first.methods.sort.reverse
45
45
  methods.each do |m|
46
46
  m.call_infos.each do |ci|
47
- assert ci.minimal?, "#{ci.call_sequence} should be minimal in the call tree"
47
+ assert(!ci.recursive)
48
48
  end
49
49
  end
50
50
  end
@@ -57,7 +57,7 @@ class AggregateTest < Test::Unit::TestCase
57
57
  c1.c
58
58
  end
59
59
 
60
- methods = result.threads.values.first.sort.reverse
60
+ methods = result.threads.first.methods.sort.reverse
61
61
  method = methods.find {|meth| meth.full_name == 'AggClass#z'}
62
62
 
63
63
  # Check AggClass#z
@@ -90,7 +90,7 @@ class AggregateTest < Test::Unit::TestCase
90
90
  c1.c
91
91
  end
92
92
 
93
- methods = result.threads.values.first.sort.reverse
93
+ methods = result.threads.first.methods.sort.reverse
94
94
  method = methods.find {|meth| meth.full_name == 'AggClass#z'}
95
95
 
96
96
  # Check AggClass#z
@@ -116,7 +116,7 @@ class AggregateTest < Test::Unit::TestCase
116
116
  c1.c
117
117
  end
118
118
 
119
- methods = result.threads.values.first.sort.reverse
119
+ methods = result.threads.first.methods.sort.reverse
120
120
  method = methods.find {|meth| meth.full_name == 'AggClass#a'}
121
121
 
122
122
  # Check AggClass#a
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class CallInfoVisitorTest < Test::Unit::TestCase
7
+ def setup
8
+ # Need to use wall time for this test due to the sleep calls
9
+ RubyProf::measure_mode = RubyProf::CPU_TIME
10
+ end
11
+
12
+ def test_visit
13
+ result = RubyProf.profile do
14
+ RubyProf::C1.hello
15
+ end
16
+
17
+ visitor = RubyProf::CallInfoVisitor.new(result.threads.first)
18
+
19
+ method_names = Array.new
20
+
21
+ visitor.visit do |call_info, event|
22
+ method_names << call_info.target.full_name if event == :enter
23
+ end
24
+
25
+ assert_equal(3, method_names.length)
26
+ assert_equal("CallInfoVisitorTest#test_visit", method_names[0])
27
+ assert_equal("<Class::RubyProf::C1>#hello", method_names[1])
28
+ assert_equal("Kernel#sleep", method_names[2])
29
+ end
30
+ end
31
+
@@ -21,7 +21,7 @@ class DuplicateNames < Test::Unit::TestCase
21
21
  end
22
22
 
23
23
  # There should be 3 foo methods
24
- methods = result.threads.values.first.sort.reverse
24
+ methods = result.threads.first.methods.sort.reverse
25
25
 
26
26
  methods = methods.select do |method|
27
27
  method.full_name == 'DuplicateNames::Foo::Bar#foo'
@@ -19,7 +19,7 @@ class DynamicMethodTest < Test::Unit::TestCase
19
19
  # Integer#times
20
20
  # DynamicMethodTest#test_dynamic_method
21
21
 
22
- methods = result.threads.values.first.sort.reverse
22
+ methods = result.threads.first.methods.sort.reverse
23
23
  assert_equal(7, methods.length)
24
24
 
25
25
  # Check times
@@ -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_equal(result.threads.first.methods.length, 4)
15
15
  end
16
16
  end
@@ -40,8 +40,8 @@ class ExcludeThreadsTest < Test::Unit::TestCase
40
40
  assert_equal(2, result.threads.length)
41
41
 
42
42
  output = Array.new
43
- result.threads.each do | thread_id, methods |
44
- methods.each do | m |
43
+ result.threads.each do |thread|
44
+ thread.methods.each do | m |
45
45
  if m.full_name.index("ExcludeThreadsTest#thread") == 0
46
46
  output.push(m.full_name)
47
47
  end
data/test/gc_test.rb ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.expand_path('../test_helper', __FILE__)
5
+
6
+ class GcTest < Test::Unit::TestCase
7
+ def setup
8
+ # Need to use wall time for this test due to the sleep calls
9
+ RubyProf::measure_mode = RubyProf::WALL_TIME
10
+ end
11
+
12
+ def run_profile
13
+ RubyProf.profile do
14
+ RubyProf::C1.hello
15
+ end
16
+ end
17
+
18
+ def test_gc
19
+ result = run_profile
20
+
21
+ thread = result.threads.first
22
+ method = thread.methods.first
23
+ call_info = method.call_infos.first
24
+
25
+ result = nil
26
+
27
+ 1000.times do
28
+ GC.start
29
+ Array.new(1000)
30
+ end
31
+
32
+ puts thread.methods
33
+ puts method.full_name
34
+ end
35
+ end
@@ -27,7 +27,7 @@ class LineNumbersTest < Test::Unit::TestCase
27
27
  numbers.method2
28
28
  end
29
29
 
30
- methods = result.threads.values.first.sort.reverse
30
+ methods = result.threads.first.methods.sort.reverse
31
31
  assert_equal(3, methods.length)
32
32
 
33
33
  method = methods[0]
@@ -50,7 +50,7 @@ class LineNumbersTest < Test::Unit::TestCase
50
50
  numbers.method3
51
51
  end
52
52
 
53
- methods = result.threads.values.first.sort_by {|method| method.full_name}
53
+ methods = result.threads.first.methods.sort_by {|method| method.full_name}
54
54
  assert_equal(3, methods.length)
55
55
 
56
56
  # Methods:
@@ -31,7 +31,7 @@ class MeasureCpuTimeTest < Test::Unit::TestCase
31
31
  # <Class::RubyProf::C1>#hello
32
32
  # Kernel#sleep
33
33
 
34
- methods = result.threads.values.first.sort.reverse
34
+ methods = result.threads.first.methods.sort.reverse
35
35
  assert_equal(3, methods.length)
36
36
 
37
37
  # Check the names
@@ -66,7 +66,7 @@ class MeasureCpuTimeTest < Test::Unit::TestCase
66
66
  # C1#hello
67
67
  # Kernel#sleep
68
68
 
69
- methods = result.threads.values.first.sort.reverse
69
+ methods = result.threads.first.methods.sort.reverse
70
70
  assert_equal(6, methods.length)
71
71
  names = methods.map(&:full_name)
72
72
  assert_equal('MeasureCpuTimeTest#test_instance_methods', names[0])
@@ -113,7 +113,7 @@ class MeasureCpuTimeTest < Test::Unit::TestCase
113
113
  # M1#hello
114
114
  # Kernel#sleep
115
115
 
116
- methods = result.threads.values.first.sort.reverse
116
+ methods = result.threads.first.methods.sort.reverse
117
117
  assert_equal(3, methods.length)
118
118
 
119
119
  assert_equal('MeasureCpuTimeTest#test_module_methods', methods[0].full_name)
@@ -147,7 +147,7 @@ class MeasureCpuTimeTest < Test::Unit::TestCase
147
147
  # M1#hello
148
148
  # Kernel#sleep
149
149
 
150
- methods = result.threads.values.first.sort.reverse
150
+ methods = result.threads.first.methods.sort.reverse
151
151
  assert_equal(6, methods.length)
152
152
  names = methods.map(&:full_name)
153
153
  assert_equal('MeasureCpuTimeTest#test_module_instance_methods', names[0])
@@ -195,7 +195,7 @@ class MeasureCpuTimeTest < Test::Unit::TestCase
195
195
  c3.hello
196
196
  end
197
197
 
198
- methods = result.threads.values.first.sort.reverse
198
+ methods = result.threads.first.methods.sort.reverse
199
199
  assert_equal(2, methods.length)
200
200
 
201
201
  assert_equal('MeasureCpuTimeTest#test_singleton', methods[0].full_name)
@@ -28,7 +28,7 @@ class MeasureProcessTimeTest < Test::Unit::TestCase
28
28
  # <Class::RubyProf::C1>#hello
29
29
  # Kernel#sleep
30
30
 
31
- methods = result.threads.values.first.sort.reverse
31
+ methods = result.threads.first.methods.sort.reverse
32
32
  puts methods[0].total_time
33
33
 
34
34
  assert_equal(3, methods.length)
@@ -63,7 +63,7 @@ class MeasureProcessTimeTest < Test::Unit::TestCase
63
63
  # C1#hello
64
64
  # Kernel#sleep
65
65
 
66
- methods = result.threads.values.first.sort.reverse
66
+ methods = result.threads.first.methods.sort.reverse
67
67
  assert_equal(6, methods.length)
68
68
 
69
69
  # Check times
@@ -108,7 +108,7 @@ class MeasureProcessTimeTest < Test::Unit::TestCase
108
108
  # M1#hello
109
109
  # Kernel#sleep
110
110
 
111
- methods = result.threads.values.first.sort.reverse
111
+ methods = result.threads.first.methods.sort.reverse
112
112
  assert_equal(3, methods.length)
113
113
 
114
114
  # Check times
@@ -141,7 +141,7 @@ class MeasureProcessTimeTest < Test::Unit::TestCase
141
141
  # M1#hello
142
142
  # Kernel#sleep
143
143
 
144
- methods = result.threads.values.first.sort.reverse
144
+ methods = result.threads.first.methods.sort.reverse
145
145
  assert_equal(6, methods.length)
146
146
 
147
147
  # Check times
@@ -188,7 +188,7 @@ class MeasureProcessTimeTest < Test::Unit::TestCase
188
188
  c3.hello
189
189
  end
190
190
 
191
- methods = result.threads.values.first.sort.reverse
191
+ methods = result.threads.first.methods.sort.reverse
192
192
  assert_equal(2, methods.length)
193
193
 
194
194
  assert_equal("MeasureProcessTimeTest#test_singleton", methods[0].full_name)