ruby-prof 0.8.1-x86-mingw32 → 0.11.0.rc1-x86-mingw32

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 (119) hide show
  1. data/CHANGES +89 -13
  2. data/LICENSE +4 -3
  3. data/{README → README.rdoc} +155 -162
  4. data/Rakefile +50 -123
  5. data/bin/ruby-prof +86 -47
  6. data/examples/empty.png +0 -0
  7. data/examples/graph.dot +106 -0
  8. data/examples/graph.png +0 -0
  9. data/examples/minus.png +0 -0
  10. data/examples/multi.flat.txt +23 -0
  11. data/examples/multi.graph.html +906 -0
  12. data/examples/multi.grind.dat +194 -0
  13. data/examples/multi.stack.html +573 -0
  14. data/examples/plus.png +0 -0
  15. data/examples/stack.html +573 -0
  16. data/ext/ruby_prof/extconf.rb +53 -0
  17. data/ext/ruby_prof/rp_call_info.c +369 -0
  18. data/ext/ruby_prof/rp_call_info.h +46 -0
  19. data/ext/ruby_prof/rp_measure.c +48 -0
  20. data/ext/ruby_prof/rp_measure.h +45 -0
  21. data/ext/ruby_prof/rp_measure_allocations.c +86 -0
  22. data/ext/ruby_prof/rp_measure_cpu_time.c +112 -0
  23. data/ext/ruby_prof/rp_measure_gc_runs.c +87 -0
  24. data/ext/ruby_prof/rp_measure_gc_time.c +73 -0
  25. data/ext/ruby_prof/rp_measure_memory.c +81 -0
  26. data/ext/ruby_prof/rp_measure_process_time.c +71 -0
  27. data/ext/ruby_prof/rp_measure_wall_time.c +42 -0
  28. data/ext/ruby_prof/rp_method.c +363 -0
  29. data/ext/ruby_prof/rp_method.h +55 -0
  30. data/ext/ruby_prof/rp_stack.c +61 -0
  31. data/ext/ruby_prof/rp_stack.h +40 -0
  32. data/ext/ruby_prof/rp_thread.c +113 -0
  33. data/ext/ruby_prof/rp_thread.h +20 -0
  34. data/ext/ruby_prof/ruby_prof.c +332 -1377
  35. data/ext/ruby_prof/ruby_prof.h +54 -188
  36. data/ext/ruby_prof/version.h +6 -3
  37. data/lib/1.8/ruby_prof.so +0 -0
  38. data/lib/1.9/ruby_prof.exp +0 -0
  39. data/lib/1.9/ruby_prof.ilk +0 -0
  40. data/lib/1.9/ruby_prof.lib +0 -0
  41. data/lib/1.9/ruby_prof.pdb +0 -0
  42. data/lib/1.9/ruby_prof.so +0 -0
  43. data/lib/ruby-prof.rb +32 -18
  44. data/lib/ruby-prof/abstract_printer.rb +15 -5
  45. data/lib/ruby-prof/aggregate_call_info.rb +11 -3
  46. data/lib/ruby-prof/call_info.rb +68 -1
  47. data/lib/ruby-prof/call_stack_printer.rb +775 -0
  48. data/lib/ruby-prof/call_tree_printer.rb +17 -9
  49. data/lib/ruby-prof/compatibility.rb +134 -0
  50. data/lib/ruby-prof/dot_printer.rb +152 -0
  51. data/lib/ruby-prof/empty.png +0 -0
  52. data/lib/ruby-prof/flat_printer.rb +23 -24
  53. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +17 -21
  54. data/lib/ruby-prof/graph_html_printer.rb +69 -39
  55. data/lib/ruby-prof/graph_printer.rb +35 -35
  56. data/lib/ruby-prof/method_info.rb +26 -4
  57. data/lib/ruby-prof/minus.png +0 -0
  58. data/lib/ruby-prof/multi_printer.rb +56 -0
  59. data/lib/ruby-prof/plus.png +0 -0
  60. data/lib/ruby-prof/profile.rb +72 -0
  61. data/lib/ruby-prof/rack.rb +31 -0
  62. data/lib/ruby-prof/symbol_to_proc.rb +3 -1
  63. data/lib/ruby-prof/task.rb +20 -19
  64. data/lib/ruby-prof/test.rb +5 -3
  65. data/lib/ruby_prof.exp +0 -0
  66. data/lib/ruby_prof.ilk +0 -0
  67. data/lib/ruby_prof.lib +0 -0
  68. data/lib/ruby_prof.pdb +0 -0
  69. data/lib/ruby_prof.so +0 -0
  70. data/lib/unprof.rb +2 -0
  71. data/test/aggregate_test.rb +29 -14
  72. data/test/basic_test.rb +3 -251
  73. data/test/bug_test.rb +6 -0
  74. data/test/duplicate_names_test.rb +4 -4
  75. data/test/dynamic_method_test.rb +61 -0
  76. data/test/enumerable_test.rb +4 -4
  77. data/test/exceptions_test.rb +6 -5
  78. data/test/exclude_threads_test.rb +47 -47
  79. data/test/exec_test.rb +5 -5
  80. data/test/line_number_test.rb +16 -16
  81. data/test/measure_allocations_test.rb +25 -0
  82. data/test/measure_cpu_time_test.rb +212 -0
  83. data/test/measure_gc_runs_test.rb +29 -0
  84. data/test/measure_gc_time_test.rb +29 -0
  85. data/test/measure_memory_test.rb +36 -0
  86. data/test/measure_process_time_test.rb +205 -0
  87. data/test/measure_wall_time_test.rb +209 -0
  88. data/test/method_elimination_test.rb +74 -0
  89. data/test/module_test.rb +12 -21
  90. data/test/multi_printer_test.rb +81 -0
  91. data/test/no_method_class_test.rb +5 -3
  92. data/test/prime.rb +7 -10
  93. data/test/prime_test.rb +3 -3
  94. data/test/printers_test.rb +180 -54
  95. data/test/recursive_test.rb +34 -72
  96. data/test/singleton_test.rb +5 -4
  97. data/test/stack_printer_test.rb +73 -0
  98. data/test/stack_test.rb +7 -7
  99. data/test/start_stop_test.rb +23 -6
  100. data/test/test_helper.rb +81 -0
  101. data/test/test_suite.rb +35 -21
  102. data/test/thread_test.rb +40 -39
  103. data/test/unique_call_path_test.rb +6 -6
  104. metadata +106 -51
  105. data/ext/ruby_prof/measure_allocations.h +0 -58
  106. data/ext/ruby_prof/measure_cpu_time.h +0 -152
  107. data/ext/ruby_prof/measure_gc_runs.h +0 -76
  108. data/ext/ruby_prof/measure_gc_time.h +0 -57
  109. data/ext/ruby_prof/measure_memory.h +0 -101
  110. data/ext/ruby_prof/measure_process_time.h +0 -52
  111. data/ext/ruby_prof/measure_wall_time.h +0 -53
  112. data/ext/ruby_prof/mingw/Rakefile +0 -23
  113. data/ext/ruby_prof/mingw/build.rake +0 -38
  114. data/rails/environment/profile.rb +0 -24
  115. data/rails/example/example_test.rb +0 -9
  116. data/rails/profile_test_helper.rb +0 -21
  117. data/test/current_failures_windows +0 -8
  118. data/test/measurement_test.rb +0 -121
  119. data/test/ruby-prof-bin +0 -20
@@ -0,0 +1,775 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+
6
+ module RubyProf
7
+ # prints a HTML visualization of the call tree
8
+ class CallStackPrinter < AbstractPrinter
9
+ include ERB::Util
10
+
11
+ # Specify print options.
12
+ #
13
+ # options - Hash table
14
+ # :min_percent - Number 0 to 100 that specifes the minimum
15
+ # %self (the methods self time divided by the
16
+ # overall total time) that a method must take
17
+ # for it to be printed out in the report.
18
+ # Default value is 0.
19
+ #
20
+ # :print_file - True or false. Specifies if a method's source
21
+ # file should be printed. Default value if false.
22
+ #
23
+ # :threshold - a float from 0 to 100 that sets the threshold of
24
+ # results displayed.
25
+ # Default value is 1.0
26
+ #
27
+ # :title - a String to overide the default "ruby-prof call tree"
28
+ # title of the report.
29
+ #
30
+ # :expansion - a float from 0 to 100 that sets the threshold of
31
+ # results that are expanded, if the percent_total
32
+ # exceeds it.
33
+ # Default value is 10.0
34
+ #
35
+ # :application - a String to overide the name of the application,
36
+ # as it appears on the report.
37
+ #
38
+ def print(output = STDOUT, options = {})
39
+ @output = output
40
+ setup_options(options)
41
+ if @graph_html = options.delete(:graph)
42
+ @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
43
+ end
44
+
45
+ @overall_threads_time = 0.0
46
+ @threads_totals = Hash.new
47
+ @result.threads.each do |thread_id, methods|
48
+ roots = methods.select{|m| m.root?}
49
+ thread_total_time = sum(roots.map{|r| self.total_time(r.call_infos)})
50
+ @overall_threads_time += thread_total_time
51
+ @threads_totals[thread_id] = thread_total_time
52
+ end
53
+
54
+ print_header
55
+
56
+ @result.threads.keys.sort.each do |thread_id|
57
+ @current_thread_id = thread_id
58
+ @overall_time = @threads_totals[thread_id]
59
+ @output.print "<div class=\"thread\">Thread: #{thread_id} (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})</div>"
60
+ @output.print "<ul name=\"thread\">"
61
+ @result.threads[thread_id].each do |m|
62
+ # $stderr.print m.dump
63
+ next unless m.root?
64
+ m.call_infos.each do |ci|
65
+ next unless ci.root?
66
+ print_stack ci, @threads_totals[thread_id]
67
+ end
68
+ end
69
+ @output.print "</ul>"
70
+ end
71
+
72
+ print_footer
73
+
74
+ copy_image_files
75
+ end
76
+
77
+ def print_stack(call_info, parent_time)
78
+ total_time = call_info.total_time
79
+ percent_parent = (total_time/parent_time)*100
80
+ percent_total = (total_time/@overall_time)*100
81
+ return unless percent_total > min_percent
82
+ color = self.color(percent_total)
83
+ kids = call_info.children
84
+ visible = percent_total >= threshold
85
+ expanded = percent_total >= expansion
86
+ display = visible ? "block" : "none"
87
+ @output.print "<li class=\"color#{color}\" style=\"display:#{display}\">"
88
+ if kids.empty?
89
+ @output.print "<img src=\"empty.png\">"
90
+ else
91
+ visible_children = kids.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
92
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
93
+ @output.print "<img class=\"toggle\" src=\"#{image}.png\">"
94
+ end
95
+ @output.printf " %4.2f%% (%4.2f%%) %s %s\n", percent_total, percent_parent, link(call_info), graph_link(call_info)
96
+ unless kids.empty?
97
+ if expanded
98
+ @output.print "<ul>"
99
+ else
100
+ @output.print '<ul style="display:none">'
101
+ end
102
+ kids.sort_by{|c| -c.total_time}.each do |callinfo|
103
+ print_stack callinfo, total_time
104
+ end
105
+ @output.print "</ul>"
106
+ end
107
+ @output.print "</li>"
108
+ end
109
+
110
+ def name(call_info)
111
+ method = call_info.target
112
+ method.full_name
113
+ end
114
+
115
+ def link(call_info)
116
+ method = call_info.target
117
+ file = File.expand_path(method.source_file)
118
+ if file =~ /\/ruby_runtime$/
119
+ h(name(call_info))
120
+ else
121
+ if RUBY_PLATFORM =~ /darwin/
122
+ "<a href=\"txmt://open?url=file://#{file}&line=#{method.line}\">#{h(name(call_info))}</a>"
123
+ else
124
+ "<a href=\"file://#{file}##{method.line}\">#{h(name(call_info))}</a>"
125
+ end
126
+ end
127
+ end
128
+
129
+ def graph_link(call_info)
130
+ total_calls = call_info.target.call_infos.inject(0){|t, ci| t += ci.called}
131
+ href = "#{@graph_html}##{method_href(call_info.target)}"
132
+ totals = @graph_html ? "<a href='#{href}'>#{total_calls}</a>" : total_calls.to_s
133
+ "[#{call_info.called} calls, #{totals} total]"
134
+ end
135
+
136
+ def method_href(method)
137
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + @current_thread_id.to_s)
138
+ end
139
+
140
+ def total_time(call_infos)
141
+ sum(call_infos.map{|ci| ci.total_time})
142
+ end
143
+
144
+ def sum(a)
145
+ a.inject(0.0){|s,t| s+=t}
146
+ end
147
+
148
+ def dump(ci)
149
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
150
+ end
151
+
152
+ def color(p)
153
+ case i = p.to_i
154
+ when 0..5
155
+ "01"
156
+ when 5..10
157
+ "05"
158
+ when 100
159
+ "9"
160
+ else
161
+ "#{i/10}"
162
+ end
163
+ end
164
+
165
+ def application
166
+ @options[:application] || $PROGRAM_NAME
167
+ end
168
+
169
+ def arguments
170
+ ARGV.join(' ')
171
+ end
172
+
173
+ def title
174
+ @title ||= @options.delete(:title) || "ruby-prof call tree"
175
+ end
176
+
177
+ def threshold
178
+ @options[:threshold] || 1.0
179
+ end
180
+
181
+ def expansion
182
+ @options[:expansion] || 10.0
183
+ end
184
+
185
+ def copy_image_files
186
+ if @output.is_a?(File)
187
+ target_dir = File.dirname(@output.path)
188
+ image_dir = File.dirname(__FILE__)
189
+ %w(empty plus minus).each do |img|
190
+ source_file = "#{image_dir}/#{img}.png"
191
+ target_file = "#{target_dir}/#{img}.png"
192
+ FileUtils.cp(source_file, target_file) unless File.exist?(target_file)
193
+ end
194
+ end
195
+ end
196
+
197
+ def print_header
198
+ @output.puts "<html><head>"
199
+ @output.puts '<meta http-equiv="content-type" content="text/html; charset=utf-8">'
200
+ @output.puts "<title>#{h title}</title>"
201
+ print_css
202
+ print_java_script
203
+ @output.puts '</head><body>'
204
+ print_title_bar
205
+ print_commands
206
+ print_help
207
+ end
208
+
209
+ def print_footer
210
+ @output.puts '<div id="sentinel"></div></body></html>'
211
+ end
212
+
213
+ def print_css
214
+ @output.puts <<-'end_css'
215
+ <style type="text/css">
216
+ <!--
217
+ body {
218
+ font-size:70%;
219
+ padding:0px;
220
+ margin:5px;
221
+ margin-right:0px;
222
+ margin-left:0px;
223
+ background: #ffffff;
224
+ }
225
+ ul {
226
+ margin-left:0px;
227
+ margin-top:0px;
228
+ margin-bottom:0px;
229
+ padding-left:0px;
230
+ list-style-type:none;
231
+ }
232
+ li {
233
+ margin-left:11px;
234
+ padding:0px;
235
+ white-space:nowrap;
236
+ border-top:1px solid #cccccc;
237
+ border-left:1px solid #cccccc;
238
+ border-bottom:none;
239
+ }
240
+ .thread {
241
+ margin-left:11px;
242
+ background:#708090;
243
+ padding-top:3px;
244
+ padding-left:12px;
245
+ padding-bottom:2px;
246
+ border-left:1px solid #CCCCCC;
247
+ border-top:1px solid #CCCCCC;
248
+ font-weight:bold;
249
+ }
250
+ .hidden {
251
+ display:none;
252
+ width:0px;
253
+ height:0px;
254
+ margin:0px;
255
+ padding:0px;
256
+ border-style:none;
257
+ }
258
+ .color01 { background:#adbdeb }
259
+ .color05 { background:#9daddb }
260
+ .color0 { background:#8d9dcb }
261
+ .color1 { background:#89bccb }
262
+ .color2 { background:#56e3e7 }
263
+ .color3 { background:#32cd70 }
264
+ .color4 { background:#a3d53c }
265
+ .color5 { background:#c4cb34 }
266
+ .color6 { background:#dcb66d }
267
+ .color7 { background:#cda59e }
268
+ .color8 { background:#be9d9c }
269
+ .color9 { background:#cf947a }
270
+ #commands {
271
+ font-size:10pt;
272
+ padding:10px;
273
+ margin-left:11px;
274
+ margin-bottom:0px;
275
+ margin-top:0px;
276
+ background:#708090;
277
+ border-top:1px solid #cccccc;
278
+ border-left:1px solid #cccccc;
279
+ border-bottom:none;
280
+ }
281
+ #titlebar {
282
+ font-size:10pt;
283
+ padding:10px;
284
+ margin-left:11px;
285
+ margin-bottom:0px;
286
+ margin-top:10px;
287
+ background:#8090a0;
288
+ border-top:1px solid #cccccc;
289
+ border-left:1px solid #cccccc;
290
+ border-bottom:none;
291
+ }
292
+ #help {
293
+ font-size:10pt;
294
+ padding:10px;
295
+ margin-left:11px;
296
+ margin-bottom:0px;
297
+ margin-top:0px;
298
+ background:#8090a0;
299
+ display:none;
300
+ border-top:1px solid #cccccc;
301
+ border-left:1px solid #cccccc;
302
+ border-bottom:none;
303
+ }
304
+ #sentinel {
305
+ height: 400px;
306
+ margin-left:11px;
307
+ background:#8090a0;
308
+ border-top:1px solid #cccccc;
309
+ border-left:1px solid #cccccc;
310
+ border-bottom:none;
311
+ }
312
+ input { margin-left:10px; }
313
+ -->
314
+ </style>
315
+ end_css
316
+ end
317
+
318
+ def print_java_script
319
+ @output.puts <<-'end_java_script'
320
+ <script type="text/javascript">
321
+ /*
322
+ Copyright (C) 2005,2009 Stefan Kaes
323
+ skaes@railsexpress.de
324
+ */
325
+
326
+ function rootNode() {
327
+ return currentThread;
328
+ }
329
+
330
+ function hideUL(node) {
331
+ var lis = node.childNodes
332
+ var l = lis.length;
333
+ for (var i=0; i < l ; i++ ) {
334
+ hideLI(lis[i]);
335
+ }
336
+ }
337
+
338
+ function showUL(node) {
339
+ var lis = node.childNodes;
340
+ var l = lis.length;
341
+ for (var i=0; i < l ; i++ ) {
342
+ showLI(lis[i]);
343
+ }
344
+ }
345
+
346
+ function findUlChild(li){
347
+ var ul = li.childNodes[2];
348
+ while (ul && ul.nodeName != "UL") {
349
+ ul = ul.nextSibling;
350
+ }
351
+ return ul;
352
+ }
353
+
354
+ function isLeafNode(li) {
355
+ var img = li.firstChild;
356
+ return (img.src.indexOf('empty.png') > -1);
357
+ }
358
+
359
+ function hideLI(li) {
360
+ if (isLeafNode(li))
361
+ return;
362
+
363
+ var img = li.firstChild;
364
+ img.src = 'plus.png';
365
+
366
+ var ul = findUlChild(li);
367
+ if (ul) {
368
+ ul.style.display = 'none';
369
+ hideUL(ul);
370
+ }
371
+ }
372
+
373
+ function showLI(li) {
374
+ if (isLeafNode(li))
375
+ return;
376
+
377
+ var img = li.firstChild;
378
+ img.src = 'minus.png';
379
+
380
+ var ul = findUlChild(li);
381
+ if (ul) {
382
+ ul.style.display = 'block';
383
+ showUL(ul);
384
+ }
385
+ }
386
+
387
+ function toggleLI(li) {
388
+ var img = li.firstChild;
389
+ if (img.src.indexOf("minus.png")>-1)
390
+ hideLI(li);
391
+ else {
392
+ if (img.src.indexOf("plus.png")>-1)
393
+ showLI(li);
394
+ }
395
+ }
396
+
397
+ function aboveThreshold(text, threshold) {
398
+ var match = text.match(/\d+[.,]\d+/);
399
+ return (match && parseFloat(match[0].replace(/,/, '.'))>=threshold);
400
+ }
401
+
402
+ function setThresholdLI(li, threshold) {
403
+ var img = li.firstChild;
404
+ var text = img.nextSibling;
405
+ var ul = findUlChild(li);
406
+
407
+ var visible = aboveThreshold(text.nodeValue, threshold) ? 1 : 0;
408
+
409
+ var count = 0;
410
+ if (ul) {
411
+ count = setThresholdUL(ul, threshold);
412
+ }
413
+ if (count>0) {
414
+ img.src = 'minus.png';
415
+ }
416
+ else {
417
+ img.src = 'empty.png';
418
+ }
419
+ if (visible) {
420
+ li.style.display = 'block'
421
+ }
422
+ else {
423
+ li.style.display = 'none'
424
+ }
425
+ return visible;
426
+ }
427
+
428
+ function setThresholdUL(node, threshold) {
429
+ var lis = node.childNodes;
430
+ var l = lis.length;
431
+
432
+ var count = 0;
433
+ for ( var i = 0; i < l ; i++ ) {
434
+ count = count + setThresholdLI(lis[i], threshold);
435
+ }
436
+
437
+ var visible = (count > 0) ? 1 : 0;
438
+ if (visible) {
439
+ node.style.display = 'block';
440
+ }
441
+ else {
442
+ node.style.display = 'none';
443
+ }
444
+ return visible;
445
+ }
446
+
447
+ function toggleChildren(img, event) {
448
+ event.cancelBubble=true;
449
+
450
+ if (img.src.indexOf('empty.png') > -1)
451
+ return;
452
+
453
+ var minus = (img.src.indexOf('minus.png') > -1);
454
+
455
+ if (minus) {
456
+ img.src = 'plus.png';
457
+ }
458
+ else
459
+ img.src = 'minus.png';
460
+
461
+ var li = img.parentNode;
462
+ var ul = findUlChild(li);
463
+ if (ul) {
464
+ if (minus)
465
+ ul.style.display = 'none';
466
+ else
467
+ ul.style.display = 'block';
468
+ }
469
+ if (minus)
470
+ moveSelectionIfNecessary(li);
471
+ }
472
+
473
+ function showChildren(li) {
474
+ var img = li.firstChild;
475
+ if (img.src.indexOf('empty.png') > -1)
476
+ return;
477
+ img.src = 'minus.png';
478
+
479
+ var ul = findUlChild(li);
480
+ if (ul) {
481
+ ul.style.display = 'block';
482
+ }
483
+ }
484
+
485
+ function setThreshold() {
486
+ var tv = document.getElementById("threshold").value;
487
+ if (tv.match(/[0-9]+([.,][0-9]+)?/)) {
488
+ var f = parseFloat(tv.replace(/,/, '.'));
489
+ var threads = document.getElementsByName("thread");
490
+ var l = threads.length;
491
+ for ( var i = 0; i < l ; i++ ) {
492
+ setThresholdUL(threads[i], f);
493
+ }
494
+ var p = selectedNode;
495
+ while (p && p.style.display=='none')
496
+ p=p.parentNode.parentNode;
497
+ if (p && p.nodeName=="LI")
498
+ selectNode(p);
499
+ }
500
+ else {
501
+ alert("Please specify a decimal number as threshold value!");
502
+ }
503
+ }
504
+
505
+ function collapseAll(event) {
506
+ event.cancelBubble=true;
507
+ var threads = document.getElementsByName("thread");
508
+ var l = threads.length;
509
+ for ( var i = 0; i < l ; i++ ) {
510
+ hideUL(threads[i]);
511
+ }
512
+ selectNode(rootNode(), null);
513
+ }
514
+
515
+ function expandAll(event) {
516
+ event.cancelBubble=true;
517
+ var threads = document.getElementsByName("thread");
518
+ var l = threads.length;
519
+ for ( var i = 0; i < l ; i++ ) {
520
+ showUL(threads[i]);
521
+ }
522
+ }
523
+
524
+ function toggleHelp(node) {
525
+ var help = document.getElementById("help");
526
+ if (node.value == "Show Help") {
527
+ node.value = "Hide Help";
528
+ help.style.display = 'block';
529
+ }
530
+ else {
531
+ node.value = "Show Help";
532
+ help.style.display = 'none';
533
+ }
534
+ }
535
+
536
+ var selectedNode = null;
537
+ var selectedColor = null;
538
+ var selectedThread = null;
539
+
540
+ function descendentOf(a,b){
541
+ while (a!=b && b!=null)
542
+ b=b.parentNode;
543
+ return (a==b);
544
+ }
545
+
546
+ function moveSelectionIfNecessary(node){
547
+ if (descendentOf(node, selectedNode))
548
+ selectNode(node, null);
549
+ }
550
+
551
+ function selectNode(node, event) {
552
+ if (event) {
553
+ event.cancelBubble = true;
554
+ thread = findThread(node);
555
+ selectThread(thread);
556
+ }
557
+ if (selectedNode) {
558
+ selectedNode.style.background = selectedColor;
559
+ }
560
+ selectedNode = node;
561
+ selectedColor = node.style.background;
562
+ selectedNode.style.background = "red";
563
+ selectedNode.scrollIntoView();
564
+ window.scrollBy(0,-400);
565
+ }
566
+
567
+ function moveUp(){
568
+ var p = selectedNode.previousSibling;
569
+ while (p && p.style.display == 'none')
570
+ p = p.previousSibling;
571
+ if (p && p.nodeName == "LI") {
572
+ selectNode(p, null);
573
+ }
574
+ }
575
+
576
+ function moveDown(){
577
+ var p = selectedNode.nextSibling;
578
+ while (p && p.style.display == 'none')
579
+ p = p.nextSibling;
580
+ if (p && p.nodeName == "LI") {
581
+ selectNode(p, null);
582
+ }
583
+ }
584
+
585
+ function moveLeft(){
586
+ var p = selectedNode.parentNode.parentNode;
587
+ if (p && p.nodeName=="LI") {
588
+ selectNode(p, null);
589
+ }
590
+ }
591
+
592
+ function moveRight(){
593
+ if (!isLeafNode(selectedNode)) {
594
+ showChildren(selectedNode);
595
+ var ul = findUlChild(selectedNode);
596
+ if (ul) {
597
+ selectNode(ul.firstChild, null);
598
+ }
599
+ }
600
+ }
601
+
602
+ function moveForward(){
603
+ if (isLeafNode(selectedNode)) {
604
+ var p = selectedNode;
605
+ while ((p.nextSibling == null || p.nextSibling.style.display=='none') && p.nodeName=="LI") {
606
+ p = p.parentNode.parentNode;
607
+ }
608
+ if (p.nodeName=="LI")
609
+ selectNode(p.nextSibling, null);
610
+ }
611
+ else {
612
+ moveRight();
613
+ }
614
+ }
615
+
616
+ function isExpandedNode(li){
617
+ var img = li.firstChild;
618
+ return(img.src.indexOf('minus.png')>-1);
619
+ }
620
+
621
+ function moveBackward(){
622
+ var p = selectedNode;
623
+ var q = p.previousSibling;
624
+ while (q != null && q.style.display=='none')
625
+ q = q.previousSibling;
626
+ if (q == null) {
627
+ p = p.parentNode.parentNode;
628
+ } else {
629
+ while (!isLeafNode(q) && isExpandedNode(q)) {
630
+ q = findUlChild(q).lastChild;
631
+ while (q.style.display=='none')
632
+ q = q.previousSibling;
633
+ }
634
+ p = q;
635
+ }
636
+ if (p.nodeName=="LI")
637
+ selectNode(p, null);
638
+ }
639
+
640
+ function moveHome() {
641
+ selectNode(currentThread);
642
+ }
643
+
644
+ var currentThreadIndex = null;
645
+
646
+ function findThread(node){
647
+ while (node && node.parentNode.nodeName!="BODY") {
648
+ node = node.parentNode;
649
+ }
650
+ return node.firstChild;
651
+ }
652
+
653
+ function selectThread(node){
654
+ var threads = document.getElementsByName("thread");
655
+ currentThread = node;
656
+ for (var i=0; i<threads.length; i++) {
657
+ if (threads[i]==currentThread.parentNode)
658
+ currentThreadIndex = i;
659
+ }
660
+ }
661
+
662
+ function nextThread(){
663
+ var threads = document.getElementsByName("thread");
664
+ if (currentThreadIndex==threads.length-1)
665
+ currentThreadIndex = 0;
666
+ else
667
+ currentThreadIndex += 1
668
+ currentThread = threads[currentThreadIndex].firstChild;
669
+ selectNode(currentThread, null);
670
+ }
671
+
672
+ function previousThread(){
673
+ var threads = document.getElementsByName("thread");
674
+ if (currentThreadIndex==0)
675
+ currentThreadIndex = threads.length-1;
676
+ else
677
+ currentThreadIndex -= 1
678
+ currentThread = threads[currentThreadIndex].firstChild;
679
+ selectNode(currentThread, null);
680
+ }
681
+
682
+ function switchThread(node, event){
683
+ event.cancelBubble = true;
684
+ selectThread(node.nextSibling.firstChild);
685
+ selectNode(currentThread, null);
686
+ }
687
+
688
+ function handleKeyEvent(event){
689
+ var code = event.charCode ? event.charCode : event.keyCode;
690
+ var str = String.fromCharCode(code);
691
+ switch (str) {
692
+ case "a": moveLeft(); break;
693
+ case "s": moveDown(); break;
694
+ case "d": moveRight(); break;
695
+ case "w": moveUp(); break;
696
+ case "f": moveForward(); break;
697
+ case "b": moveBackward(); break;
698
+ case "x": toggleChildren(selectedNode.firstChild, event); break;
699
+ case "*": toggleLI(selectedNode); break;
700
+ case "n": nextThread(); break;
701
+ case "h": moveHome(); break;
702
+ case "p": previousThread(); break;
703
+ }
704
+ }
705
+ document.onkeypress=function(event){ handleKeyEvent(event) };
706
+
707
+ window.onload=function(){
708
+ var images = document.getElementsByTagName("img");
709
+ for (var i=0; i<images.length; i++) {
710
+ var img = images[i];
711
+ if (img.className == "toggle") {
712
+ img.onclick = function(event){ toggleChildren(this, event); };
713
+ }
714
+ }
715
+ var divs = document.getElementsByTagName("div");
716
+ for (i=0; i<divs.length; i++) {
717
+ var div = divs[i];
718
+ if (div.className == "thread")
719
+ div.onclick = function(event){ switchThread(this, event) };
720
+ }
721
+ var lis = document.getElementsByTagName("li");
722
+ for (var i=0; i<lis.length; i++) {
723
+ lis[i].onclick = function(event){ selectNode(this, event); };
724
+ }
725
+ var threads = document.getElementsByName("thread");
726
+ currentThreadIndex = 0;
727
+ currentThread = threads[0].firstChild;
728
+ selectNode(currentThread, null);
729
+ }
730
+ </script>
731
+ end_java_script
732
+ end
733
+
734
+ def print_title_bar
735
+ @output.puts <<-"end_title_bar"
736
+ <div id="titlebar">
737
+ Call tree for application <b>#{h application} #{h arguments}</b><br/>
738
+ Generated on #{Time.now} with options #{h @options.inspect}<br/>
739
+ </div>
740
+ end_title_bar
741
+ end
742
+
743
+ def print_commands
744
+ @output.puts <<-"end_commands"
745
+ <div id=\"commands\">
746
+ <span style=\"font-size: 11pt; font-weight: bold;\">Threshold:</span>
747
+ <input value=\"#{h threshold}\" size=\"3\" id=\"threshold\" type=\"text\">
748
+ <input value=\"Apply\" onclick=\"setThreshold();\" type=\"submit\">
749
+ <input value=\"Expand All\" onclick=\"expandAll(event);\" type=\"submit\">
750
+ <input value=\"Collapse All\" onclick=\"collapseAll(event);\" type=\"submit\">
751
+ <input value=\"Show Help\" onclick=\"toggleHelp(this);\" type=\"submit\">
752
+ </div>
753
+ end_commands
754
+ end
755
+
756
+ def print_help
757
+ @output.puts <<-'end_help'
758
+ <div style="display: none;" id="help">
759
+ <img src="empty.png"> Enter a decimal value <i>d</i> into the threshold field and click "Apply"
760
+ to hide all nodes marked with time values lower than <i>d</i>.<br>
761
+ <img src="empty.png"> Click on "Expand All" for full tree expansion.<br>
762
+ <img src="empty.png"> Click on "Collapse All" to show only top level nodes.<br>
763
+ <img src="empty.png"> Use a, s, d, w as in Quake or Urban Terror to navigate the tree.<br>
764
+ <img src="empty.png"> Use f and b to navigate the tree in preorder forward and backwards.<br>
765
+ <img src="empty.png"> Use x to toggle visibility of a subtree.<br>
766
+ <img src="empty.png"> Use * to expand/collapse a whole subtree.<br>
767
+ <img src="empty.png"> Use h to navigate to thread root.<br>
768
+ <img src="empty.png"> Use n and p to navigate between threads.<br>
769
+ <img src="empty.png"> Click on background to move focus to a subtree.<br>
770
+ </div>
771
+ end_help
772
+ end
773
+ end
774
+ end
775
+