ruby-prof 2.0.2 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/bin/ruby-prof +336 -336
- data/docs/architecture.md +304 -122
- data/docs/reports.md +1 -0
- data/lib/ruby-prof/call_tree_visitor.rb +39 -36
- data/lib/ruby-prof/printers/abstract_printer.rb +143 -142
- data/lib/ruby-prof/printers/call_info_printer.rb +53 -53
- data/lib/ruby-prof/printers/call_stack_printer.rb +169 -168
- data/lib/ruby-prof/printers/flame_graph_printer.rb +78 -79
- data/lib/ruby-prof/version.rb +3 -3
- metadata +3 -3
|
@@ -1,142 +1,143 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
module RubyProf
|
|
4
|
-
# This is the base class for all Printers. It is never used directly.
|
|
5
|
-
class AbstractPrinter
|
|
6
|
-
# :stopdoc:
|
|
7
|
-
def self.needs_dir?
|
|
8
|
-
false
|
|
9
|
-
end
|
|
10
|
-
# :startdoc:
|
|
11
|
-
|
|
12
|
-
# Create a new printer.
|
|
13
|
-
#
|
|
14
|
-
# result should be the output generated from a profiling run
|
|
15
|
-
def initialize(result)
|
|
16
|
-
@result = result
|
|
17
|
-
@output = nil
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
attr_reader :min_percent, :max_percent, :filter_by, :sort_method
|
|
21
|
-
|
|
22
|
-
# Returns the time format used to show when a profile was run
|
|
23
|
-
def time_format
|
|
24
|
-
'%A, %B %-d at %l:%M:%S %p (%Z)'
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Prints a report to the provided output.
|
|
28
|
-
#
|
|
29
|
-
# output - Any IO object, including STDOUT or a file.
|
|
30
|
-
# The default value is STDOUT.
|
|
31
|
-
#
|
|
32
|
-
# Keyword arguments:
|
|
33
|
-
# min_percent - Number 0 to 100 that specifies the minimum
|
|
34
|
-
# %self (the methods self time divided by the
|
|
35
|
-
# overall total time) that a method must take
|
|
36
|
-
# for it to be printed out in the report.
|
|
37
|
-
# Default value is 0.
|
|
38
|
-
#
|
|
39
|
-
# max_percent - Number 0 to 100 that specifies the maximum
|
|
40
|
-
# %self for methods to include.
|
|
41
|
-
# Default value is 100.
|
|
42
|
-
#
|
|
43
|
-
# filter_by - Which time metric to use when applying
|
|
44
|
-
# min_percent and max_percent filters.
|
|
45
|
-
# Default value is :self_time.
|
|
46
|
-
#
|
|
47
|
-
# sort_method - Specifies method used for sorting method infos.
|
|
48
|
-
# Available values are :total_time, :self_time,
|
|
49
|
-
# :wait_time, :children_time.
|
|
50
|
-
# Default value depends on the printer.
|
|
51
|
-
def print(output = STDOUT, min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, **)
|
|
52
|
-
@output = output
|
|
53
|
-
@min_percent = min_percent
|
|
54
|
-
@max_percent = max_percent
|
|
55
|
-
@filter_by = filter_by
|
|
56
|
-
@sort_method = sort_method
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
File.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
@output << "
|
|
90
|
-
@output << "
|
|
91
|
-
@output << "
|
|
92
|
-
@output << "
|
|
93
|
-
@output << "\n"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RubyProf
|
|
4
|
+
# This is the base class for all Printers. It is never used directly.
|
|
5
|
+
class AbstractPrinter
|
|
6
|
+
# :stopdoc:
|
|
7
|
+
def self.needs_dir?
|
|
8
|
+
false
|
|
9
|
+
end
|
|
10
|
+
# :startdoc:
|
|
11
|
+
|
|
12
|
+
# Create a new printer.
|
|
13
|
+
#
|
|
14
|
+
# result should be the output generated from a profiling run
|
|
15
|
+
def initialize(result)
|
|
16
|
+
@result = result
|
|
17
|
+
@output = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :min_percent, :max_percent, :filter_by, :sort_method
|
|
21
|
+
|
|
22
|
+
# Returns the time format used to show when a profile was run
|
|
23
|
+
def time_format
|
|
24
|
+
'%A, %B %-d at %l:%M:%S %p (%Z)'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Prints a report to the provided output.
|
|
28
|
+
#
|
|
29
|
+
# output - Any IO object, including STDOUT or a file.
|
|
30
|
+
# The default value is STDOUT.
|
|
31
|
+
#
|
|
32
|
+
# Keyword arguments:
|
|
33
|
+
# min_percent - Number 0 to 100 that specifies the minimum
|
|
34
|
+
# %self (the methods self time divided by the
|
|
35
|
+
# overall total time) that a method must take
|
|
36
|
+
# for it to be printed out in the report.
|
|
37
|
+
# Default value is 0.
|
|
38
|
+
#
|
|
39
|
+
# max_percent - Number 0 to 100 that specifies the maximum
|
|
40
|
+
# %self for methods to include.
|
|
41
|
+
# Default value is 100.
|
|
42
|
+
#
|
|
43
|
+
# filter_by - Which time metric to use when applying
|
|
44
|
+
# min_percent and max_percent filters.
|
|
45
|
+
# Default value is :self_time.
|
|
46
|
+
#
|
|
47
|
+
# sort_method - Specifies method used for sorting method infos.
|
|
48
|
+
# Available values are :total_time, :self_time,
|
|
49
|
+
# :wait_time, :children_time.
|
|
50
|
+
# Default value depends on the printer.
|
|
51
|
+
def print(output = STDOUT, min_percent: 0, max_percent: 100, filter_by: :self_time, sort_method: nil, max_depth: nil, **)
|
|
52
|
+
@output = output
|
|
53
|
+
@min_percent = min_percent
|
|
54
|
+
@max_percent = max_percent
|
|
55
|
+
@filter_by = filter_by
|
|
56
|
+
@sort_method = sort_method
|
|
57
|
+
@max_depth = max_depth
|
|
58
|
+
print_threads
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def method_location(method)
|
|
62
|
+
if method.source_file
|
|
63
|
+
"#{method.source_file}:#{method.line}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def method_href(thread, method)
|
|
68
|
+
h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread.fiber_id.to_s)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def open_asset(file)
|
|
72
|
+
path = File.join(File.expand_path('../../assets', __FILE__), file)
|
|
73
|
+
File.open(path, 'rb').read
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def print_threads
|
|
77
|
+
@result.threads.each do |thread|
|
|
78
|
+
print_thread(thread)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def print_thread(thread)
|
|
83
|
+
print_header(thread)
|
|
84
|
+
print_methods(thread)
|
|
85
|
+
print_footer(thread)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def print_header(thread)
|
|
89
|
+
@output << "Measure Mode: %s\n" % @result.measure_mode_string
|
|
90
|
+
@output << "Thread ID: %d\n" % thread.id
|
|
91
|
+
@output << "Fiber ID: %d\n" % thread.fiber_id unless thread.id == thread.fiber_id
|
|
92
|
+
@output << "Total: %0.6f\n" % thread.total_time
|
|
93
|
+
@output << "Sort by: #{sort_method}\n"
|
|
94
|
+
@output << "\n"
|
|
95
|
+
print_column_headers
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def print_column_headers
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def print_footer(thread)
|
|
102
|
+
metric_data = {
|
|
103
|
+
0 => { label: "time", prefix: "", suffix: "spent" },
|
|
104
|
+
1 => { label: "time", prefix: "", suffix: "spent" },
|
|
105
|
+
2 => { label: "allocations", prefix: "number of ", suffix: "made" },
|
|
106
|
+
3 => { label: "memory", prefix: "", suffix: "used" }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
metric = metric_data[@result.measure_mode]
|
|
110
|
+
|
|
111
|
+
metric_label = metric[:label]
|
|
112
|
+
metric_suffix = metric[:suffix]
|
|
113
|
+
metric_prefix = metric[:prefix]
|
|
114
|
+
|
|
115
|
+
metric1 = "#{metric_label} #{metric_suffix}"
|
|
116
|
+
metric2 = "#{metric_prefix}#{metric1}"
|
|
117
|
+
metric3 = metric_label
|
|
118
|
+
|
|
119
|
+
# Output the formatted text
|
|
120
|
+
@output << <<~EOT
|
|
121
|
+
|
|
122
|
+
* recursively called methods
|
|
123
|
+
|
|
124
|
+
Columns are:
|
|
125
|
+
|
|
126
|
+
%self - The percentage of #{metric1} by this method relative to the total #{metric3} in the entire program.
|
|
127
|
+
total - The total #{metric2} by this method and its children.
|
|
128
|
+
self - The #{metric2} by this method.
|
|
129
|
+
wait - The time this method spent waiting for other threads.
|
|
130
|
+
child - The #{metric2} by this method's children.
|
|
131
|
+
calls - The number of times this method was called.
|
|
132
|
+
name - The name of the method.
|
|
133
|
+
location - The location of the method.
|
|
134
|
+
|
|
135
|
+
The interpretation of method names is:
|
|
136
|
+
|
|
137
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
|
138
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
|
139
|
+
|
|
140
|
+
EOT
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
module RubyProf
|
|
4
|
-
# Prints out the call graph based on CallTree instances. This
|
|
5
|
-
# is mainly for debugging purposes as it provides access into
|
|
6
|
-
# into RubyProf's internals.
|
|
7
|
-
#
|
|
8
|
-
# To use the printer:
|
|
9
|
-
#
|
|
10
|
-
# result = RubyProf.profile do
|
|
11
|
-
# [code to profile]
|
|
12
|
-
# end
|
|
13
|
-
#
|
|
14
|
-
# printer = RubyProf::CallInfoPrinter.new(result)
|
|
15
|
-
# printer.print(STDOUT)
|
|
16
|
-
class CallInfoPrinter < AbstractPrinter
|
|
17
|
-
TIME_WIDTH = 0
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def print_header(thread)
|
|
22
|
-
@output << "----------------------------------------------------\n"
|
|
23
|
-
@output << "Thread ID: #{thread.id}\n"
|
|
24
|
-
@output << "Fiber ID: #{thread.fiber_id}\n"
|
|
25
|
-
@output << "Total Time: #{thread.total_time}\n"
|
|
26
|
-
@output << "Sort by: #{sort_method}\n"
|
|
27
|
-
@output << "\n"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def print_methods(thread)
|
|
31
|
-
visitor = CallTreeVisitor.new(thread.call_tree)
|
|
32
|
-
|
|
33
|
-
visitor.visit do |call_tree, event|
|
|
34
|
-
if event == :enter
|
|
35
|
-
@output << " " * call_tree.depth
|
|
36
|
-
@output << call_tree.target.full_name
|
|
37
|
-
@output << " ("
|
|
38
|
-
@output << "tt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.total_time)}, "
|
|
39
|
-
@output << "st:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.self_time)}, "
|
|
40
|
-
@output << "wt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.wait_time)}, "
|
|
41
|
-
@output << "ct:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.children_time)}, "
|
|
42
|
-
@output << "call:#{call_tree.called}, "
|
|
43
|
-
@output << ")"
|
|
44
|
-
@output << "\n"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def print_footer(thread)
|
|
50
|
-
@output << "\n" << "\n"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RubyProf
|
|
4
|
+
# Prints out the call graph based on CallTree instances. This
|
|
5
|
+
# is mainly for debugging purposes as it provides access into
|
|
6
|
+
# into RubyProf's internals.
|
|
7
|
+
#
|
|
8
|
+
# To use the printer:
|
|
9
|
+
#
|
|
10
|
+
# result = RubyProf.profile do
|
|
11
|
+
# [code to profile]
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# printer = RubyProf::CallInfoPrinter.new(result)
|
|
15
|
+
# printer.print(STDOUT)
|
|
16
|
+
class CallInfoPrinter < AbstractPrinter
|
|
17
|
+
TIME_WIDTH = 0
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def print_header(thread)
|
|
22
|
+
@output << "----------------------------------------------------\n"
|
|
23
|
+
@output << "Thread ID: #{thread.id}\n"
|
|
24
|
+
@output << "Fiber ID: #{thread.fiber_id}\n"
|
|
25
|
+
@output << "Total Time: #{thread.total_time}\n"
|
|
26
|
+
@output << "Sort by: #{sort_method}\n"
|
|
27
|
+
@output << "\n"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def print_methods(thread)
|
|
31
|
+
visitor = CallTreeVisitor.new(thread.call_tree, max_depth: @max_depth)
|
|
32
|
+
|
|
33
|
+
visitor.visit do |call_tree, event|
|
|
34
|
+
if event == :enter
|
|
35
|
+
@output << " " * call_tree.depth
|
|
36
|
+
@output << call_tree.target.full_name
|
|
37
|
+
@output << " ("
|
|
38
|
+
@output << "tt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.total_time)}, "
|
|
39
|
+
@output << "st:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.self_time)}, "
|
|
40
|
+
@output << "wt:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.wait_time)}, "
|
|
41
|
+
@output << "ct:#{sprintf("%#{TIME_WIDTH}.2f", call_tree.children_time)}, "
|
|
42
|
+
@output << "call:#{call_tree.called}, "
|
|
43
|
+
@output << ")"
|
|
44
|
+
@output << "\n"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def print_footer(thread)
|
|
50
|
+
@output << "\n" << "\n"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|