ruby-prof 1.5.0-x64-mingw-ucrt → 1.6.2-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +19 -0
  3. data/bin/ruby-prof +105 -87
  4. data/ext/ruby_prof/rp_allocation.c +136 -81
  5. data/ext/ruby_prof/rp_allocation.h +8 -6
  6. data/ext/ruby_prof/rp_call_tree.c +502 -457
  7. data/ext/ruby_prof/rp_call_tree.h +47 -44
  8. data/ext/ruby_prof/rp_call_trees.c +1 -1
  9. data/ext/ruby_prof/rp_measurement.c +10 -3
  10. data/ext/ruby_prof/rp_method.c +86 -79
  11. data/ext/ruby_prof/rp_method.h +63 -62
  12. data/ext/ruby_prof/rp_profile.c +933 -948
  13. data/ext/ruby_prof/rp_profile.h +1 -0
  14. data/ext/ruby_prof/rp_thread.c +433 -410
  15. data/ext/ruby_prof/rp_thread.h +39 -39
  16. data/ext/ruby_prof/vc/ruby_prof.vcxproj +6 -3
  17. data/lib/3.1/ruby_prof.so +0 -0
  18. data/lib/3.2/ruby_prof.so +0 -0
  19. data/lib/ruby-prof/compatibility.rb +14 -0
  20. data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
  21. data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
  22. data/lib/ruby-prof/printers/multi_printer.rb +17 -17
  23. data/lib/ruby-prof/profile.rb +70 -70
  24. data/lib/ruby-prof/rack.rb +31 -21
  25. data/lib/ruby-prof/version.rb +1 -1
  26. data/test/abstract_printer_test.rb +1 -0
  27. data/test/alias_test.rb +6 -11
  28. data/test/call_tree_test.rb +94 -197
  29. data/test/call_tree_visitor_test.rb +1 -6
  30. data/test/call_trees_test.rb +2 -2
  31. data/test/{basic_test.rb → compatibility_test.rb} +8 -2
  32. data/test/duplicate_names_test.rb +1 -1
  33. data/test/dynamic_method_test.rb +1 -6
  34. data/test/enumerable_test.rb +1 -1
  35. data/test/exceptions_test.rb +2 -2
  36. data/test/exclude_methods_test.rb +3 -8
  37. data/test/exclude_threads_test.rb +4 -9
  38. data/test/fiber_test.rb +2 -58
  39. data/test/gc_test.rb +2 -2
  40. data/test/inverse_call_tree_test.rb +33 -34
  41. data/test/line_number_test.rb +1 -1
  42. data/test/marshal_test.rb +3 -3
  43. data/test/measure_allocations_test.rb +8 -17
  44. data/test/measure_memory_test.rb +3 -12
  45. data/test/measure_process_time_test.rb +32 -36
  46. data/test/measure_wall_time_test.rb +176 -181
  47. data/test/merge_test.rb +146 -0
  48. data/test/multi_printer_test.rb +0 -5
  49. data/test/no_method_class_test.rb +1 -1
  50. data/test/pause_resume_test.rb +12 -16
  51. data/test/printer_call_stack_test.rb +2 -2
  52. data/test/printer_call_tree_test.rb +2 -2
  53. data/test/printer_flat_test.rb +1 -1
  54. data/test/printer_graph_html_test.rb +2 -2
  55. data/test/printer_graph_test.rb +2 -2
  56. data/test/printers_test.rb +14 -20
  57. data/test/printing_recursive_graph_test.rb +2 -2
  58. data/test/recursive_test.rb +2 -7
  59. data/test/scheduler.rb +9 -0
  60. data/test/singleton_test.rb +1 -1
  61. data/test/stack_printer_test.rb +5 -8
  62. data/test/start_stop_test.rb +11 -14
  63. data/test/test_helper.rb +7 -0
  64. data/test/thread_test.rb +84 -19
  65. data/test/unique_call_path_test.rb +4 -4
  66. data/test/yarv_test.rb +3 -3
  67. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc83e5cdca77c3e07af7ad99f69acfbf8dde2013c435855d996925db9dcd586c
4
- data.tar.gz: 9c14d146c1194db666cec9f9ab05e4a798bd0a6ed5c535d850be581ddf8734fa
3
+ metadata.gz: 4b4e4760579e79fbe1f366a04b3fe39aad288034c2db4ac4683c0f892ac3b8e7
4
+ data.tar.gz: a1a82b7f129560014af92d6256f6e4a0ef06ee909cf2fff7fd643e92ab92f770
5
5
  SHA512:
6
- metadata.gz: 56e6c6a054fed0bdbb08a41ead10898495efb4201c19f2bd3572f3b0ac939e1770c3c4f1bb82da4399ba27f99ed135447e004263d57af86a0888a6a6a349f1fc
7
- data.tar.gz: 251eba317457ec6ea583b25656c062f92e8f55a35c7279f39a1f9987234fd9b481fbfd6ab72d71af34541ca49aad2b0feacefbc4d44ca75a4486ec003cb2fa19
6
+ metadata.gz: 58af0b67459d001ba4781b4d25544fb3d75d6806877c4695b2fd41357333bc5e8ba8b9425e7ed8b8f3aba6daaf00f32b5045757ca1284d1aa0eb131f51dcbbec
7
+ data.tar.gz: f3c3e5953671deca67472e3740bd5757dc2cab05cb4c364080e43d5b33803611b404351fbadf5402ad6118974957db40df3fa858d736231c3269c6a1e4d3995f
data/CHANGES CHANGED
@@ -1,3 +1,22 @@
1
+ 1.6.2 (2023-04-17)
2
+ =====================
3
+ * Fix Profile#merge! implementation (asksurya)
4
+ * Fix ruby-prof command line program (Charlie Savage)
5
+ * Added CMakeLists.txt file (Charlie Savage)
6
+
7
+ 1.6.1 (2023-02-21)
8
+ =====================
9
+ * Fix loading C extension for MacOS (Charlie Savage)
10
+
11
+ 1.6.0 (2023-02-20)
12
+ =====================
13
+ * Add support for Ruby's compacting garbage collector (Charlie Savage)
14
+ * Add rbs signature files (Charlie Savage)
15
+ * Update rack adapter used for profiling Rails to include latest ruby-prof features (Charlie Savage)
16
+ * Add warnings for deprecated methods (Charlie Savage)
17
+ * Update tests to not use deprecated methods (Charlie Savage)
18
+ * Improve tests on OSX (Charlie Savage)
19
+
1
20
  1.5.0 (2023-02-06)
2
21
  =====================
3
22
  * Add new Profile#merge! method that merges results for threads/fibers that share the same root method (Charlie Savage)
data/bin/ruby-prof CHANGED
@@ -1,5 +1,10 @@
1
1
  #! /usr/bin/env ruby
2
2
 
3
+ # To make testing/debugging easier test within this source tree versus an installed gem
4
+ require 'bundler/setup'
5
+ ext_path = File.expand_path(File.join(__dir__, '..', 'ext', 'ruby_prof'))
6
+ $LOAD_PATH.unshift(File.expand_path(ext_path))
7
+
3
8
  # First require ruby-prof
4
9
  require 'ruby-prof'
5
10
 
@@ -16,37 +21,38 @@ module RubyProf
16
21
  # ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]
17
22
  #
18
23
  # Options:
19
- # -p, --printer=printer Select a printer:
20
- # flat - Prints a flat profile as text (default).
21
- # graph - Prints a graph profile as text.
22
- # graph_html - Prints a graph profile as html.
23
- # call_tree - format for KCacheGrind
24
- # call_stack - prints a HTML visualization of the call tree
25
- # dot - Prints a graph profile as a dot file
26
- # multi - Creates several reports in output directory
24
+ # --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
25
+ # -E, --eval-noprof=code execute the ruby statements (not profiled)
26
+ # --exclude=methods A comma separated list of methods to exclude.
27
+ # Specify instance methods via # (Integer#times)
28
+ # Specify class methods via . (Integer.superclass)
29
+ # --exclude-common Remove common methods from the profile
30
+ # -f, --file=path Output results to a file instead of standard out.
27
31
  # -m, --min_percent=min_percent The minimum percent a method must take before
28
32
  # being included in output reports.
29
33
  # This option is not supported for call tree.
30
- # -f, --file=path Output results to a file instead of standard out.
31
34
  # --mode=measure_mode Select what ruby-prof should measure:
32
35
  # wall - Wall time (default).
33
36
  # process - Process time.
34
37
  # allocations - Object allocations (requires patched Ruby interpreter).
35
38
  # memory - Allocated memory in KB (requires patched Ruby interpreter).
39
+ # -p, --printer=printer Select a printer:
40
+ # flat - Prints a flat profile as text (default).
41
+ # graph - Prints a graph profile as text.
42
+ # graph_html - Prints a graph profile as html.
43
+ # call_tree - format for KCacheGrind
44
+ # call_stack - prints a HTML visualization of the call tree
45
+ # dot - Prints a graph profile as a dot file
46
+ # multi - Creates several reports in output directory
47
+ # -R, --require-noprof=lib require a specific library (not profiled)
36
48
  # -s, --sort=sort_mode Select how ruby-prof results should be sorted:
37
49
  # total - Total time
38
50
  # self - Self time
39
51
  # wait - Wait time
40
52
  # child - Child time
41
- # --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
42
- # -R, --require-noprof=lib require a specific library (not profiled)
43
- # -E, --eval-noprof=code execute the ruby statements (not profiled)
44
- # --exclude=methods A comma separated list of methods to exclude.
45
- # Specify instance methods via # (Integer#times)
46
- # Specify class methods via . (Integer.superclass)
47
- # --exclude-common Remove common methods from the profile
48
- # -h, --help Show help message
53
+ # --track_allocations Track allocations while profiling
49
54
  # -v, --version version Show version (1.1.0)
55
+ # -h, --help Show help message
50
56
 
51
57
  class Cmd
52
58
  # :enddoc:
@@ -63,15 +69,16 @@ module RubyProf
63
69
 
64
70
  def setup_options
65
71
  @options = OpenStruct.new
66
- options.printer = RubyProf::FlatPrinter
67
- options.measure_mode = RubyProf::WALL_TIME
68
- options.min_percent = 0
69
- options.file = nil
70
72
  options.allow_exceptions = false
71
- options.exclude_common = false
72
73
  options.exclude = Array.new
74
+ options.exclude_common = false
75
+ options.file = nil
76
+ options.measure_mode = RubyProf::WALL_TIME
77
+ options.min_percent = 0
73
78
  options.pre_libs = Array.new
74
79
  options.pre_execs = Array.new
80
+ options.printer = RubyProf::FlatPrinter
81
+ options.track_allocations = false
75
82
  end
76
83
 
77
84
  # This is copied from ActiveSupport:
@@ -118,40 +125,31 @@ module RubyProf
118
125
  opts.separator ""
119
126
  opts.separator "Options:"
120
127
 
121
- opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot, :multi],
122
- 'Select a printer:',
123
- ' flat - Prints a flat profile as text (default).',
124
- ' graph - Prints a graph profile as text.',
125
- ' graph_html - Prints a graph profile as html.',
126
- ' call_tree - format for KCacheGrind',
127
- ' call_stack - prints a HTML visualization of the call tree',
128
- ' dot - Prints a graph profile as a dot file',
129
- ' multi - Creates several reports in output directory'
130
- ) do |printer|
128
+ opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
129
+ options.allow_exceptions = true
130
+ end
131
131
 
132
- case printer
133
- when :flat
134
- options.printer = RubyProf::FlatPrinter
135
- when :graph
136
- options.printer = RubyProf::GraphPrinter
137
- when :graph_html
138
- options.printer = RubyProf::GraphHtmlPrinter
139
- when :call_tree
140
- options.printer = RubyProf::CallTreePrinter
141
- when :call_stack
142
- options.printer = RubyProf::CallStackPrinter
143
- when :dot
144
- options.printer = RubyProf::DotPrinter
145
- when :multi
146
- options.printer = RubyProf::MultiPrinter
132
+ opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
133
+ options.pre_execs << code
134
+ end
135
+
136
+ opts.on('--exclude=methods', String,
137
+ 'A comma separated list of methods to exclude.',
138
+ ' Specify instance methods via # (Integer#times)',
139
+ ' Specify class methods via . (Integer.superclass)') do |exclude_string|
140
+ exclude_string.split(',').each do |string|
141
+ match = string.strip.match(/(.*)(#|\.)(.*)/)
142
+ klass = constantize(match[1])
143
+ if match[2] == '.'
144
+ klass = klass.singleton_class
145
+ end
146
+ method = match[3].to_sym
147
+ options.exclude << [klass, method]
147
148
  end
148
149
  end
149
150
 
150
- opts.on('-m min_percent', '--min_percent=min_percent', Float,
151
- 'The minimum percent a method must take before ',
152
- ' being included in output reports.',
153
- ' This option is not supported for call tree.') do |min_percent|
154
- options.min_percent = min_percent
151
+ opts.on('--exclude-common', 'Remove common methods from the profile') do
152
+ options.exclude_common = true
155
153
  end
156
154
 
157
155
  opts.on('-f path', '--file=path',
@@ -160,6 +158,13 @@ module RubyProf
160
158
  options.old_wd = Dir.pwd
161
159
  end
162
160
 
161
+ opts.on('-m min_percent', '--min_percent=min_percent', Float,
162
+ 'The minimum percent a method must take before ',
163
+ ' being included in output reports.',
164
+ ' This option is not supported for call tree.') do |min_percent|
165
+ options.min_percent = min_percent
166
+ end
167
+
163
168
  opts.on('--mode=measure_mode',
164
169
  [:process, :wall, :allocations, :memory],
165
170
  'Select what ruby-prof should measure:',
@@ -180,6 +185,39 @@ module RubyProf
180
185
  end
181
186
  end
182
187
 
188
+ opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot, :multi],
189
+ 'Select a printer:',
190
+ ' flat - Prints a flat profile as text (default).',
191
+ ' graph - Prints a graph profile as text.',
192
+ ' graph_html - Prints a graph profile as html.',
193
+ ' call_tree - format for KCacheGrind',
194
+ ' call_stack - prints a HTML visualization of the call tree',
195
+ ' dot - Prints a graph profile as a dot file',
196
+ ' multi - Creates several reports in output directory'
197
+ ) do |printer|
198
+
199
+ case printer
200
+ when :flat
201
+ options.printer = RubyProf::FlatPrinter
202
+ when :graph
203
+ options.printer = RubyProf::GraphPrinter
204
+ when :graph_html
205
+ options.printer = RubyProf::GraphHtmlPrinter
206
+ when :call_tree
207
+ options.printer = RubyProf::CallTreePrinter
208
+ when :call_stack
209
+ options.printer = RubyProf::CallStackPrinter
210
+ when :dot
211
+ options.printer = RubyProf::DotPrinter
212
+ when :multi
213
+ options.printer = RubyProf::MultiPrinter
214
+ end
215
+ end
216
+
217
+ opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
218
+ options.pre_libs << lib
219
+ end
220
+
183
221
  opts.on('-s sort_mode', '--sort=sort_mode', [:total, :self, :wait, :child],
184
222
  'Select how ruby-prof results should be sorted:',
185
223
  ' total - Total time',
@@ -199,9 +237,8 @@ module RubyProf
199
237
  end
200
238
  end
201
239
 
202
- opts.on_tail("-h", "--help", "Show help message") do
203
- puts opts
204
- exit
240
+ opts.on('--track_allocations', 'Track allocations while profiling') do
241
+ options.track_allocations = true
205
242
  end
206
243
 
207
244
  opts.on_tail("-v version", "--version", "Show version (#{RubyProf::VERSION})") do
@@ -209,35 +246,9 @@ module RubyProf
209
246
  exit
210
247
  end
211
248
 
212
- opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
213
- options.allow_exceptions = true
214
- end
215
-
216
- opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
217
- options.pre_libs << lib
218
- end
219
-
220
- opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
221
- options.pre_execs << code
222
- end
223
-
224
- opts.on('--exclude=methods', String,
225
- 'A comma separated list of methods to exclude.',
226
- ' Specify instance methods via # (Integer#times)',
227
- ' Specify class methods via . (Integer.superclass)') do |exclude_string|
228
- exclude_string.split(',').each do |string|
229
- match = string.strip.match(/(.*)(#|\.)(.*)/)
230
- klass = constantize(match[1])
231
- if match[2] == '.'
232
- klass = klass.singleton_class
233
- end
234
- method = match[3].to_sym
235
- options.exclude << [klass, method]
236
- end
237
- end
238
-
239
- opts.on('--exclude-common', 'Remove common methods from the profile') do
240
- options.exclude_common = true
249
+ opts.on_tail("-h", "--help", "Show help message") do
250
+ puts opts
251
+ exit
241
252
  end
242
253
  end
243
254
  end
@@ -281,12 +292,18 @@ module RubyProf
281
292
  end
282
293
 
283
294
  def run
284
- script = ARGV.shift
285
- @profile = Profile.new(options.to_h)
295
+ profile_options = {:allow_exceptions => options.allow_exceptions,
296
+ :exclude_common => options.exclude_common,
297
+ :measure_mode => options.measure_mode,
298
+ :track_allocations => options.track_allocations}
299
+
300
+ @profile = Profile.new(**profile_options)
301
+
286
302
  options.exclude.each do |klass, method|
287
303
  @profile.exclude_method!(klass, method)
288
304
  end
289
305
 
306
+ script = ARGV.shift
290
307
  profile.profile do
291
308
  load script
292
309
  end
@@ -304,7 +321,8 @@ cmd = RubyProf::Cmd.new
304
321
  at_exit {
305
322
  # Create a printer
306
323
  printer = cmd.options.printer.new(cmd.profile)
307
- printer_options = {:min_percent => cmd.options.min_percent, :sort_method => cmd.options.sort_method}
324
+ printer_options = {:min_percent => cmd.options.min_percent,
325
+ :sort_method => cmd.options.sort_method}
308
326
 
309
327
  # Get output
310
328
  if cmd.options.file
@@ -2,32 +2,11 @@
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
4
  #include "rp_allocation.h"
5
+ #include "rp_method.h"
5
6
 
6
7
  VALUE cRpAllocation;
7
8
 
8
- prof_allocation_t* allocations_table_lookup(st_table* table, st_data_t key)
9
- {
10
- prof_allocation_t* result = NULL;
11
- st_data_t value;
12
- if (rb_st_lookup(table, key, &value))
13
- {
14
- result = (prof_allocation_t*)value;
15
- }
16
-
17
- return result;
18
- }
19
-
20
- void allocations_table_insert(st_table* table, st_data_t key, prof_allocation_t* allocation)
21
- {
22
- rb_st_insert(table, (st_data_t)key, (st_data_t)allocation);
23
- }
24
-
25
- st_data_t allocations_key(VALUE klass, int source_line)
26
- {
27
- return (klass << 4) + source_line;
28
- }
29
-
30
- /* ====== prof_allocation_t ====== */
9
+ // ------ prof_allocation_t ------
31
10
  prof_allocation_t* prof_allocation_create(void)
32
11
  {
33
12
  prof_allocation_t* result = ALLOC(prof_allocation_t);
@@ -43,48 +22,17 @@ prof_allocation_t* prof_allocation_create(void)
43
22
  return result;
44
23
  }
45
24
 
46
- prof_allocation_t* prof_get_allocation(VALUE self)
25
+ prof_allocation_t* prof_allocation_get(VALUE self)
47
26
  {
48
27
  /* Can't use Data_Get_Struct because that triggers the event hook
49
28
  ending up in endless recursion. */
50
29
  prof_allocation_t* result = RTYPEDDATA_DATA(self);
51
-
52
30
  if (!result)
53
31
  rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
54
32
 
55
33
  return result;
56
34
  }
57
35
 
58
- prof_allocation_t* prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg)
59
- {
60
- VALUE object = rb_tracearg_object(trace_arg);
61
- if (BUILTIN_TYPE(object) == T_IMEMO)
62
- return NULL;
63
-
64
- VALUE klass = rb_obj_class(object);
65
-
66
- int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
67
- st_data_t key = allocations_key(klass, source_line);
68
-
69
- prof_allocation_t* allocation = allocations_table_lookup(method->allocations_table, key);
70
- if (!allocation)
71
- {
72
- allocation = prof_allocation_create();
73
- allocation->source_line = source_line;
74
- allocation->source_file = rb_tracearg_path(trace_arg);
75
- allocation->klass_flags = 0;
76
- allocation->klass = resolve_klass(klass, &allocation->klass_flags);
77
-
78
- allocation->key = key;
79
- allocations_table_insert(method->allocations_table, key, allocation);
80
- }
81
-
82
- allocation->count++;
83
- allocation->memory += rb_obj_memsize_of(object);
84
-
85
- return allocation;
86
- }
87
-
88
36
  static void prof_allocation_ruby_gc_free(void* data)
89
37
  {
90
38
  if (data)
@@ -118,30 +66,39 @@ void prof_allocation_mark(void* data)
118
66
 
119
67
  prof_allocation_t* allocation = (prof_allocation_t*)data;
120
68
  if (allocation->object != Qnil)
121
- rb_gc_mark(allocation->object);
69
+ rb_gc_mark_movable(allocation->object);
122
70
 
123
71
  if (allocation->klass != Qnil)
124
- rb_gc_mark(allocation->klass);
72
+ rb_gc_mark_movable(allocation->klass);
125
73
 
126
74
  if (allocation->klass_name != Qnil)
127
- rb_gc_mark(allocation->klass_name);
75
+ rb_gc_mark_movable(allocation->klass_name);
128
76
 
129
77
  if (allocation->source_file != Qnil)
130
78
  rb_gc_mark(allocation->source_file);
131
79
  }
132
80
 
133
- static const rb_data_type_t allocation_type =
81
+ void prof_allocation_compact(void* data)
134
82
  {
135
- .wrap_struct_name = "Allocation",
136
- .function =
137
- {
138
- .dmark = prof_allocation_mark,
139
- .dfree = prof_allocation_ruby_gc_free,
140
- .dsize = prof_allocation_size,
141
- },
142
- .data = NULL,
143
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
144
- };
83
+ prof_allocation_t* allocation = (prof_allocation_t*)data;
84
+ allocation->object = rb_gc_location(allocation->object);
85
+ allocation->klass = rb_gc_location(allocation->klass);
86
+ allocation->klass_name = rb_gc_location(allocation->klass_name);
87
+ }
88
+
89
+ static const rb_data_type_t allocation_type =
90
+ {
91
+ .wrap_struct_name = "Allocation",
92
+ .function =
93
+ {
94
+ .dmark = prof_allocation_mark,
95
+ .dfree = prof_allocation_ruby_gc_free,
96
+ .dsize = prof_allocation_size,
97
+ .dcompact = prof_allocation_compact
98
+ },
99
+ .data = NULL,
100
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
101
+ };
145
102
 
146
103
  VALUE prof_allocation_wrap(prof_allocation_t* allocation)
147
104
  {
@@ -152,24 +109,122 @@ VALUE prof_allocation_wrap(prof_allocation_t* allocation)
152
109
  return allocation->object;
153
110
  }
154
111
 
155
- static VALUE prof_allocation_allocate(VALUE klass)
112
+ /* ====== Allocation Table ====== */
113
+ st_table* prof_allocations_create()
156
114
  {
157
- prof_allocation_t* allocation = prof_allocation_create();
158
- allocation->object = prof_allocation_wrap(allocation);
159
- return allocation->object;
115
+ return rb_st_init_numtable();
160
116
  }
161
117
 
162
- prof_allocation_t* prof_allocation_get(VALUE self)
118
+ static int allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
163
119
  {
164
- /* Can't use Data_Get_Struct because that triggers the event hook
165
- ending up in endless recursion. */
166
- prof_allocation_t* result = RTYPEDDATA_DATA(self);
167
- if (!result)
168
- rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
120
+ prof_allocation_free((prof_allocation_t*)value);
121
+ return ST_CONTINUE;
122
+ }
123
+
124
+ st_data_t allocations_key(VALUE klass, int source_line)
125
+ {
126
+ return (klass << 4) + source_line;
127
+ }
128
+
129
+ static int prof_allocations_collect(st_data_t key, st_data_t value, st_data_t result)
130
+ {
131
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
132
+ VALUE arr = (VALUE)result;
133
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
134
+ return ST_CONTINUE;
135
+ }
136
+
137
+ static int prof_allocations_mark_each(st_data_t key, st_data_t value, st_data_t data)
138
+ {
139
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
140
+ prof_allocation_mark(allocation);
141
+ return ST_CONTINUE;
142
+ }
143
+
144
+ void prof_allocations_mark(st_table* allocations_table)
145
+ {
146
+ rb_st_foreach(allocations_table, prof_allocations_mark_each, 0);
147
+ }
148
+
149
+ void prof_allocations_free(st_table* table)
150
+ {
151
+ rb_st_foreach(table, allocations_table_free_iterator, 0);
152
+ rb_st_free_table(table);
153
+ }
154
+
155
+ prof_allocation_t* allocations_table_lookup(st_table* table, st_data_t key)
156
+ {
157
+ prof_allocation_t* result = NULL;
158
+ st_data_t value;
159
+ if (rb_st_lookup(table, key, &value))
160
+ {
161
+ result = (prof_allocation_t*)value;
162
+ }
169
163
 
170
164
  return result;
171
165
  }
172
166
 
167
+ void allocations_table_insert(st_table* table, st_data_t key, prof_allocation_t* allocation)
168
+ {
169
+ rb_st_insert(table, (st_data_t)key, (st_data_t)allocation);
170
+ }
171
+
172
+ prof_allocation_t* prof_allocate_increment(st_table* allocations_table, rb_trace_arg_t* trace_arg)
173
+ {
174
+ VALUE object = rb_tracearg_object(trace_arg);
175
+ if (BUILTIN_TYPE(object) == T_IMEMO)
176
+ return NULL;
177
+
178
+ VALUE klass = rb_obj_class(object);
179
+
180
+ int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
181
+ st_data_t key = allocations_key(klass, source_line);
182
+
183
+ prof_allocation_t* allocation = allocations_table_lookup(allocations_table, key);
184
+ if (!allocation)
185
+ {
186
+ allocation = prof_allocation_create();
187
+ allocation->source_line = source_line;
188
+ allocation->source_file = rb_tracearg_path(trace_arg);
189
+ allocation->klass_flags = 0;
190
+ allocation->klass = resolve_klass(klass, &allocation->klass_flags);
191
+
192
+ allocation->key = key;
193
+ allocations_table_insert(allocations_table, key, allocation);
194
+ }
195
+
196
+ allocation->count++;
197
+ allocation->memory += rb_obj_memsize_of(object);
198
+
199
+ return allocation;
200
+ }
201
+
202
+ // Returns an array of allocations
203
+ VALUE prof_allocations_wrap(st_table* allocations_table)
204
+ {
205
+ VALUE result = rb_ary_new();
206
+ rb_st_foreach(allocations_table, prof_allocations_collect, result);
207
+ return result;
208
+ }
209
+
210
+ void prof_allocations_unwrap(st_table* allocations_table, VALUE allocations)
211
+ {
212
+ for (int i = 0; i < rb_array_len(allocations); i++)
213
+ {
214
+ VALUE allocation = rb_ary_entry(allocations, i);
215
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
216
+ rb_st_insert(allocations_table, allocation_data->key, (st_data_t)allocation_data);
217
+ }
218
+ }
219
+
220
+ /* ====== prof_allocation_t ====== */
221
+ static VALUE prof_allocation_allocate(VALUE klass)
222
+ {
223
+ prof_allocation_t* allocation = prof_allocation_create();
224
+ allocation->object = prof_allocation_wrap(allocation);
225
+ return allocation->object;
226
+ }
227
+
173
228
  /* call-seq:
174
229
  klass -> Class
175
230
 
@@ -238,7 +293,7 @@ static VALUE prof_allocation_memory(VALUE self)
238
293
  /* :nodoc: */
239
294
  static VALUE prof_allocation_dump(VALUE self)
240
295
  {
241
- prof_allocation_t* allocation = prof_get_allocation(self);
296
+ prof_allocation_t* allocation = prof_allocation_get(self);
242
297
 
243
298
  VALUE result = rb_hash_new();
244
299
 
@@ -256,7 +311,7 @@ static VALUE prof_allocation_dump(VALUE self)
256
311
  /* :nodoc: */
257
312
  static VALUE prof_allocation_load(VALUE self, VALUE data)
258
313
  {
259
- prof_allocation_t* allocation = prof_get_allocation(self);
314
+ prof_allocation_t* allocation = prof_allocation_get(self);
260
315
  allocation->object = self;
261
316
 
262
317
  allocation->key = RB_NUM2ULL(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
@@ -5,7 +5,6 @@
5
5
  #define _RP_ALLOCATION_
6
6
 
7
7
  #include "ruby_prof.h"
8
- #include "rp_method.h"
9
8
 
10
9
  typedef struct prof_allocation_t
11
10
  {
@@ -20,12 +19,15 @@ typedef struct prof_allocation_t
20
19
  VALUE object; /* Cache to wrapped object */
21
20
  } prof_allocation_t;
22
21
 
22
+ // Allocation (prof_allocation_t*)
23
23
  void rp_init_allocation(void);
24
- void prof_allocation_free(prof_allocation_t* allocation);
25
- void prof_allocation_mark(void* data);
26
- VALUE prof_allocation_wrap(prof_allocation_t* allocation);
27
- prof_allocation_t* prof_allocation_get(VALUE self);
28
- prof_allocation_t* prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg);
24
+ prof_allocation_t* prof_allocate_increment(st_table* allocations_table, rb_trace_arg_t* trace_arg);
29
25
 
26
+ // Allocations (st_table*)
27
+ st_table* prof_allocations_create(void);
28
+ VALUE prof_allocations_wrap(st_table* allocations_table);
29
+ void prof_allocations_unwrap(st_table* allocations_table, VALUE allocations);
30
+ void prof_allocations_mark(st_table* allocations_table);
31
+ void prof_allocations_free(st_table* table);
30
32
 
31
33
  #endif //_RP_ALLOCATION_