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,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ stacks = Hash.new{|h,k| h[k] = Hash.new{|h,k| h[k] = []}}
4
+ i = 0
5
+ File.open(ARGV[0]).each_line do |l|
6
+ i += 1
7
+ unless l =~ /^(\d+):(\d+): *\d+ms *([^ ]+) *(.*): *(\d+) *(.+)$/
8
+ next if l =~/^ *$/
9
+ puts "line doesn't match: #{l}"
10
+ next
11
+ end
12
+ details = $1.to_i, $2.to_i, $3, $4, $5.to_i, $6
13
+ thread, fiber, event, file, line, method = *details
14
+ # puts method
15
+ stack = stacks[thread][fiber]
16
+ case event
17
+ when 'call', 'c-call'
18
+ stack << method
19
+ when 'return', 'c-return'
20
+ last_method = stack.pop
21
+ if last_method != method
22
+ puts "LINE #{i}: return event without call: #{method}"
23
+ puts "STACK: #{stack.inspect}"
24
+ if stack.find(method)
25
+ puts "fixing stack"
26
+ while (popped = stack.pop) && (popped != method)
27
+ puts "popped #{popped}"
28
+ end
29
+ else
30
+ raise "stack unfixable"
31
+ end
32
+ # stack << last_method
33
+ end
34
+ when 'line'
35
+ last_method = stack[-1]
36
+ if last_method != method
37
+ unless stack.find(method)
38
+ raise "LINE #{i}: line event without call: #{method}"
39
+ end
40
+ end
41
+ else
42
+ puts "unkown event"
43
+ end
44
+ end
45
+ puts stacks.inspect
@@ -0,0 +1,36 @@
1
+ require "mkmf"
2
+
3
+ if RUBY_ENGINE != "ruby"
4
+ STDERR.puts("\n\n***** This gem is MRI-specific. It does not support #{RUBY_ENGINE}. *****\n\n")
5
+ exit(1)
6
+ end
7
+
8
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
9
+ STDERR.puts("\n\n***** Ruby version #{RUBY_VERSION} is no longer supported. Please upgrade to 2.3 or higher. *****\n\n")
10
+ exit(1)
11
+ end
12
+
13
+ # For the love of bitfields...
14
+ $CFLAGS += ' -std=c99'
15
+
16
+ # And since we are using C99
17
+ CONFIG['warnflags'].gsub!('-Wdeclaration-after-statement', '')
18
+
19
+ def add_define(name, value = nil)
20
+ if value
21
+ $defs.push("-D#{name}=#{value}")
22
+ else
23
+ $defs.push("-D#{name}")
24
+ end
25
+ end
26
+
27
+ def windows?
28
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
29
+ end
30
+
31
+ add_define("RUBY_PROF_RUBY_VERSION", RUBY_VERSION.split('.')[0..2].inject(0){|v,d| v*100+d.to_i})
32
+
33
+ # This function was added in Ruby 2.5, so once Ruby 2.4 is no longer supported this can be removed
34
+ have_func('rb_tracearg_callee_id', ["ruby.h"])
35
+
36
+ create_makefile("ruby_prof")
@@ -0,0 +1,292 @@
1
+ /* Copyright (C) 2005-2013 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
+
6
+ VALUE cRpAllocation;
7
+
8
+ prof_allocation_t*
9
+ allocations_table_lookup(st_table *table, st_data_t key)
10
+ {
11
+ prof_allocation_t* result = NULL;
12
+ st_data_t value;
13
+ if (st_lookup(table, key, &value))
14
+ {
15
+ result = (prof_allocation_t*)value;
16
+ }
17
+
18
+ return result;
19
+ }
20
+
21
+ void
22
+ allocations_table_insert(st_table *table, st_data_t key, prof_allocation_t * allocation)
23
+ {
24
+ st_insert(table, (st_data_t)key, (st_data_t)allocation);
25
+ }
26
+
27
+ st_data_t
28
+ allocations_key(VALUE klass, int source_line)
29
+ {
30
+ return (klass << 4) + source_line;
31
+ }
32
+
33
+ /* ====== prof_allocation_t ====== */
34
+ prof_allocation_t*
35
+ prof_allocation_create(void)
36
+ {
37
+ prof_allocation_t *result = ALLOC(prof_allocation_t);
38
+ result->count = 0;
39
+ result->klass = Qnil;
40
+ result->klass_name = Qnil;
41
+ result->object = Qnil;
42
+ result->memory = 0;
43
+ result->source_line = 0;
44
+ result->source_file = Qnil;
45
+ result->key = 0;
46
+
47
+ return result;
48
+ }
49
+
50
+ prof_allocation_t*
51
+ prof_allocate_increment(prof_method_t* method, rb_trace_arg_t* trace_arg)
52
+ {
53
+ VALUE object = rb_tracearg_object(trace_arg);
54
+ if (BUILTIN_TYPE(object) == T_IMEMO)
55
+ return NULL;
56
+
57
+ VALUE klass = rb_obj_class(object);
58
+
59
+ int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
60
+ st_data_t key = allocations_key(klass, source_line);
61
+
62
+ prof_allocation_t* allocation = allocations_table_lookup(method->allocations_table, key);
63
+ if (!allocation)
64
+ {
65
+ allocation = prof_allocation_create();
66
+ allocation->source_line = source_line;
67
+ allocation->source_file = rb_tracearg_path(trace_arg);
68
+ allocation->klass_flags = 0;
69
+ allocation->klass = resolve_klass(klass, &allocation->klass_flags);
70
+
71
+ allocation->key = key;
72
+ allocations_table_insert(method->allocations_table, key, allocation);
73
+ }
74
+
75
+ allocation->count++;
76
+ allocation->memory += rb_obj_memsize_of(object);
77
+
78
+ return allocation;
79
+ }
80
+
81
+ static void
82
+ prof_allocation_ruby_gc_free(void *data)
83
+ {
84
+ prof_allocation_t* allocation = (prof_allocation_t*)data;
85
+
86
+ /* Has this thread object been accessed by Ruby? If
87
+ yes clean it up so to avoid a segmentation fault. */
88
+ if (allocation->object != Qnil)
89
+ {
90
+ RDATA(allocation->object)->data = NULL;
91
+ RDATA(allocation->object)->dfree = NULL;
92
+ RDATA(allocation->object)->dmark = NULL;
93
+ }
94
+ allocation->object = Qnil;
95
+ }
96
+
97
+ void
98
+ prof_allocation_free(prof_allocation_t* allocation)
99
+ {
100
+ prof_allocation_ruby_gc_free(allocation);
101
+ xfree(allocation);
102
+ }
103
+
104
+ size_t
105
+ prof_allocation_size(const void* data)
106
+ {
107
+ return sizeof(prof_allocation_t);
108
+ }
109
+
110
+ void
111
+ prof_allocation_mark(void *data)
112
+ {
113
+ prof_allocation_t* allocation = (prof_allocation_t*)data;
114
+ if (allocation->klass != Qnil)
115
+ rb_gc_mark(allocation->klass);
116
+
117
+ if (allocation->klass_name != Qnil)
118
+ rb_gc_mark(allocation->klass_name);
119
+
120
+ if (allocation->object != Qnil)
121
+ rb_gc_mark(allocation->object);
122
+
123
+ if (allocation->source_file != Qnil)
124
+ rb_gc_mark(allocation->source_file);
125
+ }
126
+
127
+ static const rb_data_type_t allocation_type =
128
+ {
129
+ .wrap_struct_name = "Allocation",
130
+ .function =
131
+ {
132
+ .dmark = prof_allocation_mark,
133
+ .dfree = prof_allocation_ruby_gc_free,
134
+ .dsize = prof_allocation_size,
135
+ },
136
+ .data = NULL,
137
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
138
+ };
139
+
140
+ VALUE
141
+ prof_allocation_wrap(prof_allocation_t *allocation)
142
+ {
143
+ if (allocation->object == Qnil)
144
+ {
145
+ allocation->object = TypedData_Wrap_Struct(cRpAllocation, &allocation_type, allocation);
146
+ }
147
+ return allocation->object;
148
+ }
149
+
150
+ static VALUE
151
+ prof_allocation_allocate(VALUE klass)
152
+ {
153
+ prof_allocation_t* allocation = prof_allocation_create();
154
+ allocation->object = prof_allocation_wrap(allocation);
155
+ return allocation->object;
156
+ }
157
+
158
+ prof_allocation_t*
159
+ prof_allocation_get(VALUE self)
160
+ {
161
+ /* Can't use Data_Get_Struct because that triggers the event hook
162
+ ending up in endless recursion. */
163
+ prof_allocation_t* result = DATA_PTR(self);
164
+ if (!result)
165
+ rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
166
+
167
+ return result;
168
+ }
169
+
170
+ /* call-seq:
171
+ klass -> Class
172
+
173
+ Returns the type of Class being allocated. */
174
+ static VALUE
175
+ prof_allocation_klass_name(VALUE self)
176
+ {
177
+ prof_allocation_t* allocation = prof_allocation_get(self);
178
+
179
+ if (allocation->klass_name == Qnil)
180
+ allocation->klass_name = resolve_klass_name(allocation->klass, &allocation->klass_flags);
181
+
182
+ return allocation->klass_name;
183
+ }
184
+
185
+ /* call-seq:
186
+ klass_flags -> integer
187
+
188
+ Returns the klass flags */
189
+
190
+ static VALUE
191
+ prof_allocation_klass_flags(VALUE self)
192
+ {
193
+ prof_allocation_t* allocation = prof_allocation_get(self);
194
+ return INT2FIX(allocation->klass_flags);
195
+ }
196
+
197
+ /* call-seq:
198
+ source_file -> string
199
+
200
+ Returns the the line number where objects were allocated. */
201
+ static VALUE
202
+ prof_allocation_source_file(VALUE self)
203
+ {
204
+ prof_allocation_t* allocation = prof_allocation_get(self);
205
+ return allocation->source_file;
206
+ }
207
+
208
+ /* call-seq:
209
+ line -> number
210
+
211
+ Returns the the line number where objects were allocated. */
212
+ static VALUE
213
+ prof_allocation_source_line(VALUE self)
214
+ {
215
+ prof_allocation_t* allocation = prof_allocation_get(self);
216
+ return INT2FIX(allocation->source_line);
217
+ }
218
+
219
+ /* call-seq:
220
+ count -> number
221
+
222
+ Returns the number of times this class has been allocated. */
223
+ static VALUE
224
+ prof_allocation_count(VALUE self)
225
+ {
226
+ prof_allocation_t* allocation = prof_allocation_get(self);
227
+ return INT2FIX(allocation->count);
228
+ }
229
+
230
+ /* call-seq:
231
+ memory -> number
232
+
233
+ Returns the amount of memory allocated. */
234
+ static VALUE
235
+ prof_allocation_memory(VALUE self)
236
+ {
237
+ prof_allocation_t* allocation = prof_allocation_get(self);
238
+ return ULL2NUM(allocation->memory);
239
+ }
240
+
241
+ /* :nodoc: */
242
+ static VALUE
243
+ prof_allocation_dump(VALUE self)
244
+ {
245
+ prof_allocation_t* allocation = DATA_PTR(self);
246
+
247
+ VALUE result = rb_hash_new();
248
+
249
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(allocation->key));
250
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_allocation_klass_name(self));
251
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(allocation->klass_flags));
252
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), allocation->source_file);
253
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(allocation->source_line));
254
+ rb_hash_aset(result, ID2SYM(rb_intern("count")), INT2FIX(allocation->count));
255
+ rb_hash_aset(result, ID2SYM(rb_intern("memory")), LONG2FIX(allocation->memory));
256
+
257
+ return result;
258
+ }
259
+
260
+ /* :nodoc: */
261
+ static VALUE
262
+ prof_allocation_load(VALUE self, VALUE data)
263
+ {
264
+ prof_allocation_t* allocation = DATA_PTR(self);
265
+ allocation->object = self;
266
+
267
+ allocation->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
268
+ allocation->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
269
+ allocation->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
270
+ allocation->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
271
+ allocation->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
272
+ allocation->count = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("count"))));
273
+ allocation->memory = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
274
+
275
+ return data;
276
+ }
277
+
278
+ void rp_init_allocation(void)
279
+ {
280
+ cRpAllocation = rb_define_class_under(mProf, "Allocation", rb_cData);
281
+ rb_undef_method(CLASS_OF(cRpAllocation), "new");
282
+ rb_define_alloc_func(cRpAllocation, prof_allocation_allocate);
283
+
284
+ rb_define_method(cRpAllocation, "klass_name", prof_allocation_klass_name, 0);
285
+ rb_define_method(cRpAllocation, "klass_flags", prof_allocation_klass_flags, 0);
286
+ rb_define_method(cRpAllocation, "source_file", prof_allocation_source_file, 0);
287
+ rb_define_method(cRpAllocation, "line", prof_allocation_source_line, 0);
288
+ rb_define_method(cRpAllocation, "count", prof_allocation_count, 0);
289
+ rb_define_method(cRpAllocation, "memory", prof_allocation_memory, 0);
290
+ rb_define_method(cRpAllocation, "_dump_data", prof_allocation_dump, 0);
291
+ rb_define_method(cRpAllocation, "_load_data", prof_allocation_load, 1);
292
+ }
@@ -0,0 +1,31 @@
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_ALLOCATION_
5
+ #define _RP_ALLOCATION_
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_method.h"
9
+
10
+ typedef struct
11
+ {
12
+ st_data_t key; /* Key in hash table */
13
+ unsigned int klass_flags; /* Information about the type of class */
14
+ VALUE klass; /* Klass that was created */
15
+ VALUE klass_name; /* Name of the class that was created */
16
+ VALUE source_file; /* Line number where allocation happens */
17
+ int source_line; /* Line number where allocation happens */
18
+ int count; /* Number of allocations */
19
+ size_t memory; /* Amount of allocated memory */
20
+ VALUE object; /* Cache to wrapped object */
21
+ } prof_allocation_t;
22
+
23
+ void rp_init_allocation(void);
24
+ void prof_allocation_free(prof_allocation_t* allocation);
25
+ void prof_allocation_mark(void *data);
26
+ VALUE prof_allocation_wrap(prof_allocation_t* allocation);
27
+ prof_allocation_t* prof_allocation_get(VALUE self);
28
+ prof_allocation_t* prof_allocate_increment(prof_method_t *method, rb_trace_arg_t *trace_arg);
29
+
30
+
31
+ #endif //_RP_ALLOCATION_
@@ -0,0 +1,283 @@
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_call_info.h"
5
+
6
+ #define INITIAL_CALL_INFOS_SIZE 2
7
+
8
+ VALUE cRpCallnfo;
9
+
10
+ /* ======= prof_call_info_t ========*/
11
+ prof_call_info_t *
12
+ prof_call_info_create(prof_method_t *method, prof_method_t *parent, VALUE source_file, int source_line)
13
+ {
14
+ prof_call_info_t *result = ALLOC(prof_call_info_t);
15
+ result->method = method;
16
+ result->parent = parent;
17
+ result->object = Qnil;
18
+ result->measurement = prof_measurement_create();
19
+
20
+ result->visits = 0;
21
+
22
+ result->depth = 0;
23
+ result->source_line = source_line;
24
+ result->source_file = source_file;
25
+
26
+ return result;
27
+ }
28
+
29
+ static void
30
+ prof_call_info_ruby_gc_free(void *data)
31
+ {
32
+ prof_call_info_t *call_info = (prof_call_info_t*)data;
33
+
34
+ /* Has this thread object been accessed by Ruby? If
35
+ yes clean it up so to avoid a segmentation fault. */
36
+ if (call_info->object != Qnil)
37
+ {
38
+ RDATA(call_info->object)->data = NULL;
39
+ RDATA(call_info->object)->dfree = NULL;
40
+ RDATA(call_info->object)->dmark = NULL;
41
+ }
42
+ call_info->object = Qnil;
43
+ }
44
+
45
+ void
46
+ prof_call_info_free(prof_call_info_t *call_info)
47
+ {
48
+ prof_call_info_ruby_gc_free(call_info);
49
+ xfree(call_info);
50
+ }
51
+
52
+ size_t
53
+ prof_call_info_size(const void *data)
54
+ {
55
+ return sizeof(prof_call_info_t);
56
+ }
57
+
58
+ void
59
+ prof_call_info_mark(void *data)
60
+ {
61
+ prof_call_info_t *call_info = (prof_call_info_t*)data;
62
+
63
+ if (call_info->source_file != Qnil)
64
+ rb_gc_mark(call_info->source_file);
65
+
66
+ if (call_info->object != Qnil)
67
+ rb_gc_mark(call_info->object);
68
+
69
+ if (call_info->method && call_info->method->object != Qnil)
70
+ rb_gc_mark(call_info->method->object);
71
+
72
+ if (call_info->parent && call_info->parent->object != Qnil)
73
+ rb_gc_mark(call_info->parent->object);
74
+
75
+ prof_measurement_mark(call_info->measurement);
76
+ }
77
+
78
+ static const rb_data_type_t call_info_type =
79
+ {
80
+ .wrap_struct_name = "CallInfo",
81
+ .function =
82
+ {
83
+ .dmark = prof_call_info_mark,
84
+ .dfree = prof_call_info_ruby_gc_free,
85
+ .dsize = prof_call_info_size,
86
+ },
87
+ .data = NULL,
88
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
89
+ };
90
+
91
+ VALUE
92
+ prof_call_info_wrap(prof_call_info_t *call_info)
93
+ {
94
+ if (call_info->object == Qnil)
95
+ {
96
+ call_info->object = TypedData_Wrap_Struct(cRpCallnfo, &call_info_type, call_info);
97
+ }
98
+ return call_info->object;
99
+ }
100
+
101
+ static VALUE
102
+ prof_call_info_allocate(VALUE klass)
103
+ {
104
+ prof_call_info_t* call_info = prof_call_info_create(NULL, NULL, Qnil, 0);
105
+ call_info->object = prof_call_info_wrap(call_info);
106
+ return call_info->object;
107
+ }
108
+
109
+ prof_call_info_t *
110
+ prof_get_call_info(VALUE self)
111
+ {
112
+ /* Can't use Data_Get_Struct because that triggers the event hook
113
+ ending up in endless recursion. */
114
+ prof_call_info_t* result = DATA_PTR(self);
115
+
116
+ if (!result)
117
+ rb_raise(rb_eRuntimeError, "This RubyProf::CallInfo instance has already been freed, likely because its profile has been freed.");
118
+
119
+ return result;
120
+ }
121
+
122
+ /* ======= Call Info Table ========*/
123
+ st_table *
124
+ call_info_table_create()
125
+ {
126
+ return st_init_numtable();
127
+ }
128
+
129
+ size_t
130
+ call_info_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
131
+ {
132
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
133
+ }
134
+
135
+ prof_call_info_t *
136
+ call_info_table_lookup(st_table *table, st_data_t key)
137
+ {
138
+ st_data_t val;
139
+ if (st_lookup(table, (st_data_t) key, &val))
140
+ {
141
+ return (prof_call_info_t *) val;
142
+ }
143
+ else
144
+ {
145
+ return NULL;
146
+ }
147
+ }
148
+
149
+ /* ======= RubyProf::CallInfo ========*/
150
+
151
+ /* call-seq:
152
+ parent -> call_info
153
+
154
+ Returns the call_infos parent call_info object (the method that called this method).*/
155
+ static VALUE
156
+ prof_call_info_parent(VALUE self)
157
+ {
158
+ prof_call_info_t* call_info = prof_get_call_info(self);
159
+ if (call_info->parent)
160
+ return prof_method_wrap(call_info->parent);
161
+ else
162
+ return Qnil;
163
+ }
164
+
165
+ /* call-seq:
166
+ called -> MethodInfo
167
+
168
+ Returns the target method. */
169
+ static VALUE
170
+ prof_call_info_target(VALUE self)
171
+ {
172
+ prof_call_info_t *call_info = prof_get_call_info(self);
173
+ return prof_method_wrap(call_info->method);
174
+ }
175
+
176
+ /* call-seq:
177
+ called -> Measurement
178
+
179
+ Returns the measurement associated with this call_info. */
180
+ static VALUE
181
+ prof_call_info_measurement(VALUE self)
182
+ {
183
+ prof_call_info_t* call_info = prof_get_call_info(self);
184
+ return prof_measurement_wrap(call_info->measurement);
185
+ }
186
+
187
+ /* call-seq:
188
+ depth -> int
189
+
190
+ returns the depth of this call info in the call graph */
191
+ static VALUE
192
+ prof_call_info_depth(VALUE self)
193
+ {
194
+ prof_call_info_t *result = prof_get_call_info(self);
195
+ return rb_int_new(result->depth);
196
+ }
197
+
198
+ /* call-seq:
199
+ source_file => string
200
+
201
+ return the source file of the method
202
+ */
203
+ static VALUE
204
+ prof_call_info_source_file(VALUE self)
205
+ {
206
+ prof_call_info_t* result = prof_get_call_info(self);
207
+ return result->source_file;
208
+ }
209
+
210
+ /* call-seq:
211
+ line_no -> int
212
+
213
+ returns the line number of the method */
214
+ static VALUE
215
+ prof_call_info_line(VALUE self)
216
+ {
217
+ prof_call_info_t *result = prof_get_call_info(self);
218
+ return INT2FIX(result->source_line);
219
+ }
220
+
221
+ /* :nodoc: */
222
+ static VALUE
223
+ prof_call_info_dump(VALUE self)
224
+ {
225
+ prof_call_info_t* call_info_data = prof_get_call_info(self);
226
+ VALUE result = rb_hash_new();
227
+
228
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_info_data->measurement));
229
+
230
+ rb_hash_aset(result, ID2SYM(rb_intern("depth")), INT2FIX(call_info_data->depth));
231
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_info_data->source_file);
232
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_info_data->source_line));
233
+
234
+ rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_info_parent(self));
235
+ rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_info_target(self));
236
+
237
+ return result;
238
+ }
239
+
240
+ /* :nodoc: */
241
+ static VALUE
242
+ prof_call_info_load(VALUE self, VALUE data)
243
+ {
244
+ VALUE target = Qnil;
245
+ VALUE parent = Qnil;
246
+ prof_call_info_t* call_info = prof_get_call_info(self);
247
+ call_info->object = self;
248
+
249
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
250
+ call_info->measurement = prof_get_measurement(measurement);
251
+
252
+ call_info->depth = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("depth"))));
253
+ call_info->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
254
+ call_info->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
255
+
256
+ parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
257
+ if (parent != Qnil)
258
+ call_info->parent = prof_method_get(parent);
259
+
260
+ target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
261
+ call_info->method = prof_method_get(target);
262
+
263
+ return data;
264
+ }
265
+
266
+ void rp_init_call_info()
267
+ {
268
+ /* CallInfo */
269
+ cRpCallnfo = rb_define_class_under(mProf, "CallInfo", rb_cData);
270
+ rb_undef_method(CLASS_OF(cRpCallnfo), "new");
271
+ rb_define_alloc_func(cRpCallnfo, prof_call_info_allocate);
272
+
273
+ rb_define_method(cRpCallnfo, "parent", prof_call_info_parent, 0);
274
+ rb_define_method(cRpCallnfo, "target", prof_call_info_target, 0);
275
+ rb_define_method(cRpCallnfo, "measurement", prof_call_info_measurement, 0);
276
+
277
+ rb_define_method(cRpCallnfo, "depth", prof_call_info_depth, 0);
278
+ rb_define_method(cRpCallnfo, "source_file", prof_call_info_source_file, 0);
279
+ rb_define_method(cRpCallnfo, "line", prof_call_info_line, 0);
280
+
281
+ rb_define_method(cRpCallnfo, "_dump_data", prof_call_info_dump, 0);
282
+ rb_define_method(cRpCallnfo, "_load_data", prof_call_info_load, 1);
283
+ }