acunote-ruby-prof 0.9.2
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 +240 -0
- data/LICENSE +23 -0
- data/README.rdoc +439 -0
- data/Rakefile +148 -0
- data/bin/ruby-prof +236 -0
- data/examples/empty.png +0 -0
- data/examples/flat.txt +55 -0
- data/examples/graph.dot +106 -0
- data/examples/graph.html +823 -0
- data/examples/graph.png +0 -0
- data/examples/graph.txt +170 -0
- data/examples/minus.png +0 -0
- data/examples/multi.flat.txt +23 -0
- data/examples/multi.graph.html +906 -0
- data/examples/multi.grind.dat +194 -0
- data/examples/multi.stack.html +573 -0
- data/examples/plus.png +0 -0
- data/examples/stack.html +573 -0
- data/ext/ruby_prof/extconf.rb +43 -0
- data/ext/ruby_prof/measure_allocations.h +58 -0
- data/ext/ruby_prof/measure_cpu_time.h +152 -0
- data/ext/ruby_prof/measure_gc_runs.h +76 -0
- data/ext/ruby_prof/measure_gc_time.h +57 -0
- data/ext/ruby_prof/measure_memory.h +101 -0
- data/ext/ruby_prof/measure_process_time.h +52 -0
- data/ext/ruby_prof/measure_wall_time.h +53 -0
- data/ext/ruby_prof/mingw/Rakefile +23 -0
- data/ext/ruby_prof/mingw/build.rake +38 -0
- data/ext/ruby_prof/ruby_prof.c +1834 -0
- data/ext/ruby_prof/ruby_prof.h +190 -0
- data/ext/ruby_prof/version.h +4 -0
- data/lib/ruby-prof.rb +62 -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 +751 -0
- data/lib/ruby-prof/call_tree_printer.rb +133 -0
- data/lib/ruby-prof/dot_printer.rb +153 -0
- data/lib/ruby-prof/empty.png +0 -0
- data/lib/ruby-prof/flat_printer.rb +78 -0
- data/lib/ruby-prof/flat_printer_with_line_numbers.rb +72 -0
- data/lib/ruby-prof/graph_html_printer.rb +278 -0
- data/lib/ruby-prof/graph_printer.rb +245 -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 +54 -0
- data/lib/ruby-prof/plus.png +0 -0
- data/lib/ruby-prof/rack.rb +30 -0
- data/lib/ruby-prof/result.rb +70 -0
- data/lib/ruby-prof/symbol_to_proc.rb +8 -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 +290 -0
- data/test/current_failures_windows +8 -0
- data/test/do_nothing.rb +0 -0
- data/test/duplicate_names_test.rb +32 -0
- data/test/enumerable_test.rb +16 -0
- data/test/exceptions_test.rb +15 -0
- data/test/exclude_threads_test.rb +54 -0
- data/test/exec_test.rb +14 -0
- data/test/line_number_test.rb +73 -0
- data/test/measurement_test.rb +122 -0
- data/test/method_elimination_test.rb +74 -0
- data/test/module_test.rb +44 -0
- data/test/multi_printer_test.rb +81 -0
- data/test/no_method_class_test.rb +13 -0
- data/test/prime.rb +55 -0
- data/test/prime_test.rb +13 -0
- data/test/printers_test.rb +164 -0
- data/test/recursive_test.rb +236 -0
- data/test/ruby-prof-bin +20 -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 +112 -0
- data/test/test_suite.rb +32 -0
- data/test/thread_test.rb +173 -0
- data/test/unique_call_path_test.rb +225 -0
- metadata +185 -0
@@ -0,0 +1,245 @@
|
|
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. See the README
|
15
|
+
|
16
|
+
class GraphPrinter < AbstractPrinter
|
17
|
+
PERCENTAGE_WIDTH = 8
|
18
|
+
TIME_WIDTH = 10
|
19
|
+
CALL_WIDTH = 17
|
20
|
+
|
21
|
+
# Create a GraphPrinter. Result is a RubyProf::Result
|
22
|
+
# object generated from a profiling run.
|
23
|
+
def initialize(result)
|
24
|
+
super(result)
|
25
|
+
@thread_times = Hash.new
|
26
|
+
calculate_thread_times
|
27
|
+
end
|
28
|
+
|
29
|
+
def calculate_thread_times
|
30
|
+
# Cache thread times since this is an expensive
|
31
|
+
# operation with the required sorting
|
32
|
+
@result.threads.each do |thread_id, methods|
|
33
|
+
top = methods.max
|
34
|
+
|
35
|
+
thread_time = [top.total_time, 0.01].max
|
36
|
+
|
37
|
+
@thread_times[thread_id] = thread_time
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Print a graph report to the provided output.
|
42
|
+
#
|
43
|
+
# output - Any IO oject, including STDOUT or a file.
|
44
|
+
# The default value is STDOUT.
|
45
|
+
#
|
46
|
+
# options - Hash of print options. See #setup_options
|
47
|
+
# for more information.
|
48
|
+
#
|
49
|
+
def print(output = STDOUT, options = {})
|
50
|
+
@output = output
|
51
|
+
setup_options(options)
|
52
|
+
print_threads
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def print_threads
|
57
|
+
# sort assumes that spawned threads have higher object_ids
|
58
|
+
@result.threads.sort.each do |thread_id, methods|
|
59
|
+
print_methods(thread_id, methods)
|
60
|
+
@output << "\n" * 2
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def print_methods(thread_id, methods)
|
65
|
+
# Sort methods from longest to shortest total time
|
66
|
+
methods = methods.sort
|
67
|
+
|
68
|
+
toplevel = methods.last
|
69
|
+
total_time = toplevel.total_time
|
70
|
+
if total_time == 0
|
71
|
+
total_time = 0.01
|
72
|
+
end
|
73
|
+
|
74
|
+
print_heading(thread_id)
|
75
|
+
|
76
|
+
# Print each method in total time order
|
77
|
+
methods.reverse_each do |method|
|
78
|
+
total_percentage = (method.total_time/total_time) * 100
|
79
|
+
self_percentage = (method.self_time/total_time) * 100
|
80
|
+
|
81
|
+
next if total_percentage < min_percent
|
82
|
+
|
83
|
+
@output << "-" * 80 << "\n"
|
84
|
+
|
85
|
+
print_parents(thread_id, method)
|
86
|
+
|
87
|
+
# 1 is for % sign
|
88
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
|
89
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
|
90
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
|
91
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
|
92
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
|
93
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
|
94
|
+
@output << sprintf("%#{CALL_WIDTH}i", method.called)
|
95
|
+
@output << sprintf(" %s", method_name(method))
|
96
|
+
if print_file
|
97
|
+
@output << sprintf(" %s:%s", method.source_file, method.line)
|
98
|
+
end
|
99
|
+
@output << "\n"
|
100
|
+
|
101
|
+
print_children(method)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def print_heading(thread_id)
|
106
|
+
@output << "Thread ID: #{thread_id}\n"
|
107
|
+
@output << "Total Time: #{@thread_times[thread_id]}\n"
|
108
|
+
@output << "\n"
|
109
|
+
|
110
|
+
# 1 is for % sign
|
111
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
|
112
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
|
113
|
+
@output << sprintf("%#{TIME_WIDTH}s", "total")
|
114
|
+
@output << sprintf("%#{TIME_WIDTH}s", "self")
|
115
|
+
@output << sprintf("%#{TIME_WIDTH}s", "wait")
|
116
|
+
@output << sprintf("%#{TIME_WIDTH}s", "child")
|
117
|
+
@output << sprintf("%#{CALL_WIDTH}s", "calls")
|
118
|
+
@output << " Name"
|
119
|
+
@output << "\n"
|
120
|
+
end
|
121
|
+
|
122
|
+
def print_parents(thread_id, method)
|
123
|
+
method.aggregate_parents.sort_by(&:total_time).each do |caller|
|
124
|
+
next unless caller.parent
|
125
|
+
@output << " " * 2 * PERCENTAGE_WIDTH
|
126
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", caller.total_time)
|
127
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", caller.self_time)
|
128
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", caller.wait_time)
|
129
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", caller.children_time)
|
130
|
+
|
131
|
+
call_called = "#{caller.called}/#{method.called}"
|
132
|
+
@output << sprintf("%#{CALL_WIDTH}s", call_called)
|
133
|
+
@output << sprintf(" %s", caller.parent.target.full_name)
|
134
|
+
@output << "\n"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_children(method)
|
139
|
+
method.aggregate_children.sort_by(&:total_time).reverse.each do |child|
|
140
|
+
# Get children method
|
141
|
+
|
142
|
+
@output << " " * 2 * PERCENTAGE_WIDTH
|
143
|
+
|
144
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.total_time)
|
145
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.self_time)
|
146
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.wait_time)
|
147
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.children_time)
|
148
|
+
|
149
|
+
call_called = "#{child.called}/#{child.target.called}"
|
150
|
+
@output << sprintf("%#{CALL_WIDTH}s", call_called)
|
151
|
+
@output << sprintf(" %s", child.target.full_name)
|
152
|
+
@output << "\n"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class GraphPrinterWithoutAggregation < GraphPrinter
|
158
|
+
private
|
159
|
+
def print_methods(thread_id, methods)
|
160
|
+
# Sort methods from longest to shortest total time
|
161
|
+
methods = methods.sort
|
162
|
+
|
163
|
+
toplevel = methods.last
|
164
|
+
total_time = toplevel.total_time
|
165
|
+
total_time = 0.01 if total_time == 0
|
166
|
+
|
167
|
+
print_heading(thread_id)
|
168
|
+
|
169
|
+
# Print each method in total time order
|
170
|
+
methods.reverse_each do |method|
|
171
|
+
total_percentage = (method.total_time/total_time) * 100
|
172
|
+
self_percentage = (method.self_time/total_time) * 100
|
173
|
+
|
174
|
+
next if total_percentage < min_percent
|
175
|
+
|
176
|
+
parents = method.call_infos.map{|caller| caller if caller.parent}
|
177
|
+
children = method.children
|
178
|
+
call_tree = Hash.new
|
179
|
+
parents.each do |parent|
|
180
|
+
call_tree[parent] = Array.new
|
181
|
+
if parent
|
182
|
+
children.each do |child|
|
183
|
+
call_tree[parent] << child if child.call_sequence.include?(parent.call_sequence)
|
184
|
+
end
|
185
|
+
else
|
186
|
+
call_tree[parent] = children
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
call_tree.each do |parent, children|
|
191
|
+
@output << "-" * 80 << "\n"
|
192
|
+
print_parent(parent, method) if parent
|
193
|
+
|
194
|
+
# 1 is for % sign
|
195
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
|
196
|
+
@output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
|
197
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
|
198
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
|
199
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
|
200
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
|
201
|
+
@output << sprintf("%#{CALL_WIDTH}i", method.called)
|
202
|
+
@output << sprintf(" %s", method_name(method))
|
203
|
+
if print_file
|
204
|
+
@output << sprintf(" %s:%s", method.source_file, method.line)
|
205
|
+
end
|
206
|
+
@output << "\n"
|
207
|
+
|
208
|
+
print_children(children)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def print_parent(parent, method)
|
214
|
+
@output << " " * 2 * PERCENTAGE_WIDTH
|
215
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", parent.total_time)
|
216
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", parent.self_time)
|
217
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", parent.wait_time)
|
218
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", parent.children_time)
|
219
|
+
|
220
|
+
call_called = "#{parent.called}/#{method.called}"
|
221
|
+
@output << sprintf("%#{CALL_WIDTH}s", call_called)
|
222
|
+
@output << sprintf(" %s", parent.parent.target.full_name)
|
223
|
+
@output << "\n"
|
224
|
+
end
|
225
|
+
|
226
|
+
def print_children(children)
|
227
|
+
children.each do |child|
|
228
|
+
@output << " " * 2 * PERCENTAGE_WIDTH
|
229
|
+
|
230
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.total_time)
|
231
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.self_time)
|
232
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.wait_time)
|
233
|
+
@output << sprintf("%#{TIME_WIDTH}.2f", child.children_time)
|
234
|
+
|
235
|
+
call_called = "#{child.called}/#{child.target.called}"
|
236
|
+
@output << sprintf("%#{CALL_WIDTH}s", call_called)
|
237
|
+
@output << sprintf(" %s", child.target.full_name)
|
238
|
+
@output << "\n"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end #end class
|
243
|
+
|
244
|
+
end #end module
|
245
|
+
|
@@ -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
|
+
@min_depth ||= 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,54 @@
|
|
1
|
+
module RubyProf
|
2
|
+
# Helper class to simplify printing profiles of several types from
|
3
|
+
# one profiling run. Currently prints a flat profile, a callgrind
|
4
|
+
# profile, a call stack profile and a graph profile.
|
5
|
+
class MultiPrinter
|
6
|
+
def initialize(result)
|
7
|
+
@stack_printer = CallStackPrinter.new(result)
|
8
|
+
@graph_printer = GraphHtmlPrinter.new(result)
|
9
|
+
@tree_printer = CallTreePrinter.new(result)
|
10
|
+
@flat_printer = FlatPrinter.new(result)
|
11
|
+
end
|
12
|
+
|
13
|
+
# create profile files under options[:path] or the current
|
14
|
+
# directory. options[:profile] is used as the base name for the
|
15
|
+
# pofile file, defaults to "profile".
|
16
|
+
def print(options)
|
17
|
+
@profile = options.delete(:profile) || "profile"
|
18
|
+
@directory = options.delete(:path) || File.expand_path(".")
|
19
|
+
File.open(stack_profile, "w") do |f|
|
20
|
+
@stack_printer.print(f, options.merge(:graph => "#{@profile}.graph.html"))
|
21
|
+
end
|
22
|
+
File.open(graph_profile, "w") do |f|
|
23
|
+
@graph_printer.print(f, options)
|
24
|
+
end
|
25
|
+
File.open(tree_profile, "w") do |f|
|
26
|
+
@tree_printer.print(f, options)
|
27
|
+
end
|
28
|
+
File.open(flat_profile, "w") do |f|
|
29
|
+
@flat_printer.print(f, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# the name of the call stack profile file
|
34
|
+
def stack_profile
|
35
|
+
"#{@directory}/#{@profile}.stack.html"
|
36
|
+
end
|
37
|
+
|
38
|
+
# the name of the graph profile file
|
39
|
+
def graph_profile
|
40
|
+
"#{@directory}/#{@profile}.graph.html"
|
41
|
+
end
|
42
|
+
|
43
|
+
# the name of the callgrind profile file
|
44
|
+
def tree_profile
|
45
|
+
"#{@directory}/#{@profile}.grind.dat"
|
46
|
+
end
|
47
|
+
|
48
|
+
# the name of the flat profile file
|
49
|
+
def flat_profile
|
50
|
+
"#{@directory}/#{@profile}.flat.txt"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class RubyProf
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
::RubyProf.start
|
11
|
+
result = @app.call(env)
|
12
|
+
data = ::RubyProf.stop
|
13
|
+
|
14
|
+
print(data)
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def print(data)
|
19
|
+
printers = {::RubyProf::FlatPrinter => ::File.join(Dir.tmpdir, 'profile.txt'),
|
20
|
+
::RubyProf::GraphHtmlPrinter => ::File.join(Dir.tmpdir, 'profile.html')}
|
21
|
+
|
22
|
+
printers.each do |printer_klass, file_name|
|
23
|
+
printer = printer_klass.new(data)
|
24
|
+
::File.open(file_name, 'wb') do |file|
|
25
|
+
printer.print(file, :min_percent => 0.00000001 )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|