Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

object_tracing.c
Go to the documentation of this file.
1 /**********************************************************************
2
3 object_tracing.c - Object Tracing mechanism/ObjectSpace extender for MRI.
4
5 $Author$
6 created at: Mon May 27 16:27:44 2013
7
8 NOTE: This extension library is not expected to exist except C Ruby.
9 NOTE: This feature is an example usage of internal event tracing APIs.
10
11 All the files in this distribution are covered under the Ruby's
12 license (see the file COPYING).
13
14 **********************************************************************/
15
16 #include "internal.h"
17 #include "ruby/debug.h"
18 #include "objspace.h"
19
20 struct traceobj_arg {
21 int running;
22 int keep_remains;
23 VALUE newobj_trace;
24 VALUE freeobj_trace;
25 st_table *object_table; /* obj (VALUE) -> allocation_info */
26 st_table *str_table; /* cstr -> refcount */
27 struct traceobj_arg *prev_traceobj_arg;
28 };
29
30 static const char *
31 make_unique_str(st_table *tbl, const char *str, long len)
32 {
33 if (!str) {
34 return NULL;
35 }
36 else {
37 st_data_t n;
38 char *result;
39
40 if (st_lookup(tbl, (st_data_t)str, &n)) {
41 st_insert(tbl, (st_data_t)str, n+1);
42 st_get_key(tbl, (st_data_t)str, &n);
43 result = (char *)n;
44 }
45 else {
46 result = (char *)ruby_xmalloc(len+1);
47 strncpy(result, str, len);
48 result[len] = 0;
49 st_add_direct(tbl, (st_data_t)result, 1);
50 }
51 return result;
52 }
53 }
54
55 static void
56 delete_unique_str(st_table *tbl, const char *str)
57 {
58 if (str) {
59 st_data_t n;
60
61 st_lookup(tbl, (st_data_t)str, &n);
62 if (n == 1) {
63 n = (st_data_t)str;
64 st_delete(tbl, &n, 0);
65 ruby_xfree((char *)n);
66 }
67 else {
68 st_insert(tbl, (st_data_t)str, n-1);
69 }
70 }
71 }
72
73 static void
74 newobj_i(VALUE tpval, void *data)
75 {
76 struct traceobj_arg *arg = (struct traceobj_arg *)data;
77 rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
78 VALUE obj = rb_tracearg_object(tparg);
79 VALUE path = rb_tracearg_path(tparg);
80 VALUE line = rb_tracearg_lineno(tparg);
81 VALUE mid = rb_tracearg_method_id(tparg);
82 VALUE klass = rb_tracearg_defined_class(tparg);
83 struct allocation_info *info;
84 const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
85 VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
86 const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
87 st_data_t v;
88
89 if (st_lookup(arg->object_table, (st_data_t)obj, &v)) {
90 info = (struct allocation_info *)v;
91 if (arg->keep_remains) {
92 if (info->living) {
93 /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
94 }
95 }
96 /* reuse info */
97 delete_unique_str(arg->str_table, info->path);
98 delete_unique_str(arg->str_table, info->class_path);
99 }
100 else {
101 info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
102 }
103 info->living = 1;
104 info->flags = RBASIC(obj)->flags;
105 info->klass = RBASIC_CLASS(obj);
106
107 info->path = path_cstr;
108 info->line = NUM2INT(line);
109 info->mid = mid;
110 info->class_path = class_path_cstr;
111 info->generation = rb_gc_count();
112 st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
113 }
114
115 static void
116 freeobj_i(VALUE tpval, void *data)
117 {
118 struct traceobj_arg *arg = (struct traceobj_arg *)data;
119 rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
120 st_data_t obj = (st_data_t)rb_tracearg_object(tparg);
121 st_data_t v;
122 struct allocation_info *info;
123
124 if (arg->keep_remains) {
125 if (st_lookup(arg->object_table, obj, &v)) {
126 info = (struct allocation_info *)v;
127 info->living = 0;
128 }
129 }
130 else {
131 if (st_delete(arg->object_table, &obj, &v)) {
132 info = (struct allocation_info *)v;
133 delete_unique_str(arg->str_table, info->path);
134 delete_unique_str(arg->str_table, info->class_path);
135 ruby_xfree(info);
136 }
137 }
138 }
139
140 static int
141 free_keys_i(st_data_t key, st_data_t value, st_data_t data)
142 {
143 ruby_xfree((void *)key);
144 return ST_CONTINUE;
145 }
146
147 static int
148 free_values_i(st_data_t key, st_data_t value, st_data_t data)
149 {
150 ruby_xfree((void *)value);
151 return ST_CONTINUE;
152 }
153
154 static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
155 static int tmp_keep_remains; /* TODO: Do not use global variables */
156
157 static struct traceobj_arg *
158 get_traceobj_arg(void)
159 {
160 if (tmp_trace_arg == 0) {
161 tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
162 tmp_trace_arg->running = 0;
163 tmp_trace_arg->keep_remains = tmp_keep_remains;
164 tmp_trace_arg->newobj_trace = 0;
165 tmp_trace_arg->freeobj_trace = 0;
166 tmp_trace_arg->object_table = st_init_numtable();
167 tmp_trace_arg->str_table = st_init_strtable();
168 }
169 return tmp_trace_arg;
170 }
171
172 /*
173 * call-seq: trace_object_allocations_start
174 *
175 * Starts tracing object allocations.
176 *
177 */
178 static VALUE
179 trace_object_allocations_start(VALUE self)
180 {
181 struct traceobj_arg *arg = get_traceobj_arg();
182
183 if (arg->running++ > 0) {
184 /* do nothing */
185 }
186 else {
187 if (arg->newobj_trace == 0) {
188 arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
189 rb_gc_register_mark_object(arg->newobj_trace);
190 arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
191 rb_gc_register_mark_object(arg->freeobj_trace);
192 }
193 rb_tracepoint_enable(arg->newobj_trace);
194 rb_tracepoint_enable(arg->freeobj_trace);
195 }
196
197 return Qnil;
198 }
199
200 /*
201 * call-seq: trace_object_allocations_stop
202 *
203 * Stop tracing object allocations.
204 *
205 * Note that if ::trace_object_allocations_start is called n-times, then
206 * tracing will stop after calling ::trace_object_allocations_stop n-times.
207 *
208 */
209 static VALUE
210 trace_object_allocations_stop(VALUE self)
211 {
212 struct traceobj_arg *arg = get_traceobj_arg();
213
214 if (arg->running > 0) {
215 arg->running--;
216 }
217
218 if (arg->running == 0) {
219 rb_tracepoint_disable(arg->newobj_trace);
220 rb_tracepoint_disable(arg->freeobj_trace);
221 }
222
223 return Qnil;
224 }
225
226 /*
227 * call-seq: trace_object_allocations_clear
228 *
229 * Clear recorded tracing information.
230 *
231 */
232 static VALUE
233 trace_object_allocations_clear(VALUE self)
234 {
235 struct traceobj_arg *arg = get_traceobj_arg();
236
237 /* clear tables */
238 st_foreach(arg->object_table, free_values_i, 0);
239 st_clear(arg->object_table);
240 st_foreach(arg->str_table, free_keys_i, 0);
241 st_clear(arg->str_table);
242
243 /* do not touch TracePoints */
244
245 return Qnil;
246 }
247
248 /*
249 * call-seq: trace_object_allocations { block }
250 *
251 * Starts tracing object allocations from the ObjectSpace extension module.
252 *
253 * For example:
254 *
255 * require 'objspace'
256 *
257 * class C
258 * include ObjectSpace
259 *
260 * def foo
261 * trace_object_allocations do
262 * obj = Object.new
263 * p "#{allocation_sourcefile(obj)}:#{allocation_sourceline(obj)}"
264 * end
265 * end
266 * end
267 *
268 * C.new.foo #=> "objtrace.rb:8"
269 *
270 * This example has included the ObjectSpace module to make it easier to read,
271 * but you can also use the ::trace_object_allocations notation (recommended).
272 *
273 * Note that this feature introduces a huge performance decrease and huge
274 * memory consumption.
275 */
276 static VALUE
277 trace_object_allocations(VALUE self)
278 {
279 trace_object_allocations_start(self);
280 return rb_ensure(rb_yield, Qnil, trace_object_allocations_stop, self);
281 }
282
283 int rb_bug_reporter_add(void (*func)(FILE *, void *), void *data);
284 static int object_allocations_reporter_registered = 0;
285
286 static int
287 object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr)
288 {
289 FILE *out = (FILE *)ptr;
290 VALUE obj = (VALUE)key;
291 struct allocation_info *info = (struct allocation_info *)val;
292
293 fprintf(out, "-- %p (%s F: %p, ", (void *)obj, info->living ? "live" : "dead", (void *)info->flags);
294 if (info->class_path) fprintf(out, "C: %s", info->class_path);
295 else fprintf(out, "C: %p", (void *)info->klass);
296 fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line);
297 if (!NIL_P(info->mid)) {
298 VALUE m = rb_sym2str(info->mid);
299 fprintf(out, " (%s)", RSTRING_PTR(m));
300 }
301 fprintf(out, ")\n");
302
303 return ST_CONTINUE;
304 }
305
306 static void
307 object_allocations_reporter(FILE *out, void *ptr)
308 {
309 fprintf(out, "== object_allocations_reporter: START\n");
310 if (tmp_trace_arg) {
311 st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out);
312 }
313 fprintf(out, "== object_allocations_reporter: END\n");
314 }
315
316 static VALUE
317 trace_object_allocations_debug_start(VALUE self)
318 {
319 tmp_keep_remains = 1;
320 if (object_allocations_reporter_registered == 0) {
321 object_allocations_reporter_registered = 1;
322 rb_bug_reporter_add(object_allocations_reporter, 0);
323 }
324
325 return trace_object_allocations_start(self);
326 }
327
328 static struct allocation_info *
329 lookup_allocation_info(VALUE obj)
330 {
331 if (tmp_trace_arg) {
332 st_data_t info;
333 if (st_lookup(tmp_trace_arg->object_table, obj, &info)) {
334 return (struct allocation_info *)info;
335 }
336 }
337 return NULL;
338 }
339
340 struct allocation_info *
341 objspace_lookup_allocation_info(VALUE obj)
342 {
343 return lookup_allocation_info(obj);
344 }
345
346 /*
347 * call-seq: allocation_sourcefile(object) -> string
348 *
349 * Returns the source file origin from the given +object+.
350 *
351 * See ::trace_object_allocations for more information and examples.
352 */
353 static VALUE
354 allocation_sourcefile(VALUE self, VALUE obj)
355 {
356 struct allocation_info *info = lookup_allocation_info(obj);
357
358 if (info && info->path) {
359 return rb_str_new2(info->path);
360 }
361 else {
362 return Qnil;
363 }
364 }
365
366 /*
367 * call-seq: allocation_sourceline(object) -> integer
368 *
369 * Returns the original line from source for from the given +object+.
370 *
371 * See ::trace_object_allocations for more information and examples.
372 */
373 static VALUE
374 allocation_sourceline(VALUE self, VALUE obj)
375 {
376 struct allocation_info *info = lookup_allocation_info(obj);
377
378 if (info) {
379 return INT2FIX(info->line);
380 }
381 else {
382 return Qnil;
383 }
384 }
385
386 /*
387 * call-seq: allocation_class_path(object) -> string
388 *
389 * Returns the class for the given +object+.
390 *
391 * class A
392 * def foo
393 * ObjectSpace::trace_object_allocations do
394 * obj = Object.new
395 * p "#{ObjectSpace::allocation_class_path(obj)}"
396 * end
397 * end
398 * end
399 *
400 * A.new.foo #=> "Class"
401 *
402 * See ::trace_object_allocations for more information and examples.
403 */
404 static VALUE
405 allocation_class_path(VALUE self, VALUE obj)
406 {
407 struct allocation_info *info = lookup_allocation_info(obj);
408
409 if (info && info->class_path) {
410 return rb_str_new2(info->class_path);
411 }
412 else {
413 return Qnil;
414 }
415 }
416
417 /*
418 * call-seq: allocation_method_id(object) -> string
419 *
420 * Returns the method identifier for the given +object+.
421 *
422 * class A
423 * include ObjectSpace
424 *
425 * def foo
426 * trace_object_allocations do
427 * obj = Object.new
428 * p "#{allocation_class_path(obj)}##{allocation_method_id(obj)}"
429 * end
430 * end
431 * end
432 *
433 * A.new.foo #=> "Class#new"
434 *
435 * See ::trace_object_allocations for more information and examples.
436 */
437 static VALUE
438 allocation_method_id(VALUE self, VALUE obj)
439 {
440 struct allocation_info *info = lookup_allocation_info(obj);
441 if (info) {
442 return info->mid;
443 }
444 else {
445 return Qnil;
446 }
447 }
448
449 /*
450 * call-seq: allocation_generation(object) -> integer or nil
451 *
452 * Returns garbage collector generation for the given +object+.
453 *
454 * class B
455 * include ObjectSpace
456 *
457 * def foo
458 * trace_object_allocations do
459 * obj = Object.new
460 * p "Generation is #{allocation_generation(obj)}"
461 * end
462 * end
463 * end
464 *
465 * B.new.foo #=> "Generation is 3"
466 *
467 * See ::trace_object_allocations for more information and examples.
468 */
469 static VALUE
470 allocation_generation(VALUE self, VALUE obj)
471 {
472 struct allocation_info *info = lookup_allocation_info(obj);
473 if (info) {
474 return SIZET2NUM(info->generation);
475 }
476 else {
477 return Qnil;
478 }
479 }
480
481 void
482 Init_object_tracing(VALUE rb_mObjSpace)
483 {
484 #if 0
485 rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
486 #endif
487
488 rb_define_module_function(rb_mObjSpace, "trace_object_allocations", trace_object_allocations, 0);
489 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_start", trace_object_allocations_start, 0);
490 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_stop", trace_object_allocations_stop, 0);
491 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_clear", trace_object_allocations_clear, 0);
492
493 rb_define_module_function(rb_mObjSpace, "trace_object_allocations_debug_start", trace_object_allocations_debug_start, 0);
494
495 rb_define_module_function(rb_mObjSpace, "allocation_sourcefile", allocation_sourcefile, 1);
496 rb_define_module_function(rb_mObjSpace, "allocation_sourceline", allocation_sourceline, 1);
497 rb_define_module_function(rb_mObjSpace, "allocation_class_path", allocation_class_path, 1);
498 rb_define_module_function(rb_mObjSpace, "allocation_method_id", allocation_method_id, 1);
499 rb_define_module_function(rb_mObjSpace, "allocation_generation", allocation_generation, 1);
500 }
allocation_info::class_path
const char * class_path
Definition: objspace.h:14
ruby_xfree
void ruby_xfree(void *x)
Definition: gc.c:10150
strncpy
char * strncpy(char *__restrict, const char *__restrict, size_t)
rb_gc_count
size_t rb_gc_count(void)
Definition: gc.c:8711
rb_str_new2
#define rb_str_new2
Definition: intern.h:903
traceobj_arg::newobj_trace
VALUE newobj_trace
Definition: object_tracing.c:36
allocation_info::generation
size_t generation
Definition: objspace.h:16
rb_gc_register_mark_object
void rb_gc_register_mark_object(VALUE obj)
Definition: gc.c:7063
path
VALUE path
Definition: rb_mjit_min_header-2.7.0.h:7351
allocation_info::klass
VALUE klass
Definition: objspace.h:9
rb_bug_reporter_add
int rb_bug_reporter_add(void(*func)(FILE *, void *), void *data)
Definition: error.c:455
allocation_info::flags
VALUE flags
Definition: objspace.h:8
INT2FIX
#define INT2FIX(i)
Definition: ruby.h:263
rb_tracearg_lineno
VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:812
ruby_xmalloc
void * ruby_xmalloc(size_t size)
Definition: gc.c:11950
RSTRING_PTR
#define RSTRING_PTR(str)
Definition: ruby.h:1009
st_init_numtable
st_table * st_init_numtable(void)
Definition: st.c:653
RUBY_INTERNAL_EVENT_FREEOBJ
#define RUBY_INTERNAL_EVENT_FREEOBJ
Definition: ruby.h:2269
VALUE
unsigned long VALUE
Definition: ruby.h:102
st_delete
int st_delete(st_table *tab, st_data_t *key, st_data_t *value)
Definition: st.c:1418
st_add_direct
void st_add_direct(st_table *tab, st_data_t key, st_data_t value)
Definition: st.c:1251
allocation_info::path
const char * path
Definition: objspace.h:12
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:772
rb_tracearg_object
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:1008
Init_object_tracing
void Init_object_tracing(VALUE rb_mObjSpace)
Definition: object_tracing.c:482
traceobj_arg::object_table
st_table * object_table
Definition: object_tracing.c:38
ptr
struct RIMemo * ptr
Definition: debug.c:74
objspace.h
NULL
#define NULL
Definition: _sdbm.c:101
rb_tracearg_defined_class
VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:900
st_insert
int st_insert(st_table *tab, st_data_t key, st_data_t value)
Definition: st.c:1171
v
int VALUE v
Definition: rb_mjit_min_header-2.7.0.h:12332
ALLOC_N
#define ALLOC_N(type, n)
Definition: ruby.h:1663
obj
const VALUE VALUE obj
Definition: rb_mjit_min_header-2.7.0.h:5742
fprintf
int fprintf(FILE *__restrict, const char *__restrict,...) __attribute__((__format__(__printf__
st_clear
void st_clear(st_table *tab)
Definition: st.c:698
rb_tracearg_from_tracepoint
rb_trace_arg_t * rb_tracearg_from_tracepoint(VALUE tpval)
Definition: vm_trace.c:786
rb_trace_arg_struct
Definition: vm_core.h:1874
st_data_t
RUBY_SYMBOL_EXPORT_BEGIN typedef unsigned long st_data_t
Definition: st.h:22
allocation_info
Definition: objspace.h:5
st_init_strtable
st_table * st_init_strtable(void)
Definition: st.c:668
traceobj_arg::running
int running
Definition: object_tracing.c:34
RBASIC_CLASS
#define RBASIC_CLASS(obj)
Definition: ruby.h:906
allocation_info::line
unsigned long line
Definition: objspace.h:13
rb_tracepoint_enable
VALUE rb_tracepoint_enable(VALUE tpval)
Definition: vm_trace.c:1113
rb_tracepoint_disable
VALUE rb_tracepoint_disable(VALUE tpval)
Definition: vm_trace.c:1221
rb_class_path_cached
VALUE rb_class_path_cached(VALUE)
Definition: variable.c:162
traceobj_arg::str_table
st_table * str_table
Definition: object_tracing.c:39
key
key
Definition: openssl_missing.h:181
st_foreach
int st_foreach(st_table *tab, st_foreach_callback_func *func, st_data_t arg)
Definition: st.c:1718
rb_define_module_function
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1771
n
const char size_t n
Definition: rb_mjit_min_header-2.7.0.h:5456
internal.h
arg
VALUE arg
Definition: rb_mjit_min_header-2.7.0.h:5601
ST_CONTINUE
@ ST_CONTINUE
Definition: st.h:99
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.0.h:13254
st_data_t
unsigned long st_data_t
Definition: rb_mjit_min_header-2.7.0.h:5363
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
objspace_lookup_allocation_info
struct allocation_info * objspace_lookup_allocation_info(VALUE obj)
Definition: object_tracing.c:341
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
RUBY_INTERNAL_EVENT_NEWOBJ
#define RUBY_INTERNAL_EVENT_NEWOBJ
Definition: ruby.h:2268
traceobj_arg
Definition: object_tracing.c:20
traceobj_arg::keep_remains
int keep_remains
Definition: object_tracing.c:35
RBASIC
#define RBASIC(obj)
Definition: ruby.h:1267
rb_tracepoint_new
VALUE rb_tracepoint_new(VALUE target_thread_not_supported_yet, rb_event_flag_t events, void(*func)(VALUE, void *), void *data)
Definition: vm_trace.c:1394
OBJ_FROZEN
#define OBJ_FROZEN(x)
Definition: ruby.h:1375
traceobj_arg::prev_traceobj_arg
struct traceobj_arg * prev_traceobj_arg
Definition: object_tracing.c:40
len
uint8_t len
Definition: escape.c:17
rb_sym2str
VALUE rb_sym2str(VALUE)
Definition: symbol.c:784
SIZET2NUM
#define SIZET2NUM(v)
Definition: ruby.h:295
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
allocation_info::mid
VALUE mid
Definition: objspace.h:15
rb_ensure
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1114
traceobj_arg::freeobj_trace
VALUE freeobj_trace
Definition: object_tracing.c:37
NUM2INT
#define NUM2INT(x)
Definition: ruby.h:715
Qnil
#define Qnil
Definition: ruby.h:469
st_lookup
int st_lookup(st_table *tab, st_data_t key, st_data_t *value)
Definition: st.c:1101
allocation_info::living
int living
Definition: objspace.h:7
rb_tracearg_method_id
VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:886
rb_tracearg_path
VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:818
RSTRING_LEN
#define RSTRING_LEN(str)
Definition: ruby.h:1005
st_table
Definition: st.h:79
RTEST
#define RTEST(v)
Definition: ruby.h:481
debug.h
__sFILE
Definition: vsnprintf.c:169
st_get_key
int st_get_key(st_table *tab, st_data_t key, st_data_t *result)
Definition: st.c:1130