aboutsummaryrefslogtreecommitdiffstats
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
parent14f6c8ca8ae785b14718cac1ad8891ca20e604e1 (diff)
downloadruby-050dd10d6f8933268ed0fd211eb5bcc8ad49f678.tar.gz
* 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
-rw-r--r--ChangeLog14
-rw-r--r--ext/objspace/object_tracing.c196
-rw-r--r--ext/objspace/objspace.c4
-rw-r--r--gc.c8
-rw-r--r--internal.h3
-rw-r--r--test/objspace/test_objspace.rb18
6 files changed, 242 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 7ce749e112..9c3d6da5d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Mon May 27 18:57:28 2013 Koichi Sasada <ko1@atdot.net>
+
+ * 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'.
+
Mon May 27 17:33:28 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
* tool/rbinstall.rb (install_recursive): add maxdepth option.
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);
}
diff --git a/gc.c b/gc.c
index c405110ebf..b06b988eab 100644
--- a/gc.c
+++ b/gc.c
@@ -3946,6 +3946,12 @@ gc_count_add_each_types(VALUE hash, const char *name, const size_t *types)
}
#endif
+size_t
+rb_gc_count(void)
+{
+ return rb_objspace.count;
+}
+
/*
* call-seq:
* GC.count -> Integer
@@ -3959,7 +3965,7 @@ gc_count_add_each_types(VALUE hash, const char *name, const size_t *types)
static VALUE
gc_count(VALUE self)
{
- return UINT2NUM(rb_objspace.count);
+ return SIZET2NUM(rb_gc_count());
}
/*
diff --git a/internal.h b/internal.h
index 89cce7e69a..be522c8b09 100644
--- a/internal.h
+++ b/internal.h
@@ -443,6 +443,9 @@ void rb_gc_mark_global_tbl(void);
void rb_mark_generic_ivar(VALUE);
void rb_mark_generic_ivar_tbl(void);
+/* gc.c */
+size_t rb_gc_count();
+
RUBY_SYMBOL_EXPORT_END
#if defined(__cplusplus)
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index 8a8d7ce8ea..11e69bd7e8 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -108,4 +108,22 @@ class TestObjSpace < Test::Unit::TestCase
}
eom
end
+
+ def test_traceobject
+ o0 = Object.new
+ ObjectSpace.trace_object_allocations{
+ o1 = Object.new; line1 = __LINE__
+ o2 = "a"+"b" ; line2 = __LINE__
+ o3 = [1, 2] ; line3 = __LINE__
+
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(o0))
+ assert_equal(nil, ObjectSpace.allocation_sourceline(o0))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1))
+ assert_equal(line1, ObjectSpace.allocation_sourceline(o1))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2))
+ assert_equal(line2, ObjectSpace.allocation_sourceline(o2))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o3))
+ assert_equal(line3, ObjectSpace.allocation_sourceline(o3))
+ }
+ end
end