Ruby  2.5.0dev(2017-10-22revision60238)
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;
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 {
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, (st_data_t *)&result);
43  }
44  else {
45  result = (char *)ruby_xmalloc(len+1);
46  strncpy(result, str, len);
47  result[len] = 0;
48  st_add_direct(tbl, (st_data_t)result, 1);
49  }
50  return result;
51  }
52 }
53 
54 static void
55 delete_unique_str(st_table *tbl, const char *str)
56 {
57  if (str) {
58  st_data_t n;
59 
60  st_lookup(tbl, (st_data_t)str, &n);
61  if (n == 1) {
62  st_delete(tbl, (st_data_t *)&str, 0);
63  ruby_xfree((char *)str);
64  }
65  else {
66  st_insert(tbl, (st_data_t)str, n-1);
67  }
68  }
69 }
70 
71 static void
72 newobj_i(VALUE tpval, void *data)
73 {
74  struct traceobj_arg *arg = (struct traceobj_arg *)data;
76  VALUE obj = rb_tracearg_object(tparg);
77  VALUE path = rb_tracearg_path(tparg);
78  VALUE line = rb_tracearg_lineno(tparg);
79  VALUE mid = rb_tracearg_method_id(tparg);
80  VALUE klass = rb_tracearg_defined_class(tparg);
81  struct allocation_info *info;
82  const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
83  VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
84  const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
85 
86  if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
87  if (arg->keep_remains) {
88  if (info->living) {
89  /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
90  }
91  }
92  /* reuse info */
93  delete_unique_str(arg->str_table, info->path);
94  delete_unique_str(arg->str_table, info->class_path);
95  }
96  else {
97  info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
98  }
99  info->living = 1;
100  info->flags = RBASIC(obj)->flags;
101  info->klass = RBASIC_CLASS(obj);
102 
103  info->path = path_cstr;
104  info->line = NUM2INT(line);
105  info->mid = mid;
106  info->class_path = class_path_cstr;
107  info->generation = rb_gc_count();
108  st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
109 }
110 
111 static void
112 freeobj_i(VALUE tpval, void *data)
113 {
114  struct traceobj_arg *arg = (struct traceobj_arg *)data;
116  VALUE obj = rb_tracearg_object(tparg);
117  struct allocation_info *info;
118 
119  if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
120  if (arg->keep_remains) {
121  info->living = 0;
122  }
123  else {
124  st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
125  delete_unique_str(arg->str_table, info->path);
126  delete_unique_str(arg->str_table, info->class_path);
127  ruby_xfree(info);
128  }
129  }
130 }
131 
132 static int
133 free_keys_i(st_data_t key, st_data_t value, void *data)
134 {
135  ruby_xfree((void *)key);
136  return ST_CONTINUE;
137 }
138 
139 static int
140 free_values_i(st_data_t key, st_data_t value, void *data)
141 {
142  ruby_xfree((void *)value);
143  return ST_CONTINUE;
144 }
145 
146 static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */
147 static int tmp_keep_remains; /* TODO: Do not use global variables */
148 
149 static struct traceobj_arg *
150 get_traceobj_arg(void)
151 {
152  if (tmp_trace_arg == 0) {
153  tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1);
154  tmp_trace_arg->running = 0;
155  tmp_trace_arg->keep_remains = tmp_keep_remains;
156  tmp_trace_arg->newobj_trace = 0;
157  tmp_trace_arg->freeobj_trace = 0;
158  tmp_trace_arg->object_table = st_init_numtable();
159  tmp_trace_arg->str_table = st_init_strtable();
160  }
161  return tmp_trace_arg;
162 }
163 
164 /*
165  * call-seq: trace_object_allocations_start
166  *
167  * Starts tracing object allocations.
168  *
169  */
170 static VALUE
171 trace_object_allocations_start(VALUE self)
172 {
173  struct traceobj_arg *arg = get_traceobj_arg();
174 
175  if (arg->running++ > 0) {
176  /* do nothing */
177  }
178  else {
179  if (arg->newobj_trace == 0) {
182  }
185  }
186 
187  return Qnil;
188 }
189 
190 /*
191  * call-seq: trace_object_allocations_stop
192  *
193  * Stop tracing object allocations.
194  *
195  * Note that if ::trace_object_allocations_start is called n-times, then
196  * tracing will stop after calling ::trace_object_allocations_stop n-times.
197  *
198  */
199 static VALUE
200 trace_object_allocations_stop(VALUE self)
201 {
202  struct traceobj_arg *arg = get_traceobj_arg();
203 
204  if (arg->running > 0) {
205  arg->running--;
206  }
207 
208  if (arg->running == 0) {
211  arg->newobj_trace = 0;
212  arg->freeobj_trace = 0;
213  }
214 
215  return Qnil;
216 }
217 
218 /*
219  * call-seq: trace_object_allocations_clear
220  *
221  * Clear recorded tracing information.
222  *
223  */
224 static VALUE
225 trace_object_allocations_clear(VALUE self)
226 {
227  struct traceobj_arg *arg = get_traceobj_arg();
228 
229  /* clear tables */
230  st_foreach(arg->object_table, free_values_i, 0);
231  st_clear(arg->object_table);
232  st_foreach(arg->str_table, free_keys_i, 0);
233  st_clear(arg->str_table);
234 
235  /* do not touch TracePoints */
236 
237  return Qnil;
238 }
239 
240 /*
241  * call-seq: trace_object_allocations { block }
242  *
243  * Starts tracing object allocations from the ObjectSpace extension module.
244  *
245  * For example:
246  *
247  * require 'objspace'
248  *
249  * class C
250  * include ObjectSpace
251  *
252  * def foo
253  * trace_object_allocations do
254  * obj = Object.new
255  * p "#{allocation_sourcefile(obj)}:#{allocation_sourceline(obj)}"
256  * end
257  * end
258  * end
259  *
260  * C.new.foo #=> "objtrace.rb:8"
261  *
262  * This example has included the ObjectSpace module to make it easier to read,
263  * but you can also use the ::trace_object_allocations notation (recommended).
264  *
265  * Note that this feature introduces a huge performance decrease and huge
266  * memory consumption.
267  */
268 static VALUE
269 trace_object_allocations(VALUE self)
270 {
271  trace_object_allocations_start(self);
272  return rb_ensure(rb_yield, Qnil, trace_object_allocations_stop, self);
273 }
274 
275 int rb_bug_reporter_add(void (*func)(FILE *, void *), void *data);
276 static int object_allocations_reporter_registered = 0;
277 
278 static int
279 object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr)
280 {
281  FILE *out = (FILE *)ptr;
282  VALUE obj = (VALUE)key;
283  struct allocation_info *info = (struct allocation_info *)val;
284 
285  fprintf(out, "-- %p (%s F: %p, ", (void *)obj, info->living ? "live" : "dead", (void *)info->flags);
286  if (info->class_path) fprintf(out, "C: %s", info->class_path);
287  else fprintf(out, "C: %p", (void *)info->klass);
288  fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line);
289  if (!NIL_P(info->mid)) {
290  VALUE m = rb_sym2str(info->mid);
291  fprintf(out, " (%s)", RSTRING_PTR(m));
292  }
293  fprintf(out, ")\n");
294 
295  return ST_CONTINUE;
296 }
297 
298 static void
299 object_allocations_reporter(FILE *out, void *ptr)
300 {
301  fprintf(out, "== object_allocations_reporter: START\n");
302  if (tmp_trace_arg) {
303  st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out);
304  }
305  fprintf(out, "== object_allocations_reporter: END\n");
306 }
307 
308 static VALUE
309 trace_object_allocations_debug_start(VALUE self)
310 {
311  tmp_keep_remains = 1;
312  if (object_allocations_reporter_registered == 0) {
313  object_allocations_reporter_registered = 1;
314  rb_bug_reporter_add(object_allocations_reporter, 0);
315  }
316 
317  return trace_object_allocations_start(self);
318 }
319 
320 static struct allocation_info *
321 lookup_allocation_info(VALUE obj)
322 {
323  if (tmp_trace_arg) {
324  struct allocation_info *info;
325  if (st_lookup(tmp_trace_arg->object_table, obj, (st_data_t *)&info)) {
326  return info;
327  }
328  }
329  return NULL;
330 }
331 
332 struct allocation_info *
334 {
335  return lookup_allocation_info(obj);
336 }
337 
338 /*
339  * call-seq: allocation_sourcefile(object) -> string
340  *
341  * Returns the source file origin from the given +object+.
342  *
343  * See ::trace_object_allocations for more information and examples.
344  */
345 static VALUE
346 allocation_sourcefile(VALUE self, VALUE obj)
347 {
348  struct allocation_info *info = lookup_allocation_info(obj);
349 
350  if (info && info->path) {
351  return rb_str_new2(info->path);
352  }
353  else {
354  return Qnil;
355  }
356 }
357 
358 /*
359  * call-seq: allocation_sourceline(object) -> integer
360  *
361  * Returns the original line from source for from the given +object+.
362  *
363  * See ::trace_object_allocations for more information and examples.
364  */
365 static VALUE
366 allocation_sourceline(VALUE self, VALUE obj)
367 {
368  struct allocation_info *info = lookup_allocation_info(obj);
369 
370  if (info) {
371  return INT2FIX(info->line);
372  }
373  else {
374  return Qnil;
375  }
376 }
377 
378 /*
379  * call-seq: allocation_class_path(object) -> string
380  *
381  * Returns the class for the given +object+.
382  *
383  * class A
384  * def foo
385  * ObjectSpace::trace_object_allocations do
386  * obj = Object.new
387  * p "#{ObjectSpace::allocation_class_path(obj)}"
388  * end
389  * end
390  * end
391  *
392  * A.new.foo #=> "Class"
393  *
394  * See ::trace_object_allocations for more information and examples.
395  */
396 static VALUE
397 allocation_class_path(VALUE self, VALUE obj)
398 {
399  struct allocation_info *info = lookup_allocation_info(obj);
400 
401  if (info && info->class_path) {
402  return rb_str_new2(info->class_path);
403  }
404  else {
405  return Qnil;
406  }
407 }
408 
409 /*
410  * call-seq: allocation_method_id(object) -> string
411  *
412  * Returns the method identifier for the given +object+.
413  *
414  * class A
415  * include ObjectSpace
416  *
417  * def foo
418  * trace_object_allocations do
419  * obj = Object.new
420  * p "#{allocation_class_path(obj)}##{allocation_method_id(obj)}"
421  * end
422  * end
423  * end
424  *
425  * A.new.foo #=> "Class#new"
426  *
427  * See ::trace_object_allocations for more information and examples.
428  */
429 static VALUE
430 allocation_method_id(VALUE self, VALUE obj)
431 {
432  struct allocation_info *info = lookup_allocation_info(obj);
433  if (info) {
434  return info->mid;
435  }
436  else {
437  return Qnil;
438  }
439 }
440 
441 /*
442  * call-seq: allocation_generation(object) -> integer or nil
443  *
444  * Returns garbage collector generation for the given +object+.
445  *
446  * class B
447  * include ObjectSpace
448  *
449  * def foo
450  * trace_object_allocations do
451  * obj = Object.new
452  * p "Generation is #{allocation_generation(obj)}"
453  * end
454  * end
455  * end
456  *
457  * B.new.foo #=> "Generation is 3"
458  *
459  * See ::trace_object_allocations for more information and examples.
460  */
461 static VALUE
462 allocation_generation(VALUE self, VALUE obj)
463 {
464  struct allocation_info *info = lookup_allocation_info(obj);
465  if (info) {
466  return SIZET2NUM(info->generation);
467  }
468  else {
469  return Qnil;
470  }
471 }
472 
473 void
475 {
476 #if 0
477  rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
478 #endif
479 
480  rb_define_module_function(rb_mObjSpace, "trace_object_allocations", trace_object_allocations, 0);
481  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_start", trace_object_allocations_start, 0);
482  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_stop", trace_object_allocations_stop, 0);
483  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_clear", trace_object_allocations_clear, 0);
484 
485  rb_define_module_function(rb_mObjSpace, "trace_object_allocations_debug_start", trace_object_allocations_debug_start, 0);
486 
487  rb_define_module_function(rb_mObjSpace, "allocation_sourcefile", allocation_sourcefile, 1);
488  rb_define_module_function(rb_mObjSpace, "allocation_sourceline", allocation_sourceline, 1);
489  rb_define_module_function(rb_mObjSpace, "allocation_class_path", allocation_class_path, 1);
490  rb_define_module_function(rb_mObjSpace, "allocation_method_id", allocation_method_id, 1);
491  rb_define_module_function(rb_mObjSpace, "allocation_generation", allocation_generation, 1);
492 }
st_table * object_table
Definition: st.h:79
#define NUM2INT(x)
Definition: ruby.h:684
#define st_foreach
Definition: regint.h:186
Definition: st.h:99
int st_get_key(st_table *, st_data_t, st_data_t *)
Definition: st.c:1062
size_t rb_gc_count(void)
Definition: gc.c:6766
#define st_delete
Definition: regint.h:182
VALUE klass
Definition: objspace.h:9
#define st_lookup
Definition: regint.h:185
st_table * str_table
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1035
#define st_init_strtable
Definition: regint.h:180
RUBY_SYMBOL_EXPORT_BEGIN typedef unsigned long st_data_t
Definition: st.h:22
size_t generation
Definition: objspace.h:16
struct traceobj_arg * prev_traceobj_arg
#define ALLOC_N(type, n)
Definition: ruby.h:1587
#define val
#define NIL_P(v)
Definition: ruby.h:451
unsigned long line
Definition: objspace.h:13
void Init_object_tracing(VALUE rb_mObjSpace)
#define rb_str_new2
Definition: intern.h:835
rb_trace_arg_t * rb_tracearg_from_tracepoint(VALUE tpval)
Definition: vm_trace.c:717
#define RSTRING_LEN(str)
Definition: ruby.h:971
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1731
VALUE rb_yield(VALUE)
Definition: vm_eval.c:973
VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:786
VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:800
void ruby_xfree(void *x)
Definition: gc.c:8085
#define Qnil
Definition: ruby.h:438
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:1211
unsigned long VALUE
Definition: ruby.h:85
#define RBASIC(obj)
Definition: ruby.h:1197
VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:752
register unsigned int len
Definition: zonetab.h:51
void * ruby_xmalloc(size_t size)
Definition: gc.c:7997
#define RSTRING_PTR(str)
Definition: ruby.h:975
struct allocation_info * objspace_lookup_allocation_info(VALUE obj)
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:857
#define INT2FIX(i)
Definition: ruby.h:232
const char * class_path
Definition: objspace.h:14
#define RUBY_INTERNAL_EVENT_NEWOBJ
Definition: ruby.h:2106
#define RUBY_INTERNAL_EVENT_FREEOBJ
Definition: ruby.h:2107
#define st_init_numtable
Definition: regint.h:178
#define RBASIC_CLASS(obj)
Definition: ruby.h:878
VALUE rb_class_path_cached(VALUE)
Definition: variable.c:319
#define RTEST(v)
Definition: ruby.h:450
VALUE newobj_trace
#define st_add_direct
Definition: regint.h:187
#define OBJ_FROZEN(x)
Definition: ruby.h:1304
VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg)
Definition: vm_trace.c:758
#define st_insert
Definition: regint.h:184
int rb_bug_reporter_add(void(*func)(FILE *, void *), void *data)
Definition: error.c:353
VALUE rb_tracepoint_enable(VALUE tpval)
Definition: vm_trace.c:1011
void st_clear(st_table *)
Definition: st.c:655
VALUE rb_define_module(const char *name)
Definition: class.c:768
VALUE flags
Definition: objspace.h:8
#define NULL
Definition: _sdbm.c:102
const char * path
Definition: objspace.h:12
VALUE rb_tracepoint_disable(VALUE tpval)
Definition: vm_trace.c:1030
#define SIZET2NUM(v)
Definition: ruby.h:264
VALUE freeobj_trace
#define rb_sym2str(sym)
Definition: console.c:107