ruby-prof 1.0.0

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