ruby-prof 1.1.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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +532 -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 +279 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +271 -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 +67 -0
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
  17. data/ext/ruby_prof/rp_measurement.c +230 -0
  18. data/ext/ruby_prof/rp_measurement.h +50 -0
  19. data/ext/ruby_prof/rp_method.c +630 -0
  20. data/ext/ruby_prof/rp_method.h +70 -0
  21. data/ext/ruby_prof/rp_profile.c +895 -0
  22. data/ext/ruby_prof/rp_profile.h +37 -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 +337 -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 +52 -0
  32. data/lib/ruby-prof/assets/call_stack_printer.html.erb +713 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  34. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  35. data/lib/ruby-prof/call_info.rb +57 -0
  36. data/lib/ruby-prof/call_info_visitor.rb +38 -0
  37. data/lib/ruby-prof/compatibility.rb +109 -0
  38. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  39. data/lib/ruby-prof/measurement.rb +14 -0
  40. data/lib/ruby-prof/method_info.rb +90 -0
  41. data/lib/ruby-prof/printers/abstract_printer.rb +127 -0
  42. data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
  43. data/lib/ruby-prof/printers/call_stack_printer.rb +182 -0
  44. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
  45. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  46. data/lib/ruby-prof/printers/flat_printer.rb +52 -0
  47. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -0
  48. data/lib/ruby-prof/printers/graph_printer.rb +114 -0
  49. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  50. data/lib/ruby-prof/profile.rb +33 -0
  51. data/lib/ruby-prof/rack.rb +171 -0
  52. data/lib/ruby-prof/task.rb +147 -0
  53. data/lib/ruby-prof/thread.rb +35 -0
  54. data/lib/ruby-prof/version.rb +3 -0
  55. data/lib/unprof.rb +10 -0
  56. data/ruby-prof.gemspec +58 -0
  57. data/test/abstract_printer_test.rb +26 -0
  58. data/test/alias_test.rb +129 -0
  59. data/test/basic_test.rb +129 -0
  60. data/test/call_info_visitor_test.rb +31 -0
  61. data/test/duplicate_names_test.rb +32 -0
  62. data/test/dynamic_method_test.rb +53 -0
  63. data/test/enumerable_test.rb +21 -0
  64. data/test/exceptions_test.rb +24 -0
  65. data/test/exclude_methods_test.rb +146 -0
  66. data/test/exclude_threads_test.rb +53 -0
  67. data/test/fiber_test.rb +73 -0
  68. data/test/gc_test.rb +96 -0
  69. data/test/line_number_test.rb +161 -0
  70. data/test/marshal_test.rb +119 -0
  71. data/test/measure_allocations.rb +30 -0
  72. data/test/measure_allocations_test.rb +385 -0
  73. data/test/measure_allocations_trace_test.rb +385 -0
  74. data/test/measure_memory_trace_test.rb +756 -0
  75. data/test/measure_process_time_test.rb +849 -0
  76. data/test/measure_times.rb +54 -0
  77. data/test/measure_wall_time_test.rb +459 -0
  78. data/test/multi_printer_test.rb +71 -0
  79. data/test/no_method_class_test.rb +15 -0
  80. data/test/parser_timings.rb +24 -0
  81. data/test/pause_resume_test.rb +166 -0
  82. data/test/prime.rb +56 -0
  83. data/test/printer_call_stack_test.rb +28 -0
  84. data/test/printer_call_tree_test.rb +31 -0
  85. data/test/printer_flat_test.rb +68 -0
  86. data/test/printer_graph_html_test.rb +60 -0
  87. data/test/printer_graph_test.rb +41 -0
  88. data/test/printers_test.rb +141 -0
  89. data/test/printing_recursive_graph_test.rb +81 -0
  90. data/test/rack_test.rb +157 -0
  91. data/test/recursive_test.rb +210 -0
  92. data/test/singleton_test.rb +38 -0
  93. data/test/stack_printer_test.rb +64 -0
  94. data/test/start_stop_test.rb +109 -0
  95. data/test/test_helper.rb +24 -0
  96. data/test/thread_test.rb +144 -0
  97. data/test/unique_call_path_test.rb +190 -0
  98. data/test/yarv_test.rb +56 -0
  99. metadata +191 -0
@@ -0,0 +1,50 @@
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
+ void prof_measurement_free(prof_measurement_t* measurement);
44
+ VALUE prof_measurement_wrap(prof_measurement_t *measurement);
45
+ prof_measurement_t* prof_get_measurement(VALUE self);
46
+ void prof_measurement_mark(void *data);
47
+
48
+ void rp_init_measure(void);
49
+
50
+ #endif //__rp_measurementMENT_H__
@@ -0,0 +1,630 @@
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 method 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)->dmark = NULL;
245
+ RDATA(method->object)->dfree = NULL;
246
+ RDATA(method->object)->data = NULL;
247
+ method->object = Qnil;
248
+ }
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
+ prof_measurement_free(method->measurement);
265
+ xfree(method);
266
+ }
267
+
268
+ size_t
269
+ prof_method_size(const void *data)
270
+ {
271
+ return sizeof(prof_method_t);
272
+ }
273
+
274
+ void
275
+ prof_method_mark(void *data)
276
+ {
277
+ prof_method_t* method = (prof_method_t*)data;
278
+ rb_gc_mark(method->klass_name);
279
+ rb_gc_mark(method->method_name);
280
+
281
+ if (method->klass != Qnil)
282
+ rb_gc_mark(method->klass);
283
+
284
+ if (method->object != Qnil)
285
+ rb_gc_mark(method->object);
286
+
287
+ prof_measurement_mark(method->measurement);
288
+
289
+ st_foreach(method->parent_call_infos, prof_method_mark_call_infos, 0);
290
+ st_foreach(method->child_call_infos, prof_method_mark_call_infos, 0);
291
+ st_foreach(method->allocations_table, prof_method_mark_allocations, 0);
292
+ }
293
+
294
+ static VALUE
295
+ prof_method_allocate(VALUE klass)
296
+ {
297
+ prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
298
+ method_data->object = prof_method_wrap(method_data);
299
+ return method_data->object;
300
+ }
301
+
302
+ VALUE
303
+ prof_method_wrap(prof_method_t *method)
304
+ {
305
+ if (method->object == Qnil)
306
+ {
307
+ method->object = Data_Wrap_Struct(cRpMethodInfo, prof_method_mark, prof_method_ruby_gc_free, method);
308
+ }
309
+ return method->object;
310
+ }
311
+
312
+ prof_method_t *
313
+ prof_method_get(VALUE self)
314
+ {
315
+ /* Can't use Data_Get_Struct because that triggers the event hook
316
+ ending up in endless recursion. */
317
+ prof_method_t* result = DATA_PTR(self);
318
+
319
+ if (!result)
320
+ {
321
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
322
+ }
323
+
324
+ return result;
325
+ }
326
+
327
+ st_table *
328
+ method_table_create()
329
+ {
330
+ return st_init_numtable();
331
+ }
332
+
333
+ static int
334
+ method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
335
+ {
336
+ prof_method_free((prof_method_t *)value);
337
+ return ST_CONTINUE;
338
+ }
339
+
340
+ void
341
+ method_table_free(st_table *table)
342
+ {
343
+ st_foreach(table, method_table_free_iterator, 0);
344
+ st_free_table(table);
345
+ }
346
+
347
+ size_t
348
+ method_table_insert(st_table *table, st_data_t key, prof_method_t *val)
349
+ {
350
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
351
+ }
352
+
353
+ prof_method_t *
354
+ method_table_lookup(st_table *table, st_data_t key)
355
+ {
356
+ st_data_t val;
357
+ if (st_lookup(table, (st_data_t)key, &val))
358
+ {
359
+ return (prof_method_t *) val;
360
+ }
361
+ else
362
+ {
363
+ return NULL;
364
+ }
365
+ }
366
+
367
+ /* ================ Method Info =================*/
368
+ /* Document-class: RubyProf::MethodInfo
369
+ The RubyProf::MethodInfo class stores profiling data for a method.
370
+ One instance of the RubyProf::MethodInfo class is created per method
371
+ called per thread. Thus, if a method is called in two different
372
+ thread then there will be two RubyProf::MethodInfo objects
373
+ created. RubyProf::MethodInfo objects can be accessed via
374
+ the RubyProf::Profile object.
375
+ */
376
+
377
+ /* call-seq:
378
+ callers -> array
379
+
380
+ Returns an array of call info objects that called this method (ie, parents).*/
381
+ static VALUE
382
+ prof_method_callers(VALUE self)
383
+ {
384
+ prof_method_t* method = prof_get_method(self);
385
+ VALUE result = rb_ary_new();
386
+ st_foreach(method->parent_call_infos, prof_method_collect_call_infos, result);
387
+ return result;
388
+ }
389
+
390
+ /* call-seq:
391
+ callees -> array
392
+
393
+ Returns an array of call info objects that this method called (ie, children).*/
394
+ static VALUE
395
+ prof_method_callees(VALUE self)
396
+ {
397
+ prof_method_t* method = prof_get_method(self);
398
+ VALUE result = rb_ary_new();
399
+ st_foreach(method->child_call_infos, prof_method_collect_call_infos, result);
400
+ return result;
401
+ }
402
+
403
+ /* call-seq:
404
+ allocations -> array
405
+
406
+ Returns an array of allocation information.*/
407
+ static VALUE
408
+ prof_method_allocations(VALUE self)
409
+ {
410
+ prof_method_t* method = prof_get_method(self);
411
+ VALUE result = rb_ary_new();
412
+ st_foreach(method->allocations_table, prof_method_collect_allocations, result);
413
+ return result;
414
+ }
415
+
416
+ /* call-seq:
417
+ called -> Measurement
418
+
419
+ Returns the measurement associated with this method. */
420
+ static VALUE
421
+ prof_method_measurement(VALUE self)
422
+ {
423
+ prof_method_t* method = prof_get_method(self);
424
+ return prof_measurement_wrap(method->measurement);
425
+ }
426
+
427
+ /* call-seq:
428
+ source_file => string
429
+
430
+ return the source file of the method
431
+ */
432
+ static VALUE prof_method_source_file(VALUE self)
433
+ {
434
+ prof_method_t* method = prof_method_get(self);
435
+ return method->source_file;
436
+ }
437
+
438
+ /* call-seq:
439
+ line_no -> int
440
+
441
+ returns the line number of the method */
442
+ static VALUE
443
+ prof_method_line(VALUE self)
444
+ {
445
+ prof_method_t* method = prof_method_get(self);
446
+ return INT2FIX(method->source_line);
447
+ }
448
+
449
+ /* call-seq:
450
+ klass_name -> string
451
+
452
+ Returns the name of this method's class. Singleton classes
453
+ will have the form <Object::Object>. */
454
+
455
+ static VALUE
456
+ prof_method_klass_name(VALUE self)
457
+ {
458
+ prof_method_t *method = prof_method_get(self);
459
+ if (method->klass_name == Qnil)
460
+ method->klass_name = resolve_klass_name(method->klass, &method->klass_flags);
461
+
462
+ return method->klass_name;
463
+ }
464
+
465
+ /* call-seq:
466
+ klass_flags -> integer
467
+
468
+ Returns the klass flags */
469
+
470
+ static VALUE
471
+ prof_method_klass_flags(VALUE self)
472
+ {
473
+ prof_method_t* method = prof_method_get(self);
474
+ return INT2FIX(method->klass_flags);
475
+ }
476
+
477
+ /* call-seq:
478
+ method_name -> string
479
+
480
+ Returns the name of this method in the format Object#method. Singletons
481
+ methods will be returned in the format <Object::Object>#method.*/
482
+
483
+ static VALUE
484
+ prof_method_name(VALUE self)
485
+ {
486
+ prof_method_t *method = prof_method_get(self);
487
+ return method->method_name;
488
+ }
489
+
490
+ /* call-seq:
491
+ root? -> boolean
492
+
493
+ Returns the true if this method is at the top of the call stack */
494
+ static VALUE
495
+ prof_method_root(VALUE self)
496
+ {
497
+ prof_method_t *method = prof_method_get(self);
498
+ return method->root ? Qtrue : Qfalse;
499
+ }
500
+
501
+ /* call-seq:
502
+ recursive? -> boolean
503
+
504
+ Returns the true if this method is recursively invoked */
505
+ static VALUE
506
+ prof_method_recursive(VALUE self)
507
+ {
508
+ prof_method_t* method = prof_method_get(self);
509
+ return method->recursive ? Qtrue : Qfalse;
510
+ }
511
+
512
+ /* call-seq:
513
+ excluded? -> boolean
514
+
515
+ Returns the true if this method was excluded */
516
+ static VALUE
517
+ prof_method_excluded(VALUE self)
518
+ {
519
+ prof_method_t* method = prof_method_get(self);
520
+ return method->excluded ? Qtrue : Qfalse;
521
+ }
522
+
523
+ /* :nodoc: */
524
+ static VALUE
525
+ prof_method_dump(VALUE self)
526
+ {
527
+ prof_method_t* method_data = DATA_PTR(self);
528
+ VALUE result = rb_hash_new();
529
+
530
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
531
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(method_data->klass_flags));
532
+ rb_hash_aset(result, ID2SYM(rb_intern("method_name")), method_data->method_name);
533
+
534
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(method_data->key));
535
+ rb_hash_aset(result, ID2SYM(rb_intern("root")), prof_method_root(self));
536
+ rb_hash_aset(result, ID2SYM(rb_intern("recursive")), prof_method_recursive(self));
537
+ rb_hash_aset(result, ID2SYM(rb_intern("excluded")), prof_method_excluded(self));
538
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), method_data->source_file);
539
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(method_data->source_line));
540
+
541
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(method_data->measurement));
542
+
543
+ rb_hash_aset(result, ID2SYM(rb_intern("callers")), prof_method_callers(self));
544
+ rb_hash_aset(result, ID2SYM(rb_intern("callees")), prof_method_callees(self));
545
+
546
+ rb_hash_aset(result, ID2SYM(rb_intern("allocations")), prof_method_allocations(self));
547
+
548
+ return result;
549
+ }
550
+
551
+ /* :nodoc: */
552
+ static VALUE
553
+ prof_method_load(VALUE self, VALUE data)
554
+ {
555
+ prof_method_t* method_data = RDATA(self)->data;
556
+ method_data->object = self;
557
+
558
+ method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
559
+ method_data->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
560
+ method_data->method_name = rb_hash_aref(data, ID2SYM(rb_intern("method_name")));
561
+ method_data->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
562
+
563
+ method_data->root = rb_hash_aref(data, ID2SYM(rb_intern("root"))) == Qtrue ? true : false;
564
+ method_data->recursive = rb_hash_aref(data, ID2SYM(rb_intern("recursive"))) == Qtrue ? true : false;
565
+ method_data->excluded = rb_hash_aref(data, ID2SYM(rb_intern("excluded"))) == Qtrue ? true : false;
566
+
567
+ method_data->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
568
+ method_data->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
569
+
570
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
571
+ method_data->measurement = prof_get_measurement(measurement);
572
+
573
+ VALUE callers = rb_hash_aref(data, ID2SYM(rb_intern("callers")));
574
+ for (int i = 0; i < rb_array_len(callers); i++)
575
+ {
576
+ VALUE call_info = rb_ary_entry(callers, i);
577
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
578
+ st_data_t key = call_info_data->parent ? call_info_data->parent->key : method_key(Qnil, 0);
579
+ call_info_table_insert(method_data->parent_call_infos, key, call_info_data);
580
+ }
581
+
582
+ VALUE callees = rb_hash_aref(data, ID2SYM(rb_intern("callees")));
583
+ for (int i = 0; i < rb_array_len(callees); i++)
584
+ {
585
+ VALUE call_info = rb_ary_entry(callees, i);
586
+ prof_call_info_t *call_info_data = prof_get_call_info(call_info);
587
+
588
+ st_data_t key = call_info_data->method ? call_info_data->method->key : method_key(Qnil, 0);
589
+ call_info_table_insert(method_data->child_call_infos, key, call_info_data);
590
+ }
591
+
592
+ VALUE allocations = rb_hash_aref(data, ID2SYM(rb_intern("allocations")));
593
+ for (int i = 0; i < rb_array_len(allocations); i++)
594
+ {
595
+ VALUE allocation = rb_ary_entry(allocations, i);
596
+ prof_allocation_t* allocation_data = prof_allocation_get(allocation);
597
+
598
+ st_insert(method_data->allocations_table, allocation_data->key, (st_data_t)allocation_data);
599
+ }
600
+ return data;
601
+ }
602
+
603
+ void rp_init_method_info()
604
+ {
605
+ /* MethodInfo */
606
+ cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cData);
607
+ rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
608
+ rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
609
+
610
+ rb_define_method(cRpMethodInfo, "klass_name", prof_method_klass_name, 0);
611
+ rb_define_method(cRpMethodInfo, "klass_flags", prof_method_klass_flags, 0);
612
+
613
+ rb_define_method(cRpMethodInfo, "method_name", prof_method_name, 0);
614
+
615
+ rb_define_method(cRpMethodInfo, "callers", prof_method_callers, 0);
616
+ rb_define_method(cRpMethodInfo, "callees", prof_method_callees, 0);
617
+ rb_define_method(cRpMethodInfo, "allocations", prof_method_allocations, 0);
618
+
619
+ rb_define_method(cRpMethodInfo, "measurement", prof_method_measurement, 0);
620
+
621
+ rb_define_method(cRpMethodInfo, "source_file", prof_method_source_file, 0);
622
+ rb_define_method(cRpMethodInfo, "line", prof_method_line, 0);
623
+
624
+ rb_define_method(cRpMethodInfo, "root?", prof_method_root, 0);
625
+ rb_define_method(cRpMethodInfo, "recursive?", prof_method_recursive, 0);
626
+ rb_define_method(cRpMethodInfo, "excluded?", prof_method_excluded, 0);
627
+
628
+ rb_define_method(cRpMethodInfo, "_dump_data", prof_method_dump, 0);
629
+ rb_define_method(cRpMethodInfo, "_load_data", prof_method_load, 1);
630
+ }