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,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
+ }