ruby-prof 0.18.0 → 1.2.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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +44 -1
  3. data/LICENSE +2 -2
  4. data/README.rdoc +1 -483
  5. data/Rakefile +3 -6
  6. data/bin/ruby-prof +111 -128
  7. data/ext/ruby_prof/extconf.rb +6 -38
  8. data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
  9. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  10. data/ext/ruby_prof/rp_allocation.c +259 -0
  11. data/ext/ruby_prof/rp_allocation.h +31 -0
  12. data/ext/ruby_prof/rp_call_tree.c +353 -0
  13. data/ext/ruby_prof/rp_call_tree.h +43 -0
  14. data/ext/ruby_prof/rp_call_trees.c +266 -0
  15. data/ext/ruby_prof/rp_call_trees.h +29 -0
  16. data/ext/ruby_prof/rp_measure_allocations.c +25 -51
  17. data/ext/ruby_prof/rp_measure_memory.c +21 -56
  18. data/ext/ruby_prof/rp_measure_process_time.c +37 -43
  19. data/ext/ruby_prof/rp_measure_wall_time.c +40 -21
  20. data/ext/ruby_prof/rp_measurement.c +221 -0
  21. data/ext/ruby_prof/rp_measurement.h +50 -0
  22. data/ext/ruby_prof/rp_method.c +279 -439
  23. data/ext/ruby_prof/rp_method.h +33 -45
  24. data/ext/ruby_prof/rp_profile.c +902 -0
  25. data/ext/ruby_prof/rp_profile.h +36 -0
  26. data/ext/ruby_prof/rp_stack.c +163 -132
  27. data/ext/ruby_prof/rp_stack.h +18 -28
  28. data/ext/ruby_prof/rp_thread.c +192 -124
  29. data/ext/ruby_prof/rp_thread.h +18 -8
  30. data/ext/ruby_prof/ruby_prof.c +36 -778
  31. data/ext/ruby_prof/ruby_prof.h +11 -45
  32. data/ext/ruby_prof/vc/ruby_prof.vcxproj +18 -12
  33. data/lib/ruby-prof.rb +4 -21
  34. data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
  35. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  36. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  37. data/lib/ruby-prof/call_tree.rb +57 -0
  38. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  39. data/lib/ruby-prof/compatibility.rb +37 -107
  40. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  41. data/lib/ruby-prof/measurement.rb +17 -0
  42. data/lib/ruby-prof/method_info.rb +47 -90
  43. data/lib/ruby-prof/printers/abstract_printer.rb +73 -50
  44. data/lib/ruby-prof/printers/call_info_printer.rb +24 -12
  45. data/lib/ruby-prof/printers/call_stack_printer.rb +66 -152
  46. data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
  47. data/lib/ruby-prof/printers/dot_printer.rb +5 -5
  48. data/lib/ruby-prof/printers/flat_printer.rb +6 -24
  49. data/lib/ruby-prof/printers/graph_html_printer.rb +6 -192
  50. data/lib/ruby-prof/printers/graph_printer.rb +11 -14
  51. data/lib/ruby-prof/printers/multi_printer.rb +66 -23
  52. data/lib/ruby-prof/profile.rb +10 -3
  53. data/lib/ruby-prof/thread.rb +5 -20
  54. data/lib/ruby-prof/version.rb +1 -1
  55. data/ruby-prof.gemspec +9 -2
  56. data/test/abstract_printer_test.rb +0 -27
  57. data/test/alias_test.rb +126 -0
  58. data/test/basic_test.rb +1 -86
  59. data/test/call_tree_visitor_test.rb +32 -0
  60. data/test/call_trees_test.rb +66 -0
  61. data/test/dynamic_method_test.rb +0 -2
  62. data/test/exclude_methods_test.rb +17 -12
  63. data/test/fiber_test.rb +214 -23
  64. data/test/gc_test.rb +105 -0
  65. data/test/inverse_call_tree_test.rb +175 -0
  66. data/test/line_number_test.rb +118 -40
  67. data/test/marshal_test.rb +115 -0
  68. data/test/measure_allocations.rb +30 -0
  69. data/test/measure_allocations_test.rb +361 -12
  70. data/test/measure_allocations_trace_test.rb +375 -0
  71. data/test/measure_memory_trace_test.rb +1101 -0
  72. data/test/measure_process_time_test.rb +757 -33
  73. data/test/measure_times.rb +56 -0
  74. data/test/measure_wall_time_test.rb +329 -149
  75. data/test/multi_printer_test.rb +1 -34
  76. data/test/pause_resume_test.rb +24 -15
  77. data/test/prime.rb +1 -1
  78. data/test/prime_script.rb +6 -0
  79. data/test/printer_call_stack_test.rb +28 -0
  80. data/test/printer_call_tree_test.rb +31 -0
  81. data/test/printer_flat_test.rb +68 -0
  82. data/test/printer_graph_html_test.rb +60 -0
  83. data/test/printer_graph_test.rb +41 -0
  84. data/test/printers_test.rb +32 -166
  85. data/test/printing_recursive_graph_test.rb +26 -72
  86. data/test/recursive_test.rb +68 -77
  87. data/test/stack_printer_test.rb +2 -15
  88. data/test/start_stop_test.rb +22 -25
  89. data/test/test_helper.rb +6 -261
  90. data/test/thread_test.rb +11 -54
  91. data/test/unique_call_path_test.rb +25 -107
  92. data/test/yarv_test.rb +1 -0
  93. metadata +43 -41
  94. data/examples/flat.txt +0 -50
  95. data/examples/graph.dot +0 -84
  96. data/examples/graph.html +0 -823
  97. data/examples/graph.txt +0 -139
  98. data/examples/multi.flat.txt +0 -23
  99. data/examples/multi.graph.html +0 -760
  100. data/examples/multi.grind.dat +0 -114
  101. data/examples/multi.stack.html +0 -547
  102. data/examples/stack.html +0 -547
  103. data/ext/ruby_prof/rp_call_info.c +0 -425
  104. data/ext/ruby_prof/rp_call_info.h +0 -53
  105. data/ext/ruby_prof/rp_measure.c +0 -40
  106. data/ext/ruby_prof/rp_measure.h +0 -45
  107. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  108. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  109. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  110. data/lib/ruby-prof/aggregate_call_info.rb +0 -76
  111. data/lib/ruby-prof/assets/call_stack_printer.css.html +0 -117
  112. data/lib/ruby-prof/assets/call_stack_printer.js.html +0 -385
  113. data/lib/ruby-prof/call_info.rb +0 -115
  114. data/lib/ruby-prof/call_info_visitor.rb +0 -40
  115. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
  116. data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
  117. data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
  118. data/test/aggregate_test.rb +0 -136
  119. data/test/block_test.rb +0 -74
  120. data/test/call_info_test.rb +0 -78
  121. data/test/call_info_visitor_test.rb +0 -31
  122. data/test/issue137_test.rb +0 -63
  123. data/test/measure_cpu_time_test.rb +0 -212
  124. data/test/measure_gc_runs_test.rb +0 -32
  125. data/test/measure_gc_time_test.rb +0 -36
  126. data/test/measure_memory_test.rb +0 -33
  127. data/test/method_elimination_test.rb +0 -84
  128. data/test/module_test.rb +0 -45
  129. data/test/stack_test.rb +0 -138
@@ -5,56 +5,22 @@
5
5
  #define __RUBY_PROF_H__
6
6
 
7
7
  #include <ruby.h>
8
+ #include <ruby/debug.h>
8
9
  #include <stdio.h>
9
-
10
- #if RUBY_PROF_RUBY_VERSION == 10806
11
- # error 1.8.6 is not supported. Please upgrade to 1.9.3 or higher.
12
- #endif
13
-
14
- #if RUBY_PROF_RUBY_VERSION == 10807
15
- # error 1.8.7 is not supported. Please upgrade to 1.9.3 or higher.
10
+ #include <stdbool.h>
11
+
12
+ #ifndef rb_st_lookup
13
+ #define rb_st_foreach st_foreach
14
+ #define rb_st_free_table st_free_table
15
+ #define rb_st_init_numtable st_init_numtable
16
+ #define rb_st_insert st_insert
17
+ #define rb_st_lookup st_lookup
16
18
  #endif
17
19
 
18
- #if RUBY_PROF_RUBY_VERSION == 10900
19
- # error 1.9.0 is not supported. Please upgrade to 1.9.3 or higher.
20
- #endif
21
-
22
- #if RUBY_PROF_RUBY_VERSION == 10901
23
- # error 1.9.1 is not supported. Please upgrade to 1.9.3 or higher.
24
- #endif
25
-
26
- #if RUBY_PROF_RUBY_VERSION == 10902
27
- # error 1.9.2 is not supported. Please upgrade to 1.9.3 or higher.
28
- #endif
29
-
30
- #include "rp_measure.h"
31
- #include "rp_method.h"
32
- #include "rp_call_info.h"
33
- #include "rp_stack.h"
34
- #include "rp_thread.h"
35
20
 
36
21
  extern VALUE mProf;
37
- extern VALUE cProfile;
38
-
39
- void method_key(prof_method_key_t* key, VALUE klass, ID mid);
40
-
41
- typedef struct
42
- {
43
- VALUE running;
44
- VALUE paused;
45
-
46
- prof_measurer_t* measurer;
47
- VALUE threads;
48
-
49
- st_table* threads_tbl;
50
- st_table* exclude_threads_tbl;
51
- st_table* include_threads_tbl;
52
- st_table* exclude_methods_tbl;
53
- thread_data_t* last_thread_data;
54
- double measurement_at_pause_resume;
55
- int merge_fibers;
56
- int allow_exceptions;
57
- } prof_profile_t;
58
22
 
23
+ // This method is not exposed in Ruby header files - at least not as of Ruby 2.6.3 :(
24
+ extern size_t rb_obj_memsize_of(VALUE);
59
25
 
60
26
  #endif //__RUBY_PROF_H__
@@ -64,7 +64,7 @@
64
64
  </PropertyGroup>
65
65
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
66
66
  <TargetExt>.so</TargetExt>
67
- <OutDir>..</OutDir>
67
+ <OutDir>..\..\..\lib</OutDir>
68
68
  </PropertyGroup>
69
69
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
70
70
  <ClCompile>
@@ -102,35 +102,41 @@
102
102
  </ItemDefinitionGroup>
103
103
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
104
104
  <ClCompile>
105
- <AdditionalIncludeDirectories>C:\msys64\usr\local\ruby263vc\include\ruby-2.6.0\x64-mswin64_140;C:\msys64\usr\local\ruby263vc\include\ruby-2.6.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
105
+ <AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.7.0vc\include\ruby-2.7.0\x64-mswin64_140;C:\msys64\usr\local\ruby-2.7.0vc\include\ruby-2.7.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
106
106
  <Optimization>Disabled</Optimization>
107
+ <PreprocessorDefinitions>HAVE_RB_TRACEARG_CALLEE_ID;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107
108
  </ClCompile>
108
109
  <Link>
109
- <AdditionalLibraryDirectories>C:\msys64\usr\local\ruby263vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
110
- <AdditionalDependencies>x64-vcruntime140-ruby260.lib;%(AdditionalDependencies)</AdditionalDependencies>
110
+ <AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.7.0vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
111
+ <AdditionalDependencies>x64-vcruntime140-ruby270.lib;%(AdditionalDependencies)</AdditionalDependencies>
111
112
  <ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
113
+ <SubSystem>Console</SubSystem>
112
114
  </Link>
113
115
  </ItemDefinitionGroup>
114
116
  <ItemGroup>
115
- <ClInclude Include="..\rp_call_info.h" />
116
- <ClInclude Include="..\rp_measure.h" />
117
+ <ClInclude Include="..\rp_aggregate_call_tree.h" />
118
+ <ClInclude Include="..\rp_allocation.h" />
119
+ <ClInclude Include="..\rp_call_tree.h" />
120
+ <ClInclude Include="..\rp_call_trees.h" />
121
+ <ClInclude Include="..\rp_measurement.h" />
117
122
  <ClInclude Include="..\rp_method.h" />
123
+ <ClInclude Include="..\rp_profile.h" />
118
124
  <ClInclude Include="..\rp_stack.h" />
119
125
  <ClInclude Include="..\rp_thread.h" />
120
126
  <ClInclude Include="..\ruby_prof.h" />
121
- <ClInclude Include="..\version.h" />
122
127
  </ItemGroup>
123
128
  <ItemGroup>
124
- <ClCompile Include="..\rp_call_info.c" />
125
- <ClCompile Include="..\rp_measure.c" />
129
+ <ClCompile Include="..\rp_aggregate_call_tree.c" />
130
+ <ClCompile Include="..\rp_allocation.c" />
131
+ <ClCompile Include="..\rp_call_tree.c" />
132
+ <ClCompile Include="..\rp_call_trees.c" />
133
+ <ClCompile Include="..\rp_measurement.c" />
126
134
  <ClCompile Include="..\rp_measure_allocations.c" />
127
- <ClCompile Include="..\rp_measure_cpu_time.c" />
128
- <ClCompile Include="..\rp_measure_gc_runs.c" />
129
- <ClCompile Include="..\rp_measure_gc_time.c" />
130
135
  <ClCompile Include="..\rp_measure_memory.c" />
131
136
  <ClCompile Include="..\rp_measure_process_time.c" />
132
137
  <ClCompile Include="..\rp_measure_wall_time.c" />
133
138
  <ClCompile Include="..\rp_method.c" />
139
+ <ClCompile Include="..\rp_profile.c" />
134
140
  <ClCompile Include="..\rp_stack.c" />
135
141
  <ClCompile Include="..\rp_thread.c" />
136
142
  <ClCompile Include="..\ruby_prof.c" />
@@ -8,57 +8,40 @@ rescue LoadError
8
8
  require "ruby_prof.so"
9
9
  end
10
10
 
11
- module RubyProf
12
- module DeprecationWarnings
13
- def deprecation_warning(feature, recommendation = nil)
14
- $stderr.puts "DEPRECATION WARNING: #{feature}"
15
- $stderr.puts recommendation unless recommendation.nil?
16
- end
17
- end
18
- extend DeprecationWarnings
19
- end
20
-
21
11
  require 'ruby-prof/version'
22
- require 'ruby-prof/call_info'
12
+ require 'ruby-prof/call_tree'
23
13
  require 'ruby-prof/compatibility'
14
+ require 'ruby-prof/measurement'
24
15
  require 'ruby-prof/method_info'
25
16
  require 'ruby-prof/profile'
26
17
  require 'ruby-prof/rack'
27
18
  require 'ruby-prof/thread'
28
19
 
29
20
  module RubyProf
30
- autoload :AggregateCallInfo, 'ruby-prof/aggregate_call_info'
31
- autoload :CallInfoVisitor, 'ruby-prof/call_info_visitor'
32
-
21
+ autoload :CallTreeVisitor, 'ruby-prof/call_tree_visitor'
33
22
  autoload :AbstractPrinter, 'ruby-prof/printers/abstract_printer'
34
23
  autoload :CallInfoPrinter, 'ruby-prof/printers/call_info_printer'
35
24
  autoload :CallStackPrinter, 'ruby-prof/printers/call_stack_printer'
36
25
  autoload :CallTreePrinter, 'ruby-prof/printers/call_tree_printer'
37
26
  autoload :DotPrinter, 'ruby-prof/printers/dot_printer'
38
27
  autoload :FlatPrinter, 'ruby-prof/printers/flat_printer'
39
- autoload :FlatPrinterWithLineNumbers, 'ruby-prof/printers/flat_printer_with_line_numbers'
40
28
  autoload :GraphHtmlPrinter, 'ruby-prof/printers/graph_html_printer'
41
29
  autoload :GraphPrinter, 'ruby-prof/printers/graph_printer'
42
30
  autoload :MultiPrinter, 'ruby-prof/printers/multi_printer'
43
31
 
32
+ # :nodoc:
44
33
  # Checks if the user specified the clock mode via
45
34
  # the RUBY_PROF_MEASURE_MODE environment variable
46
35
  def self.figure_measure_mode
47
36
  case ENV["RUBY_PROF_MEASURE_MODE"]
48
37
  when "wall", "wall_time"
49
38
  RubyProf.measure_mode = RubyProf::WALL_TIME
50
- when "cpu", "cpu_time"
51
- RubyProf.measure_mode = RubyProf::CPU_TIME
52
39
  when "allocations"
53
40
  RubyProf.measure_mode = RubyProf::ALLOCATIONS
54
41
  when "memory"
55
42
  RubyProf.measure_mode = RubyProf::MEMORY
56
43
  when "process", "process_time"
57
44
  RubyProf.measure_mode = RubyProf::PROCESS_TIME
58
- when "gc_time"
59
- RubyProf.measure_mode = RubyProf::GC_TIME
60
- when "gc_runs"
61
- RubyProf.measure_mode = RubyProf::GC_RUNS
62
45
  else
63
46
  # the default is defined in the measure_mode reader
64
47
  end
@@ -0,0 +1,710 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
5
+ <title>ruby-prof call tree</title>
6
+ <style type="text/css">
7
+ body {
8
+ font-size: 70%;
9
+ padding: 0;
10
+ margin: 5px;
11
+ margin-right: 0px;
12
+ margin-left: 0px;
13
+ background: #ffffff;
14
+ }
15
+
16
+ ul {
17
+ margin-left: 0px;
18
+ margin-top: 0px;
19
+ margin-bottom: 0px;
20
+ padding-left: 0px;
21
+ list-style-type: none;
22
+ }
23
+
24
+ li {
25
+ margin-left: 11px;
26
+ padding: 0px;
27
+ white-space: nowrap;
28
+ border-top: 1px solid #cccccc;
29
+ border-left: 1px solid #cccccc;
30
+ border-bottom: none;
31
+ }
32
+
33
+ .thread {
34
+ margin-left: 11px;
35
+ background: #708090;
36
+ padding-top: 3px;
37
+ padding-left: 12px;
38
+ padding-bottom: 2px;
39
+ border-left: 1px solid #CCCCCC;
40
+ border-top: 1px solid #CCCCCC;
41
+ font-weight: bold;
42
+ }
43
+
44
+ .hidden {
45
+ display: none;
46
+ width: 0px;
47
+ height: 0px;
48
+ margin: 0px;
49
+ padding: 0px;
50
+ border-style: none;
51
+ }
52
+
53
+ .color01 {
54
+ background: #adbdeb
55
+ }
56
+
57
+ .color05 {
58
+ background: #9daddb
59
+ }
60
+
61
+ .color0 {
62
+ background: #8d9dcb
63
+ }
64
+
65
+ .color1 {
66
+ background: #89bccb
67
+ }
68
+
69
+ .color2 {
70
+ background: #56e3e7
71
+ }
72
+
73
+ .color3 {
74
+ background: #32cd70
75
+ }
76
+
77
+ .color4 {
78
+ background: #a3d53c
79
+ }
80
+
81
+ .color5 {
82
+ background: #c4cb34
83
+ }
84
+
85
+ .color6 {
86
+ background: #dcb66d
87
+ }
88
+
89
+ .color7 {
90
+ background: #cda59e
91
+ }
92
+
93
+ .color8 {
94
+ background: #be9d9c
95
+ }
96
+
97
+ .color9 {
98
+ background: #cf947a
99
+ }
100
+
101
+ #commands {
102
+ font-size: 10pt;
103
+ padding: 10px;
104
+ margin-left: 11px;
105
+ margin-bottom: 0px;
106
+ margin-top: 0px;
107
+ background: #708090;
108
+ border-top: 1px solid #cccccc;
109
+ border-left: 1px solid #cccccc;
110
+ border-bottom: none;
111
+ }
112
+
113
+ #titlebar {
114
+ font-size: 10pt;
115
+ padding: 10px;
116
+ margin-left: 11px;
117
+ margin-bottom: 0px;
118
+ margin-top: 10px;
119
+ background: #8090a0;
120
+ border-top: 1px solid #cccccc;
121
+ border-left: 1px solid #cccccc;
122
+ border-bottom: none;
123
+ }
124
+
125
+ #help {
126
+ font-size: 10pt;
127
+ padding: 10px;
128
+ margin-left: 11px;
129
+ margin-bottom: 0px;
130
+ margin-top: 0px;
131
+ background: #8090a0;
132
+ display: none;
133
+ border-top: 1px solid #cccccc;
134
+ border-left: 1px solid #cccccc;
135
+ border-bottom: none;
136
+ }
137
+
138
+ #sentinel {
139
+ height: 400px;
140
+ margin-left: 11px;
141
+ background: #8090a0;
142
+ border-top: 1px solid #cccccc;
143
+ border-left: 1px solid #cccccc;
144
+ border-bottom: none;
145
+ }
146
+
147
+ input {
148
+ margin-left: 10px;
149
+ }
150
+
151
+ .toggle {
152
+ background: url(data:image/png;base64,<%= base64_image %>) no-repeat left center;
153
+ float: left;
154
+ width: 9px;
155
+ height: 9px;
156
+ margin: 2px 1px 1px 1px;
157
+ }
158
+
159
+ .toggle.minus {
160
+ background-position: -9px 0;
161
+ }
162
+
163
+ .toggle.plus {
164
+ background-position: -18px 0;
165
+ }
166
+ </style>
167
+
168
+ <script type="text/javascript">
169
+ function rootNode()
170
+ {
171
+ return currentThread
172
+ }
173
+
174
+ function showUL(node, show)
175
+ {
176
+ Array.prototype.forEach.call(node.childNodes, function(child)
177
+ {
178
+ if (child.nodeName == 'LI')
179
+ toggle(child, show)
180
+ })
181
+ }
182
+
183
+ function findUlChild(li)
184
+ {
185
+ var ul = li.childNodes[2]
186
+ while (ul && ul.nodeName != "UL")
187
+ {
188
+ ul = ul.nextSibling
189
+ }
190
+ return ul
191
+ }
192
+
193
+ function isLeafNode(li)
194
+ {
195
+ var element = li.querySelector('a')
196
+ return element.classList.contains('empty')
197
+ }
198
+
199
+ function toggle(li, show)
200
+ {
201
+ if (isLeafNode(li))
202
+ return
203
+
204
+ var img = li.firstChild
205
+ img.className = 'toggle '
206
+ img.className += show ? 'minus' : 'plus'
207
+
208
+ var ul = findUlChild(li)
209
+ if (ul)
210
+ {
211
+ ul.style.display = show ? 'block' : 'none'
212
+ showUL(ul, true)
213
+ }
214
+ }
215
+
216
+ function toggleLI(li)
217
+ {
218
+ var img = li.firstChild
219
+ if (img.className.indexOf("minus") > -1)
220
+ toggle(li, false)
221
+ else
222
+ {
223
+ if (img.className.indexOf("plus") > -1)
224
+ toggle(li, true)
225
+ }
226
+ }
227
+
228
+ function aboveThreshold(text, threshold)
229
+ {
230
+ var match = text.match(/\d+[.,]\d+%/)
231
+ if (!match)
232
+ {
233
+ return true
234
+ }
235
+ else
236
+ {
237
+ var value = parseFloat(match[0].replace(/,/, '.'))
238
+ return value >= threshold
239
+ }
240
+ }
241
+
242
+ function setThresholdLI(li, threshold)
243
+ {
244
+ var a = li.querySelector('a')
245
+ var span = li.querySelector('span')
246
+ var ul = li.querySelector('ul')
247
+
248
+ var visible = aboveThreshold(span.textContent, threshold) ? 1 : 0
249
+
250
+ var count = 0
251
+ if (ul)
252
+ {
253
+ count = setThresholdUL(ul, threshold)
254
+ }
255
+
256
+ if (count > 0)
257
+ {
258
+ a.className = 'toggle minus'
259
+ }
260
+ else
261
+ {
262
+ a.className = 'toggle empty'
263
+ }
264
+
265
+ if (visible)
266
+ {
267
+ li.style.display = 'block'
268
+ } else
269
+ {
270
+ li.style.display = 'none'
271
+ }
272
+ return visible
273
+ }
274
+
275
+ function setThresholdUL(node, threshold)
276
+ {
277
+ var count = 0
278
+ Array.prototype.forEach.call(node.childNodes, function(child)
279
+ {
280
+ if (child.nodeName == 'LI')
281
+ count = count + setThresholdLI(child, threshold)
282
+ })
283
+
284
+ var visible = (count > 0) ? 1 : 0
285
+ if (visible)
286
+ {
287
+ node.style.display = 'block'
288
+ } else
289
+ {
290
+ node.style.display = 'none'
291
+ }
292
+ return visible
293
+ }
294
+
295
+ function toggleChildren(img, event)
296
+ {
297
+ event.cancelBubble = true
298
+ if (img.className.indexOf('empty') > -1)
299
+ return
300
+
301
+ var minus = (img.className.indexOf('minus') > -1)
302
+
303
+ if (minus)
304
+ {
305
+ img.className = 'toggle plus'
306
+ } else
307
+ img.className = 'toggle minus'
308
+
309
+ var li = img.parentNode
310
+ var ul = findUlChild(li)
311
+ if (ul)
312
+ {
313
+ if (minus)
314
+ ul.style.display = 'none'
315
+ else
316
+ ul.style.display = 'block'
317
+ }
318
+ if (minus)
319
+ moveSelectionIfNecessary(li)
320
+ }
321
+
322
+ function showChildren(li)
323
+ {
324
+ var img = li.firstChild
325
+ if (img.className.indexOf('empty') > -1)
326
+ return
327
+ img.className = 'toggle minus'
328
+
329
+ var ul = findUlChild(li)
330
+ if (ul)
331
+ {
332
+ ul.style.display = 'block'
333
+ }
334
+ }
335
+
336
+ function setThreshold()
337
+ {
338
+ var tv = document.getElementById("threshold").value
339
+ if (tv.match(/[0-9]+([.,][0-9]+)?/))
340
+ {
341
+ var f = parseFloat(tv.replace(/,/, '.'))
342
+ var threads = document.getElementsByName("thread")
343
+ var l = threads.length
344
+ for (var i = 0; i < l; i++)
345
+ {
346
+ setThresholdUL(threads[i], f)
347
+ }
348
+ var p = selectedNode
349
+ while (p && p.style.display == 'none')
350
+ p = p.parentNode.parentNode
351
+ if (p && p.nodeName == "LI")
352
+ selectNode(p)
353
+ } else
354
+ {
355
+ alert("Please specify a decimal number as threshold value!")
356
+ }
357
+ }
358
+
359
+ function expandAll(event)
360
+ {
361
+ toggleAll(event, true)
362
+ }
363
+
364
+ function collapseAll(event)
365
+ {
366
+ toggleAll(event, false)
367
+ selectNode(rootNode(), null)
368
+ }
369
+
370
+ function toggleAll(event, show)
371
+ {
372
+ event.cancelBubble = true
373
+ var threads = document.getElementsByName("thread")
374
+ var l = threads.length
375
+ for (var i = 0; i < l; i++)
376
+ {
377
+ showUL(threads[i], show)
378
+ }
379
+ }
380
+
381
+ function toggleHelp(node)
382
+ {
383
+ var help = document.getElementById("help")
384
+ if (node.value == "Show Help")
385
+ {
386
+ node.value = "Hide Help"
387
+ help.style.display = 'block'
388
+ } else
389
+ {
390
+ node.value = "Show Help"
391
+ help.style.display = 'none'
392
+ }
393
+ }
394
+
395
+ var selectedNode = null
396
+ var selectedColor = null
397
+ var selectedThread = null
398
+
399
+ function descendentOf(a, b)
400
+ {
401
+ while (a != b && b != null)
402
+ b = b.parentNode
403
+ return (a == b)
404
+ }
405
+
406
+ function moveSelectionIfNecessary(node)
407
+ {
408
+ if (descendentOf(node, selectedNode))
409
+ selectNode(node, null)
410
+ }
411
+
412
+ function selectNode(node, event)
413
+ {
414
+ if (event)
415
+ {
416
+ event.cancelBubble = true
417
+ thread = findThread(node)
418
+ selectThread(thread)
419
+ }
420
+ if (selectedNode)
421
+ {
422
+ selectedNode.style.background = selectedColor
423
+ }
424
+ selectedNode = node
425
+ selectedColor = node.style.background
426
+ selectedNode.style.background = "red"
427
+ selectedNode.scrollIntoView()
428
+ window.scrollBy(0, -400)
429
+ }
430
+
431
+ function moveUp()
432
+ {
433
+ move(selectedNode.previousSibling)
434
+ }
435
+
436
+ function moveDown()
437
+ {
438
+ move(selectedNode.nextSibling)
439
+ }
440
+
441
+ function move(p)
442
+ {
443
+ while (p && p.style.display == 'none')
444
+ p = p.nextSibling
445
+ if (p && p.nodeName == "LI")
446
+ {
447
+ selectNode(p, null)
448
+ }
449
+ }
450
+
451
+ function moveLeft()
452
+ {
453
+ var p = selectedNode.parentNode.parentNode
454
+ if (p && p.nodeName == "LI")
455
+ {
456
+ selectNode(p, null)
457
+ }
458
+ }
459
+
460
+ function moveRight()
461
+ {
462
+ if (!isLeafNode(selectedNode))
463
+ {
464
+ showChildren(selectedNode)
465
+ var ul = findUlChild(selectedNode)
466
+ if (ul)
467
+ {
468
+ selectNode(ul.firstChild, null)
469
+ }
470
+ }
471
+ }
472
+
473
+ function moveForward()
474
+ {
475
+ if (isLeafNode(selectedNode))
476
+ {
477
+ var p = selectedNode
478
+ while ((p.nextSibling == null || p.nextSibling.style.display == 'none') && p.nodeName == "LI")
479
+ {
480
+ p = p.parentNode.parentNode
481
+ }
482
+ if (p.nodeName == "LI")
483
+ selectNode(p.nextSibling, null)
484
+ } else
485
+ {
486
+ moveRight()
487
+ }
488
+ }
489
+
490
+ function isExpandedNode(li)
491
+ {
492
+ var img = li.firstChild
493
+ return (img.className.indexOf('minus') > -1)
494
+ }
495
+
496
+ function moveBackward()
497
+ {
498
+ var p = selectedNode
499
+ var q = p.previousSibling
500
+ while (q != null && q.style.display == 'none')
501
+ q = q.previousSibling
502
+ if (q == null)
503
+ {
504
+ p = p.parentNode.parentNode
505
+ } else
506
+ {
507
+ while (!isLeafNode(q) && isExpandedNode(q))
508
+ {
509
+ q = findUlChild(q).lastChild
510
+ while (q.style.display == 'none')
511
+ q = q.previousSibling
512
+ }
513
+ p = q
514
+ }
515
+ if (p.nodeName == "LI")
516
+ selectNode(p, null)
517
+ }
518
+
519
+ function moveHome()
520
+ {
521
+ selectNode(currentThread)
522
+ }
523
+
524
+ var currentThreadIndex = null
525
+
526
+ function findThread(node)
527
+ {
528
+ while (node && !node.parentNode.nodeName.match(/BODY|DIV/g))
529
+ {
530
+ node = node.parentNode
531
+ }
532
+ return node.firstChild
533
+ }
534
+
535
+ function selectThread(node)
536
+ {
537
+ var threads = document.getElementsByName("thread")
538
+ currentThread = node
539
+ for (var i = 0; i < threads.length; i++)
540
+ {
541
+ if (threads[i] == currentThread.parentNode)
542
+ currentThreadIndex = i
543
+ }
544
+ }
545
+
546
+ function nextThread()
547
+ {
548
+ var threads = document.getElementsByName("thread")
549
+ if (currentThreadIndex == threads.length - 1)
550
+ currentThreadIndex = 0
551
+ else
552
+ currentThreadIndex += 1
553
+ currentThread = threads[currentThreadIndex].firstChild
554
+ selectNode(currentThread, null)
555
+ }
556
+
557
+ function previousThread()
558
+ {
559
+ var threads = document.getElementsByName("thread")
560
+ if (currentThreadIndex == 0)
561
+ currentThreadIndex = threads.length - 1
562
+ else
563
+ currentThreadIndex -= 1
564
+ currentThread = threads[currentThreadIndex].firstChild
565
+ selectNode(currentThread, null)
566
+ }
567
+
568
+ function switchThread(node, event)
569
+ {
570
+ event.cancelBubble = true
571
+ selectThread(node.nextSibling.firstChild)
572
+ selectNode(currentThread, null)
573
+ }
574
+
575
+ function handleKeyEvent(event)
576
+ {
577
+ var code = event.charCode ? event.charCode : event.keyCode
578
+ var str = String.fromCharCode(code)
579
+ switch (str)
580
+ {
581
+ case "a":
582
+ moveLeft()
583
+ break
584
+ case "s":
585
+ moveDown()
586
+ break
587
+ case "d":
588
+ moveRight()
589
+ break
590
+ case "w":
591
+ moveUp()
592
+ break
593
+ case "f":
594
+ moveForward()
595
+ break
596
+ case "b":
597
+ moveBackward()
598
+ break
599
+ case "x":
600
+ toggleChildren(selectedNode.firstChild, event)
601
+ break
602
+ case "*":
603
+ toggleLI(selectedNode)
604
+ break
605
+ case "n":
606
+ nextThread()
607
+ break
608
+ case "h":
609
+ moveHome()
610
+ break
611
+ case "p":
612
+ previousThread()
613
+ break
614
+ }
615
+ }
616
+
617
+ document.onkeypress = function (event)
618
+ {
619
+ handleKeyEvent(event)
620
+ }
621
+
622
+ window.onload = function ()
623
+ {
624
+ var images = document.querySelectorAll(".toggle")
625
+ for (var i = 0; i < images.length; i++)
626
+ {
627
+ var img = images[i]
628
+ img.onclick = function (event)
629
+ {
630
+ toggleChildren(this, event)
631
+ return false
632
+ }
633
+ }
634
+ var divs = document.getElementsByTagName("div")
635
+ for (i = 0; i < divs.length; i++)
636
+ {
637
+ var div = divs[i]
638
+ if (div.className == "thread")
639
+ div.onclick = function (event)
640
+ {
641
+ switchThread(this, event)
642
+ }
643
+ }
644
+ var lis = document.getElementsByTagName("li")
645
+ for (var i = 0; i < lis.length; i++)
646
+ {
647
+ lis[i].onclick = function (event)
648
+ {
649
+ selectNode(this, event)
650
+ }
651
+ }
652
+
653
+ var threads = document.getElementsByName("thread")
654
+ currentThreadIndex = 0
655
+ currentThread = threads[0].querySelector('li')
656
+ selectNode(currentThread, null)
657
+ }
658
+ </script>
659
+
660
+ <% @overall_time = @result.threads.reduce(0) do |val, thread|
661
+ val += thread.total_time
662
+ end %>
663
+ </head>
664
+ <body>
665
+ <div style="display: inline-block;">
666
+ <div id="titlebar">
667
+ Call tree for application <strong><%= application %> <%= arguments %></strong><br/> Generated on <%= Time.now %>
668
+ with options <%= @options.inspect %><br/>
669
+ </div>
670
+ <div id="commands">
671
+ <span style="font-size: 11pt; font-weight: bold;">Threshold:</span>
672
+ <input value="1.0" size="3" id="threshold" type="text">
673
+ <input value="Apply" onclick="setThreshold();" type="submit">
674
+ <input value="Expand All" onclick="expandAll(event);" type="submit">
675
+ <input value="Collapse All" onclick="collapseAll(event);" type="submit">
676
+ <input value="Show Help" onclick="toggleHelp(this);" type="submit">
677
+ </div>
678
+ <ul style="display: none;" id="help">
679
+ <li>* indicates recursively called methods</li>
680
+ <li>Enter a decimal value <i>d</i> into the threshold field and click "Apply" to hide all nodes marked with time
681
+ values lower than <em>d</em>.
682
+ </li>
683
+ <li>Click on "Expand All" for full tree expansion.</li>
684
+ <li>Click on "Collapse All" to show only top level nodes.</li>
685
+ <li>Use a, s, d, w as in Quake or Urban Terror to navigate the tree.</li>
686
+ <li>Use f and b to navigate the tree in preorder forward and backwards.</li>
687
+ <li>Use x to toggle visibility of a subtree.</li>
688
+ <li>Use * to expand/collapse a whole subtree.</li>
689
+ <li>Use h to navigate to thread root.</li>
690
+ <li>Use n and p to navigate between threads.</li>
691
+ <li>Click on background to move focus to a subtree.</li>
692
+ </ul>
693
+
694
+ <% @result.threads.each do |thread| %>
695
+ <% thread_percent = 100 * (thread.total_time / @overall_time)
696
+ thread_info = "#{"%4.2f%%" % thread_percent} ~ #{@overall_time}" %>
697
+ <div class="thread">
698
+ <span>Thread: <%= thread.id %>, Fiber: <%= thread.fiber_id %> (<%= thread_info %>)</span>
699
+ <ul name="thread">
700
+ <% visited = Set.new
701
+ output = StringIO.new('')
702
+ print_stack(output, visited, thread.call_tree, thread.call_tree.total_time) %>
703
+ <%= output.string %>
704
+ </ul>
705
+ </div>
706
+ <% end %>
707
+ <div id="sentinel"></div>
708
+ </div>
709
+ </body>
710
+ </html>