ruby-prof 1.1.0

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