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;
25  st_table *object_table; /* obj (VALUE) -> allocation_info */
26  st_table *str_table; /* cstr -> refcount */
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 {
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;
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);
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;
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;
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 *
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
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