ruby-prof 0.18.0 → 1.2.0

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