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
@@ -1,248 +1,174 @@
1
1
  /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
- #include "ruby_prof.h"
4
+ #include "rp_allocation.h"
5
+ #include "rp_call_trees.h"
6
+ #include "rp_method.h"
5
7
 
6
- #define RP_REL_GET(r, off) ((r) & (1 << (off)))
7
- #define RP_REL_SET(r, off) \
8
- do { \
9
- r |= (1 << (off)); \
10
- } while (0)
11
-
12
- VALUE cMethodInfo;
8
+ VALUE cRpMethodInfo;
13
9
 
14
10
  /* ================ Helper Functions =================*/
15
- static VALUE
16
- figure_singleton_name(VALUE klass)
11
+ VALUE resolve_klass(VALUE klass, unsigned int* klass_flags)
17
12
  {
18
- volatile VALUE attached, super;
19
- volatile VALUE attached_str, super_str;
20
- volatile VALUE result = Qnil;
21
-
22
- /* We have come across a singleton object. First
23
- figure out what it is attached to.*/
24
- attached = rb_iv_get(klass, "__attached__");
13
+ VALUE result = klass;
25
14
 
26
- /* Is this a singleton class acting as a metaclass? */
27
- if (BUILTIN_TYPE(attached) == T_CLASS)
15
+ if (klass == 0 || klass == Qnil)
28
16
  {
29
- attached_str = rb_class_name(attached);
30
- result = rb_str_new2("<Class::");
31
- rb_str_append(result, attached_str);
32
- rb_str_cat2(result, ">");
17
+ result = Qnil;
33
18
  }
34
-
35
- /* Is this for singleton methods on a module? */
36
- else if (BUILTIN_TYPE(attached) == T_MODULE)
19
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
37
20
  {
38
- attached_str = rb_class_name(attached);
39
- result = rb_str_new2("<Module::");
40
- rb_str_append(result, attached_str);
41
- rb_str_cat2(result, ">");
42
- }
21
+ /* We have come across a singleton object. First
22
+ figure out what it is attached to.*/
23
+ VALUE attached = rb_iv_get(klass, "__attached__");
43
24
 
44
- /* Is this for singleton methods on an object? */
45
- else if (BUILTIN_TYPE(attached) == T_OBJECT)
46
- {
47
- /* Make sure to get the super class so that we don't
48
- mistakenly grab a T_ICLASS which would lead to
49
- unknown method errors. */
50
- super = rb_class_superclass(klass);
51
- super_str = rb_class_name(super);
52
- result = rb_str_new2("<Object::");
53
- rb_str_append(result, super_str);
54
- rb_str_cat2(result, ">");
25
+ /* Is this a singleton class acting as a metaclass? */
26
+ if (BUILTIN_TYPE(attached) == T_CLASS)
27
+ {
28
+ *klass_flags |= kClassSingleton;
29
+ result = attached;
30
+ }
31
+ /* Is this for singleton methods on a module? */
32
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
33
+ {
34
+ *klass_flags |= kModuleSingleton;
35
+ result = attached;
36
+ }
37
+ /* Is this for singleton methods on an object? */
38
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
39
+ {
40
+ *klass_flags |= kObjectSingleton;
41
+ result = rb_class_superclass(klass);
42
+ }
43
+ /* Ok, this could be other things like an array made put onto
44
+ a singleton object (yeah, it happens, see the singleton
45
+ objects test case). */
46
+ else
47
+ {
48
+ *klass_flags |= kOtherSingleton;
49
+ result = klass;
50
+ }
55
51
  }
56
-
57
- /* Ok, this could be other things like an array made put onto
58
- a singleton object (yeah, it happens, see the singleton
59
- objects test case). */
60
- else
52
+ /* Is this an include for a module? If so get the actual
53
+ module class since we want to combine all profiling
54
+ results for that module. */
55
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
61
56
  {
62
- result = rb_any_to_s(klass);
57
+ unsigned int dummy;
58
+ *klass_flags |= kModuleIncludee;
59
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
63
60
  }
64
-
65
61
  return result;
66
62
  }
67
63
 
68
- static VALUE
69
- klass_name(VALUE klass)
64
+ VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags)
70
65
  {
71
- volatile VALUE result = Qnil;
66
+ VALUE result = Qnil;
72
67
 
73
- if (klass == 0 || klass == Qnil)
68
+ if (klass == Qnil)
74
69
  {
75
70
  result = rb_str_new2("[global]");
76
71
  }
77
- else if (BUILTIN_TYPE(klass) == T_MODULE)
78
- {
79
- result = rb_class_name(klass);
80
- }
81
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
72
+ else if (*klass_flags & kOtherSingleton)
82
73
  {
83
- result = figure_singleton_name(klass);
84
- }
85
- else if (BUILTIN_TYPE(klass) == T_CLASS)
86
- {
87
- result = rb_class_name(klass);
74
+ result = rb_any_to_s(klass);
88
75
  }
89
76
  else
90
77
  {
91
- /* Should never happen. */
92
- result = rb_str_new2("[unknown]");
78
+ result = rb_class_name(klass);
93
79
  }
94
80
 
95
81
  return result;
96
82
  }
97
83
 
98
- static VALUE
99
- method_name(ID mid)
84
+ st_data_t method_key(VALUE klass, VALUE msym)
100
85
  {
101
- volatile VALUE name = Qnil;
86
+ VALUE resolved_klass = klass;
102
87
 
103
- if (RTEST(mid)) {
104
- name = rb_id2str(mid);
105
- return rb_str_dup(name);
106
- } else {
107
- return rb_str_new2("[no method]");
88
+ /* Is this an include for a module? If so get the actual
89
+ module class since we want to combine all profiling
90
+ results for that module. */
91
+ if (klass == 0 || klass == Qnil)
92
+ {
93
+ resolved_klass = Qnil;
108
94
  }
95
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
96
+ {
97
+ resolved_klass = RBASIC(klass)->klass;
98
+ }
99
+
100
+ return (resolved_klass << 4) + (msym);
109
101
  }
110
102
 
111
- static VALUE
112
- full_name(VALUE klass, ID mid)
103
+ /* ====== Allocation Table ====== */
104
+ st_table* allocations_table_create()
113
105
  {
114
- volatile VALUE klass_str, method_str;
115
- volatile VALUE result = Qnil;
116
-
117
- klass_str = klass_name(klass);
118
- method_str = method_name(mid);
119
-
120
- result = rb_str_dup(klass_str);
121
- rb_str_cat2(result, "#");
122
- rb_str_append(result, method_str);
123
-
124
- return result;
106
+ return rb_st_init_numtable();
125
107
  }
126
108
 
127
- static VALUE
128
- source_klass_name(VALUE source_klass)
109
+ static int allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
129
110
  {
130
- volatile VALUE klass_str;
131
- volatile VALUE result = Qnil;
132
-
133
- if (RTEST(source_klass)) {
134
- klass_str = rb_class_name(source_klass);
135
- result = rb_str_dup(klass_str);
136
- } else {
137
- result = rb_str_new2("[global]");
138
- }
139
-
140
- return result;
111
+ prof_allocation_free((prof_allocation_t*)value);
112
+ return ST_CONTINUE;
141
113
  }
142
114
 
143
- static VALUE
144
- calltree_name(VALUE source_klass, int relation, ID mid)
115
+ static int prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
145
116
  {
146
- volatile VALUE klass_str, klass_path, joiner;
147
- volatile VALUE method_str;
148
- volatile VALUE result = Qnil;
149
-
150
- klass_str = source_klass_name(source_klass);
151
- method_str = method_name(mid);
152
-
153
- klass_path = rb_str_split(klass_str, "::");
154
- joiner = rb_str_new2("/");
155
- result = rb_ary_join(klass_path, joiner);
156
-
157
- rb_str_cat2(result, "::");
158
-
159
- if (RP_REL_GET(relation, kObjectSingleton)) {
160
- rb_str_cat2(result, "*");
161
- }
162
-
163
- if (RP_REL_GET(relation, kModuleSingleton)) {
164
- rb_str_cat2(result, "^");
165
- }
166
-
167
- rb_str_append(result, method_str);
168
-
169
- return result;
117
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
118
+ VALUE arr = (VALUE)result;
119
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
120
+ return ST_CONTINUE;
170
121
  }
171
122
 
172
- void
173
- method_key(prof_method_key_t* key, VALUE klass, ID mid)
123
+ static int prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
174
124
  {
175
- /* Is this an include for a module? If so get the actual
176
- module class since we want to combine all profiling
177
- results for that module. */
178
- if (klass != 0 && BUILTIN_TYPE(klass) == T_ICLASS) {
179
- klass = RBASIC(klass)->klass;
180
- }
125
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
126
+ prof_allocation_mark(allocation);
127
+ return ST_CONTINUE;
128
+ }
181
129
 
182
- key->klass = klass;
183
- key->mid = mid;
184
- key->key = (klass << 4) + (mid << 2);
130
+ void allocations_table_free(st_table* table)
131
+ {
132
+ rb_st_foreach(table, allocations_table_free_iterator, 0);
133
+ rb_st_free_table(table);
185
134
  }
186
135
 
187
136
  /* ================ prof_method_t =================*/
188
-
189
- prof_method_t*
190
- prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
137
+ prof_method_t* prof_get_method(VALUE self)
191
138
  {
192
- prof_method_t *result = ALLOC(prof_method_t);
193
-
194
- result->key = ALLOC(prof_method_key_t);
195
- method_key(result->key, klass, mid);
196
-
197
- result->excluded = 0;
198
- result->recursive = 0;
199
-
200
- result->call_infos = prof_call_infos_create();
201
- result->visits = 0;
202
-
203
- result->object = Qnil;
204
-
205
- if (source_file != NULL)
206
- {
207
- size_t len = strlen(source_file) + 1;
208
- char *buffer = ALLOC_N(char, len);
209
-
210
- MEMCPY(buffer, source_file, char, len);
211
- result->source_file = buffer;
212
- } else {
213
- result->source_file = source_file;
214
- }
215
-
216
- result->source_klass = Qnil;
217
- result->line = line;
139
+ /* Can't use Data_Get_Struct because that triggers the event hook
140
+ ending up in endless recursion. */
141
+ prof_method_t* result = DATA_PTR(self);
218
142
 
219
- result->resolved = 0;
220
- result->relation = 0;
143
+ if (!result)
144
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
221
145
 
222
146
  return result;
223
147
  }
224
148
 
225
- prof_method_t*
226
- prof_method_create_excluded(VALUE klass, ID mid)
149
+ prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
227
150
  {
228
- prof_method_t *result = ALLOC(prof_method_t);
151
+ prof_method_t* result = ALLOC(prof_method_t);
152
+ result->key = method_key(klass, msym);
153
+ result->klass_flags = 0;
229
154
 
230
- result->key = ALLOC(prof_method_key_t);
231
- method_key(result->key, klass, mid);
155
+ /* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
156
+ in the allocation results so we want to avoid it until after the profile run is complete. */
157
+ result->klass = resolve_klass(klass, &result->klass_flags);
158
+ result->klass_name = Qnil;
159
+ result->method_name = msym;
160
+ result->measurement = prof_measurement_create();
232
161
 
233
- /* Invisible with this flag set. */
234
- result->excluded = 1;
235
- result->recursive = 0;
162
+ result->call_trees = prof_call_trees_create();
163
+ result->allocations_table = allocations_table_create();
236
164
 
237
- result->call_infos = 0;
238
165
  result->visits = 0;
166
+ result->recursive = false;
239
167
 
240
168
  result->object = Qnil;
241
- result->source_klass = Qnil;
242
- result->line = 0;
243
169
 
244
- result->resolved = 0;
245
- result->relation = 0;
170
+ result->source_file = source_file;
171
+ result->source_line = source_line;
246
172
 
247
173
  return result;
248
174
  }
@@ -253,211 +179,102 @@ prof_method_create_excluded(VALUE klass, ID mid)
253
179
  be freed before the parent profile. Thus we add in a free function
254
180
  for the garbage collector so that if it does get called will nil
255
181
  out our Ruby object reference.*/
256
- static void
257
- prof_method_ruby_gc_free(prof_method_t* method)
182
+ static void prof_method_ruby_gc_free(void* data)
258
183
  {
259
- /* Has this thread object been accessed by Ruby? If
260
- yes clean it up so to avoid a segmentation fault. */
261
- if (method->object != Qnil)
262
- {
263
- RDATA(method->object)->data = NULL;
264
- RDATA(method->object)->dfree = NULL;
265
- RDATA(method->object)->dmark = NULL;
266
- }
267
- method->object = Qnil;
184
+ prof_method_t* method = (prof_method_t*)data;
185
+ method->object = Qnil;
268
186
  }
269
187
 
270
- static void
271
- prof_method_free(prof_method_t* method)
188
+ static void prof_method_free(prof_method_t* method)
272
189
  {
273
- prof_method_ruby_gc_free(method);
274
-
275
- if (method->call_infos) {
276
- prof_call_infos_free(method->call_infos);
277
- xfree(method->call_infos);
278
- }
279
-
280
- xfree(method->key);
281
-
282
- method->key = NULL;
283
- method->source_klass = Qnil;
190
+ /* Has this method object been accessed by Ruby? If
191
+ yes clean it up so to avoid a segmentation fault. */
192
+ if (method->object != Qnil)
193
+ {
194
+ RDATA(method->object)->dmark = NULL;
195
+ RDATA(method->object)->dfree = NULL;
196
+ RDATA(method->object)->data = NULL;
197
+ method->object = Qnil;
198
+ }
284
199
 
285
- xfree(method);
200
+ allocations_table_free(method->allocations_table);
201
+ prof_call_trees_free(method->call_trees);
202
+ prof_measurement_free(method->measurement);
203
+ xfree(method);
286
204
  }
287
205
 
288
- void
289
- prof_method_mark(prof_method_t *method)
206
+ size_t prof_method_size(const void* data)
290
207
  {
291
- if (method->key->klass) {
292
- rb_gc_mark(method->key->klass);
293
- }
294
-
295
- if (method->source_klass) {
296
- rb_gc_mark(method->source_klass);
297
- }
298
-
299
- if (method->object) {
300
- rb_gc_mark(method->object);
301
- }
302
-
303
- if (method->call_infos) {
304
- prof_call_infos_mark(method->call_infos);
305
- }
208
+ return sizeof(prof_method_t);
306
209
  }
307
210
 
308
- static VALUE
309
- resolve_source_klass(prof_method_t* method)
211
+ void prof_method_mark(void* data)
310
212
  {
311
- volatile VALUE klass, next_klass;
312
- volatile VALUE attached;
313
- unsigned int relation;
213
+ prof_method_t* method = (prof_method_t*)data;
314
214
 
315
- /* We want to group methods according to their source-level
316
- definitions, not their implementation class. Follow module
317
- inclusions and singleton classes back to a meaningful root
318
- while keeping track of these relationships. */
215
+ if (method->object != Qnil)
216
+ rb_gc_mark(method->object);
319
217
 
320
- if (method->resolved) {
321
- return method->source_klass;
322
- }
218
+ rb_gc_mark(method->klass_name);
219
+ rb_gc_mark(method->method_name);
220
+ rb_gc_mark(method->source_file);
323
221
 
324
- klass = method->key->klass;
325
- relation = 0;
222
+ if (method->klass != Qnil)
223
+ rb_gc_mark(method->klass);
326
224
 
327
- while (1)
328
- {
329
- /* This is a global/unknown class */
330
- if (klass == 0 || klass == Qnil)
331
- break;
225
+ prof_measurement_mark(method->measurement);
332
226
 
333
- /* Is this a singleton class? (most common case) */
334
- if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
335
- {
336
- /* We have come across a singleton object. First
337
- figure out what it is attached to.*/
338
- attached = rb_iv_get(klass, "__attached__");
339
-
340
- /* Is this a singleton class acting as a metaclass?
341
- Or for singleton methods on a module? */
342
- if (BUILTIN_TYPE(attached) == T_CLASS ||
343
- BUILTIN_TYPE(attached) == T_MODULE)
344
- {
345
- RP_REL_SET(relation, kModuleSingleton);
346
- klass = attached;
347
- }
348
- /* Is this for singleton methods on an object? */
349
- else if (BUILTIN_TYPE(attached) == T_OBJECT)
350
- {
351
- RP_REL_SET(relation, kObjectSingleton);
352
- next_klass = rb_class_superclass(klass);
353
- klass = next_klass;
354
- }
355
- /* This is a singleton of an instance of a builtin type. */
356
- else
357
- {
358
- RP_REL_SET(relation, kObjectSingleton);
359
- next_klass = rb_class_superclass(klass);
360
- klass = next_klass;
361
- }
362
- }
363
- /* Is this an include for a module? If so get the actual
364
- module class since we want to combine all profiling
365
- results for that module. */
366
- else if (BUILTIN_TYPE(klass) == T_ICLASS)
367
- {
368
- RP_REL_SET(relation, kModuleIncludee);
369
- next_klass = RBASIC(klass)->klass;
370
- klass = next_klass;
371
- }
372
- /* No transformations apply; so bail. */
373
- else
374
- {
375
- break;
376
- }
377
- }
378
-
379
- method->resolved = 1;
380
- method->relation = relation;
381
- method->source_klass = klass;
382
-
383
- return klass;
227
+ rb_st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
384
228
  }
385
229
 
386
- VALUE
387
- prof_method_wrap(prof_method_t *result)
230
+ static VALUE prof_method_allocate(VALUE klass)
388
231
  {
389
- if (result->object == Qnil) {
390
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
391
- }
392
- return result->object;
232
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
233
+ method_data->object = prof_method_wrap(method_data);
234
+ return method_data->object;
393
235
  }
394
236
 
395
- static prof_method_t *
396
- get_prof_method(VALUE self)
237
+ VALUE prof_method_wrap(prof_method_t* method)
397
238
  {
398
- /* Can't use Data_Get_Struct because that triggers the event hook
399
- ending up in endless recursion. */
400
- prof_method_t* result = DATA_PTR(self);
401
-
402
- if (!result) {
403
- rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
404
- }
405
-
406
- return result;
407
- }
408
-
409
- /* ================ Method Table =================*/
410
- int
411
- method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2)
412
- {
413
- return (key1->klass != key2->klass) || (key1->mid != key2->mid);
414
- }
415
-
416
- st_index_t
417
- method_table_hash(prof_method_key_t *key)
418
- {
419
- return key->key;
239
+ if (method->object == Qnil)
240
+ {
241
+ method->object = Data_Wrap_Struct(cRpMethodInfo, prof_method_mark, prof_method_ruby_gc_free, method);
242
+ }
243
+ return method->object;
420
244
  }
421
245
 
422
- struct st_hash_type type_method_hash = {
423
- method_table_cmp,
424
- method_table_hash
425
- };
426
-
427
- st_table *
428
- method_table_create()
246
+ st_table* method_table_create()
429
247
  {
430
- return st_init_table(&type_method_hash);
248
+ return rb_st_init_numtable();
431
249
  }
432
250
 
433
- static int
434
- method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
251
+ static int method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
435
252
  {
436
- prof_method_free((prof_method_t *)value);
253
+ prof_method_free((prof_method_t*)value);
437
254
  return ST_CONTINUE;
438
255
  }
439
256
 
440
- void
441
- method_table_free(st_table *table)
257
+ void method_table_free(st_table* table)
442
258
  {
443
- st_foreach(table, method_table_free_iterator, 0);
444
- st_free_table(table);
259
+ rb_st_foreach(table, method_table_free_iterator, 0);
260
+ rb_st_free_table(table);
445
261
  }
446
262
 
447
- size_t
448
- method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
263
+ size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val)
449
264
  {
450
- return st_insert(table, (st_data_t) key, (st_data_t) val);
265
+ return rb_st_insert(table, (st_data_t)key, (st_data_t)val);
451
266
  }
452
267
 
453
- prof_method_t *
454
- method_table_lookup(st_table *table, const prof_method_key_t* key)
268
+ prof_method_t* method_table_lookup(st_table* table, st_data_t key)
455
269
  {
456
270
  st_data_t val;
457
- if (st_lookup(table, (st_data_t)key, &val)) {
458
- return (prof_method_t *) val;
459
- } else {
460
- return NULL;
271
+ if (rb_st_lookup(table, (st_data_t)key, &val))
272
+ {
273
+ return (prof_method_t*)val;
274
+ }
275
+ else
276
+ {
277
+ return NULL;
461
278
  }
462
279
  }
463
280
 
@@ -472,52 +289,46 @@ the RubyProf::Profile object.
472
289
  */
473
290
 
474
291
  /* call-seq:
475
- line_no -> int
292
+ allocations -> array
476
293
 
477
- returns the line number of the method */
478
- static VALUE
479
- prof_method_line(VALUE self)
294
+ Returns an array of allocation information.*/
295
+ static VALUE prof_method_allocations(VALUE self)
480
296
  {
481
- int line = get_prof_method(self)->line;
482
- return rb_int_new(line);
297
+ prof_method_t* method = prof_get_method(self);
298
+ VALUE result = rb_ary_new();
299
+ rb_st_foreach(method->allocations_table, prof_method_collect_allocations, result);
300
+ return result;
483
301
  }
484
302
 
485
303
  /* call-seq:
486
- source_file => string
304
+ called -> Measurement
487
305
 
488
- return the source file of the method
489
- */
490
- static VALUE prof_method_source_file(VALUE self)
306
+ Returns the measurement associated with this method. */
307
+ static VALUE prof_method_measurement(VALUE self)
491
308
  {
492
- const char* sf = get_prof_method(self)->source_file;
493
- if(!sf) {
494
- return rb_str_new2("ruby_runtime");
495
- } else {
496
- return rb_str_new2(sf);
497
- }
309
+ prof_method_t* method = prof_get_method(self);
310
+ return prof_measurement_wrap(method->measurement);
498
311
  }
499
312
 
500
-
501
313
  /* call-seq:
502
- method_class -> klass
314
+ source_file => string
503
315
 
504
- Returns the Ruby klass that owns this method. */
505
- static VALUE
506
- prof_method_klass(VALUE self)
316
+ return the source file of the method
317
+ */
318
+ static VALUE prof_method_source_file(VALUE self)
507
319
  {
508
- prof_method_t *result = get_prof_method(self);
509
- return result->key->klass;
320
+ prof_method_t* method = prof_get_method(self);
321
+ return method->source_file;
510
322
  }
511
323
 
512
324
  /* call-seq:
513
- method_id -> ID
325
+ line_no -> int
514
326
 
515
- Returns the id of this method. */
516
- static VALUE
517
- prof_method_id(VALUE self)
327
+ returns the line number of the method */
328
+ static VALUE prof_method_line(VALUE self)
518
329
  {
519
- prof_method_t *result = get_prof_method(self);
520
- return ID2SYM(result->key->mid);
330
+ prof_method_t* method = prof_get_method(self);
331
+ return INT2FIX(method->source_line);
521
332
  }
522
333
 
523
334
  /* call-seq:
@@ -526,105 +337,134 @@ prof_method_id(VALUE self)
526
337
  Returns the name of this method's class. Singleton classes
527
338
  will have the form <Object::Object>. */
528
339
 
529
- static VALUE
530
- prof_klass_name(VALUE self)
340
+ static VALUE prof_method_klass_name(VALUE self)
531
341
  {
532
- prof_method_t *method = get_prof_method(self);
533
- return klass_name(method->key->klass);
342
+ prof_method_t* method = prof_get_method(self);
343
+ if (method->klass_name == Qnil)
344
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
345
+
346
+ return method->klass_name;
534
347
  }
535
348
 
536
349
  /* call-seq:
537
- method_name -> string
350
+ klass_flags -> integer
538
351
 
539
- Returns the name of this method in the format Object#method. Singletons
540
- methods will be returned in the format <Object::Object>#method.*/
352
+ Returns the klass flags */
541
353
 
542
- static VALUE
543
- prof_method_name(VALUE self)
354
+ static VALUE prof_method_klass_flags(VALUE self)
544
355
  {
545
- prof_method_t *method = get_prof_method(self);
546
- return method_name(method->key->mid);
356
+ prof_method_t* method = prof_get_method(self);
357
+ return INT2FIX(method->klass_flags);
547
358
  }
548
359
 
549
360
  /* call-seq:
550
- full_name -> string
361
+ method_name -> string
551
362
 
552
- Returns the full name of this method in the format Object#method.*/
363
+ Returns the name of this method in the format Object#method. Singletons
364
+ methods will be returned in the format <Object::Object>#method.*/
553
365
 
554
- static VALUE
555
- prof_full_name(VALUE self)
366
+ static VALUE prof_method_name(VALUE self)
556
367
  {
557
- prof_method_t *method = get_prof_method(self);
558
- return full_name(method->key->klass, method->key->mid);
368
+ prof_method_t* method = prof_get_method(self);
369
+ return method->method_name;
559
370
  }
560
371
 
561
372
  /* call-seq:
562
- call_infos -> Array of call_info
373
+ recursive? -> boolean
563
374
 
564
- Returns an array of call info objects that contain profiling information
565
- about the current method.*/
566
- static VALUE
567
- prof_method_call_infos(VALUE self)
375
+ Returns the true if this method is recursively invoked */
376
+ static VALUE prof_method_recursive(VALUE self)
568
377
  {
569
- prof_method_t *method = get_prof_method(self);
570
- if (method->call_infos->object == Qnil) {
571
- method->call_infos->object = prof_call_infos_wrap(method->call_infos);
572
- }
573
- return method->call_infos->object;
378
+ prof_method_t* method = prof_get_method(self);
379
+ return method->recursive ? Qtrue : Qfalse;
574
380
  }
575
381
 
576
382
  /* call-seq:
577
- recursive? -> boolean
383
+ call_trees -> CallTrees
578
384
 
579
- Returns the true if this method is recursive */
580
- static VALUE
581
- prof_method_recursive(VALUE self)
385
+ Returns the CallTrees associated with this method. */
386
+ static VALUE prof_method_call_trees(VALUE self)
582
387
  {
583
- prof_method_t *method = get_prof_method(self);
584
- return method->recursive ? Qtrue : Qfalse;
388
+ prof_method_t* method = prof_get_method(self);
389
+ return prof_call_trees_wrap(method->call_trees);
585
390
  }
586
391
 
587
- /* call-seq:
588
- source_klass -> klass
589
-
590
- Returns the Ruby klass of the natural source-level definition. */
591
- static VALUE
592
- prof_source_klass(VALUE self)
392
+ /* :nodoc: */
393
+ static VALUE prof_method_dump(VALUE self)
593
394
  {
594
- prof_method_t *method = get_prof_method(self);
595
- return resolve_source_klass(method);
596
- }
395
+ prof_method_t* method_data = DATA_PTR(self);
396
+ VALUE result = rb_hash_new();
597
397
 
598
- /* call-seq:
599
- calltree_name -> string
398
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
399
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
400
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
401
+
402
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
403
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
404
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
405
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
406
+
407
+ rb_hash_aset(result, ID2SYM(rb_intern("call_trees")), prof_call_trees_wrap(method_data->call_trees));
408
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
409
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
600
410
 
601
- Returns the full name of this method in the calltree format.*/
411
+ return result;
412
+ }
602
413
 
603
- static VALUE
604
- prof_calltree_name(VALUE self)
414
+ /* :nodoc: */
415
+ static VALUE prof_method_load(VALUE self, VALUE data)
605
416
  {
606
- prof_method_t *method = get_prof_method(self);
607
- volatile VALUE source_klass = resolve_source_klass(method);
608
- return calltree_name(source_klass, method->relation, method->key->mid);
417
+ prof_method_t* method_data = RDATA(self)->data;
418
+ method_data->object = self;
419
+
420
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
421
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
422
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
423
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
424
+
425
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
426
+
427
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
428
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
429
+
430
+ VALUE call_trees = rb_hash_aref(data, ID2SYM(rb_intern("call_trees")));
431
+ method_data->call_trees = prof_get_call_trees(call_trees);
432
+
433
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
434
+ method_data->measurement = prof_get_measurement(measurement);
435
+
436
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
437
+ for (int i = 0; i < rb_array_len(allocations); i++)
438
+ {
439
+ VALUE allocation = rb_ary_entry(allocations, i);
440
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
441
+
442
+ rb_st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
443
+ }
444
+ return data;
609
445
  }
610
446
 
611
447
  void rp_init_method_info()
612
448
  {
613
449
  /* MethodInfo */
614
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
615
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
450
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
451
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
452
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
616
453
 
617
- rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
618
- rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
619
- rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
620
- rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
621
- rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
454
+ rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
455
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
456
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
622
457
 
623
- rb_define_method(cMethodInfo, "call_infos", prof_method_call_infos, 0);
624
- rb_define_method(cMethodInfo, "source_klass", prof_source_klass, 0);
625
- rb_define_method(cMethodInfo, "source_file", prof_method_source_file, 0);
626
- rb_define_method(cMethodInfo, "line", prof_method_line, 0);
458
+ rb_define_method(cRpMethodInfo, "call_trees", prof_method_call_trees, 0);
627
459
 
628
- rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
629
- rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
630
- }
460
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
461
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
462
+
463
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
464
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
465
+
466
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
467
+
468
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
469
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
470
+ }