ruby-prof 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 (97) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +523 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +5 -0
  5. data/Rakefile +110 -0
  6. data/bin/ruby-prof +380 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +36 -0
  9. data/ext/ruby_prof/rp_allocation.c +292 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +283 -0
  12. data/ext/ruby_prof/rp_call_info.h +35 -0
  13. data/ext/ruby_prof/rp_measure_allocations.c +52 -0
  14. data/ext/ruby_prof/rp_measure_memory.c +42 -0
  15. data/ext/ruby_prof/rp_measure_process_time.c +63 -0
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
  17. data/ext/ruby_prof/rp_measurement.c +236 -0
  18. data/ext/ruby_prof/rp_measurement.h +49 -0
  19. data/ext/ruby_prof/rp_method.c +642 -0
  20. data/ext/ruby_prof/rp_method.h +70 -0
  21. data/ext/ruby_prof/rp_profile.c +881 -0
  22. data/ext/ruby_prof/rp_profile.h +36 -0
  23. data/ext/ruby_prof/rp_stack.c +196 -0
  24. data/ext/ruby_prof/rp_stack.h +56 -0
  25. data/ext/ruby_prof/rp_thread.c +338 -0
  26. data/ext/ruby_prof/rp_thread.h +36 -0
  27. data/ext/ruby_prof/ruby_prof.c +48 -0
  28. data/ext/ruby_prof/ruby_prof.h +17 -0
  29. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  30. data/ext/ruby_prof/vc/ruby_prof.vcxproj +143 -0
  31. data/lib/ruby-prof.rb +53 -0
  32. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  34. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  35. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  36. data/lib/ruby-prof/call_info.rb +57 -0
  37. data/lib/ruby-prof/call_info_visitor.rb +38 -0
  38. data/lib/ruby-prof/compatibility.rb +109 -0
  39. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  40. data/lib/ruby-prof/measurement.rb +14 -0
  41. data/lib/ruby-prof/method_info.rb +90 -0
  42. data/lib/ruby-prof/printers/abstract_printer.rb +118 -0
  43. data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
  44. data/lib/ruby-prof/printers/call_stack_printer.rb +269 -0
  45. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
  46. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  47. data/lib/ruby-prof/printers/flat_printer.rb +52 -0
  48. data/lib/ruby-prof/printers/graph_html_printer.rb +64 -0
  49. data/lib/ruby-prof/printers/graph_printer.rb +114 -0
  50. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  51. data/lib/ruby-prof/profile.rb +33 -0
  52. data/lib/ruby-prof/rack.rb +171 -0
  53. data/lib/ruby-prof/task.rb +147 -0
  54. data/lib/ruby-prof/thread.rb +35 -0
  55. data/lib/ruby-prof/version.rb +3 -0
  56. data/lib/unprof.rb +10 -0
  57. data/ruby-prof.gemspec +58 -0
  58. data/test/abstract_printer_test.rb +26 -0
  59. data/test/alias_test.rb +129 -0
  60. data/test/basic_test.rb +129 -0
  61. data/test/call_info_visitor_test.rb +31 -0
  62. data/test/duplicate_names_test.rb +32 -0
  63. data/test/dynamic_method_test.rb +53 -0
  64. data/test/enumerable_test.rb +21 -0
  65. data/test/exceptions_test.rb +24 -0
  66. data/test/exclude_methods_test.rb +146 -0
  67. data/test/exclude_threads_test.rb +53 -0
  68. data/test/line_number_test.rb +161 -0
  69. data/test/marshal_test.rb +119 -0
  70. data/test/measure_allocations.rb +30 -0
  71. data/test/measure_allocations_test.rb +385 -0
  72. data/test/measure_allocations_trace_test.rb +385 -0
  73. data/test/measure_memory_trace_test.rb +756 -0
  74. data/test/measure_process_time_test.rb +849 -0
  75. data/test/measure_times.rb +54 -0
  76. data/test/measure_wall_time_test.rb +459 -0
  77. data/test/multi_printer_test.rb +71 -0
  78. data/test/no_method_class_test.rb +15 -0
  79. data/test/parser_timings.rb +24 -0
  80. data/test/pause_resume_test.rb +166 -0
  81. data/test/prime.rb +56 -0
  82. data/test/printer_call_tree_test.rb +31 -0
  83. data/test/printer_flat_test.rb +68 -0
  84. data/test/printer_graph_html_test.rb +60 -0
  85. data/test/printer_graph_test.rb +41 -0
  86. data/test/printers_test.rb +141 -0
  87. data/test/printing_recursive_graph_test.rb +81 -0
  88. data/test/rack_test.rb +157 -0
  89. data/test/recursive_test.rb +210 -0
  90. data/test/singleton_test.rb +38 -0
  91. data/test/stack_printer_test.rb +64 -0
  92. data/test/start_stop_test.rb +109 -0
  93. data/test/test_helper.rb +24 -0
  94. data/test/thread_test.rb +144 -0
  95. data/test/unique_call_path_test.rb +190 -0
  96. data/test/yarv_test.rb +56 -0
  97. metadata +189 -0
@@ -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__
@@ -0,0 +1,642 @@
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
+ #include "rp_allocation.h"
5
+ #include "rp_call_info.h"
6
+ #include "rp_method.h"
7
+
8
+ VALUE cRpMethodInfo;
9
+
10
+ /* ================ Helper Functions =================*/
11
+ VALUE
12
+ resolve_klass(VALUE klass, unsigned int *klass_flags)
13
+ {
14
+ VALUE result = klass;
15
+
16
+ if (klass == 0 || klass == Qnil)
17
+ {
18
+ result = Qnil;
19
+ }
20
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
21
+ {
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__");
25
+
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
+ }
52
+ }
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)
57
+ {
58
+ unsigned int dummy;
59
+ *klass_flags |= kModuleIncludee;
60
+ result = resolve_klass(RBASIC(klass)->klass, &dummy);
61
+ }
62
+ return result;
63
+ }
64
+
65
+ VALUE
66
+ resolve_klass_name(VALUE klass, unsigned int* klass_flags)
67
+ {
68
+ VALUE result = Qnil;
69
+
70
+ if (klass == Qnil)
71
+ {
72
+ result = rb_str_new2("[global]");
73
+ }
74
+ else if (*klass_flags & kOtherSingleton)
75
+ {
76
+ result = rb_any_to_s(klass);
77
+ }
78
+ else
79
+ {
80
+ result = rb_class_name(klass);
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ st_data_t
87
+ method_key(VALUE klass, VALUE msym)
88
+ {
89
+ VALUE resolved_klass = klass;
90
+
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;
97
+ }
98
+ else if (BUILTIN_TYPE(klass) == T_ICLASS)
99
+ {
100
+ resolved_klass = RBASIC(klass)->klass;
101
+ }
102
+
103
+ return (resolved_klass << 4) + (msym);
104
+ }
105
+
106
+ /* ====== Allocation Table ====== */
107
+ st_table*
108
+ allocations_table_create()
109
+ {
110
+ return st_init_numtable();
111
+ }
112
+
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;
118
+ }
119
+
120
+ static int
121
+ prof_method_collect_allocations(st_data_t key, st_data_t value, st_data_t result)
122
+ {
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;
127
+ }
128
+
129
+ static int
130
+ prof_method_mark_allocations(st_data_t key, st_data_t value, st_data_t data)
131
+ {
132
+ prof_allocation_t* allocation = (prof_allocation_t*)value;
133
+ prof_allocation_mark(allocation);
134
+ return ST_CONTINUE;
135
+ }
136
+
137
+ void
138
+ allocations_table_free(st_table* table)
139
+ {
140
+ st_foreach(table, allocations_table_free_iterator, 0);
141
+ st_free_table(table);
142
+ }
143
+
144
+ /* ================ prof_method_t =================*/
145
+ static prof_method_t*
146
+ prof_get_method(VALUE self)
147
+ {
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);
151
+
152
+ if (!result)
153
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
154
+
155
+ return result;
156
+ }
157
+
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
+
179
+ result->visits = 0;
180
+ result->recursive = false;
181
+
182
+ result->object = Qnil;
183
+
184
+ result->source_file = source_file;
185
+ result->source_line = source_line;
186
+
187
+ return result;
188
+ }
189
+
190
+ prof_method_t*
191
+ prof_method_create_excluded(VALUE klass, VALUE msym)
192
+ {
193
+ prof_method_t* result = prof_method_create(klass, msym, Qnil, 0);
194
+ result->excluded = 1;
195
+ return result;
196
+ }
197
+
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
+ }
206
+
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
+ }
214
+
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
+ }
221
+
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);
227
+ }
228
+
229
+ /* The underlying c structures are freed when the parent profile is freed.
230
+ However, on shutdown the Ruby GC frees objects in any will-nilly order.
231
+ That means the ruby thread object wrapping the c thread struct may
232
+ be freed before the parent profile. Thus we add in a free function
233
+ for the garbage collector so that if it does get called will nil
234
+ out our Ruby object reference.*/
235
+ static void
236
+ prof_method_ruby_gc_free(void *data)
237
+ {
238
+ prof_method_t* method = (prof_method_t*)data;
239
+
240
+ /* Has this thread object been accessed by Ruby? If
241
+ yes clean it up so to avoid a segmentation fault. */
242
+ if (method->object != Qnil)
243
+ {
244
+ RDATA(method->object)->data = NULL;
245
+ RDATA(method->object)->dfree = NULL;
246
+ RDATA(method->object)->dmark = NULL;
247
+ }
248
+ method->object = Qnil;
249
+ method->klass_name = Qnil;
250
+ method->method_name = Qnil;
251
+ }
252
+
253
+ static void
254
+ prof_method_free(prof_method_t* method)
255
+ {
256
+ prof_method_ruby_gc_free(method);
257
+ allocations_table_free(method->allocations_table);
258
+
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);
263
+
264
+ xfree(method);
265
+ }
266
+
267
+ size_t
268
+ prof_method_size(const void *data)
269
+ {
270
+ return sizeof(prof_method_t);
271
+ }
272
+
273
+ void
274
+ prof_method_mark(void *data)
275
+ {
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);
282
+
283
+ if (method->object != Qnil)
284
+ rb_gc_mark(method->object);
285
+
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);
291
+ }
292
+
293
+ static const rb_data_type_t method_info_type =
294
+ {
295
+ .wrap_struct_name = "MethodInfo",
296
+ .function =
297
+ {
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
+ };
305
+
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;
312
+ }
313
+
314
+ VALUE
315
+ prof_method_wrap(prof_method_t *method)
316
+ {
317
+ if (method->object == Qnil)
318
+ {
319
+ method->object = TypedData_Wrap_Struct(cRpMethodInfo, &method_info_type, method);
320
+ }
321
+ return method->object;
322
+ }
323
+
324
+ prof_method_t *
325
+ prof_method_get(VALUE self)
326
+ {
327
+ /* Can't use Data_Get_Struct because that triggers the event hook
328
+ ending up in endless recursion. */
329
+ prof_method_t* result = DATA_PTR(self);
330
+
331
+ if (!result)
332
+ {
333
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
334
+ }
335
+
336
+ return result;
337
+ }
338
+
339
+ st_table *
340
+ method_table_create()
341
+ {
342
+ return st_init_numtable();
343
+ }
344
+
345
+ static int
346
+ method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
347
+ {
348
+ prof_method_free((prof_method_t *)value);
349
+ return ST_CONTINUE;
350
+ }
351
+
352
+ void
353
+ method_table_free(st_table *table)
354
+ {
355
+ st_foreach(table, method_table_free_iterator, 0);
356
+ st_free_table(table);
357
+ }
358
+
359
+ size_t
360
+ method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
361
+ {
362
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
363
+ }
364
+
365
+ prof_method_t *
366
+ method_table_lookup(st_table *table, st_data_t key)
367
+ {
368
+ st_data_t val;
369
+ if (st_lookup(table, (st_data_t)key, &val))
370
+ {
371
+ return (prof_method_t *) val;
372
+ }
373
+ else
374
+ {
375
+ return NULL;
376
+ }
377
+ }
378
+
379
+ /* ================ Method Info =================*/
380
+ /* Document-class: RubyProf::MethodInfo
381
+ The RubyProf::MethodInfo class stores profiling data for a method.
382
+ One instance of the RubyProf::MethodInfo class is created per method
383
+ called per thread. Thus, if a method is called in two different
384
+ thread then there will be two RubyProf::MethodInfo objects
385
+ created. RubyProf::MethodInfo objects can be accessed via
386
+ the RubyProf::Profile object.
387
+ */
388
+
389
+ /* call-seq:
390
+ callers -> array
391
+
392
+ Returns an array of call info objects that called this method (ie, parents).*/
393
+ static VALUE
394
+ prof_method_callers(VALUE self)
395
+ {
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;
400
+ }
401
+
402
+ /* call-seq:
403
+ callees -> array
404
+
405
+ Returns an array of call info objects that this method called (ie, children).*/
406
+ static VALUE
407
+ prof_method_callees(VALUE self)
408
+ {
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;
413
+ }
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
+ }
427
+
428
+ /* call-seq:
429
+ called -> Measurement
430
+
431
+ Returns the measurement associated with this method. */
432
+ static VALUE
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)
445
+ {
446
+ prof_method_t* method = prof_method_get(self);
447
+ return method->source_file;
448
+ }
449
+
450
+ /* call-seq:
451
+ line_no -> int
452
+
453
+ returns the line number of the method */
454
+ static VALUE
455
+ prof_method_line(VALUE self)
456
+ {
457
+ prof_method_t* method = prof_method_get(self);
458
+ return INT2FIX(method->source_line);
459
+ }
460
+
461
+ /* call-seq:
462
+ klass_name -> string
463
+
464
+ Returns the name of this method's class. Singleton classes
465
+ will have the form <Object::Object>. */
466
+
467
+ static VALUE
468
+ prof_method_klass_name(VALUE self)
469
+ {
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;
475
+ }
476
+
477
+ /* call-seq:
478
+ klass_flags -> integer
479
+
480
+ Returns the klass flags */
481
+
482
+ static VALUE
483
+ prof_method_klass_flags(VALUE self)
484
+ {
485
+ prof_method_t* method = prof_method_get(self);
486
+ return INT2FIX(method->klass_flags);
487
+ }
488
+
489
+ /* call-seq:
490
+ method_name -> string
491
+
492
+ Returns the name of this method in the format Object#method. Singletons
493
+ methods will be returned in the format <Object::Object>#method.*/
494
+
495
+ static VALUE
496
+ prof_method_name(VALUE self)
497
+ {
498
+ prof_method_t *method = prof_method_get(self);
499
+ return method->method_name;
500
+ }
501
+
502
+ /* call-seq:
503
+ root? -> boolean
504
+
505
+ Returns the true if this method is at the top of the call stack */
506
+ static VALUE
507
+ prof_method_root(VALUE self)
508
+ {
509
+ prof_method_t *method = prof_method_get(self);
510
+ return method->root ? Qtrue : Qfalse;
511
+ }
512
+
513
+ /* call-seq:
514
+ recursive? -> boolean
515
+
516
+ Returns the true if this method is recursively invoked */
517
+ static VALUE
518
+ prof_method_recursive(VALUE self)
519
+ {
520
+ prof_method_t* method = prof_method_get(self);
521
+ return method->recursive ? Qtrue : Qfalse;
522
+ }
523
+
524
+ /* call-seq:
525
+ excluded? -> boolean
526
+
527
+ Returns the true if this method was excluded */
528
+ static VALUE
529
+ prof_method_excluded(VALUE self)
530
+ {
531
+ prof_method_t* method = prof_method_get(self);
532
+ return method->excluded ? Qtrue : Qfalse;
533
+ }
534
+
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);
545
+
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));
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: */
564
+ static VALUE
565
+ prof_method_load(VALUE self, VALUE data)
566
+ {
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;
613
+ }
614
+
615
+ void rp_init_method_info()
616
+ {
617
+ /* MethodInfo */
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);
642
+ }