ruby-prof 1.4.4-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGES +608 -0
- data/LICENSE +25 -0
- data/README.md +5 -0
- data/Rakefile +98 -0
- data/bin/ruby-prof +328 -0
- data/bin/ruby-prof-check-trace +45 -0
- data/ext/ruby_prof/extconf.rb +22 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
- data/ext/ruby_prof/rp_allocation.c +287 -0
- data/ext/ruby_prof/rp_allocation.h +31 -0
- data/ext/ruby_prof/rp_call_tree.c +367 -0
- data/ext/ruby_prof/rp_call_tree.h +43 -0
- data/ext/ruby_prof/rp_call_trees.c +288 -0
- data/ext/ruby_prof/rp_call_trees.h +28 -0
- data/ext/ruby_prof/rp_measure_allocations.c +47 -0
- data/ext/ruby_prof/rp_measure_memory.c +46 -0
- data/ext/ruby_prof/rp_measure_process_time.c +66 -0
- data/ext/ruby_prof/rp_measure_wall_time.c +64 -0
- data/ext/ruby_prof/rp_measurement.c +237 -0
- data/ext/ruby_prof/rp_measurement.h +50 -0
- data/ext/ruby_prof/rp_method.c +491 -0
- data/ext/ruby_prof/rp_method.h +62 -0
- data/ext/ruby_prof/rp_profile.c +915 -0
- data/ext/ruby_prof/rp_profile.h +35 -0
- data/ext/ruby_prof/rp_stack.c +212 -0
- data/ext/ruby_prof/rp_stack.h +53 -0
- data/ext/ruby_prof/rp_thread.c +362 -0
- data/ext/ruby_prof/rp_thread.h +39 -0
- data/ext/ruby_prof/ruby_prof.c +52 -0
- data/ext/ruby_prof/ruby_prof.h +26 -0
- data/ext/ruby_prof/vc/ruby_prof.sln +39 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +160 -0
- data/lib/3.1/ruby_prof.so +0 -0
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +711 -0
- data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
- data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
- data/lib/ruby-prof/call_tree.rb +57 -0
- data/lib/ruby-prof/call_tree_visitor.rb +36 -0
- data/lib/ruby-prof/compatibility.rb +99 -0
- data/lib/ruby-prof/exclude_common_methods.rb +198 -0
- data/lib/ruby-prof/measurement.rb +17 -0
- data/lib/ruby-prof/method_info.rb +78 -0
- data/lib/ruby-prof/printers/abstract_printer.rb +137 -0
- data/lib/ruby-prof/printers/call_info_printer.rb +53 -0
- data/lib/ruby-prof/printers/call_stack_printer.rb +180 -0
- data/lib/ruby-prof/printers/call_tree_printer.rb +147 -0
- data/lib/ruby-prof/printers/dot_printer.rb +132 -0
- data/lib/ruby-prof/printers/flat_printer.rb +53 -0
- data/lib/ruby-prof/printers/graph_html_printer.rb +63 -0
- data/lib/ruby-prof/printers/graph_printer.rb +113 -0
- data/lib/ruby-prof/printers/multi_printer.rb +127 -0
- data/lib/ruby-prof/profile.rb +37 -0
- data/lib/ruby-prof/rack.rb +95 -0
- data/lib/ruby-prof/task.rb +147 -0
- data/lib/ruby-prof/thread.rb +20 -0
- data/lib/ruby-prof/version.rb +3 -0
- data/lib/ruby-prof.rb +52 -0
- data/lib/unprof.rb +10 -0
- data/ruby-prof.gemspec +64 -0
- data/test/abstract_printer_test.rb +26 -0
- data/test/alias_test.rb +122 -0
- data/test/basic_test.rb +43 -0
- data/test/call_tree_visitor_test.rb +32 -0
- data/test/call_trees_test.rb +66 -0
- data/test/duplicate_names_test.rb +32 -0
- data/test/dynamic_method_test.rb +67 -0
- data/test/enumerable_test.rb +21 -0
- data/test/exceptions_test.rb +24 -0
- data/test/exclude_methods_test.rb +151 -0
- data/test/exclude_threads_test.rb +53 -0
- data/test/fiber_test.rb +129 -0
- data/test/gc_test.rb +100 -0
- data/test/inverse_call_tree_test.rb +175 -0
- data/test/line_number_test.rb +158 -0
- data/test/marshal_test.rb +145 -0
- data/test/measure_allocations.rb +26 -0
- data/test/measure_allocations_test.rb +333 -0
- data/test/measure_memory_test.rb +688 -0
- data/test/measure_process_time_test.rb +1614 -0
- data/test/measure_times.rb +56 -0
- data/test/measure_wall_time_test.rb +426 -0
- data/test/multi_printer_test.rb +71 -0
- data/test/no_method_class_test.rb +15 -0
- data/test/pause_resume_test.rb +175 -0
- data/test/prime.rb +54 -0
- data/test/prime_script.rb +6 -0
- data/test/printer_call_stack_test.rb +27 -0
- data/test/printer_call_tree_test.rb +30 -0
- data/test/printer_flat_test.rb +99 -0
- data/test/printer_graph_html_test.rb +59 -0
- data/test/printer_graph_test.rb +40 -0
- data/test/printers_test.rb +141 -0
- data/test/printing_recursive_graph_test.rb +81 -0
- data/test/profile_test.rb +16 -0
- data/test/rack_test.rb +93 -0
- data/test/recursive_test.rb +430 -0
- data/test/singleton_test.rb +38 -0
- data/test/stack_printer_test.rb +64 -0
- data/test/start_stop_test.rb +109 -0
- data/test/test_helper.rb +13 -0
- data/test/thread_test.rb +144 -0
- data/test/unique_call_path_test.rb +136 -0
- data/test/yarv_test.rb +60 -0
- metadata +187 -0
@@ -0,0 +1,355 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style media="all" type="text/css">
|
5
|
+
body {
|
6
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
7
|
+
background-color: #F5F7FB;
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
font-size: 16px;
|
11
|
+
}
|
12
|
+
|
13
|
+
table {
|
14
|
+
border-collapse: collapse;
|
15
|
+
/* border: 1px solid #E1E3E7; */
|
16
|
+
border: 1px solid #F3F9FE;
|
17
|
+
font-size: 14px;
|
18
|
+
line-height: normal;
|
19
|
+
width: 100%;
|
20
|
+
}
|
21
|
+
|
22
|
+
table:empty {
|
23
|
+
display: none;
|
24
|
+
}
|
25
|
+
|
26
|
+
th:first-child {
|
27
|
+
border-left: 0;
|
28
|
+
}
|
29
|
+
|
30
|
+
th {
|
31
|
+
text-align: center;
|
32
|
+
background: #F3F9FE;
|
33
|
+
padding: 1rem;
|
34
|
+
/* border-left: 1px solid #E1E3E7; */
|
35
|
+
text-transform: uppercase;
|
36
|
+
font-size: 13px;
|
37
|
+
letter-spacing: 1px;
|
38
|
+
}
|
39
|
+
|
40
|
+
tr.break td {
|
41
|
+
border: 0;
|
42
|
+
border-top: 1px solid #BDC3DD;
|
43
|
+
padding: 0;
|
44
|
+
margin: 0;
|
45
|
+
}
|
46
|
+
|
47
|
+
tr.method td {
|
48
|
+
font-weight: bold;
|
49
|
+
}
|
50
|
+
|
51
|
+
td {
|
52
|
+
padding: 0.5em;
|
53
|
+
}
|
54
|
+
|
55
|
+
td:first-child {
|
56
|
+
width: 190px;
|
57
|
+
}
|
58
|
+
|
59
|
+
tr > td > table {
|
60
|
+
box-shadow: 0px -2px 1px #F9FAFB, 0px 1px 1px #E4EEEE, 0px 1px 2px #EEF3F8;
|
61
|
+
border-radius: 2px;
|
62
|
+
overflow: hidden;
|
63
|
+
}
|
64
|
+
|
65
|
+
tr.break + tr > td.method_name {
|
66
|
+
font-weight: bold;
|
67
|
+
}
|
68
|
+
|
69
|
+
td {
|
70
|
+
/* I'm removing this border because I think it looks way cleaner without it but feel free to add it if you feel it's necessary */
|
71
|
+
/* border-left: 1px solid #E1E3E7; */
|
72
|
+
text-align: center;
|
73
|
+
}
|
74
|
+
|
75
|
+
a {
|
76
|
+
text-decoration: none;
|
77
|
+
}
|
78
|
+
|
79
|
+
.method_name {
|
80
|
+
text-align: left;
|
81
|
+
}
|
82
|
+
|
83
|
+
tfoot td {
|
84
|
+
text-align: left;
|
85
|
+
}
|
86
|
+
|
87
|
+
.report-header {
|
88
|
+
background-color: #0D2483;
|
89
|
+
color: white;
|
90
|
+
padding: 1.5rem 2rem;
|
91
|
+
}
|
92
|
+
|
93
|
+
.report-header > div {
|
94
|
+
display: flex;
|
95
|
+
flex-direction: row;
|
96
|
+
align-items: center;
|
97
|
+
}
|
98
|
+
|
99
|
+
.report-header h1 {
|
100
|
+
margin-bottom: 0;
|
101
|
+
}
|
102
|
+
|
103
|
+
h1, h2, h3, h4, h5, h6 {
|
104
|
+
margin-top: 0;
|
105
|
+
margin-bottom: 0.5rem;
|
106
|
+
}
|
107
|
+
|
108
|
+
h6 {
|
109
|
+
font-size: 0.75rem;
|
110
|
+
text-transform: uppercase;
|
111
|
+
letter-spacing: 1.5px;
|
112
|
+
color: rgba(255, 255, 255, 0.6);
|
113
|
+
}
|
114
|
+
|
115
|
+
.table-header {
|
116
|
+
padding: 3rem 0 1rem;
|
117
|
+
}
|
118
|
+
|
119
|
+
.table-wrapper {
|
120
|
+
margin: 1rem auto;
|
121
|
+
max-width: 1440px;
|
122
|
+
padding: 4rem 5rem;
|
123
|
+
background: white;
|
124
|
+
border-radius: 0.5rem;
|
125
|
+
}
|
126
|
+
|
127
|
+
.center {
|
128
|
+
max-width: 1440px;
|
129
|
+
margin: 0 auto;
|
130
|
+
}
|
131
|
+
|
132
|
+
.layout-right {
|
133
|
+
margin-left: auto;
|
134
|
+
}
|
135
|
+
|
136
|
+
.text-right {
|
137
|
+
display: flex;
|
138
|
+
align-items: flex-end;
|
139
|
+
flex-direction: column;
|
140
|
+
}
|
141
|
+
|
142
|
+
.timestamp {
|
143
|
+
font-size: 12px;
|
144
|
+
margin-bottom: 1rem;
|
145
|
+
color: rgba(255, 255, 255, 0.6);
|
146
|
+
}
|
147
|
+
|
148
|
+
.logo {
|
149
|
+
width: 140px;
|
150
|
+
height: 30px;
|
151
|
+
opacity: 0.5;
|
152
|
+
background-repeat: no-repeat;
|
153
|
+
background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 190 41'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23fff%7D%3C/style%3E%3C/defs%3E%3Cpath class='cls-1' d='M63.49 16.74c0-1.37-.91-2-2.26-2h-3.38v3.93h3.58c1.24.01 2.06-.56 2.06-1.93zM110.43 14.75h-3.6v5h3.6a2.32 2.32 0 0 0 2.5-2.49 2.34 2.34 0 0 0-2.5-2.51zM128.77 14.75h-3.53v4.94h3.53a2.28 2.28 0 0 0 2.47-2.45 2.3 2.3 0 0 0-2.47-2.49zM61.81 21.83h-4v4.32h3.91c1.44 0 2.45-.62 2.45-2.14s-.99-2.18-2.36-2.18zM21 14.75h-3.57v4.94H21a2.28 2.28 0 0 0 2.47-2.45A2.3 2.3 0 0 0 21 14.75z'/%3E%3Cpath class='cls-1' d='M184 .44H5.87A4.93 4.93 0 0 0 .94 5.37V35.5a4.94 4.94 0 0 0 4.93 4.94H184a4.94 4.94 0 0 0 4.94-4.94V5.37A4.94 4.94 0 0 0 184 .44zm-34.38 10.78c4.79 0 8.46 2.89 9.18 7.54h-3.86a5.48 5.48 0 0 0-10.68 0h-3.83c.71-4.65 4.36-7.54 9.19-7.54zM24 29.44L20.46 23h-3v6.46h-3.72v-18h7.68c3.41 0 5.78 2.07 5.78 5.76a5.34 5.34 0 0 1-2.88 5.14l3.82 7.06zm24.31-7.3c0 4.8-2.92 7.56-7.63 7.56S33 26.94 33 22.14V11.48h3.69v10.61c0 2.81 1.37 4.32 4 4.32s3.94-1.51 3.94-4.32V11.48h3.69zm13.92 7.3h-8v-18h7.52c3.24 0 5.59 1.61 5.59 5A4 4 0 0 1 65.51 20 4.22 4.22 0 0 1 68 24.21c0 3.43-2.4 5.23-5.81 5.23zm19.66-6.92v6.92h-3.7v-6.92l-6.81-11h4.29L80 18.85l4.34-7.37h4.32zm16-.5h-7.62v-3.26h7.59zm13 1h-4v6.39h-3.72v-18h7.77c3.41 0 5.79 2.09 5.79 5.79s-2.41 5.85-5.82 5.85zm20.86 6.39L128.26 23h-3v6.46h-3.72v-18h7.69c3.4 0 5.78 2.07 5.78 5.76a5.34 5.34 0 0 1-2.88 5.14l3.87 7.08zm17.85.26c-4.88 0-8.55-2.95-9.21-7.68h3.81A5.49 5.49 0 0 0 155 22h3.84c-.69 4.75-4.38 7.7-9.22 7.7zm18.4-.23h-3.72v-3.7H168zm6.36-7.54h-10.05v-3.26h10.08zm1.44-7.16h-11.49v-3.26h11.52z'/%3E%3C/svg%3E");
|
154
|
+
}
|
155
|
+
</style>
|
156
|
+
</head>
|
157
|
+
<body>
|
158
|
+
<div class="report-header">
|
159
|
+
<div class="center">
|
160
|
+
<div class="layout-left">
|
161
|
+
<h6>Profile Report</h6>
|
162
|
+
<h1><%= @result.measure_mode_string.capitalize %></h1>
|
163
|
+
</div>
|
164
|
+
<div class="layout-right text-right">
|
165
|
+
<div class="timestamp"><%= Time.now.strftime(self.time_format) %></div>
|
166
|
+
<div class="logo"></div>
|
167
|
+
</div>
|
168
|
+
</div>
|
169
|
+
</div>
|
170
|
+
<div class="table-wrapper">
|
171
|
+
<table>
|
172
|
+
<tr>
|
173
|
+
<th>Thread ID</th>
|
174
|
+
<th>Fiber ID</th>
|
175
|
+
<th>Total</th>
|
176
|
+
</tr>
|
177
|
+
<% for thread in @result.threads %>
|
178
|
+
<tr>
|
179
|
+
<td><%= thread.id %></td>
|
180
|
+
<td><a href="#<%= thread.fiber_id %>"><%= thread.fiber_id %></a></td>
|
181
|
+
<td><%= thread.total_time %></td>
|
182
|
+
</tr>
|
183
|
+
<% end %>
|
184
|
+
</table>
|
185
|
+
|
186
|
+
<!-- Methods Tables -->
|
187
|
+
<%
|
188
|
+
for thread in @result.threads
|
189
|
+
methods = thread.methods
|
190
|
+
total_time = thread.total_time
|
191
|
+
%>
|
192
|
+
<h2><a name="<%= thread.fiber_id %>">Thread <%= thread.id %>, Fiber: <%= thread.fiber_id %></a></h2>
|
193
|
+
<table>
|
194
|
+
<thead>
|
195
|
+
<tr>
|
196
|
+
<th>%Total</th>
|
197
|
+
<th>%Self</th>
|
198
|
+
<th>Total</th>
|
199
|
+
<th>Self</th>
|
200
|
+
<th>Wait</th>
|
201
|
+
<th>Child</th>
|
202
|
+
<th>Calls</th>
|
203
|
+
<th class="method_name">Name</th>
|
204
|
+
<% if @result.track_allocations? %>
|
205
|
+
<th>Allocations</th>
|
206
|
+
<% end %>
|
207
|
+
<th>Line</th>
|
208
|
+
</tr>
|
209
|
+
</thead>
|
210
|
+
<tbody>
|
211
|
+
<% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
|
212
|
+
sorted_methods = sort_method ? methods.sort_by(&sort_method) : methods.sort
|
213
|
+
sorted_methods.reverse_each do |method|
|
214
|
+
total_percentage = (method.total_time/total_time) * 100
|
215
|
+
next if total_percentage < min_percent
|
216
|
+
next if min_time && method.total_time < min_time
|
217
|
+
self_percentage = (method.self_time/total_time) * 100 %>
|
218
|
+
|
219
|
+
<!-- Parents -->
|
220
|
+
<% for caller in method.call_trees.callers.sort
|
221
|
+
next if min_time && caller.total_time < min_time %>
|
222
|
+
<tr>
|
223
|
+
<td> </td>
|
224
|
+
<td> </td>
|
225
|
+
<td><%= sprintf("%.2f", caller.total_time) %></td>
|
226
|
+
<td><%= sprintf("%.2f", caller.self_time) %></td>
|
227
|
+
<td><%= sprintf("%.2f", caller.wait_time) %></td>
|
228
|
+
<td><%= sprintf("%.2f", caller.children_time) %></td>
|
229
|
+
<td><%= "#{caller.called}/#{method.called}" %></td>
|
230
|
+
<td class="method_name"><%= create_link(thread, total_time, caller.parent.target) %></td>
|
231
|
+
<% if @result.track_allocations? %>
|
232
|
+
<td>-</td>
|
233
|
+
<% end %>
|
234
|
+
<td><%= if caller.parent.target.source_file
|
235
|
+
file_link(caller.parent.target.source_file, caller.line)
|
236
|
+
end %></td>
|
237
|
+
</tr>
|
238
|
+
<% end %>
|
239
|
+
<tr class="method">
|
240
|
+
<td><%= sprintf("%.2f%%", total_percentage) %></td>
|
241
|
+
<td><%= sprintf("%.2f%%", self_percentage) %></td>
|
242
|
+
<td><%= sprintf("%.2f", method.total_time) %></td>
|
243
|
+
<td><%= sprintf("%.2f", method.self_time) %></td>
|
244
|
+
<td><%= sprintf("%.2f", method.wait_time) %></td>
|
245
|
+
<td><%= sprintf("%.2f", method.children_time) %></td>
|
246
|
+
<td><%= sprintf("%i", method.called) %></td>
|
247
|
+
<td class="method_name">
|
248
|
+
<a name="<%= method_href(thread, method) %>">
|
249
|
+
<%= method.recursive? ? "*" : " " %><%= h method.full_name %>
|
250
|
+
</a>
|
251
|
+
</td>
|
252
|
+
|
253
|
+
<% if @result.track_allocations? %>
|
254
|
+
<td>
|
255
|
+
<% count = method.allocations.reduce(0) do |size, allocation|
|
256
|
+
size += allocation.count
|
257
|
+
end %>
|
258
|
+
<% if count > 0 %>
|
259
|
+
<a class="allocations" href="#<%= method_href(thread, method) %>_allocations" %><%= count %></a>
|
260
|
+
<% else %>
|
261
|
+
0
|
262
|
+
<% end %>
|
263
|
+
</td>
|
264
|
+
<% end %>
|
265
|
+
|
266
|
+
<td><%= if method.source_file
|
267
|
+
file_link(method.source_file, method.line)
|
268
|
+
end %></td>
|
269
|
+
</tr>
|
270
|
+
|
271
|
+
<% if @result.track_allocations? %>
|
272
|
+
<tr>
|
273
|
+
<td colspan="10">
|
274
|
+
<table id="<%= method_href(thread, method) %>_allocations" class="allocations" style="display: none">
|
275
|
+
<% for allocation in method.allocations %>
|
276
|
+
<tr>
|
277
|
+
<td>
|
278
|
+
<%= allocation.klass_name %>
|
279
|
+
</td>
|
280
|
+
<td>
|
281
|
+
<%= "#{allocation.count} (#{allocation.memory} Bytes)" %>
|
282
|
+
</td>
|
283
|
+
<td>
|
284
|
+
<%= "#{allocation.source_file}:#{allocation.line}" %>
|
285
|
+
</td>
|
286
|
+
</tr>
|
287
|
+
<% end %>
|
288
|
+
</table>
|
289
|
+
|
290
|
+
</td>
|
291
|
+
</tr>
|
292
|
+
<% end %>
|
293
|
+
|
294
|
+
<!-- Children -->
|
295
|
+
<% for callee in method.call_trees.callees.sort_by(&:total_time).reverse
|
296
|
+
next if min_time && callee.total_time < min_time %>
|
297
|
+
<tr>
|
298
|
+
<td> </td>
|
299
|
+
<td> </td>
|
300
|
+
<td><%= sprintf("%.2f", callee.total_time) %></td>
|
301
|
+
<td><%= sprintf("%.2f", callee.self_time) %></td>
|
302
|
+
<td><%= sprintf("%.2f", callee.wait_time) %></td>
|
303
|
+
<td><%= sprintf("%.2f", callee.children_time) %></td>
|
304
|
+
<td><%= "#{callee.called}/#{callee.target.called}" %></td>
|
305
|
+
<td class="method_name"><%= create_link(thread, total_time, callee.target) %></td>
|
306
|
+
<% if @result.track_allocations? %>
|
307
|
+
<td>-</td>
|
308
|
+
<% end %>
|
309
|
+
<td><%= file_link(callee.source_file, callee.line) %>
|
310
|
+
</tr>
|
311
|
+
<% end %>
|
312
|
+
|
313
|
+
<!-- Create divider row -->
|
314
|
+
<tr class="break">
|
315
|
+
<td colspan="10"></td>
|
316
|
+
</tr>
|
317
|
+
<% end %>
|
318
|
+
</tbody>
|
319
|
+
<tfoot>
|
320
|
+
<tr>
|
321
|
+
<td colspan="9">* indicates recursively called methods</td>
|
322
|
+
</tr>
|
323
|
+
</tfoot>
|
324
|
+
</table>
|
325
|
+
<% end %>
|
326
|
+
</div>
|
327
|
+
|
328
|
+
<script type="application/javascript">
|
329
|
+
function toggleAllocations(element)
|
330
|
+
{
|
331
|
+
if (element.style.display == 'none')
|
332
|
+
element.style.display = 'block'
|
333
|
+
else
|
334
|
+
element.style.display = 'none'
|
335
|
+
}
|
336
|
+
|
337
|
+
function onClick(event)
|
338
|
+
{
|
339
|
+
if (event.target.tagName == 'A' && event.target.classList.contains('allocations'))
|
340
|
+
{
|
341
|
+
var url = new URL(event.target.href)
|
342
|
+
var element = document.querySelector(url.hash)
|
343
|
+
toggleAllocations(element)
|
344
|
+
event.preventDefault()
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
window.addEventListener('DOMContentLoaded', function(event)
|
349
|
+
{
|
350
|
+
document.addEventListener('click', onClick)
|
351
|
+
})
|
352
|
+
|
353
|
+
</script>
|
354
|
+
</body>
|
355
|
+
</html>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RubyProf
|
4
|
+
# The CallTree class is used to track the relationships between methods. It is a helper class used by
|
5
|
+
# RubyProf::MethodInfo to keep track of which methods called a given method and which methods a given
|
6
|
+
# method called. Each CallTree has a parent and target method. You cannot create a CallTree object directly,
|
7
|
+
# they are generated while running a profile.
|
8
|
+
class CallTree
|
9
|
+
# The number of times the parent method called the target method
|
10
|
+
def called
|
11
|
+
self.measurement.called
|
12
|
+
end
|
13
|
+
|
14
|
+
# The total time resulting from the parent method calling the target method
|
15
|
+
def total_time
|
16
|
+
self.measurement.total_time
|
17
|
+
end
|
18
|
+
|
19
|
+
# The self time (of the parent) resulting from the parent method calling the target method
|
20
|
+
def self_time
|
21
|
+
self.measurement.self_time
|
22
|
+
end
|
23
|
+
|
24
|
+
# The wait time (of the parent) resulting from the parent method calling the target method
|
25
|
+
def wait_time
|
26
|
+
self.measurement.wait_time
|
27
|
+
end
|
28
|
+
|
29
|
+
# The time spent in child methods resulting from the parent method calling the target method
|
30
|
+
def children_time
|
31
|
+
self.total_time - self.self_time - self.wait_time
|
32
|
+
end
|
33
|
+
|
34
|
+
# Compares two CallTree instances. The comparison is based on the CallTree#parent, CallTree#target,
|
35
|
+
# and total time.
|
36
|
+
def <=>(other)
|
37
|
+
if self.target == other.target && self.parent == other.parent
|
38
|
+
0
|
39
|
+
elsif self.total_time < other.total_time
|
40
|
+
-1
|
41
|
+
elsif self.total_time > other.total_time
|
42
|
+
1
|
43
|
+
else
|
44
|
+
self.target.full_name <=> other.target.full_name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# :nodoc:
|
49
|
+
def to_s
|
50
|
+
"<#{self.class.name} - #{self.target.full_name}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
self.to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RubyProf
|
2
|
+
# The call info visitor class does a depth-first traversal across a
|
3
|
+
# list of call infos. At each call_tree node, the visitor executes
|
4
|
+
# the block provided in the #visit method. The block is passed two
|
5
|
+
# parameters, the event and the call_tree instance. Event will be
|
6
|
+
# either :enter or :exit.
|
7
|
+
#
|
8
|
+
# visitor = RubyProf::CallTreeVisitor.new(result.threads.first.call_tree)
|
9
|
+
#
|
10
|
+
# method_names = Array.new
|
11
|
+
#
|
12
|
+
# visitor.visit do |call_tree, event|
|
13
|
+
# method_names << call_tree.target.full_name if event == :enter
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# puts method_names
|
17
|
+
class CallTreeVisitor
|
18
|
+
def initialize(call_tree)
|
19
|
+
@call_tree = call_tree
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit(&block)
|
23
|
+
visit_call_tree(@call_tree, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def visit_call_tree(call_tree, &block)
|
29
|
+
yield call_tree, :enter
|
30
|
+
call_tree.children.each do |child|
|
31
|
+
visit_call_tree(child, &block)
|
32
|
+
end
|
33
|
+
yield call_tree, :exit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# These methods are deprecated and are available for backwards compatability.
|
4
|
+
module RubyProf
|
5
|
+
# call-seq:
|
6
|
+
# measure_mode -> measure_mode
|
7
|
+
#
|
8
|
+
# Returns what ruby-prof is measuring. Valid values include:
|
9
|
+
#
|
10
|
+
# * RubyProf::WALL_TIME
|
11
|
+
# * RubyProf::PROCESS_TIME
|
12
|
+
# * RubyProf::ALLOCATIONS
|
13
|
+
# * RubyProf::MEMORY
|
14
|
+
def self.measure_mode
|
15
|
+
@measure_mode ||= RubyProf::WALL_TIME
|
16
|
+
end
|
17
|
+
|
18
|
+
# call-seq:
|
19
|
+
# measure_mode=value -> void
|
20
|
+
#
|
21
|
+
# Specifies what ruby-prof should measure. Valid values include:
|
22
|
+
#
|
23
|
+
# * RubyProf::WALL_TIME - Wall time measures the real-world time elapsed between any two moments. If there are other processes concurrently running on the system that use significant CPU or disk time during a profiling run then the reported results will be larger than expected. On Windows, wall time is measured using GetTickCount(), on MacOS by mach_absolute_time, on Linux by clock_gettime and otherwise by gettimeofday.
|
24
|
+
# * RubyProf::PROCESS_TIME - Process time measures the time used by a process between any two moments. It is unaffected by other processes concurrently running on the system. Remember with process time that calls to methods like sleep will not be included in profiling results. On Windows, process time is measured using GetProcessTimes and on other platforms by clock_gettime.
|
25
|
+
# * RubyProf::ALLOCATIONS - Object allocations measures show how many objects each method in a program allocates. Measurements are done via Ruby's GC.stat api.
|
26
|
+
# * RubyProf::MEMORY - Memory measures how much memory each method in a program uses. Measurements are done via Ruby's TracePoint api.
|
27
|
+
def self.measure_mode=(value)
|
28
|
+
@measure_mode = value
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the threads that ruby-prof should exclude from profiling
|
32
|
+
def self.exclude_threads
|
33
|
+
@exclude_threads ||= Array.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# Specifies which threads ruby-prof should exclude from profiling
|
37
|
+
def self.exclude_threads=(value)
|
38
|
+
@exclude_threads = value
|
39
|
+
end
|
40
|
+
|
41
|
+
# Starts profiling
|
42
|
+
def self.start
|
43
|
+
ensure_not_running!
|
44
|
+
@profile = Profile.new(:measure_mode => measure_mode, :exclude_threads => exclude_threads)
|
45
|
+
@profile.start
|
46
|
+
end
|
47
|
+
|
48
|
+
# Pauses profiling
|
49
|
+
def self.pause
|
50
|
+
ensure_running!
|
51
|
+
@profile.pause
|
52
|
+
end
|
53
|
+
|
54
|
+
# Is a profile running?
|
55
|
+
def self.running?
|
56
|
+
if defined?(@profile) and @profile
|
57
|
+
@profile.running?
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Resume profiling
|
64
|
+
def self.resume
|
65
|
+
ensure_running!
|
66
|
+
@profile.resume
|
67
|
+
end
|
68
|
+
|
69
|
+
# Stops profiling
|
70
|
+
def self.stop
|
71
|
+
ensure_running!
|
72
|
+
result = @profile.stop
|
73
|
+
@profile = nil
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
# Profiles a block
|
78
|
+
def self.profile(options = {}, &block)
|
79
|
+
ensure_not_running!
|
80
|
+
options = {:measure_mode => measure_mode, :exclude_threads => exclude_threads }.merge!(options)
|
81
|
+
Profile.profile(options, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
# :nodoc:
|
85
|
+
def self.start_script(script)
|
86
|
+
start
|
87
|
+
load script
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def self.ensure_running!
|
93
|
+
raise(RuntimeError, "RubyProf.start was not yet called") unless running?
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.ensure_not_running!
|
97
|
+
raise(RuntimeError, "RubyProf is already running") if running?
|
98
|
+
end
|
99
|
+
end
|