ruby-prof 0.18.0 → 1.0.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +23 -0
  3. data/LICENSE +2 -2
  4. data/README.rdoc +1 -483
  5. data/Rakefile +3 -6
  6. data/bin/ruby-prof +65 -30
  7. data/ext/ruby_prof/extconf.rb +6 -38
  8. data/ext/ruby_prof/rp_allocation.c +292 -0
  9. data/ext/ruby_prof/rp_allocation.h +31 -0
  10. data/ext/ruby_prof/rp_call_info.c +137 -279
  11. data/ext/ruby_prof/rp_call_info.h +16 -34
  12. data/ext/ruby_prof/rp_measure_allocations.c +25 -49
  13. data/ext/ruby_prof/rp_measure_memory.c +21 -56
  14. data/ext/ruby_prof/rp_measure_process_time.c +28 -36
  15. data/ext/ruby_prof/rp_measure_wall_time.c +36 -19
  16. data/ext/ruby_prof/rp_measurement.c +236 -0
  17. data/ext/ruby_prof/rp_measurement.h +49 -0
  18. data/ext/ruby_prof/rp_method.c +395 -383
  19. data/ext/ruby_prof/rp_method.h +34 -39
  20. data/ext/ruby_prof/rp_profile.c +881 -0
  21. data/ext/ruby_prof/rp_profile.h +36 -0
  22. data/ext/ruby_prof/rp_stack.c +103 -80
  23. data/ext/ruby_prof/rp_stack.h +5 -12
  24. data/ext/ruby_prof/rp_thread.c +149 -88
  25. data/ext/ruby_prof/rp_thread.h +15 -6
  26. data/ext/ruby_prof/ruby_prof.c +11 -757
  27. data/ext/ruby_prof/ruby_prof.h +4 -47
  28. data/ext/ruby_prof/vc/ruby_prof.vcxproj +10 -8
  29. data/lib/ruby-prof.rb +2 -17
  30. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  31. data/lib/ruby-prof/call_info.rb +35 -93
  32. data/lib/ruby-prof/call_info_visitor.rb +19 -21
  33. data/lib/ruby-prof/compatibility.rb +37 -107
  34. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  35. data/lib/ruby-prof/measurement.rb +14 -0
  36. data/lib/ruby-prof/method_info.rb +52 -83
  37. data/lib/ruby-prof/printers/abstract_printer.rb +66 -52
  38. data/lib/ruby-prof/printers/call_info_printer.rb +13 -3
  39. data/lib/ruby-prof/printers/call_stack_printer.rb +32 -28
  40. data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
  41. data/lib/ruby-prof/printers/dot_printer.rb +5 -5
  42. data/lib/ruby-prof/printers/flat_printer.rb +6 -24
  43. data/lib/ruby-prof/printers/graph_html_printer.rb +7 -192
  44. data/lib/ruby-prof/printers/graph_printer.rb +13 -15
  45. data/lib/ruby-prof/printers/multi_printer.rb +66 -23
  46. data/lib/ruby-prof/profile.rb +10 -3
  47. data/lib/ruby-prof/rack.rb +0 -3
  48. data/lib/ruby-prof/thread.rb +12 -12
  49. data/lib/ruby-prof/version.rb +1 -1
  50. data/ruby-prof.gemspec +2 -2
  51. data/test/abstract_printer_test.rb +0 -27
  52. data/test/alias_test.rb +129 -0
  53. data/test/basic_test.rb +41 -40
  54. data/test/call_info_visitor_test.rb +3 -3
  55. data/test/dynamic_method_test.rb +0 -2
  56. data/test/line_number_test.rb +120 -39
  57. data/test/marshal_test.rb +119 -0
  58. data/test/measure_allocations.rb +30 -0
  59. data/test/measure_allocations_test.rb +371 -12
  60. data/test/measure_allocations_trace_test.rb +385 -0
  61. data/test/measure_memory_trace_test.rb +756 -0
  62. data/test/measure_process_time_test.rb +821 -33
  63. data/test/measure_times.rb +54 -0
  64. data/test/measure_wall_time_test.rb +349 -145
  65. data/test/multi_printer_test.rb +1 -34
  66. data/test/parser_timings.rb +24 -0
  67. data/test/pause_resume_test.rb +5 -5
  68. data/test/prime.rb +2 -0
  69. data/test/printer_call_tree_test.rb +31 -0
  70. data/test/printer_flat_test.rb +68 -0
  71. data/test/printer_graph_html_test.rb +60 -0
  72. data/test/printer_graph_test.rb +41 -0
  73. data/test/printers_test.rb +32 -166
  74. data/test/printing_recursive_graph_test.rb +26 -72
  75. data/test/recursive_test.rb +72 -77
  76. data/test/stack_printer_test.rb +2 -15
  77. data/test/start_stop_test.rb +22 -25
  78. data/test/test_helper.rb +5 -248
  79. data/test/thread_test.rb +11 -54
  80. data/test/unique_call_path_test.rb +16 -28
  81. data/test/yarv_test.rb +1 -0
  82. metadata +24 -34
  83. data/examples/flat.txt +0 -50
  84. data/examples/graph.dot +0 -84
  85. data/examples/graph.html +0 -823
  86. data/examples/graph.txt +0 -139
  87. data/examples/multi.flat.txt +0 -23
  88. data/examples/multi.graph.html +0 -760
  89. data/examples/multi.grind.dat +0 -114
  90. data/examples/multi.stack.html +0 -547
  91. data/examples/stack.html +0 -547
  92. data/ext/ruby_prof/rp_measure.c +0 -40
  93. data/ext/ruby_prof/rp_measure.h +0 -45
  94. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  95. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  96. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  97. data/lib/ruby-prof/aggregate_call_info.rb +0 -76
  98. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
  99. data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
  100. data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
  101. data/test/aggregate_test.rb +0 -136
  102. data/test/block_test.rb +0 -74
  103. data/test/call_info_test.rb +0 -78
  104. data/test/fiber_test.rb +0 -79
  105. data/test/issue137_test.rb +0 -63
  106. data/test/measure_cpu_time_test.rb +0 -212
  107. data/test/measure_gc_runs_test.rb +0 -32
  108. data/test/measure_gc_time_test.rb +0 -36
  109. data/test/measure_memory_test.rb +0 -33
  110. data/test/method_elimination_test.rb +0 -84
  111. data/test/module_test.rb +0 -45
  112. data/test/stack_test.rb +0 -138
@@ -0,0 +1,49 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __rp_measurementMENT_H__
5
+ #define __rp_measurementMENT_H__
6
+
7
+ #include "ruby_prof.h"
8
+
9
+ extern VALUE mMeasure;
10
+
11
+ typedef double (*get_measurement)(rb_trace_arg_t *trace_arg);
12
+
13
+ typedef enum
14
+ {
15
+ MEASURE_WALL_TIME,
16
+ MEASURE_PROCESS_TIME,
17
+ MEASURE_ALLOCATIONS,
18
+ MEASURE_MEMORY
19
+ } prof_measure_mode_t;
20
+
21
+ typedef struct
22
+ {
23
+ get_measurement measure;
24
+ prof_measure_mode_t mode;
25
+ double multiplier;
26
+ bool track_allocations;
27
+ } prof_measurer_t;
28
+
29
+ /* Callers and callee information for a method. */
30
+ typedef struct prof_measurement_t
31
+ {
32
+ double total_time;
33
+ double self_time;
34
+ double wait_time;
35
+ int called;
36
+ VALUE object;
37
+ } prof_measurement_t;
38
+
39
+ prof_measurer_t *prof_get_measurer(prof_measure_mode_t measure, bool track_allocations);
40
+ double prof_measure(prof_measurer_t *measurer, rb_trace_arg_t* trace_arg);
41
+
42
+ prof_measurement_t *prof_measurement_create(void);
43
+ VALUE prof_measurement_wrap(prof_measurement_t *measurement);
44
+ prof_measurement_t* prof_get_measurement(VALUE self);
45
+ void prof_measurement_mark(void *data);
46
+
47
+ void rp_init_measure(void);
48
+
49
+ #endif //__rp_measurementMENT_H__
@@ -1,250 +1,229 @@
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_info.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
12
+ resolve_klass(VALUE klass, unsigned int *klass_flags)
17
13
  {
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__");
14
+ VALUE result = klass;
25
15
 
26
- /* Is this a singleton class acting as a metaclass? */
27
- if (BUILTIN_TYPE(attached) == T_CLASS)
16
+ if (klass == 0 || klass == Qnil)
28
17
  {
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, ">");
18
+ result = Qnil;
33
19
  }
34
-
35
- /* Is this for singleton methods on a module? */
36
- else if (BUILTIN_TYPE(attached) == T_MODULE)
20
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
37
21
  {
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
- }
22
+ /* We have come across a singleton object. First
23
+ figure out what it is attached to.*/
24
+ VALUE attached = rb_iv_get(klass, "__attached__");
43
25
 
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, ">");
26
+ /* Is this a singleton class acting as a metaclass? */
27
+ if (BUILTIN_TYPE(attached) == T_CLASS)
28
+ {
29
+ *klass_flags |= kClassSingleton;
30
+ result = attached;
31
+ }
32
+ /* Is this for singleton methods on a module? */
33
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
34
+ {
35
+ *klass_flags |= kModuleSingleton;
36
+ result = attached;
37
+ }
38
+ /* Is this for singleton methods on an object? */
39
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
40
+ {
41
+ *klass_flags |= kObjectSingleton;
42
+ result = rb_class_superclass(klass);
43
+ }
44
+ /* Ok, this could be other things like an array made put onto
45
+ a singleton object (yeah, it happens, see the singleton
46
+ objects test case). */
47
+ else
48
+ {
49
+ *klass_flags |= kOtherSingleton;
50
+ result = klass;
51
+ }
55
52
  }
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
53
+ /* Is this an include for a module? If so get the actual
54
+ module class since we want to combine all profiling
55
+ results for that module. */
56
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
61
57
  {
62
- result = rb_any_to_s(klass);
58
+ unsigned int dummy;
59
+ *klass_flags |= kModuleIncludee;
60
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
63
61
  }
64
-
65
62
  return result;
66
63
  }
67
64
 
68
- static VALUE
69
- klass_name(VALUE klass)
65
+ VALUE
66
+ resolve_klass_name(VALUE klass, unsigned int* klass_flags)
70
67
  {
71
- volatile VALUE result = Qnil;
68
+ VALUE result = Qnil;
72
69
 
73
- if (klass == 0 || klass == Qnil)
70
+ if (klass == Qnil)
74
71
  {
75
72
  result = rb_str_new2("[global]");
76
73
  }
77
- else if (BUILTIN_TYPE(klass) == T_MODULE)
74
+ else if (*klass_flags & kOtherSingleton)
78
75
  {
79
- result = rb_class_name(klass);
80
- }
81
- else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
82
- {
83
- result = figure_singleton_name(klass);
84
- }
85
- else if (BUILTIN_TYPE(klass) == T_CLASS)
86
- {
87
- result = rb_class_name(klass);
76
+ result = rb_any_to_s(klass);
88
77
  }
89
78
  else
90
79
  {
91
- /* Should never happen. */
92
- result = rb_str_new2("[unknown]");
80
+ result = rb_class_name(klass);
93
81
  }
94
82
 
95
83
  return result;
96
84
  }
97
85
 
98
- static VALUE
99
- method_name(ID mid)
86
+ st_data_t
87
+ method_key(VALUE klass, VALUE msym)
100
88
  {
101
- volatile VALUE name = Qnil;
89
+ VALUE resolved_klass = klass;
102
90
 
103
- if (RTEST(mid)) {
104
- name = rb_id2str(mid);
105
- return rb_str_dup(name);
106
- } else {
107
- return rb_str_new2("[no method]");
91
+ /* Is this an include for a module? If so get the actual
92
+ module class since we want to combine all profiling
93
+ results for that module. */
94
+ if (klass == 0 || klass == Qnil)
95
+ {
96
+ resolved_klass = Qnil;
108
97
  }
98
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
99
+ {
100
+ resolved_klass = RBASIC(klass)->klass;
101
+ }
102
+
103
+ return (resolved_klass << 4) + (msym);
109
104
  }
110
105
 
111
- static VALUE
112
- full_name(VALUE klass, ID mid)
106
+ /* ====== Allocation Table ====== */
107
+ st_table*
108
+ allocations_table_create()
113
109
  {
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);
110
+ return st_init_numtable();
111
+ }
123
112
 
124
- return result;
113
+ static int
114
+ allocations_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
115
+ {
116
+ prof_allocation_free((prof_allocation_t*)value);
117
+ return ST_CONTINUE;
125
118
  }
126
119
 
127
- static VALUE
128
- source_klass_name(VALUE source_klass)
120
+ static int
121
+ prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
129
122
  {
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;
123
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
124
+ VALUE arr = (VALUE)result;
125
+ rb_ary_push(arr, prof_allocation_wrap(allocation));
126
+ return ST_CONTINUE;
141
127
  }
142
128
 
143
- static VALUE
144
- calltree_name(VALUE source_klass, int relation, ID mid)
129
+ static int
130
+ prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
145
131
  {
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;
132
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
133
+ prof_allocation_mark(allocation);
134
+ return ST_CONTINUE;
170
135
  }
171
136
 
172
137
  void
173
- method_key(prof_method_key_t* key, VALUE klass, ID mid)
138
+ allocations_table_free(st_table* table)
174
139
  {
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
- }
181
-
182
- key->klass = klass;
183
- key->mid = mid;
184
- key->key = (klass << 4) + (mid << 2);
140
+ st_foreach(table, allocations_table_free_iterator, 0);
141
+ st_free_table(table);
185
142
  }
186
143
 
187
144
  /* ================ prof_method_t =================*/
188
-
189
- prof_method_t*
190
- prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
145
+ static prof_method_t*
146
+ prof_get_method(VALUE self)
191
147
  {
192
- prof_method_t *result = ALLOC(prof_method_t);
148
+ /* Can't use Data_Get_Struct because that triggers the event hook
149
+ ending up in endless recursion. */
150
+ prof_method_t* result = DATA_PTR(self);
193
151
 
194
- result->key = ALLOC(prof_method_key_t);
195
- method_key(result->key, klass, mid);
152
+ if (!result)
153
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
196
154
 
197
- result->excluded = 0;
198
- result->recursive = 0;
155
+ return result;
156
+ }
199
157
 
200
- result->call_infos = prof_call_infos_create();
158
+ prof_method_t*
159
+ prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
160
+ {
161
+ prof_method_t *result = ALLOC(prof_method_t);
162
+ result->key = method_key(klass, msym);
163
+ result->klass_flags = 0;
164
+
165
+ /* Note we do not call resolve_klass_name now because that causes an object allocation that shows up
166
+ in the allocation results so we want to avoid it until after the profile run is complete. */
167
+ result->klass = resolve_klass(klass, &result->klass_flags);
168
+ result->klass_name = Qnil;
169
+ result->method_name = msym;
170
+ result->measurement = prof_measurement_create();
171
+
172
+ result->root = false;
173
+ result->excluded = false;
174
+
175
+ result->parent_call_infos = method_table_create();
176
+ result->child_call_infos = method_table_create();
177
+ result->allocations_table = allocations_table_create();
178
+
201
179
  result->visits = 0;
180
+ result->recursive = false;
202
181
 
203
182
  result->object = Qnil;
204
183
 
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;
218
-
219
- result->resolved = 0;
220
- result->relation = 0;
184
+ result->source_file = source_file;
185
+ result->source_line = source_line;
221
186
 
222
187
  return result;
223
188
  }
224
189
 
225
190
  prof_method_t*
226
- prof_method_create_excluded(VALUE klass, ID mid)
191
+ prof_method_create_excluded(VALUE klass, VALUE msym)
227
192
  {
228
- prof_method_t *result = ALLOC(prof_method_t);
229
-
230
- result->key = ALLOC(prof_method_key_t);
231
- method_key(result->key, klass, mid);
232
-
233
- /* Invisible with this flag set. */
193
+ prof_method_t* result = prof_method_create(klass, msym, Qnil, 0);
234
194
  result->excluded = 1;
235
- result->recursive = 0;
195
+ return result;
196
+ }
236
197
 
237
- result->call_infos = 0;
238
- result->visits = 0;
198
+ static int
199
+ prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
200
+ {
201
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
202
+ VALUE arr = (VALUE)result;
203
+ rb_ary_push(arr, prof_call_info_wrap(call_info));
204
+ return ST_CONTINUE;
205
+ }
239
206
 
240
- result->object = Qnil;
241
- result->source_klass = Qnil;
242
- result->line = 0;
207
+ static int
208
+ prof_method_mark_call_infos(st_data_t key, st_data_t value, st_data_t data)
209
+ {
210
+ prof_call_info_t* call_info = (prof_call_info_t*)value;
211
+ prof_call_info_mark(call_info);
212
+ return ST_CONTINUE;
213
+ }
243
214
 
244
- result->resolved = 0;
245
- result->relation = 0;
215
+ static int
216
+ call_infos_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
217
+ {
218
+ prof_call_info_free((prof_call_info_t*)value);
219
+ return ST_CONTINUE;
220
+ }
246
221
 
247
- return result;
222
+ void
223
+ call_info_table_free(st_table* table)
224
+ {
225
+ st_foreach(table, call_infos_free_iterator, 0);
226
+ st_free_table(table);
248
227
  }
249
228
 
250
229
  /* The underlying c structures are freed when the parent profile is freed.
@@ -254,9 +233,11 @@ prof_method_create_excluded(VALUE klass, ID mid)
254
233
  for the garbage collector so that if it does get called will nil
255
234
  out our Ruby object reference.*/
256
235
  static void
257
- prof_method_ruby_gc_free(prof_method_t* method)
236
+ prof_method_ruby_gc_free(void *data)
258
237
  {
259
- /* Has this thread object been accessed by Ruby? If
238
+ prof_method_t* method = (prof_method_t*)data;
239
+
240
+ /* Has this thread object been accessed by Ruby? If
260
241
  yes clean it up so to avoid a segmentation fault. */
261
242
  if (method->object != Qnil)
262
243
  {
@@ -265,169 +246,100 @@ prof_method_ruby_gc_free(prof_method_t* method)
265
246
  RDATA(method->object)->dmark = NULL;
266
247
  }
267
248
  method->object = Qnil;
249
+ method->klass_name = Qnil;
250
+ method->method_name = Qnil;
268
251
  }
269
252
 
270
253
  static void
271
254
  prof_method_free(prof_method_t* method)
272
255
  {
273
256
  prof_method_ruby_gc_free(method);
257
+ allocations_table_free(method->allocations_table);
274
258
 
275
- if (method->call_infos) {
276
- prof_call_infos_free(method->call_infos);
277
- xfree(method->call_infos);
278
- }
279
-
280
- xfree(method->key);
259
+ /* Remember call infos are referenced by their parent method and child method, so we only want
260
+ to iterate over one of them to avoid a double freeing */
261
+ call_info_table_free(method->parent_call_infos);
262
+ xfree(method->child_call_infos);
281
263
 
282
- method->key = NULL;
283
- method->source_klass = Qnil;
264
+ xfree(method);
265
+ }
284
266
 
285
- xfree(method);
267
+ size_t
268
+ prof_method_size(const void *data)
269
+ {
270
+ return sizeof(prof_method_t);
286
271
  }
287
272
 
288
273
  void
289
- prof_method_mark(prof_method_t *method)
274
+ prof_method_mark(void *data)
290
275
  {
291
- if (method->key->klass) {
292
- rb_gc_mark(method->key->klass);
293
- }
276
+ prof_method_t* method = (prof_method_t*)data;
277
+ rb_gc_mark(method->klass_name);
278
+ rb_gc_mark(method->method_name);
279
+
280
+ if (method->klass != Qnil)
281
+ rb_gc_mark(method->klass);
294
282
 
295
- if (method->source_klass) {
296
- rb_gc_mark(method->source_klass);
297
- }
298
-
299
- if (method->object) {
283
+ if (method->object != Qnil)
300
284
  rb_gc_mark(method->object);
301
- }
302
285
 
303
- if (method->call_infos) {
304
- prof_call_infos_mark(method->call_infos);
305
- }
286
+ prof_measurement_mark(method->measurement);
287
+
288
+ st_foreach(method->parent_call_infos, prof_method_mark_call_infos, 0);
289
+ st_foreach(method->child_call_infos, prof_method_mark_call_infos, 0);
290
+ st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
306
291
  }
307
292
 
308
- static VALUE
309
- resolve_source_klass(prof_method_t* method)
293
+ static const rb_data_type_t method_info_type =
310
294
  {
311
- volatile VALUE klass, next_klass;
312
- volatile VALUE attached;
313
- unsigned int relation;
314
-
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. */
319
-
320
- if (method->resolved) {
321
- return method->source_klass;
322
- }
323
-
324
- klass = method->key->klass;
325
- relation = 0;
326
-
327
- while (1)
295
+ .wrap_struct_name = "MethodInfo",
296
+ .function =
328
297
  {
329
- /* This is a global/unknown class */
330
- if (klass == 0 || klass == Qnil)
331
- break;
332
-
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;
298
+ .dmark = prof_method_mark,
299
+ .dfree = prof_method_ruby_gc_free,
300
+ .dsize = prof_method_size,
301
+ },
302
+ .data = NULL,
303
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
304
+ };
382
305
 
383
- return klass;
306
+ static VALUE
307
+ prof_method_allocate(VALUE klass)
308
+ {
309
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
310
+ method_data->object = prof_method_wrap(method_data);
311
+ return method_data->object;
384
312
  }
385
313
 
386
314
  VALUE
387
- prof_method_wrap(prof_method_t *result)
315
+ prof_method_wrap(prof_method_t *method)
388
316
  {
389
- if (result->object == Qnil) {
390
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
317
+ if (method->object == Qnil)
318
+ {
319
+ method->object = TypedData_Wrap_Struct(cRpMethodInfo, &method_info_type, method);
391
320
  }
392
- return result->object;
321
+ return method->object;
393
322
  }
394
323
 
395
- static prof_method_t *
396
- get_prof_method(VALUE self)
324
+ prof_method_t *
325
+ prof_method_get(VALUE self)
397
326
  {
398
327
  /* Can't use Data_Get_Struct because that triggers the event hook
399
328
  ending up in endless recursion. */
400
329
  prof_method_t* result = DATA_PTR(self);
401
330
 
402
- if (!result) {
331
+ if (!result)
332
+ {
403
333
  rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
404
334
  }
405
335
 
406
336
  return result;
407
337
  }
408
338
 
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;
420
- }
421
-
422
- struct st_hash_type type_method_hash = {
423
- method_table_cmp,
424
- method_table_hash
425
- };
426
-
427
339
  st_table *
428
340
  method_table_create()
429
341
  {
430
- return st_init_table(&type_method_hash);
342
+ return st_init_numtable();
431
343
  }
432
344
 
433
345
  static int
@@ -445,18 +357,21 @@ method_table_free(st_table *table)
445
357
  }
446
358
 
447
359
  size_t
448
- method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
360
+ method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
449
361
  {
450
- return st_insert(table, (st_data_t) key, (st_data_t) val);
362
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
451
363
  }
452
364
 
453
365
  prof_method_t *
454
- method_table_lookup(st_table *table, const prof_method_key_t* key)
366
+ method_table_lookup(st_table *table, st_data_t key)
455
367
  {
456
368
  st_data_t val;
457
- if (st_lookup(table, (st_data_t)key, &val)) {
369
+ if (st_lookup(table, (st_data_t)key, &val))
370
+ {
458
371
  return (prof_method_t *) val;
459
- } else {
372
+ }
373
+ else
374
+ {
460
375
  return NULL;
461
376
  }
462
377
  }
@@ -472,52 +387,75 @@ the RubyProf::Profile object.
472
387
  */
473
388
 
474
389
  /* call-seq:
475
- line_no -> int
390
+ callers -> array
476
391
 
477
- returns the line number of the method */
392
+ Returns an array of call info objects that called this method (ie, parents).*/
478
393
  static VALUE
479
- prof_method_line(VALUE self)
394
+ prof_method_callers(VALUE self)
480
395
  {
481
- int line = get_prof_method(self)->line;
482
- return rb_int_new(line);
396
+ prof_method_t* method = prof_get_method(self);
397
+ VALUE result = rb_ary_new();
398
+ st_foreach(method->parent_call_infos, prof_method_collect_call_infos, result);
399
+ return result;
483
400
  }
484
401
 
485
402
  /* call-seq:
486
- source_file => string
403
+ callees -> array
487
404
 
488
- return the source file of the method
489
- */
490
- static VALUE prof_method_source_file(VALUE self)
405
+ Returns an array of call info objects that this method called (ie, children).*/
406
+ static VALUE
407
+ prof_method_callees(VALUE self)
491
408
  {
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
- }
409
+ prof_method_t* method = prof_get_method(self);
410
+ VALUE result = rb_ary_new();
411
+ st_foreach(method->child_call_infos, prof_method_collect_call_infos, result);
412
+ return result;
498
413
  }
499
414
 
415
+ /* call-seq:
416
+ allocations -> array
417
+
418
+ Returns an array of allocation information.*/
419
+ static VALUE
420
+ prof_method_allocations(VALUE self)
421
+ {
422
+ prof_method_t* method = prof_get_method(self);
423
+ VALUE result = rb_ary_new();
424
+ st_foreach(method->allocations_table, prof_method_collect_allocations, result);
425
+ return result;
426
+ }
500
427
 
501
428
  /* call-seq:
502
- method_class -> klass
429
+ called -> Measurement
503
430
 
504
- Returns the Ruby klass that owns this method. */
431
+ Returns the measurement associated with this method. */
505
432
  static VALUE
506
- prof_method_klass(VALUE self)
433
+ prof_method_measurement(VALUE self)
434
+ {
435
+ prof_method_t* method = prof_get_method(self);
436
+ return prof_measurement_wrap(method->measurement);
437
+ }
438
+
439
+ /* call-seq:
440
+ source_file => string
441
+
442
+ return the source file of the method
443
+ */
444
+ static VALUE prof_method_source_file(VALUE self)
507
445
  {
508
- prof_method_t *result = get_prof_method(self);
509
- return result->key->klass;
446
+ prof_method_t* method = prof_method_get(self);
447
+ return method->source_file;
510
448
  }
511
449
 
512
450
  /* call-seq:
513
- method_id -> ID
451
+ line_no -> int
514
452
 
515
- Returns the id of this method. */
453
+ returns the line number of the method */
516
454
  static VALUE
517
- prof_method_id(VALUE self)
455
+ prof_method_line(VALUE self)
518
456
  {
519
- prof_method_t *result = get_prof_method(self);
520
- return ID2SYM(result->key->mid);
457
+ prof_method_t* method = prof_method_get(self);
458
+ return INT2FIX(method->source_line);
521
459
  }
522
460
 
523
461
  /* call-seq:
@@ -527,104 +465,178 @@ Returns the name of this method's class. Singleton classes
527
465
  will have the form <Object::Object>. */
528
466
 
529
467
  static VALUE
530
- prof_klass_name(VALUE self)
468
+ prof_method_klass_name(VALUE self)
531
469
  {
532
- prof_method_t *method = get_prof_method(self);
533
- return klass_name(method->key->klass);
470
+ prof_method_t *method = prof_method_get(self);
471
+ if (method->klass_name == Qnil)
472
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
473
+
474
+ return method->klass_name;
534
475
  }
535
476
 
536
477
  /* call-seq:
537
- method_name -> string
478
+ klass_flags -> integer
538
479
 
539
- Returns the name of this method in the format Object#method. Singletons
540
- methods will be returned in the format <Object::Object>#method.*/
480
+ Returns the klass flags */
541
481
 
542
482
  static VALUE
543
- prof_method_name(VALUE self)
483
+ prof_method_klass_flags(VALUE self)
544
484
  {
545
- prof_method_t *method = get_prof_method(self);
546
- return method_name(method->key->mid);
485
+ prof_method_t* method = prof_method_get(self);
486
+ return INT2FIX(method->klass_flags);
547
487
  }
548
488
 
549
489
  /* call-seq:
550
- full_name -> string
490
+ method_name -> string
551
491
 
552
- Returns the full name of this method in the format Object#method.*/
492
+ Returns the name of this method in the format Object#method. Singletons
493
+ methods will be returned in the format <Object::Object>#method.*/
553
494
 
554
495
  static VALUE
555
- prof_full_name(VALUE self)
496
+ prof_method_name(VALUE self)
556
497
  {
557
- prof_method_t *method = get_prof_method(self);
558
- return full_name(method->key->klass, method->key->mid);
498
+ prof_method_t *method = prof_method_get(self);
499
+ return method->method_name;
559
500
  }
560
501
 
561
502
  /* call-seq:
562
- call_infos -> Array of call_info
503
+ root? -> boolean
563
504
 
564
- Returns an array of call info objects that contain profiling information
565
- about the current method.*/
505
+ Returns the true if this method is at the top of the call stack */
566
506
  static VALUE
567
- prof_method_call_infos(VALUE self)
507
+ prof_method_root(VALUE self)
568
508
  {
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;
509
+ prof_method_t *method = prof_method_get(self);
510
+ return method->root ? Qtrue : Qfalse;
574
511
  }
575
512
 
576
513
  /* call-seq:
577
514
  recursive? -> boolean
578
515
 
579
- Returns the true if this method is recursive */
516
+ Returns the true if this method is recursively invoked */
580
517
  static VALUE
581
518
  prof_method_recursive(VALUE self)
582
519
  {
583
- prof_method_t *method = get_prof_method(self);
584
- return method->recursive ? Qtrue : Qfalse;
520
+ prof_method_t* method = prof_method_get(self);
521
+ return method->recursive ? Qtrue : Qfalse;
585
522
  }
586
523
 
587
524
  /* call-seq:
588
- source_klass -> klass
525
+ excluded? -> boolean
589
526
 
590
- Returns the Ruby klass of the natural source-level definition. */
527
+ Returns the true if this method was excluded */
591
528
  static VALUE
592
- prof_source_klass(VALUE self)
529
+ prof_method_excluded(VALUE self)
593
530
  {
594
- prof_method_t *method = get_prof_method(self);
595
- return resolve_source_klass(method);
531
+ prof_method_t* method = prof_method_get(self);
532
+ return method->excluded ? Qtrue : Qfalse;
596
533
  }
597
534
 
598
- /* call-seq:
599
- calltree_name -> string
535
+ /* :nodoc: */
536
+ static VALUE
537
+ prof_method_dump(VALUE self)
538
+ {
539
+ prof_method_t* method_data = DATA_PTR(self);
540
+ VALUE result = rb_hash_new();
541
+
542
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
543
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
544
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
600
545
 
601
- Returns the full name of this method in the calltree format.*/
546
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
547
+ rb_hash_aset(result, ID2SYM(rb_intern("root")), prof_method_root(self));
548
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
549
+ rb_hash_aset(result, ID2SYM(rb_intern("excluded")), prof_method_excluded(self));
550
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
551
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
602
552
 
553
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
554
+
555
+ rb_hash_aset(result, ID2SYM(rb_intern("callers")), prof_method_callers(self));
556
+ rb_hash_aset(result, ID2SYM(rb_intern("callees")), prof_method_callees(self));
557
+
558
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
559
+
560
+ return result;
561
+ }
562
+
563
+ /* :nodoc: */
603
564
  static VALUE
604
- prof_calltree_name(VALUE self)
565
+ prof_method_load(VALUE self, VALUE data)
605
566
  {
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);
567
+ prof_method_t* method_data = RDATA(self)->data;
568
+ method_data->object = self;
569
+
570
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
571
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
572
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
573
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
574
+
575
+ method_data->root = rb_hash_aref(data, ID2SYM(rb_intern("root"))) == Qtrue ? true : false;
576
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
577
+ method_data->excluded = rb_hash_aref(data, ID2SYM(rb_intern("excluded"))) == Qtrue ? true : false;
578
+
579
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
580
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
581
+
582
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
583
+ method_data->measurement = prof_get_measurement(measurement);
584
+
585
+ VALUE callers = rb_hash_aref(data, ID2SYM(rb_intern("callers")));
586
+ for (int i = 0; i < rb_array_len(callers); i++)
587
+ {
588
+ VALUE call_info = rb_ary_entry(callers, i);
589
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
590
+ st_data_t key = call_info_data->parent ? call_info_data->parent->key : method_key(Qnil, 0);
591
+ call_info_table_insert(method_data->parent_call_infos, key, call_info_data);
592
+ }
593
+
594
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("callees")));
595
+ for (int i = 0; i < rb_array_len(callees); i++)
596
+ {
597
+ VALUE call_info = rb_ary_entry(callees, i);
598
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
599
+
600
+ st_data_t key = call_info_data->method ? call_info_data->method->key : method_key(Qnil, 0);
601
+ call_info_table_insert(method_data->child_call_infos, key, call_info_data);
602
+ }
603
+
604
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
605
+ for (int i = 0; i < rb_array_len(allocations); i++)
606
+ {
607
+ VALUE allocation = rb_ary_entry(allocations, i);
608
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
609
+
610
+ st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
611
+ }
612
+ return data;
609
613
  }
610
614
 
611
615
  void rp_init_method_info()
612
616
  {
613
617
  /* MethodInfo */
614
- cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
615
- rb_undef_method(CLASS_OF(cMethodInfo), "new");
616
-
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);
622
-
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);
627
-
628
- rb_define_method(cMethodInfo, "recursive?", prof_method_recursive, 0);
629
- rb_define_method(cMethodInfo, "calltree_name", prof_calltree_name, 0);
618
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
619
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
620
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
621
+
622
+ rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
623
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
624
+
625
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
626
+
627
+ rb_define_method(cRpMethodInfo, "callers", prof_method_callers, 0);
628
+ rb_define_method(cRpMethodInfo, "callees", prof_method_callees, 0);
629
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
630
+
631
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
632
+
633
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
634
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
635
+
636
+ rb_define_method(cRpMethodInfo, "root?", prof_method_root, 0);
637
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
638
+ rb_define_method(cRpMethodInfo, "excluded?", prof_method_excluded, 0);
639
+
640
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
641
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
630
642
  }