Ruby  2.1.10p492(2016-04-01revision54464)
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 "ruby/ruby.h"
17 #include "ruby/debug.h"
18 #include "objspace.h"
19 #include "internal.h"
20 
21 struct traceobj_arg {
22  int running;
26  st_table *object_table; /* obj (VALUE) -> allocation_info */
27  st_table *str_table; /* cstr -> refcount */
29 };
30 
31 static const char *
32 make_unique_str(st_table *tbl, const char *str, long len)
33 {
34  if (!str) {
35  return NULL;
36  }
37  else {
38  st_data_t n;
39  char *result;
40 
41  if (st_lookup(tbl, (st_data_t)str, &n)) {
42  st_insert(tbl, (st_data_t)str, n+1);
43  st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
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  st_delete(tbl, (st_data_t *)&str, 0);
64  ruby_xfree((char *)str);
65  }
66  else {
67  st_insert(tbl, (st_data_t)str, n-1);
68  }
69  }
70 }
71 
72 static void
73 newobj_i(VALUE tpval, void *data)
74 {
75  struct traceobj_arg *arg = (struct traceobj_arg *)data;
77  VALUE obj = rb_tracearg_object(tparg);
78  VALUE path = rb_tracearg_path(tparg);
79  VALUE line = rb_tracearg_lineno(tparg);
80  VALUE mid = rb_tracearg_method_id(tparg);
81  VALUE klass = rb_tracearg_defined_class(tparg);
82  struct allocation_info *info;
83  const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
85  const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
86 
87  if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
88  if (arg->keep_remains) {
89  if (info->living) {
90  /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
91  }
92  }
93  /* reuse info */
94  delete_unique_str(arg->str_table, info->path);
95  delete_unique_str(arg->str_table, info->class_path);
96  }
97  else {
98  info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
99  }
100  info->living = 1;
101  info->flags = RBASIC(obj)->flags;
102  info->klass = RBASIC_CLASS(obj);
103 
104  info->path = path_cstr;
105  info->line = NUM2INT(line);
106  info->mid = mid;
107  info->class_path = class_path_cstr;
108  info->generation = rb_gc_count();
109  st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
110 }
111 
112 static void
113 freeobj_i(VALUE tpval, void *data)
114 {
115  struct traceobj_arg *arg = (struct traceobj_arg *)data;
117  VALUE obj = rb_tracearg_object(tparg);
118  struct allocation_info *info;
119 
120  if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
121  if (arg->keep_remains) {
122  info->living = 0;
123  }
124  else {
125  st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
126  delete_unique_str(arg->str_table, info->path);
127  delete_unique_str(arg->str_table, info->class_path);
128  ruby_xfree(info);
129  }
130  }
131 }
132 
133 static int
134 free_keys_i(st_data_t key, st_data_t value, void *data)
135 {
136  ruby_xfree((void *)key);
137  return ST_CONTINUE;
138 }
139 
140 static int
142 {
143  ruby_xfree((void *)value);
144  return ST_CONTINUE;
145 }
146 
147 static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
148 static int tmp_keep_remains; /* TODO: Do not use global variables */
149 
150 static struct traceobj_arg *
152 {
153  if (tmp_trace_arg == 0) {
154  tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
155  tmp_trace_arg->running = 0;
161  }
162  return tmp_trace_arg;
163 }
164 
165 /*
166  * call-seq: trace_object_allocations_start
167  *
168  * Starts tracing object allocations.
169  *
170  */
171 static VALUE
173 {
174  struct traceobj_arg *arg = get_traceobj_arg();
175 
176  if (arg->running++ > 0) {
177  /* do nothing */
178  }
179  else {
180  if (arg->newobj_trace == 0) {
183  }
186  }
187 
188  return Qnil;
189 }
190 
191 /*
192  * call-seq: trace_object_allocations_stop
193  *
194  * Stop tracing object allocations.
195  *
196  * Note that if ::trace_object_allocations_start is called n-times, then
197  * tracing will stop after calling ::trace_object_allocations_stop n-times.
198  *
199  */
200 static VALUE
202 {
203  struct traceobj_arg *arg = get_traceobj_arg();
204 
205  if (arg->running > 0) {
206  arg->running--;
207  }
208 
209  if (arg->running == 0) {
212  arg->newobj_trace = 0;
213  arg->freeobj_trace = 0;
214  }
215 
216  return Qnil;
217 }
218 
219 /*
220  * call-seq: trace_object_allocations_clear
221  *
222  * Clear recorded tracing information.
223  *
224  */
225 static VALUE
227 {
228  struct traceobj_arg *arg = get_traceobj_arg();
229 
230  /* clear tables */
232  st_clear(arg->object_table);
233  st_foreach(arg->str_table, free_keys_i, 0);
234  st_clear(arg->str_table);
235 
236  /* do not touch TracePoints */
237 
238  return Qnil;
239 }
240 
241 /*
242  * call-seq: trace_object_allocations { block }
243  *
244  * Starts tracing object allocations from the ObjectSpace extension module.
245  *
246  * For example:
247  *
248  * require 'objspace'
249  *
250  * class C
251  * include ObjectSpace
252  *
253  * def foo
254  * trace_object_allocations do
255  * obj = Object.new
256  * p "#{allocation_sourcefile(obj)}:#{allocation_sourceline(obj)}"
257  * end
258  * end
259  * end
260  *
261  * C.new.foo #=> "objtrace.rb:8"
262  *
263  * This example has included the ObjectSpace module to make it easier to read,
264  * but you can also use the ::trace_object_allocations notation (recommended).
265  *
266  * Note that this feature introduces a huge performance decrease and huge
267  * memory consumption.
268  */
269 static VALUE
271 {
274 }
275 
276 int rb_bug_reporter_add(void (*func)(FILE *, void *), void *data);
278 
279 static int
281 {
282  FILE *out = (FILE *)ptr;
283  VALUE obj = (VALUE)key;
284  struct allocation_info *info = (struct allocation_info *)val;
285 
286  fprintf(out, "-- %p (%s F: %p, ", (void *)obj, info->living ? "live" : "dead", (void *)info->flags);
287  if (info->class_path) fprintf(out, "C: %s", info->class_path);
288  else fprintf(out, "C: %p", (void *)info->klass);
289  fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line);
290  if (!NIL_P(info->mid)) fprintf(out, " (%s)", rb_id2name(SYM2ID(info->mid)));
291  fprintf(out, ")\n");
292 
293  return ST_CONTINUE;
294 }
295 
296 static void
298 {
299  fprintf(out, "== object_allocations_reporter: START\n");
300  if (tmp_trace_arg) {
302  }
303  fprintf(out, "== object_allocations_reporter: END\n");
304 }
305 
306 static VALUE
308 {
309  tmp_keep_remains = 1;
313  }
314 
315  return trace_object_allocations_start(self);
316 }
317 
318 static struct allocation_info *
320 {
321  if (tmp_trace_arg) {
322  struct allocation_info *info;
323  if (st_lookup(tmp_trace_arg->object_table, obj, (st_data_t *)&info)) {
324  return info;
325  }
326  }
327  return NULL;
328 }
329 
330 struct allocation_info *
332 {
333  return lookup_allocation_info(obj);
334 }
335 
336 /*
337  * call-seq: allocation_sourcefile(object) -> string
338  *
339  * Returns the source file origin from the given +object+.
340  *
341  * See ::trace_object_allocations for more information and examples.
342  */
343 static VALUE
345 {
346  struct allocation_info *info = lookup_allocation_info(obj);
347 
348  if (info && info->path) {
349  return rb_str_new2(info->path);
350  }
351  else {
352  return Qnil;
353  }
354 }
355 
356 /*
357  * call-seq: allocation_sourceline(object) -> string
358  *
359  * Returns the original line from source for from the given +object+.
360  *
361  * See ::trace_object_allocations for more information and examples.
362  */
363 static VALUE
365 {
366  struct allocation_info *info = lookup_allocation_info(obj);
367 
368  if (info) {
369  return INT2FIX(info->line);
370  }
371  else {
372  return Qnil;
373  }
374 }
375 
376 /*
377  * call-seq: allocation_class_path(object) -> string
378  *
379  * Returns the class for the given +object+.
380  *
381  * class A
382  * def foo
383  * ObjectSpace::trace_object_allocations do
384  * obj = Object.new
385  * p "#{ObjectSpace::allocation_class_path(obj)}"
386  * end
387  * end
388  * end
389  *
390  * A.new.foo #=> "Class"
391  *
392  * See ::trace_object_allocations for more information and examples.
393  */
394 static VALUE
396 {
397  struct allocation_info *info = lookup_allocation_info(obj);
398 
399  if (info && info->class_path) {
400  return rb_str_new2(info->class_path);
401  }
402  else {
403  return Qnil;
404  }
405 }
406 
407 /*
408  * call-seq: allocation_method_id(object) -> string
409  *
410  * Returns the method identifier for the given +object+.
411  *
412  * class A
413  * include ObjectSpace
414  *
415  * def foo
416  * trace_object_allocations do
417  * obj = Object.new
418  * p "#{allocation_class_path(obj)}##{allocation_method_id(obj)}"
419  * end
420  * end
421  * end
422  *
423  * A.new.foo #=> "Class#new"
424  *
425  * See ::trace_object_allocations for more information and examples.
426  */
427 static VALUE
429 {
430  struct allocation_info *info = lookup_allocation_info(obj);
431  if (info) {
432  return info->mid;
433  }
434  else {
435  return Qnil;
436  }
437 }
438 
439 /*
440  * call-seq: allocation_generation(object) -> Fixnum
441  *
442  * Returns garbage collector generation for the given +object+.
443  *
444  * class B
445  * include ObjectSpace
446  *
447  * def foo
448  * trace_object_allocations do
449  * obj = Object.new
450  * p "Generation is #{allocation_generation(obj)}"
451  * end
452  * end
453  * end
454  *
455  * B.new.foo #=> "Generation is 3"
456  *
457  * See ::trace_object_allocations for more information and examples.
458  */
459 static VALUE
461 {
462  struct allocation_info *info = lookup_allocation_info(obj);
463  if (info) {
464  return SIZET2NUM(info->generation);
465  }
466  else {
467  return Qnil;
468  }
469 }
470 
471 void
473 {
474 #if 0
475  rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
476 #endif
477 
478  rb_define_module_function(rb_mObjSpace, "trace_object_allocations", trace_object_allocations, 0);
479  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_start", trace_object_allocations_start, 0);
480  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_stop", trace_object_allocations_stop, 0);
481  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_clear", trace_object_allocations_clear, 0);
482 
483  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_debug_start", trace_object_allocations_debug_start, 0);
484 
485  rb_define_module_function(rb_mObjSpace, "allocation_sourcefile", allocation_sourcefile, 1);
486  rb_define_module_function(rb_mObjSpace, "allocation_sourceline", allocation_sourceline, 1);
487  rb_define_module_function(rb_mObjSpace, "allocation_class_path", allocation_class_path, 1);
488  rb_define_module_function(rb_mObjSpace, "allocation_method_id", allocation_method_id, 1);
489  rb_define_module_function(rb_mObjSpace, "allocation_generation", allocation_generation, 1);
490 }
st_table * object_table
static VALUE trace_object_allocations(VALUE self)
Definition: st.h:69
#define NUM2INT(x)
Definition: ruby.h:630
int st_insert(st_table *, st_data_t, st_data_t)
SSL_METHOD *(* func)(void)
Definition: ossl_ssl.c:113
#define SYM2ID(x)
Definition: ruby.h:356
int st_get_key(st_table *, st_data_t, st_data_t *)
static VALUE allocation_sourceline(VALUE self, VALUE obj)
st_table * st_init_strtable(void)
Definition: st.c:284
size_t rb_gc_count(void)
Definition: gc.c:5222
VALUE klass
Definition: objspace.h:9
st_table * str_table
static VALUE trace_object_allocations_stop(VALUE self)
static int free_keys_i(st_data_t key, st_data_t value, void *data)
RUBY_SYMBOL_EXPORT_BEGIN typedef unsigned long st_data_t
Definition: st.h:20
size_t generation
Definition: objspace.h:16
struct traceobj_arg * prev_traceobj_arg
int st_lookup(st_table *, st_data_t, st_data_t *)
#define ALLOC_N(type, n)
Definition: ruby.h:1341
#define val
static VALUE allocation_method_id(VALUE self, VALUE obj)
static int free_values_i(st_data_t key, st_data_t value, void *data)
int rb_bug_reporter_add(void(*func)(FILE *, void *), void *data)
Definition: error.c:282
#define NIL_P(v)
Definition: ruby.h:438
void st_add_direct(st_table *, st_data_t, st_data_t)
Definition: st.c:629
int st_delete(st_table *, st_data_t *, st_data_t *)
unsigned long line
Definition: objspace.h:13
static int tmp_keep_remains
void Init_object_tracing(VALUE rb_mObjSpace)
#define OBJ_FROZEN(x)
Definition: ruby.h:1193
static VALUE allocation_class_path(VALUE self, VALUE obj)
#define rb_str_new2
Definition: intern.h:840
static struct traceobj_arg * tmp_trace_arg
static struct traceobj_arg * get_traceobj_arg(void)
rb_trace_arg_t * rb_tracearg_from_tracepoint(VALUE tpval)
Definition: vm_trace.c:731
#define RSTRING_LEN(str)
Definition: ruby.h:841
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1661
VALUE rb_yield(VALUE)
Definition: vm_eval.c:948
VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:800
static void delete_unique_str(st_table *tbl, const char *str)
VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:807
void ruby_xfree(void *x)
Definition: gc.c:6245
static void freeobj_i(VALUE tpval, void *data)
static int object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr)
#define Qnil
Definition: ruby.h:427
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:1179
unsigned long VALUE
Definition: ruby.h:88
static VALUE result
Definition: nkf.c:40
#define RBASIC(obj)
Definition: ruby.h:1116
VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:766
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
Definition: eval.c:839
static VALUE allocation_sourcefile(VALUE self, VALUE obj)
static const char * make_unique_str(st_table *tbl, const char *str, long len)
st_table * st_init_numtable(void)
Definition: st.c:272
void * ruby_xmalloc(size_t size)
Definition: gc.c:6159
#define RSTRING_PTR(str)
Definition: ruby.h:845
struct allocation_info * objspace_lookup_allocation_info(VALUE obj)
static int object_allocations_reporter_registered
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:864
static VALUE trace_object_allocations_debug_start(VALUE self)
#define INT2FIX(i)
Definition: ruby.h:231
static struct allocation_info * lookup_allocation_info(VALUE obj)
const char * class_path
Definition: objspace.h:14
#define RUBY_INTERNAL_EVENT_NEWOBJ
Definition: ruby.h:1740
#define RUBY_INTERNAL_EVENT_FREEOBJ
Definition: ruby.h:1741
#define RBASIC_CLASS(obj)
Definition: ruby.h:759
VALUE rb_class_path_cached(VALUE)
Definition: variable.c:281
uint8_t key[16]
Definition: random.c:1250
#define RTEST(v)
Definition: ruby.h:437
VALUE newobj_trace
VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:772
static VALUE trace_object_allocations_clear(VALUE self)
const char * rb_id2name(ID id)
Definition: ripper.c:17271
VALUE rb_tracepoint_enable(VALUE tpval)
Definition: vm_trace.c:1009
static VALUE allocation_generation(VALUE self, VALUE obj)
void st_clear(st_table *)
Definition: st.c:308
VALUE rb_define_module(const char *name)
Definition: class.c:727
static void object_allocations_reporter(FILE *out, void *ptr)
VALUE flags
Definition: objspace.h:8
#define NULL
Definition: _sdbm.c:102
const char * path
Definition: objspace.h:12
int st_foreach(st_table *, int(*)(ANYARGS), st_data_t)
Definition: st.c:1034
VALUE rb_tracepoint_disable(VALUE tpval)
Definition: vm_trace.c:1028
static void newobj_i(VALUE tpval, void *data)
#define SIZET2NUM(v)
Definition: ruby.h:262
static VALUE trace_object_allocations_start(VALUE self)
VALUE freeobj_trace