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,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,279 @@
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 allocation 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)->dmark = NULL;
91
+ RDATA(allocation->object)->dfree = NULL;
92
+ RDATA(allocation->object)->data = NULL;
93
+ allocation->object = Qnil;
94
+ }
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
+ VALUE
128
+ prof_allocation_wrap(prof_allocation_t *allocation)
129
+ {
130
+ if (allocation->object == Qnil)
131
+ {
132
+ allocation->object = Data_Wrap_Struct(cRpAllocation, prof_allocation_mark , prof_allocation_ruby_gc_free, allocation);
133
+ }
134
+ return allocation->object;
135
+ }
136
+
137
+ static VALUE
138
+ prof_allocation_allocate(VALUE klass)
139
+ {
140
+ prof_allocation_t* allocation = prof_allocation_create();
141
+ allocation->object = prof_allocation_wrap(allocation);
142
+ return allocation->object;
143
+ }
144
+
145
+ prof_allocation_t*
146
+ prof_allocation_get(VALUE self)
147
+ {
148
+ /* Can't use Data_Get_Struct because that triggers the event hook
149
+ ending up in endless recursion. */
150
+ prof_allocation_t* result = DATA_PTR(self);
151
+ if (!result)
152
+ rb_raise(rb_eRuntimeError, "This RubyProf::Allocation instance has already been freed, likely because its profile has been freed.");
153
+
154
+ return result;
155
+ }
156
+
157
+ /* call-seq:
158
+ klass -> Class
159
+
160
+ Returns the type of Class being allocated. */
161
+ static VALUE
162
+ prof_allocation_klass_name(VALUE self)
163
+ {
164
+ prof_allocation_t* allocation = prof_allocation_get(self);
165
+
166
+ if (allocation->klass_name == Qnil)
167
+ allocation->klass_name = resolve_klass_name(allocation->klass, &allocation->klass_flags);
168
+
169
+ return allocation->klass_name;
170
+ }
171
+
172
+ /* call-seq:
173
+ klass_flags -> integer
174
+
175
+ Returns the klass flags */
176
+
177
+ static VALUE
178
+ prof_allocation_klass_flags(VALUE self)
179
+ {
180
+ prof_allocation_t* allocation = prof_allocation_get(self);
181
+ return INT2FIX(allocation->klass_flags);
182
+ }
183
+
184
+ /* call-seq:
185
+ source_file -> string
186
+
187
+ Returns the the line number where objects were allocated. */
188
+ static VALUE
189
+ prof_allocation_source_file(VALUE self)
190
+ {
191
+ prof_allocation_t* allocation = prof_allocation_get(self);
192
+ return allocation->source_file;
193
+ }
194
+
195
+ /* call-seq:
196
+ line -> number
197
+
198
+ Returns the the line number where objects were allocated. */
199
+ static VALUE
200
+ prof_allocation_source_line(VALUE self)
201
+ {
202
+ prof_allocation_t* allocation = prof_allocation_get(self);
203
+ return INT2FIX(allocation->source_line);
204
+ }
205
+
206
+ /* call-seq:
207
+ count -> number
208
+
209
+ Returns the number of times this class has been allocated. */
210
+ static VALUE
211
+ prof_allocation_count(VALUE self)
212
+ {
213
+ prof_allocation_t* allocation = prof_allocation_get(self);
214
+ return INT2FIX(allocation->count);
215
+ }
216
+
217
+ /* call-seq:
218
+ memory -> number
219
+
220
+ Returns the amount of memory allocated. */
221
+ static VALUE
222
+ prof_allocation_memory(VALUE self)
223
+ {
224
+ prof_allocation_t* allocation = prof_allocation_get(self);
225
+ return ULL2NUM(allocation->memory);
226
+ }
227
+
228
+ /* :nodoc: */
229
+ static VALUE
230
+ prof_allocation_dump(VALUE self)
231
+ {
232
+ prof_allocation_t* allocation = DATA_PTR(self);
233
+
234
+ VALUE result = rb_hash_new();
235
+
236
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(allocation->key));
237
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_allocation_klass_name(self));
238
+ rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(allocation->klass_flags));
239
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), allocation->source_file);
240
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(allocation->source_line));
241
+ rb_hash_aset(result, ID2SYM(rb_intern("count")), INT2FIX(allocation->count));
242
+ rb_hash_aset(result, ID2SYM(rb_intern("memory")), LONG2FIX(allocation->memory));
243
+
244
+ return result;
245
+ }
246
+
247
+ /* :nodoc: */
248
+ static VALUE
249
+ prof_allocation_load(VALUE self, VALUE data)
250
+ {
251
+ prof_allocation_t* allocation = DATA_PTR(self);
252
+ allocation->object = self;
253
+
254
+ allocation->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
255
+ allocation->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
256
+ allocation->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
257
+ allocation->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
258
+ allocation->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
259
+ allocation->count = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("count"))));
260
+ allocation->memory = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
261
+
262
+ return data;
263
+ }
264
+
265
+ void rp_init_allocation(void)
266
+ {
267
+ cRpAllocation = rb_define_class_under(mProf, "Allocation", rb_cData);
268
+ rb_undef_method(CLASS_OF(cRpAllocation), "new");
269
+ rb_define_alloc_func(cRpAllocation, prof_allocation_allocate);
270
+
271
+ rb_define_method(cRpAllocation, "klass_name", prof_allocation_klass_name, 0);
272
+ rb_define_method(cRpAllocation, "klass_flags", prof_allocation_klass_flags, 0);
273
+ rb_define_method(cRpAllocation, "source_file", prof_allocation_source_file, 0);
274
+ rb_define_method(cRpAllocation, "line", prof_allocation_source_line, 0);
275
+ rb_define_method(cRpAllocation, "count", prof_allocation_count, 0);
276
+ rb_define_method(cRpAllocation, "memory", prof_allocation_memory, 0);
277
+ rb_define_method(cRpAllocation, "_dump_data", prof_allocation_dump, 0);
278
+ rb_define_method(cRpAllocation, "_load_data", prof_allocation_load, 1);
279
+ }
@@ -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,271 @@
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 call info 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)->dmark = NULL;
39
+ RDATA(call_info->object)->dfree = NULL;
40
+ RDATA(call_info->object)->data = NULL;
41
+ call_info->object = Qnil;
42
+ }
43
+ }
44
+
45
+ void
46
+ prof_call_info_free(prof_call_info_t *call_info)
47
+ {
48
+ prof_measurement_free(call_info->measurement);
49
+ prof_call_info_ruby_gc_free(call_info);
50
+ xfree(call_info);
51
+ }
52
+
53
+ size_t
54
+ prof_call_info_size(const void *data)
55
+ {
56
+ return sizeof(prof_call_info_t);
57
+ }
58
+
59
+ void
60
+ prof_call_info_mark(void *data)
61
+ {
62
+ prof_call_info_t *call_info = (prof_call_info_t*)data;
63
+
64
+ if (call_info->source_file != Qnil)
65
+ rb_gc_mark(call_info->source_file);
66
+
67
+ if (call_info->object != Qnil)
68
+ rb_gc_mark(call_info->object);
69
+
70
+ if (call_info->method && call_info->method->object != Qnil)
71
+ rb_gc_mark(call_info->method->object);
72
+
73
+ if (call_info->parent && call_info->parent->object != Qnil)
74
+ rb_gc_mark(call_info->parent->object);
75
+
76
+ prof_measurement_mark(call_info->measurement);
77
+ }
78
+
79
+ VALUE
80
+ prof_call_info_wrap(prof_call_info_t *call_info)
81
+ {
82
+ if (call_info->object == Qnil)
83
+ {
84
+ call_info->object = Data_Wrap_Struct(cRpCallnfo, prof_call_info_mark, prof_call_info_ruby_gc_free, call_info);
85
+ }
86
+ return call_info->object;
87
+ }
88
+
89
+ static VALUE
90
+ prof_call_info_allocate(VALUE klass)
91
+ {
92
+ prof_call_info_t* call_info = prof_call_info_create(NULL, NULL, Qnil, 0);
93
+ call_info->object = prof_call_info_wrap(call_info);
94
+ return call_info->object;
95
+ }
96
+
97
+ prof_call_info_t *
98
+ prof_get_call_info(VALUE self)
99
+ {
100
+ /* Can't use Data_Get_Struct because that triggers the event hook
101
+ ending up in endless recursion. */
102
+ prof_call_info_t* result = DATA_PTR(self);
103
+
104
+ if (!result)
105
+ rb_raise(rb_eRuntimeError, "This RubyProf::CallInfo instance has already been freed, likely because its profile has been freed.");
106
+
107
+ return result;
108
+ }
109
+
110
+ /* ======= Call Info Table ========*/
111
+ st_table *
112
+ call_info_table_create()
113
+ {
114
+ return st_init_numtable();
115
+ }
116
+
117
+ size_t
118
+ call_info_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
119
+ {
120
+ return st_insert(table, (st_data_t) key, (st_data_t) val);
121
+ }
122
+
123
+ prof_call_info_t *
124
+ call_info_table_lookup(st_table *table, st_data_t key)
125
+ {
126
+ st_data_t val;
127
+ if (st_lookup(table, (st_data_t) key, &val))
128
+ {
129
+ return (prof_call_info_t *) val;
130
+ }
131
+ else
132
+ {
133
+ return NULL;
134
+ }
135
+ }
136
+
137
+ /* ======= RubyProf::CallInfo ========*/
138
+
139
+ /* call-seq:
140
+ parent -> call_info
141
+
142
+ Returns the call_infos parent call_info object (the method that called this method).*/
143
+ static VALUE
144
+ prof_call_info_parent(VALUE self)
145
+ {
146
+ prof_call_info_t* call_info = prof_get_call_info(self);
147
+ if (call_info->parent)
148
+ return prof_method_wrap(call_info->parent);
149
+ else
150
+ return Qnil;
151
+ }
152
+
153
+ /* call-seq:
154
+ called -> MethodInfo
155
+
156
+ Returns the target method. */
157
+ static VALUE
158
+ prof_call_info_target(VALUE self)
159
+ {
160
+ prof_call_info_t *call_info = prof_get_call_info(self);
161
+ return prof_method_wrap(call_info->method);
162
+ }
163
+
164
+ /* call-seq:
165
+ called -> Measurement
166
+
167
+ Returns the measurement associated with this call_info. */
168
+ static VALUE
169
+ prof_call_info_measurement(VALUE self)
170
+ {
171
+ prof_call_info_t* call_info = prof_get_call_info(self);
172
+ return prof_measurement_wrap(call_info->measurement);
173
+ }
174
+
175
+ /* call-seq:
176
+ depth -> int
177
+
178
+ returns the depth of this call info in the call graph */
179
+ static VALUE
180
+ prof_call_info_depth(VALUE self)
181
+ {
182
+ prof_call_info_t *result = prof_get_call_info(self);
183
+ return rb_int_new(result->depth);
184
+ }
185
+
186
+ /* call-seq:
187
+ source_file => string
188
+
189
+ return the source file of the method
190
+ */
191
+ static VALUE
192
+ prof_call_info_source_file(VALUE self)
193
+ {
194
+ prof_call_info_t* result = prof_get_call_info(self);
195
+ return result->source_file;
196
+ }
197
+
198
+ /* call-seq:
199
+ line_no -> int
200
+
201
+ returns the line number of the method */
202
+ static VALUE
203
+ prof_call_info_line(VALUE self)
204
+ {
205
+ prof_call_info_t *result = prof_get_call_info(self);
206
+ return INT2FIX(result->source_line);
207
+ }
208
+
209
+ /* :nodoc: */
210
+ static VALUE
211
+ prof_call_info_dump(VALUE self)
212
+ {
213
+ prof_call_info_t* call_info_data = prof_get_call_info(self);
214
+ VALUE result = rb_hash_new();
215
+
216
+ rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_info_data->measurement));
217
+
218
+ rb_hash_aset(result, ID2SYM(rb_intern("depth")), INT2FIX(call_info_data->depth));
219
+ rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_info_data->source_file);
220
+ rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(call_info_data->source_line));
221
+
222
+ rb_hash_aset(result, ID2SYM(rb_intern("parent")), prof_call_info_parent(self));
223
+ rb_hash_aset(result, ID2SYM(rb_intern("target")), prof_call_info_target(self));
224
+
225
+ return result;
226
+ }
227
+
228
+ /* :nodoc: */
229
+ static VALUE
230
+ prof_call_info_load(VALUE self, VALUE data)
231
+ {
232
+ VALUE target = Qnil;
233
+ VALUE parent = Qnil;
234
+ prof_call_info_t* call_info = prof_get_call_info(self);
235
+ call_info->object = self;
236
+
237
+ VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
238
+ call_info->measurement = prof_get_measurement(measurement);
239
+
240
+ call_info->depth = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("depth"))));
241
+ call_info->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
242
+ call_info->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
243
+
244
+ parent = rb_hash_aref(data, ID2SYM(rb_intern("parent")));
245
+ if (parent != Qnil)
246
+ call_info->parent = prof_method_get(parent);
247
+
248
+ target = rb_hash_aref(data, ID2SYM(rb_intern("target")));
249
+ call_info->method = prof_method_get(target);
250
+
251
+ return data;
252
+ }
253
+
254
+ void rp_init_call_info()
255
+ {
256
+ /* CallInfo */
257
+ cRpCallnfo = rb_define_class_under(mProf, "CallInfo", rb_cData);
258
+ rb_undef_method(CLASS_OF(cRpCallnfo), "new");
259
+ rb_define_alloc_func(cRpCallnfo, prof_call_info_allocate);
260
+
261
+ rb_define_method(cRpCallnfo, "parent", prof_call_info_parent, 0);
262
+ rb_define_method(cRpCallnfo, "target", prof_call_info_target, 0);
263
+ rb_define_method(cRpCallnfo, "measurement", prof_call_info_measurement, 0);
264
+
265
+ rb_define_method(cRpCallnfo, "depth", prof_call_info_depth, 0);
266
+ rb_define_method(cRpCallnfo, "source_file", prof_call_info_source_file, 0);
267
+ rb_define_method(cRpCallnfo, "line", prof_call_info_line, 0);
268
+
269
+ rb_define_method(cRpCallnfo, "_dump_data", prof_call_info_dump, 0);
270
+ rb_define_method(cRpCallnfo, "_load_data", prof_call_info_load, 1);
271
+ }