summaryrefslogtreecommitdiff
path: root/ext/objspace
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-05-27 10:01:45 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-05-27 10:01:45 +0000
commit050dd10d6f8933268ed0fd211eb5bcc8ad49f678 (patch)
tree12191d6df36efdbf607ac146a674963d8f17d6cc /ext/objspace
parent14f6c8ca8ae785b14718cac1ad8891ca20e604e1 (diff)
* ext/objspace/objspace.c: support ObjectSpace.trace_object_allocations.
Read the following test to know HOWTO. This feature is a sample of RUBY_INTERNAL_EVENT. * test/objspace/test_objspace.rb: add a test. * ext/objspace/object_tracing.c: ditto. * gc.c (rb_gc_count): add. THis function returns GC count. * internal.h: add decl. of rb_gc_count(). Same as `GC.count'. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40957 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/objspace')
-rw-r--r--ext/objspace/object_tracing.c196
-rw-r--r--ext/objspace/objspace.c4
2 files changed, 200 insertions, 0 deletions
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
new file mode 100644
index 0000000000..2dc531c738
--- /dev/null
+++ b/ext/objspace/object_tracing.c
@@ -0,0 +1,196 @@
+/**********************************************************************
+
+ object_traceing.c - Object Tracing mechanism/ObjectSpace extender for MRI.
+
+ $Author: sorah $
+ created at: Mon May 27 16:27:44 2013
+
+ NOTE: This extension library is not expected to exist except C Ruby.
+ NOTE: This feature is an example usage of internal event tracing APIs.
+
+ All the files in this distribution are covered under the Ruby's
+ license (see the file COPYING).
+
+**********************************************************************/
+
+#include "ruby/ruby.h"
+#include "ruby/debug.h"
+
+size_t rb_gc_count(void); /* from gc.c */
+
+struct traceobj_arg {
+ VALUE newobj_trace;
+ VALUE freeobj_trace;
+ st_table *object_table;
+ st_table *path_table;
+ struct traceobj_arg *prev_traceobj_arg;
+};
+
+struct traceobj_arg *traceobj_arg; /* TODO: do not use GLOBAL VARIABLE!!! */
+
+struct allocation_info {
+ char *path;
+ unsigned long line;
+ size_t generation;
+};
+
+static void
+newobj_i(VALUE tpval, void *data)
+{
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
+ VALUE obj = rb_tracearg_object(tparg);
+ VALUE path = rb_tracearg_path(tparg);
+ VALUE line = rb_tracearg_lineno(tparg);
+ int path_len = RSTRING_LEN(path);
+ struct allocation_info *info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
+ char *path_cstr = ruby_xmalloc(path_len + 1);
+ char *path_stored_cstr;
+
+ strncpy(path_cstr, RSTRING_PTR(path), path_len);
+ path_cstr[path_len] = 0;
+
+ if (st_get_key(arg->path_table, (st_data_t)path_cstr, (st_data_t *)&path_stored_cstr)) {
+ st_data_t n;
+ st_lookup(arg->path_table, (st_data_t)path_stored_cstr, &n);
+ st_insert(arg->path_table, (st_data_t)path_stored_cstr, n+1);
+ ruby_xfree(path_cstr);
+ path_cstr = path_stored_cstr;
+ }
+ else {
+ st_add_direct(arg->path_table, (st_data_t)path_cstr, 1);
+ }
+
+ info->path = path_cstr;
+ info->line = NUM2INT(line);
+ info->generation = rb_gc_count();
+ st_insert(arg->path_table, (st_data_t)path_cstr, 0);
+ st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
+}
+
+static void
+freeobj_i(VALUE tpval, void *data)
+{
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
+ VALUE obj = rb_tracearg_object(tparg);
+ struct allocation_info *info;
+ st_data_t n;
+ if (st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info)) {
+ st_lookup(arg->path_table, (st_data_t)info->path, &n);
+ if (n == 1) {
+ st_delete(arg->path_table, (st_data_t *)&info->path, 0);
+ ruby_xfree(info->path);
+ }
+ else {
+ st_insert(arg->path_table, (st_data_t)info->path, n-1);
+ }
+
+ ruby_xfree(info);
+ }
+}
+
+static int
+free_keys_i(st_data_t key, st_data_t value, void *data)
+{
+ ruby_xfree((void *)key);
+ return ST_CONTINUE;
+}
+
+static int
+free_values_i(st_data_t key, st_data_t value, void *data)
+{
+ ruby_xfree((void *)value);
+ return ST_CONTINUE;
+}
+
+static VALUE
+stop_trace_object_allocations(void *data)
+{
+ struct traceobj_arg *arg = (struct traceobj_arg *)data;
+ rb_tracepoint_disable(arg->newobj_trace);
+ rb_tracepoint_disable(arg->freeobj_trace);
+ st_foreach(arg->object_table, free_values_i, 0);
+ st_foreach(arg->path_table, free_keys_i, 0);
+ st_free_table(arg->object_table);
+ st_free_table(arg->path_table);
+ traceobj_arg = arg->prev_traceobj_arg;
+
+ return Qnil;
+}
+
+static VALUE
+trace_object_allocations(VALUE objspace)
+{
+ struct traceobj_arg arg;
+
+ arg.newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, &arg);
+ arg.freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREE, freeobj_i, &arg);
+ arg.object_table = st_init_numtable();
+ arg.path_table = st_init_strtable();
+
+ arg.prev_traceobj_arg = traceobj_arg;
+ traceobj_arg = &arg;
+
+ rb_tracepoint_enable(arg.newobj_trace);
+ rb_tracepoint_enable(arg.freeobj_trace);
+
+ return rb_ensure(rb_yield, Qnil, stop_trace_object_allocations, (VALUE)&arg);
+}
+
+struct allocation_info *
+allocation_info(VALUE obj)
+{
+ if (traceobj_arg) {
+ struct allocation_info *info;
+ if (st_lookup(traceobj_arg->object_table, obj, (st_data_t *)&info)) {
+ return info;
+ }
+ }
+ return NULL;
+}
+
+static VALUE
+allocation_sourcefile(VALUE objspace, VALUE obj)
+{
+ struct allocation_info *info = allocation_info(obj);
+ if (info) {
+ return rb_str_new2(info->path);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+static VALUE
+allocation_sourceline(VALUE objspace, VALUE obj)
+{
+ struct allocation_info *info = allocation_info(obj);
+ if (info) {
+ return INT2FIX(info->line);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+static VALUE
+allocation_generation(VALUE objspace, VALUE obj)
+{
+ struct allocation_info *info = allocation_info(obj);
+ if (info) {
+ return SIZET2NUM(info->generation);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+void
+Init_object_tracing(VALUE rb_mObjSpace)
+{
+ rb_define_module_function(rb_mObjSpace, "trace_object_allocations", trace_object_allocations, 0);
+ rb_define_module_function(rb_mObjSpace, "allocation_sourcefile", allocation_sourcefile, 1);
+ rb_define_module_function(rb_mObjSpace, "allocation_sourceline", allocation_sourceline, 1);
+ rb_define_module_function(rb_mObjSpace, "allocation_generation", allocation_generation, 1);
+}
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 720c918555..a077a1e4e0 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -779,6 +779,8 @@ reachable_objects_from(VALUE self, VALUE obj)
}
}
+void Init_object_tracing(VALUE rb_mObjSpace);
+
/* objspace library extends ObjectSpace module and add several
* methods to get internal statistic information about
* object/memory management.
@@ -807,4 +809,6 @@ Init_objspace(void)
rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0);
rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0);
rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
+
+ Init_object_tracing(rb_mObjSpace);
}