ruby-prof 0.6.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/CHANGES +116 -0
  2. data/LICENSE +23 -0
  3. data/README +307 -0
  4. data/Rakefile +141 -0
  5. data/bin/ruby-prof +192 -0
  6. data/examples/flat.txt +55 -0
  7. data/examples/graph.html +823 -0
  8. data/examples/graph.txt +170 -0
  9. data/ext/extconf.rb +21 -0
  10. data/ext/extconf.rb.rej +13 -0
  11. data/ext/measure_allocations.h +43 -0
  12. data/ext/measure_cpu_time.h +138 -0
  13. data/ext/measure_memory.h +42 -0
  14. data/ext/measure_process_time.h +41 -0
  15. data/ext/measure_wall_time.h +42 -0
  16. data/ext/ruby_prof.c +1628 -0
  17. data/lib/ruby-prof.rb +43 -0
  18. data/lib/ruby-prof/abstract_printer.rb +42 -0
  19. data/lib/ruby-prof/call_tree_printer.rb +76 -0
  20. data/lib/ruby-prof/call_tree_printer.rb.rej +27 -0
  21. data/lib/ruby-prof/flat_printer.rb +79 -0
  22. data/lib/ruby-prof/graph_html_printer.rb +255 -0
  23. data/lib/ruby-prof/graph_printer.rb +163 -0
  24. data/lib/ruby-prof/profile_test_case.rb +80 -0
  25. data/lib/ruby-prof/task.rb +147 -0
  26. data/lib/ruby_prof.so +0 -0
  27. data/lib/unprof.rb +8 -0
  28. data/rails_plugin/ruby-prof/init.rb +8 -0
  29. data/rails_plugin/ruby-prof/lib/profiling.rb +57 -0
  30. data/test/basic_test.rb +190 -0
  31. data/test/duplicate_names_test.rb +33 -0
  32. data/test/line_number_test.rb +69 -0
  33. data/test/measure_mode_test.rb +79 -0
  34. data/test/module_test.rb +57 -0
  35. data/test/no_method_class_test.rb +14 -0
  36. data/test/prime.rb +60 -0
  37. data/test/prime1.rb +17 -0
  38. data/test/prime2.rb +26 -0
  39. data/test/prime3.rb +17 -0
  40. data/test/prime_test.rb +24 -0
  41. data/test/printers_test.rb +74 -0
  42. data/test/profile_unit_test.rb +24 -0
  43. data/test/recursive_test.rb +144 -0
  44. data/test/singleton_test.rb +38 -0
  45. data/test/start_test.rb +24 -0
  46. data/test/test_helper.rb +55 -0
  47. data/test/test_suite.rb +19 -0
  48. data/test/thread_test.rb +135 -0
  49. data/test/timing_test.rb +133 -0
  50. metadata +116 -0
@@ -0,0 +1,163 @@
1
+ require 'ruby-prof/abstract_printer'
2
+
3
+ module RubyProf
4
+ # Generates graph[link:files/examples/graph_txt.html] profile reports as text.
5
+ # To use the graph printer:
6
+ #
7
+ # result = RubyProf.profile do
8
+ # [code to profile]
9
+ # end
10
+ #
11
+ # printer = RubyProf::GraphPrinter.new(result, 5)
12
+ # printer.print(STDOUT, 0)
13
+ #
14
+ # The constructor takes two arguments. The first is
15
+ # a RubyProf::Result object generated from a profiling
16
+ # run. The second is the minimum %total (the methods
17
+ # total time divided by the overall total time) that
18
+ # a method must take for it to be printed out in
19
+ # the report. Use this parameter to eliminate methods
20
+ # that are not important to the overall profiling results.
21
+
22
+ class GraphPrinter < AbstractPrinter
23
+ PERCENTAGE_WIDTH = 8
24
+ TIME_WIDTH = 10
25
+ CALL_WIDTH = 17
26
+
27
+ # Create a GraphPrinter. Result is a RubyProf::Result
28
+ # object generated from a profiling run.
29
+ def initialize(result)
30
+ super(result)
31
+ @thread_times = Hash.new
32
+ calculate_thread_times
33
+ end
34
+
35
+ def calculate_thread_times
36
+ # Cache thread times since this is an expensive
37
+ # operation with the required sorting
38
+ @result.threads.each do |thread_id, methods|
39
+ top = methods.sort.last
40
+
41
+ thread_time = 0.01
42
+ thread_time = top.total_time if top.total_time > 0
43
+
44
+ @thread_times[thread_id] = thread_time
45
+ end
46
+ end
47
+
48
+ # Print a graph report to the provided output.
49
+ #
50
+ # output - Any IO oject, including STDOUT or a file.
51
+ # The default value is STDOUT.
52
+ #
53
+ # options - Hash of print options. See #setup_options
54
+ # for more information.
55
+ #
56
+ def print(output = STDOUT, options = {})
57
+ @output = output
58
+ setup_options(options)
59
+ print_threads
60
+ end
61
+
62
+ private
63
+ def print_threads
64
+ # sort assumes that spawned threads have higher object_ids
65
+ @result.threads.sort.each do |thread_id, methods|
66
+ print_methods(thread_id, methods)
67
+ @output << "\n" * 2
68
+ end
69
+ end
70
+
71
+ def print_methods(thread_id, methods)
72
+ # Sort methods from longest to shortest total time
73
+ methods = methods.sort
74
+
75
+ toplevel = methods.last
76
+ total_time = toplevel.total_time
77
+ if total_time == 0
78
+ total_time = 0.01
79
+ end
80
+
81
+ print_heading(thread_id)
82
+
83
+ # Print each method in total time order
84
+ methods.reverse_each do |method|
85
+ total_percentage = (method.total_time/total_time) * 100
86
+ self_percentage = (method.self_time/total_time) * 100
87
+
88
+ next if total_percentage < min_percent
89
+
90
+ @output << "-" * 80 << "\n"
91
+
92
+ print_parents(thread_id, method)
93
+
94
+ # 1 is for % sign
95
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
96
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
97
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
98
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
99
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
100
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
101
+ @output << sprintf("%#{CALL_WIDTH}i", method.called)
102
+ @output << sprintf(" %s", method_name(method))
103
+ if print_file
104
+ @output << sprintf(" %s:%s", method.source_file, method.line)
105
+ end
106
+ @output << "\n"
107
+
108
+ print_children(method)
109
+ end
110
+ end
111
+
112
+ def print_heading(thread_id)
113
+ @output << "Thread ID: #{thread_id}\n"
114
+ @output << "Total Time: #{@thread_times[thread_id]}\n"
115
+ @output << "\n"
116
+
117
+ # 1 is for % sign
118
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
119
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
120
+ @output << sprintf("%#{TIME_WIDTH}s", "total")
121
+ @output << sprintf("%#{TIME_WIDTH}s", "self")
122
+ @output << sprintf("%#{TIME_WIDTH}s", "wait")
123
+ @output << sprintf("%#{TIME_WIDTH}s", "child")
124
+ @output << sprintf("%#{CALL_WIDTH}s", "calls")
125
+ @output << " Name"
126
+ @output << "\n"
127
+ end
128
+
129
+ def print_parents(thread_id, method)
130
+ method.parents.each do |caller|
131
+ @output << " " * 2 * PERCENTAGE_WIDTH
132
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.total_time)
133
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.self_time)
134
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.wait_time)
135
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.children_time)
136
+
137
+ call_called = "#{caller.called}/#{method.called}"
138
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
139
+ @output << sprintf(" %s", caller.target.full_name)
140
+ @output << "\n"
141
+ end
142
+ end
143
+
144
+ def print_children(method)
145
+ method.children.each do |child|
146
+ # Get children method
147
+
148
+ @output << " " * 2 * PERCENTAGE_WIDTH
149
+
150
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.total_time)
151
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.self_time)
152
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.wait_time)
153
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.children_time)
154
+
155
+ call_called = "#{child.called}/#{child.target.called}"
156
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
157
+ @output << sprintf(" %s", child.target.full_name)
158
+ @output << "\n"
159
+ end
160
+ end
161
+ end
162
+ end
163
+
@@ -0,0 +1,80 @@
1
+ # Make sure to first load the libraries we will override
2
+ require 'test/unit'
3
+ require 'ruby-prof'
4
+
5
+ module Test
6
+ module Unit
7
+ class TestCase
8
+
9
+ alias :run__profile__ :run
10
+
11
+ def run(result, &block)
12
+ test_name = @method_name.to_sym
13
+ alias_test_name = (@method_name + '__profile__').to_sym
14
+
15
+ self.class.class_eval("alias :#{alias_test_name} :#{test_name}")
16
+
17
+ self.class.send(:define_method, test_name) do
18
+ # Run the profiler
19
+ RubyProf.start
20
+ __send__(alias_test_name)
21
+ result = RubyProf.stop
22
+
23
+ create_output_directory
24
+
25
+ # Get the result file name
26
+ file_name = name.gsub(/\(/, '_').gsub(/\)/, '')
27
+ file_name = self.underscore(file_name)
28
+ file_path = File.join(output_directory, file_name)
29
+ file_path += file_extension
30
+
31
+ # Create a printer
32
+ printer = self.printer.new(result)
33
+
34
+ # Write the results
35
+ File.open(file_path, 'w') do |file|
36
+ printer.print(file, min_percent)
37
+ end
38
+ end
39
+
40
+ self.run__profile__(result, &block)
41
+ end
42
+
43
+ # Taken from rails
44
+ def underscore(camel_cased_word)
45
+ camel_cased_word.to_s.gsub(/::/, '/').
46
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
47
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
48
+ tr("-", "_").downcase
49
+ end
50
+
51
+ # Add some additional methods
52
+ def min_percent
53
+ 1
54
+ end
55
+
56
+ def output_directory
57
+ # Put results in subdirectory called profile
58
+ File.join(Dir.getwd, 'profile')
59
+ end
60
+
61
+ def create_output_directory
62
+ if not File.exist?(output_directory)
63
+ Dir.mkdir(output_directory)
64
+ end
65
+ end
66
+
67
+ def file_extension
68
+ if printer == RubyProf::FlatPrinter
69
+ '.html'
70
+ else
71
+ '.txt'
72
+ end
73
+ end
74
+
75
+ def printer
76
+ RubyProf::GraphHtmlPrinter
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'fileutils'
6
+
7
+ module RubyProf
8
+
9
+ # Define a task library for profiling unit tests with ruby-prof.
10
+ #
11
+ # All of the options provided by
12
+ # the Rake:TestTask are supported except the loader
13
+ # which is set to ruby-prof. For detailed information
14
+ # please refer to the Rake:TestTask documentation.
15
+ #
16
+ # ruby-prof specific options include:
17
+ #
18
+ # output_dir - For each file specified an output
19
+ # file with profile information will be
20
+ # written to the output directory.
21
+ # By default, the output directory is
22
+ # called "profile" and is created underneath
23
+ # the current working directory.
24
+ #
25
+ # printer - Specifies the output printer. Valid values include
26
+ # :flat, :graph, :graph_html and :call_tree.
27
+ #
28
+ # min_percent - Methods that take less than the specified percent
29
+ # will not be written out.
30
+ #
31
+ # Example:
32
+ #
33
+ # require 'ruby-prof/task'
34
+ #
35
+ # RubyProf::ProfileTask.new do |t|
36
+ # t.test_files = FileList['test/test*.rb']
37
+ # t.output_dir = "c:/temp"
38
+ # t.printer = :graph
39
+ # t.min_percent = 10
40
+ # end
41
+ #
42
+ # If rake is invoked with a "TEST=filename" command line option,
43
+ # then the list of test files will be overridden to include only the
44
+ # filename specified on the command line. This provides an easy way
45
+ # to run just one test.
46
+ #
47
+ # If rake is invoked with a "TESTOPTS=options" command line option,
48
+ # then the given options are passed to the test process after a
49
+ # '--'. This allows Test::Unit options to be passed to the test
50
+ # suite.
51
+ #
52
+ # Examples:
53
+ #
54
+ # rake profile # run tests normally
55
+ # rake profile TEST=just_one_file.rb # run just one test file.
56
+ # rake profile TESTOPTS="-v" # run in verbose mode
57
+ # rake profile TESTOPTS="--runner=fox" # use the fox test runner
58
+
59
+ class ProfileTask < Rake::TestTask
60
+ attr_accessor :output_dir
61
+ attr_accessor :min_percent
62
+ attr_accessor :printer
63
+
64
+ def initialize(name = :profile)
65
+ super(name)
66
+ end
67
+
68
+ # Create the tasks defined by this task lib.
69
+ def define
70
+ lib_path = @libs.join(File::PATH_SEPARATOR)
71
+ desc "Profile" + (@name==:profile ? "" : " for #{@name}")
72
+
73
+ task @name do
74
+ create_output_directory
75
+
76
+ @ruby_opts.unshift( "-I#{lib_path}" )
77
+ @ruby_opts.unshift( "-w" ) if @warning
78
+ @ruby_opts.push("-S ruby-prof")
79
+ @ruby_opts.push("--printer #{@printer}")
80
+ @ruby_opts.push("--min_percent #{@min_percent}")
81
+
82
+ file_list.each do |file_path|
83
+ run_script(file_path)
84
+ end
85
+ end
86
+ self
87
+ end
88
+
89
+ # Run script
90
+ def run_script(script_path)
91
+ run_code = ''
92
+ RakeFileUtils.verbose(@verbose) do
93
+ file_name = File.basename(script_path, File.extname(script_path))
94
+ case @printer
95
+ when :flat, :graph, :call_tree
96
+ file_name += ".txt"
97
+ when :graph_html
98
+ file_name += ".html"
99
+ else
100
+ file_name += ".txt"
101
+ end
102
+
103
+ output_file_path = File.join(output_directory, file_name)
104
+
105
+ command_line = @ruby_opts.join(" ") +
106
+ " --file=" + output_file_path +
107
+ " " + script_path
108
+
109
+ puts "ruby " + command_line
110
+ # We have to catch the exeption to continue on. However,
111
+ # the error message will have been output to STDERR
112
+ # already by the time we get here so we don't have to
113
+ # do that again
114
+ begin
115
+ ruby command_line
116
+ rescue => e
117
+ STDOUT << e << "\n"
118
+ STDOUT.flush
119
+ end
120
+ puts ""
121
+ puts ""
122
+ end
123
+ end
124
+
125
+ def output_directory
126
+ File.expand_path(@output_dir)
127
+ end
128
+
129
+ def create_output_directory
130
+ if not File.exist?(output_directory)
131
+ Dir.mkdir(output_directory)
132
+ end
133
+ end
134
+
135
+ def clean_output_directory
136
+ if File.exist?(output_directory)
137
+ files = Dir.glob(output_directory + '/*')
138
+ FileUtils.rm(files)
139
+ end
140
+ end
141
+
142
+ def option_list # :nodoc:
143
+ ENV['OPTIONS'] || @options.join(" ") || ""
144
+ end
145
+ end
146
+ end
147
+
data/lib/ruby_prof.so ADDED
Binary file
data/lib/unprof.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "ruby-prof"
2
+
3
+ at_exit {
4
+ result = RubyProf.stop
5
+ printer = RubyProf::FlatPrinter.new(result)
6
+ printer.print(STDOUT)
7
+ }
8
+ RubyProf.start
@@ -0,0 +1,8 @@
1
+ require 'profiling'
2
+
3
+ # Grab log path from current rails configuration
4
+ ActionController::Profiling::LOG_PATH = File.expand_path(File.dirname(config.log_path))
5
+
6
+ ActionController::Base.class_eval do
7
+ include ActionController::Profiling
8
+ end
@@ -0,0 +1,57 @@
1
+ require 'ruby-prof'
2
+
3
+ module ActionController #:nodoc:
4
+ # The ruby-prof module times the performance of actions and reports to the logger. If the Active Record
5
+ # package has been included, a separate timing section for database calls will be added as well.
6
+ module Profiling #:nodoc:
7
+ LOG_PATH = nil
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ alias_method_chain :perform_action, :profiling
12
+ end
13
+ end
14
+
15
+ def perform_action_with_profiling
16
+ # Profling could be running if this
17
+ # is a render_component call.
18
+ if RubyProf.running? or not logger
19
+ perform_action_without_profiling
20
+ else
21
+ result = RubyProf.profile do
22
+ perform_action_without_profiling
23
+ end
24
+
25
+ output = StringIO.new
26
+ output << " [#{complete_request_uri rescue "unknown"}]"
27
+ output << "\n\n"
28
+
29
+ # Create a flat printer
30
+ printer = RubyProf::FlatPrinter.new(result)
31
+
32
+ # Skip anything less than 1% - which is a lot of
33
+ # stuff in Rails. Don't print the source file
34
+ # its too noisy.
35
+ printer.print(output, {:min_percent => 1,
36
+ :print_file => false})
37
+ logger.info(output.string)
38
+
39
+ ## Example for Graph html printer
40
+ #printer = RubyProf::GraphHtmlPrinter.new(result)
41
+ #path = File.join(LOG_PATH, 'call_graph.html')
42
+ #File.open(path, 'w') do |file|
43
+ #printer.print(file, {:min_percent => 1,
44
+ #:print_file => true})
45
+ #end
46
+
47
+ ## Used for KCacheGrind visualizations
48
+ #printer = RubyProf::CallTreePrinter.new(result)
49
+ #path = File.join(LOG_PATH, 'callgrind.out')
50
+ #File.open(path, 'w') do |file|
51
+ #printer.print(file, {:min_percent => 1,
52
+ #:print_file => true})
53
+ #end
54
+ end
55
+ end
56
+ end
57
+ end