ruby-prof 0.18.0 → 1.0.0

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