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

Sign up to get free protection for your applications and to get access to all the features.
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
+