[前][次][番号順一覧][スレッド一覧]

ruby-changes:28905

From: ko1 <ko1@a...>
Date: Mon, 27 May 2013 19:01:59 +0900 (JST)
Subject: [ruby-changes:28905] ko1:r40957 (trunk): * ext/objspace/objspace.c: support ObjectSpace.trace_object_allocations.

ko1	2013-05-27 19:01:45 +0900 (Mon, 27 May 2013)

  New Revision: 40957

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=40957

  Log:
    * 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'.

  Added files:
    trunk/ext/objspace/object_tracing.c
  Modified files:
    trunk/ChangeLog
    trunk/ext/objspace/objspace.c
    trunk/gc.c
    trunk/internal.h
    trunk/test/objspace/test_objspace.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 40956)
+++ ChangeLog	(revision 40957)
@@ -1,3 +1,17 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Mon May 27 18:57:28 2013  Koichi Sasada  <ko1@a...>
+
+	* 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@r...>
 
 	* tool/rbinstall.rb (install_recursive): add maxdepth option.
Index: gc.c
===================================================================
--- gc.c	(revision 40956)
+++ gc.c	(revision 40957)
@@ -3946,6 +3946,12 @@ gc_count_add_each_types(VALUE hash, cons https://github.com/ruby/ruby/blob/trunk/gc.c#L3946
 }
 #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, cons https://github.com/ruby/ruby/blob/trunk/gc.c#L3965
 static VALUE
 gc_count(VALUE self)
 {
-    return UINT2NUM(rb_objspace.count);
+    return SIZET2NUM(rb_gc_count());
 }
 
 /*
Index: ext/objspace/objspace.c
===================================================================
--- ext/objspace/objspace.c	(revision 40956)
+++ ext/objspace/objspace.c	(revision 40957)
@@ -779,6 +779,8 @@ reachable_objects_from(VALUE self, VALUE https://github.com/ruby/ruby/blob/trunk/ext/objspace/objspace.c#L779
     }
 }
 
+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) https://github.com/ruby/ruby/blob/trunk/ext/objspace/objspace.c#L809
     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);
 }
Index: ext/objspace/object_tracing.c
===================================================================
--- ext/objspace/object_tracing.c	(revision 0)
+++ ext/objspace/object_tracing.c	(revision 40957)
@@ -0,0 +1,196 @@ https://github.com/ruby/ruby/blob/trunk/ext/objspace/object_tracing.c#L1
+/**********************************************************************
+
+  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);
+}
Index: internal.h
===================================================================
--- internal.h	(revision 40956)
+++ internal.h	(revision 40957)
@@ -443,6 +443,9 @@ void rb_gc_mark_global_tbl(void); https://github.com/ruby/ruby/blob/trunk/internal.h#L443
 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)
Index: test/objspace/test_objspace.rb
===================================================================
--- test/objspace/test_objspace.rb	(revision 40956)
+++ test/objspace/test_objspace.rb	(revision 40957)
@@ -108,4 +108,22 @@ class TestObjSpace < Test::Unit::TestCas https://github.com/ruby/ruby/blob/trunk/test/objspace/test_objspace.rb#L108
     }
     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

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]