skaes-ruby-prof 0.7.3
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.
- data/CHANGES +202 -0
- data/LICENSE +23 -0
- data/README +436 -0
- data/Rakefile +129 -0
- data/bin/ruby-prof +207 -0
- data/examples/flat.txt +55 -0
- data/examples/graph.html +823 -0
- data/examples/graph.txt +170 -0
- data/ext/extconf.rb +34 -0
- data/ext/measure_allocations.h +58 -0
- data/ext/measure_cpu_time.h +152 -0
- data/ext/measure_gc_runs.h +76 -0
- data/ext/measure_gc_time.h +57 -0
- data/ext/measure_memory.h +101 -0
- data/ext/measure_process_time.h +52 -0
- data/ext/measure_wall_time.h +53 -0
- data/ext/mingw/Rakefile +23 -0
- data/ext/mingw/build.rake +38 -0
- data/ext/ruby_prof.c +1747 -0
- data/ext/ruby_prof.h +185 -0
- data/ext/vc/ruby_prof.sln +20 -0
- data/ext/vc/ruby_prof.vcproj +241 -0
- data/ext/version.h +4 -0
- data/lib/ruby-prof.rb +51 -0
- data/lib/ruby-prof/abstract_printer.rb +41 -0
- data/lib/ruby-prof/aggregate_call_info.rb +68 -0
- data/lib/ruby-prof/call_info.rb +112 -0
- data/lib/ruby-prof/call_stack_printer.rb +746 -0
- data/lib/ruby-prof/call_tree_printer.rb +84 -0
- data/lib/ruby-prof/empty.png +0 -0
- data/lib/ruby-prof/flat_printer.rb +79 -0
- data/lib/ruby-prof/graph_html_printer.rb +272 -0
- data/lib/ruby-prof/graph_printer.rb +164 -0
- data/lib/ruby-prof/method_info.rb +131 -0
- data/lib/ruby-prof/minus.png +0 -0
- data/lib/ruby-prof/multi_printer.rb +55 -0
- data/lib/ruby-prof/plus.png +0 -0
- data/lib/ruby-prof/result.rb +70 -0
- data/lib/ruby-prof/task.rb +146 -0
- data/lib/ruby-prof/test.rb +148 -0
- data/lib/unprof.rb +8 -0
- data/rails/environment/profile.rb +24 -0
- data/rails/example/example_test.rb +9 -0
- data/rails/profile_test_helper.rb +21 -0
- data/test/aggregate_test.rb +136 -0
- data/test/basic_test.rb +283 -0
- data/test/duplicate_names_test.rb +32 -0
- data/test/exceptions_test.rb +15 -0
- data/test/exclude_threads_test.rb +54 -0
- data/test/line_number_test.rb +73 -0
- data/test/measurement_test.rb +121 -0
- data/test/method_elimination_test.rb +74 -0
- data/test/module_test.rb +54 -0
- data/test/multi_printer_test.rb +81 -0
- data/test/no_method_class_test.rb +13 -0
- data/test/prime.rb +58 -0
- data/test/prime_test.rb +13 -0
- data/test/printers_test.rb +73 -0
- data/test/recursive_test.rb +215 -0
- data/test/singleton_test.rb +38 -0
- data/test/stack_printer_test.rb +74 -0
- data/test/stack_test.rb +138 -0
- data/test/start_stop_test.rb +95 -0
- data/test/test_suite.rb +26 -0
- data/test/thread_test.rb +159 -0
- data/test/unique_call_path_test.rb +206 -0
- metadata +128 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module RubyProf
|
|
2
|
+
class MethodInfo
|
|
3
|
+
include Comparable
|
|
4
|
+
|
|
5
|
+
def <=>(other)
|
|
6
|
+
if self.total_time < other.total_time
|
|
7
|
+
-1
|
|
8
|
+
elsif self.total_time > other.total_time
|
|
9
|
+
1
|
|
10
|
+
elsif self.min_depth < other.min_depth
|
|
11
|
+
1
|
|
12
|
+
elsif self.min_depth > other.min_depth
|
|
13
|
+
-1
|
|
14
|
+
else
|
|
15
|
+
-1 * (self.full_name <=> other.full_name)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def called
|
|
20
|
+
@called ||= begin
|
|
21
|
+
call_infos.inject(0) do |sum, call_info|
|
|
22
|
+
sum += call_info.called
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def total_time
|
|
28
|
+
@total_time ||= begin
|
|
29
|
+
call_infos.inject(0) do |sum, call_info|
|
|
30
|
+
sum += call_info.total_time if call_info.minimal?
|
|
31
|
+
sum
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self_time
|
|
37
|
+
@self_time ||= begin
|
|
38
|
+
call_infos.inject(0) do |sum, call_info|
|
|
39
|
+
sum += call_info.self_time
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def wait_time
|
|
45
|
+
@wait_time ||= begin
|
|
46
|
+
call_infos.inject(0) do |sum, call_info|
|
|
47
|
+
sum += call_info.wait_time
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def children_time
|
|
53
|
+
@children_time ||= begin
|
|
54
|
+
call_infos.inject(0) do |sum, call_info|
|
|
55
|
+
sum += call_info.children_time if call_info.minimal?
|
|
56
|
+
sum
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def min_depth
|
|
62
|
+
call_infos.map do |call_info|
|
|
63
|
+
call_info.depth
|
|
64
|
+
end.min
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def root?
|
|
68
|
+
@root ||= begin
|
|
69
|
+
call_infos.find do |call_info|
|
|
70
|
+
not call_info.root?
|
|
71
|
+
end.nil?
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def children
|
|
76
|
+
@children ||= begin
|
|
77
|
+
call_infos.map do |call_info|
|
|
78
|
+
call_info.children
|
|
79
|
+
end.flatten
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def aggregate_parents
|
|
84
|
+
# Group call info's based on their parents
|
|
85
|
+
groups = self.call_infos.inject(Hash.new) do |hash, call_info|
|
|
86
|
+
key = call_info.parent ? call_info.parent.target : self
|
|
87
|
+
(hash[key] ||= []) << call_info
|
|
88
|
+
hash
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
groups.map do |key, value|
|
|
92
|
+
AggregateCallInfo.new(value)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def aggregate_children
|
|
97
|
+
# Group call info's based on their targets
|
|
98
|
+
groups = self.children.inject(Hash.new) do |hash, call_info|
|
|
99
|
+
key = call_info.target
|
|
100
|
+
(hash[key] ||= []) << call_info
|
|
101
|
+
hash
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
groups.map do |key, value|
|
|
105
|
+
AggregateCallInfo.new(value)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def to_s
|
|
110
|
+
full_name
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def dump
|
|
114
|
+
res = ""
|
|
115
|
+
res << "MINFO: #{klass_name}##{method_name} total_time: #{total_time} (#{full_name})\n"
|
|
116
|
+
call_infos.each do |ci|
|
|
117
|
+
pinfo = ci.root? ? "TOPLEVEL" : (p=ci.parent.target; "#{p.klass_name}##{p.method_name} (#{ci.parent.object_id}) (#{p.full_name})")
|
|
118
|
+
res << "CINFO[#{ci.object_id}] called #{ci.called} times from #{pinfo}\n"
|
|
119
|
+
end
|
|
120
|
+
res
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# remove method from the call graph. should not be called directly.
|
|
124
|
+
def eliminate!
|
|
125
|
+
# $stderr.puts "eliminating #{self}"
|
|
126
|
+
call_infos.each{ |call_info| call_info.eliminate! }
|
|
127
|
+
call_infos.clear
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module RubyProf
|
|
2
|
+
class MultiPrinter
|
|
3
|
+
# Helper class to simplify printing profiles of several types from
|
|
4
|
+
# one profiling run. Currently prints a flat profile, a callgrind
|
|
5
|
+
# profile, a call stack profile and a graph profile.
|
|
6
|
+
|
|
7
|
+
def initialize(result)
|
|
8
|
+
@stack_printer = CallStackPrinter.new(result)
|
|
9
|
+
@graph_printer = GraphHtmlPrinter.new(result)
|
|
10
|
+
@tree_printer = CallTreePrinter.new(result)
|
|
11
|
+
@flat_printer = FlatPrinter.new(result)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# create profile files under options[:path] or the current
|
|
15
|
+
# directory. options[:profile] is used as the base name for the
|
|
16
|
+
# pofile file, defaults to "profile".
|
|
17
|
+
def print(options)
|
|
18
|
+
@profile = options.delete(:profile) || "profile"
|
|
19
|
+
@directory = options.delete(:path) || File.expand_path(".")
|
|
20
|
+
File.open(stack_profile, "w") do |f|
|
|
21
|
+
@stack_printer.print(f, options.merge(:graph => "#{@profile}.graph.html"))
|
|
22
|
+
end
|
|
23
|
+
File.open(graph_profile, "w") do |f|
|
|
24
|
+
@graph_printer.print(f, options)
|
|
25
|
+
end
|
|
26
|
+
File.open(tree_profile, "w") do |f|
|
|
27
|
+
@tree_printer.print(f, options)
|
|
28
|
+
end
|
|
29
|
+
File.open(flat_profile, "w") do |f|
|
|
30
|
+
@flat_printer.print(f, options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# the name of the call stack profile file
|
|
35
|
+
def stack_profile
|
|
36
|
+
"#{@directory}/#{@profile}.stack.html"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# the name of the graph profile file
|
|
40
|
+
def graph_profile
|
|
41
|
+
"#{@directory}/#{@profile}.graph.html"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# the name of the callgrind profile file
|
|
45
|
+
def tree_profile
|
|
46
|
+
"#{@directory}/#{@profile}.grind.dat"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# the name of the flat profile file
|
|
50
|
+
def flat_profile
|
|
51
|
+
"#{@directory}/#{@profile}.flat.txt"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
module RubyProf
|
|
3
|
+
class Result
|
|
4
|
+
# this method gets called internally when profiling is stopped.
|
|
5
|
+
# it determines for each call_info whether it is minimal: a
|
|
6
|
+
# call_info is minimal in a call tree if the call_info is not a
|
|
7
|
+
# descendant of a call_info of the same method
|
|
8
|
+
def compute_minimality
|
|
9
|
+
threads.each do |threadid, method_infos|
|
|
10
|
+
root_methods = method_infos.select{|mi| mi.root?}
|
|
11
|
+
root_methods.each do |mi|
|
|
12
|
+
mi.call_infos.select{|ci| ci.root?}.each do |call_info_root|
|
|
13
|
+
call_info_root.compute_minimality(Set.new)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# eliminate some calls from the graph by merging the information into callers.
|
|
20
|
+
# matchers can be a list of strings or regular expressions or the name of a file containing regexps.
|
|
21
|
+
def eliminate_methods!(matchers)
|
|
22
|
+
matchers = read_regexps_from_file(matchers) if matchers.is_a?(String)
|
|
23
|
+
eliminated = []
|
|
24
|
+
threads.each do |thread_id, methods|
|
|
25
|
+
matchers.each{ |matcher| eliminated.concat(eliminate_methods(methods, matcher)) }
|
|
26
|
+
end
|
|
27
|
+
compute_minimality # is this really necessary?
|
|
28
|
+
eliminated
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def dump
|
|
32
|
+
threads.each do |thread_id, methods|
|
|
33
|
+
$stderr.puts "Call Info Dump for thread id #{thread_id}"
|
|
34
|
+
methods.each do |method_info|
|
|
35
|
+
$stderr.puts method_info.dump
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# read regexps from file
|
|
43
|
+
def read_regexps_from_file(file_name)
|
|
44
|
+
matchers = []
|
|
45
|
+
File.open(matchers).each_line do |l|
|
|
46
|
+
next if (l =~ /^(#.*|\s*)$/) # emtpy lines and lines starting with #
|
|
47
|
+
matchers << Regexp.new(l.strip)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# eliminate methods matching matcher
|
|
52
|
+
def eliminate_methods(methods, matcher)
|
|
53
|
+
eliminated = []
|
|
54
|
+
i = 0
|
|
55
|
+
while i < methods.size
|
|
56
|
+
method_info = methods[i]
|
|
57
|
+
method_name = method_info.full_name
|
|
58
|
+
if matcher === method_name
|
|
59
|
+
raise "can't eliminate root method" if method_info.root?
|
|
60
|
+
eliminated << methods.delete_at(i)
|
|
61
|
+
method_info.eliminate!
|
|
62
|
+
else
|
|
63
|
+
i += 1
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
eliminated
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
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
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Now load ruby-prof and away we go
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'ruby-prof'
|
|
4
|
+
require 'benchmark'
|
|
5
|
+
|
|
6
|
+
module RubyProf
|
|
7
|
+
module Test
|
|
8
|
+
PROFILE_OPTIONS = {
|
|
9
|
+
:measure_modes => [RubyProf::PROCESS_TIME],
|
|
10
|
+
:count => 10,
|
|
11
|
+
:printers => [RubyProf::FlatPrinter, RubyProf::GraphHtmlPrinter],
|
|
12
|
+
:min_percent => 0.05,
|
|
13
|
+
:output_dir => Dir.pwd }
|
|
14
|
+
|
|
15
|
+
def output_dir
|
|
16
|
+
PROFILE_OPTIONS[:output_dir]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run(result)
|
|
20
|
+
return if @method_name.to_s == "default_test"
|
|
21
|
+
|
|
22
|
+
yield(self.class::STARTED, name)
|
|
23
|
+
@_result = result
|
|
24
|
+
run_warmup
|
|
25
|
+
PROFILE_OPTIONS[:measure_modes].each do |measure_mode|
|
|
26
|
+
data = run_profile(measure_mode)
|
|
27
|
+
report_profile(data, measure_mode)
|
|
28
|
+
result.add_run
|
|
29
|
+
end
|
|
30
|
+
yield(self.class::FINISHED, name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def run_test
|
|
34
|
+
begin
|
|
35
|
+
setup
|
|
36
|
+
yield
|
|
37
|
+
rescue ::Test::Unit::AssertionFailedError => e
|
|
38
|
+
add_failure(e.message, e.backtrace)
|
|
39
|
+
rescue StandardError, ScriptError
|
|
40
|
+
add_error($!)
|
|
41
|
+
ensure
|
|
42
|
+
begin
|
|
43
|
+
teardown
|
|
44
|
+
rescue ::Test::Unit::AssertionFailedError => e
|
|
45
|
+
add_failure(e.message, e.backtrace)
|
|
46
|
+
rescue StandardError, ScriptError
|
|
47
|
+
add_error($!)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def run_warmup
|
|
53
|
+
print "\n#{self.class.name}##{method_name}"
|
|
54
|
+
|
|
55
|
+
run_test do
|
|
56
|
+
bench = Benchmark.realtime do
|
|
57
|
+
__send__(@method_name)
|
|
58
|
+
end
|
|
59
|
+
puts " (%.2fs warmup)" % bench
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def run_profile(measure_mode)
|
|
64
|
+
RubyProf.measure_mode = measure_mode
|
|
65
|
+
|
|
66
|
+
print ' '
|
|
67
|
+
PROFILE_OPTIONS[:count].times do |i|
|
|
68
|
+
run_test do
|
|
69
|
+
begin
|
|
70
|
+
print '.'
|
|
71
|
+
$stdout.flush
|
|
72
|
+
GC.disable
|
|
73
|
+
|
|
74
|
+
RubyProf.resume do
|
|
75
|
+
__send__(@method_name)
|
|
76
|
+
end
|
|
77
|
+
ensure
|
|
78
|
+
GC.enable
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
data = RubyProf.stop
|
|
84
|
+
bench = data.threads.values.inject(0) do |total, method_infos|
|
|
85
|
+
top = method_infos.sort.last
|
|
86
|
+
total += top.total_time
|
|
87
|
+
total
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
puts "\n #{measure_mode_name(measure_mode)}: #{format_profile_total(bench, measure_mode)}\n"
|
|
91
|
+
|
|
92
|
+
data
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def format_profile_total(total, measure_mode)
|
|
96
|
+
case measure_mode
|
|
97
|
+
when RubyProf::PROCESS_TIME, RubyProf::WALL_TIME
|
|
98
|
+
"%.2f seconds" % total
|
|
99
|
+
when RubyProf::MEMORY
|
|
100
|
+
"%.2f kilobytes" % total
|
|
101
|
+
when RubyProf::ALLOCATIONS
|
|
102
|
+
"%d allocations" % total
|
|
103
|
+
else
|
|
104
|
+
"%.2f #{measure_mode}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def report_profile(data, measure_mode)
|
|
109
|
+
PROFILE_OPTIONS[:printers].each do |printer_klass|
|
|
110
|
+
printer = printer_klass.new(data)
|
|
111
|
+
|
|
112
|
+
# Makes sure the output directory exits
|
|
113
|
+
FileUtils.mkdir_p(output_dir)
|
|
114
|
+
|
|
115
|
+
# Open the file
|
|
116
|
+
file_name = report_filename(printer, measure_mode)
|
|
117
|
+
|
|
118
|
+
File.open(file_name, 'wb') do |file|
|
|
119
|
+
printer.print(file, PROFILE_OPTIONS)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# The report filename is test_name + measure_mode + report_type
|
|
125
|
+
def report_filename(printer, measure_mode)
|
|
126
|
+
suffix =
|
|
127
|
+
case printer
|
|
128
|
+
when RubyProf::FlatPrinter; 'flat.txt'
|
|
129
|
+
when RubyProf::GraphPrinter; 'graph.txt'
|
|
130
|
+
when RubyProf::GraphHtmlPrinter; 'graph.html'
|
|
131
|
+
when RubyProf::CallTreePrinter; 'tree.txt'
|
|
132
|
+
else printer.to_s.downcase
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
"#{output_dir}/#{method_name}_#{measure_mode_name(measure_mode)}_#{suffix}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def measure_mode_name(measure_mode)
|
|
139
|
+
case measure_mode
|
|
140
|
+
when RubyProf::PROCESS_TIME; 'process_time'
|
|
141
|
+
when RubyProf::WALL_TIME; 'wall_time'
|
|
142
|
+
when RubyProf::MEMORY; 'memory'
|
|
143
|
+
when RubyProf::ALLOCATIONS; 'allocations'
|
|
144
|
+
else "measure#{measure_mode}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|