ruby-prof 1.7.2 → 2.0.0

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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGES → CHANGELOG.md} +112 -178
  3. data/README.md +5 -5
  4. data/bin/ruby-prof +1 -4
  5. data/docs/advanced-usage.md +132 -0
  6. data/docs/alternatives.md +98 -0
  7. data/docs/architecture.md +122 -0
  8. data/docs/best-practices.md +27 -0
  9. data/docs/getting-started.md +130 -0
  10. data/docs/history.md +11 -0
  11. data/docs/index.md +45 -0
  12. data/docs/profiling-rails.md +64 -0
  13. data/docs/public/examples/example.rb +33 -0
  14. data/docs/public/examples/generate_reports.rb +92 -0
  15. data/docs/public/examples/reports/call_info.txt +27 -0
  16. data/docs/public/examples/reports/call_stack.html +835 -0
  17. data/docs/public/examples/reports/callgrind.out +150 -0
  18. data/docs/public/examples/reports/flame_graph.html +408 -0
  19. data/docs/public/examples/reports/flat.txt +45 -0
  20. data/docs/public/examples/reports/graph.dot +129 -0
  21. data/docs/public/examples/reports/graph.html +1319 -0
  22. data/docs/public/examples/reports/graph.txt +100 -0
  23. data/docs/public/examples/reports/graphviz_viewer.html +1 -0
  24. data/docs/public/images/call_stack.png +0 -0
  25. data/docs/public/images/class_diagram.png +0 -0
  26. data/docs/public/images/dot_printer.png +0 -0
  27. data/docs/public/images/flame_graph.png +0 -0
  28. data/docs/public/images/flat.png +0 -0
  29. data/docs/public/images/graph.png +0 -0
  30. data/docs/public/images/graph_html.png +0 -0
  31. data/docs/public/images/ruby-prof-logo.svg +1 -0
  32. data/docs/reports.md +150 -0
  33. data/docs/stylesheets/extra.css +80 -0
  34. data/ext/ruby_prof/rp_allocation.c +0 -15
  35. data/ext/ruby_prof/rp_allocation.h +29 -33
  36. data/ext/ruby_prof/rp_call_tree.c +3 -0
  37. data/ext/ruby_prof/rp_call_tree.h +1 -4
  38. data/ext/ruby_prof/rp_call_trees.h +1 -4
  39. data/ext/ruby_prof/rp_measurement.c +0 -5
  40. data/ext/ruby_prof/rp_measurement.h +49 -53
  41. data/ext/ruby_prof/rp_method.c +3 -0
  42. data/ext/ruby_prof/rp_method.h +1 -4
  43. data/ext/ruby_prof/rp_profile.c +1 -1
  44. data/ext/ruby_prof/rp_profile.h +1 -5
  45. data/ext/ruby_prof/rp_stack.h +50 -53
  46. data/ext/ruby_prof/rp_thread.h +1 -4
  47. data/ext/ruby_prof/ruby_prof.h +1 -4
  48. data/ext/ruby_prof/vc/ruby_prof.vcxproj +7 -8
  49. data/lib/ruby-prof/assets/call_stack_printer.html.erb +746 -711
  50. data/lib/ruby-prof/assets/flame_graph_printer.html.erb +412 -0
  51. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -355
  52. data/lib/ruby-prof/call_tree.rb +57 -57
  53. data/lib/ruby-prof/call_tree_visitor.rb +36 -36
  54. data/lib/ruby-prof/measurement.rb +17 -17
  55. data/lib/ruby-prof/printers/abstract_printer.rb +19 -33
  56. data/lib/ruby-prof/printers/call_info_printer.rb +53 -53
  57. data/lib/ruby-prof/printers/call_stack_printer.rb +168 -180
  58. data/lib/ruby-prof/printers/call_tree_printer.rb +132 -145
  59. data/lib/ruby-prof/printers/dot_printer.rb +177 -132
  60. data/lib/ruby-prof/printers/flame_graph_printer.rb +79 -0
  61. data/lib/ruby-prof/printers/flat_printer.rb +52 -52
  62. data/lib/ruby-prof/printers/graph_html_printer.rb +62 -63
  63. data/lib/ruby-prof/printers/graph_printer.rb +112 -113
  64. data/lib/ruby-prof/printers/multi_printer.rb +134 -127
  65. data/lib/ruby-prof/profile.rb +13 -0
  66. data/lib/ruby-prof/rack.rb +114 -105
  67. data/lib/ruby-prof/task.rb +147 -147
  68. data/lib/ruby-prof/thread.rb +20 -20
  69. data/lib/ruby-prof/version.rb +1 -1
  70. data/lib/ruby-prof.rb +50 -52
  71. data/lib/unprof.rb +10 -10
  72. data/ruby-prof.gemspec +5 -5
  73. data/test/abstract_printer_test.rb +25 -27
  74. data/test/alias_test.rb +203 -117
  75. data/test/call_tree_builder.rb +126 -126
  76. data/test/call_tree_visitor_test.rb +27 -27
  77. data/test/call_trees_test.rb +66 -66
  78. data/test/duplicate_names_test.rb +32 -32
  79. data/test/dynamic_method_test.rb +50 -50
  80. data/test/exceptions_test.rb +24 -24
  81. data/test/exclude_threads_test.rb +48 -48
  82. data/test/fiber_test.rb +72 -72
  83. data/test/inverse_call_tree_test.rb +174 -174
  84. data/test/line_number_test.rb +138 -1
  85. data/test/marshal_test.rb +144 -145
  86. data/test/measure_allocations.rb +26 -26
  87. data/test/measure_allocations_test.rb +340 -1
  88. data/test/measure_process_time_test.rb +3098 -3142
  89. data/test/measure_times.rb +56 -56
  90. data/test/measure_wall_time_test.rb +511 -372
  91. data/test/measurement_test.rb +82 -82
  92. data/test/merge_test.rb +48 -48
  93. data/test/multi_printer_test.rb +52 -66
  94. data/test/no_method_class_test.rb +15 -15
  95. data/test/pause_resume_test.rb +171 -171
  96. data/test/prime.rb +54 -54
  97. data/test/prime_script.rb +5 -5
  98. data/test/printer_call_stack_test.rb +28 -27
  99. data/test/printer_call_tree_test.rb +30 -30
  100. data/test/printer_flame_graph_test.rb +82 -0
  101. data/test/printer_flat_test.rb +99 -99
  102. data/test/printer_graph_html_test.rb +62 -59
  103. data/test/printer_graph_test.rb +42 -40
  104. data/test/printers_test.rb +28 -44
  105. data/test/printing_recursive_graph_test.rb +81 -81
  106. data/test/profile_test.rb +101 -101
  107. data/test/rack_test.rb +103 -93
  108. data/test/recursive_test.rb +139 -139
  109. data/test/scheduler.rb +4 -0
  110. data/test/singleton_test.rb +39 -38
  111. data/test/stack_printer_test.rb +61 -61
  112. data/test/start_stop_test.rb +106 -106
  113. data/test/test_helper.rb +4 -0
  114. data/test/thread_test.rb +29 -29
  115. data/test/unique_call_path_test.rb +123 -123
  116. data/test/yarv_test.rb +56 -56
  117. metadata +53 -11
  118. data/ext/ruby_prof/rp_measure_memory.c +0 -46
  119. data/lib/ruby-prof/compatibility.rb +0 -113
  120. data/test/compatibility_test.rb +0 -49
  121. data/test/measure_memory_test.rb +0 -1193
@@ -1,355 +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>&nbsp;</td>
224
- <td>&nbsp;</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>&nbsp;</td>
299
- <td>&nbsp;</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>
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_name %></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 = @min_time || (@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>&nbsp;</td>
224
+ <td>&nbsp;</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}" %>
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>&nbsp;</td>
299
+ <td>&nbsp;</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>