[#35789] [Ruby 1.9 - Bug #407] (Open) String#<< — Shyouhei Urabe <redmine@...>

チケット #407 が報告されました。 (by Shyouhei Urabe)

13 messages 2008/08/06

[#35845] [Bug #437] test_strftime(TestTime) fails on Solaris — Shugo Maeda <redmine@...>

Bug #437: test_strftime(TestTime) fails on Solaris

24 messages 2008/08/13
[#35855] Re: [Bug #437] test_strftime(TestTime) fails on Solaris — "Shugo Maeda" <shugo@...> 2008/08/15

前田です。

[#35856] Re: [Bug #437] test_strftime(TestTime) fails on Solaris — SATOH Fumiyasu <fumiyas@...> 2008/08/15

さとうふみやす @ OSS テクノロジです。

[#35857] Re: [Bug #437] test_strftime(TestTime) fails on Solaris — Yukihiro Matsumoto <matz@...> 2008/08/15

まつもと ゆきひろです

[#35870] Re: [Bug #437] test_strftime(TestTime) fails on Solaris — "Shugo Maeda" <shugo@...> 2008/08/18

前田です。

[#35863] Refactoring of enumerating prime numbers — "Yugui (Yuki Sonoda)" <yugui@...>

Yuguiです。

20 messages 2008/08/16
[#35865] Re: Refactoring of enumerating prime numbers — keiju@... (keiju ISHITSUKA) 2008/08/17

けいじゅ@いしつかです.

[#35867] Re: Refactoring of enumerating prime numbers — "Yugui (Yuki Sonoda)" <yugui@...> 2008/08/17

Yuguiです。

[#35875] Re: Refactoring of enumerating prime numbers — keiju@... (keiju ISHITSUKA) 2008/08/19

けいじゅ@いしつかです.

[#35877] Re: Refactoring of enumerating prime numbers — Nobuyoshi Nakada <nobu@...> 2008/08/19

なかだです。

[#35882] Re: Refactoring of enumerating prime numbers — keiju@... (石塚圭樹) 2008/08/20

けいじゅ@いしつかです.

[#35904] [Feature:1.9] pack format 'm' based on RFC 4648 — "Yusuke ENDOH" <mame@...>

遠藤です。

14 messages 2008/08/21
[#36442] [Feature #471] pack format 'm' based on RFC 4648 — Yuki Sonoda <redmine@...> 2008/09/22

チケット #471 が更新されました。 (by Yuki Sonoda)

[#35906] %N for Time#strftime — "Shugo Maeda" <shugo@...>

前田です。

13 messages 2008/08/21

[#35986] 1.9と1.8で、delegateのインスタンスのクラス名の違う — Fujioka <fuj@...>

xibbarこと藤岡です。

17 messages 2008/08/26
[#35987] Re: 1.9と1.8で、delegateのインスタンスのクラス名の違う — Yukihiro Matsumoto <matz@...> 2008/08/26

まつもと ゆきひろです

[#35991] Re: 1.9と1.8で、delegateのインスタンスのクラス名の違う — keiju@... (石塚圭樹) 2008/08/26

けいじゅ@いしつかです.

[#35994] Re: 1.9と1.8で、delegateのインスタンスのクラス名の違う — Fujioka <fuj@...> 2008/08/27

藤岡です。

[#35998] Re: 1.9と1.8で、delegateのインスタンスのクラス名の違う — keiju@... (石塚圭樹) 2008/08/27

けいじゅ@いしつかです.

[#36066] Numeric#scalar? — Tadayoshi Funaba <tadf@...>

1.9 の Numeric#scalar? について、適当でないのでは (real? などのほうがい

24 messages 2008/08/31
[#36069] Re: Numeric#scalar? — Shin-ichiro HARA <sinara@...> 2008/08/31

原です。

[#36104] Re: Numeric#scalar? — Tadayoshi Funaba <tadf@...> 2008/09/02

> やはり、scalar? はずれているんじゃないかな。real? の方がいい

[#36122] Re: Numeric#scalar? — Shin-ichiro HARA <sinara@...> 2008/09/03

原です。

[#36133] Re: Numeric#scalar? — Tadayoshi Funaba <tadf@...> 2008/09/03

> ここで、scalar? を疑問視する理由を復習すると、たとえば、「複

[#36173] Re: Numeric#scalar? — Tadayoshi Funaba <tadf@...> 2008/09/05

1.9.1 までに時間がないので scalar? だけ何とかしたいと思っていましたが、

[#36183] Re: Numeric#scalar? — "Shugo Maeda" <shugo@...> 2008/09/06

前田です。

[#36186] Re: Numeric#scalar? — Shin-ichiro HARA <sinara@...> 2008/09/06

原です。

[ruby-dev:35806] Re: make profiler for gc

From: Narihiro Nakamura <authorNari@...>
Date: 2008-08-09 18:11:48 UTC
List: ruby-dev #35806
nariです。

返信ありがとうございます。

Yukihiro Matsumoto さんは書きました:
> いいんじゃないかと思いますが、これだとstatisticのキーはあいか
> わらずSymbolになってますね。すでに指摘したようにネストしたク
> ラスに対応するため、ここはClassそのものか、あるいは文字列がキー
> になるべきではないかと思います。

すみません。うっかりしていました。
その様に修正します。

Takao Kouji さんは書きました:
> これは、nari くんを含め、直接話をしたときに
> GC::Profiler.enabled? という案もでたのですが、
> どうでしょうかね。

すみません、これはruby-coreでも指摘された点で、
すでに手元では修正済みだったのでした。


指摘を頂いた点を修正しました。

== module,method
* GC::Profiler
Profiler用のモジュール

* GC::Profiler.enabled?
現在のステータスを返却

* GC::Profiler.enable
Profilerを有効化

* GC::Profiler.disable
Profilerを無効化

* GC::Profiler.clear
Profilerにて保持していたデータを削除

* GC::Profiler.report
Profile結果表示

* GC::Profiler.result
Profile結果を文字列で返却

* ObjectSpace.statistic
ObjectSpace内の統計を取ります。
戻り値 :
{:TOTAL=>10000, :FREE=>3011, "Foo::Bar"=>6, "Array"=>404, ...}


今回添付したパッチで問題なければコミットしたいと思います。

-- 
Narihiro Nakamura <authorNari@gmail.com>

Attachments (1)

gc_profiler.diff (18.1 KB, text/x-diff)
Index: gc.c
===================================================================
--- gc.c	(revision 18458)
+++ gc.c	(working copy)
@@ -92,6 +92,133 @@
 
 #undef GC_DEBUG
 
+/* for GC profile */
+#define GC_PROFILE_MORE_DETAIL 0
+typedef struct gc_profile_record {
+    double gc_time;
+    double gc_mark_time;
+    double gc_sweep_time;
+    double gc_invoke_time;
+    size_t heap_use_slots;
+    size_t heap_live_objects;
+    size_t heap_free_objects;
+    size_t heap_total_objects;
+    size_t heap_use_size;
+    size_t heap_total_size;
+    int have_finalize;
+    size_t allocate_increase;
+    size_t allocate_limit;
+} gc_profile_record;
+
+/* referenced document http://kzk9.net/column/time.html */
+static double
+getrusage_time(void)
+{
+    struct rusage usage;
+    struct timeval time;
+    getrusage(RUSAGE_SELF, &usage);
+    time = usage.ru_utime;
+    return time.tv_sec + time.tv_usec * 1e-6;
+}
+
+#define GC_PROF_TIMER_START do {\
+	if (objspace->profile.run) {\
+	    if (!objspace->profile.record) {\
+		objspace->profile.size = 1000;\
+		objspace->profile.record = malloc(sizeof(gc_profile_record) * objspace->profile.size);\
+	    }\
+	    if (count >= objspace->profile.size) {\
+		objspace->profile.size += 1000;\
+		objspace->profile.record = realloc(objspace->profile.record, sizeof(gc_profile_record) * objspace->profile.size);\
+	    }\
+	    if (!objspace->profile.record) {\
+		rb_bug("gc_profile malloc or realloc miss");\
+	    }\
+	    MEMZERO(&objspace->profile.record[count], gc_profile_record, 1);\
+	    gc_time = getrusage_time();\
+	    objspace->profile.record[count].gc_invoke_time = gc_time - objspace->profile.invoke_time;\
+	}\
+    } while(0)
+
+#define GC_PROF_TIMER_STOP do {\
+	if (objspace->profile.run) {\
+	    gc_time = getrusage_time() - gc_time;\
+	    if(gc_time < 0) gc_time = 0;\
+	    objspace->profile.record[count].gc_time = gc_time;\
+	    objspace->profile.count++;\
+	}\
+    } while(0)
+
+#if GC_PROFILE_MORE_DETAIL
+#define INIT_GC_PROF_PARAMS double gc_time = 0, mark_time = 0, sweep_time = 0;\
+    size_t count = objspace->profile.count
+
+#define GC_PROF_MARK_TIMER_START do {\
+	if (objspace->profile.run) {\
+	    mark_time = getrusage_time();\
+	}\
+    } while(0)
+
+#define GC_PROF_MARK_TIMER_STOP do {\
+	if (objspace->profile.run) {\
+	    mark_time = getrusage_time() - mark_time;\
+	    if (mark_time < 0) mark_time = 0;\
+	    objspace->profile.record[count].gc_mark_time = mark_time;\
+	}\
+    } while(0)
+
+#define GC_PROF_SWEEP_TIMER_START do {\
+	if (objspace->profile.run) {\
+	    sweep_time = getrusage_time();\
+	}\
+    } while(0)
+
+#define GC_PROF_SWEEP_TIMER_STOP do {\
+	if (objspace->profile.run) {\
+	    sweep_time = getrusage_time() - sweep_time;\
+	    if (sweep_time < 0) sweep_time = 0;\
+	    objspace->profile.record[count].gc_sweep_time = sweep_time;\
+	}\
+    } while(0)
+#define GC_PROF_SET_MALLOC_INFO do {\
+	if (objspace->profile.run) {\
+	    size_t count = objspace->profile.count;\
+	    objspace->profile.record[count].allocate_increase = malloc_increase;\
+	    objspace->profile.record[count].allocate_limit = malloc_limit; \
+	}\
+    } while(0)
+#define GC_PROF_SET_HEAP_INFO do {\
+	if (objspace->profile.run) {\
+	    size_t count = objspace->profile.count;\
+	    objspace->profile.record[count].heap_use_slots = heaps_used;\
+	    objspace->profile.record[count].heap_live_objects = live;\
+	    objspace->profile.record[count].heap_free_objects = freed;\
+	    objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
+	    objspace->profile.record[count].have_finalize = final_list ? Qtrue : Qfalse;\
+	    objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
+	    objspace->profile.record[count].heap_total_size = heaps_used * (HEAP_OBJ_LIMIT * sizeof(RVALUE));\
+	}\
+    } while(0)
+
+#else
+#define INIT_GC_PROF_PARAMS double gc_time = 0;\
+    size_t count = objspace->profile.count
+#define GC_PROF_MARK_TIMER_START
+#define GC_PROF_MARK_TIMER_STOP
+#define GC_PROF_SWEEP_TIMER_START
+#define GC_PROF_SWEEP_TIMER_STOP
+#define GC_PROF_SET_MALLOC_INFO
+#define GC_PROF_SET_HEAP_INFO do {\
+	if (objspace->profile.run) {\
+	    size_t count = objspace->profile.count;\
+	    objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
+	    objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
+	    objspace->profile.record[count].heap_total_size = heaps_used * HEAP_SIZE;\
+	}\
+    } while(0)
+#endif
+
+
 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
 #pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
 #endif
@@ -176,6 +303,13 @@
 	VALUE *ptr;
 	int overflow;
     } markstack;
+    struct {
+	int run;
+	gc_profile_record *record;
+	size_t count;
+	size_t size;
+	double invoke_time;
+    } profile;
     struct gc_list *global_list;
     unsigned int count;
     int gc_stress;
@@ -309,6 +443,73 @@
     return bool;
 }
 
+/*
+ *  call-seq:
+ *    GC::Profiler.enable?                 => true or false
+ *
+ *  returns current status of GC profile mode.
+ */
+
+static VALUE
+gc_profile_enable_get(VALUE self)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return objspace->profile.run;
+}
+
+/*
+ *  call-seq:
+ *    GC::Profiler.enable          => nil
+ *
+ *  updates GC profile mode.
+ *  start profiler for GC.
+ *
+ */
+
+static VALUE
+gc_profile_enable(void)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+
+    objspace->profile.run = Qtrue;
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *    GC::Profiler.disable          => nil
+ *
+ *  updates GC profile mode.
+ *  stop profiler for GC.
+ *
+ */
+
+static VALUE
+gc_profile_disable(void)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+
+    objspace->profile.run = Qfalse;
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *    GC::Profiler.clear          => nil
+ *
+ *  clear before profile data.
+ *
+ */
+
+static VALUE
+gc_profile_clear(void)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    MEMZERO(objspace->profile.record, gc_profile_record, objspace->profile.size);
+    objspace->profile.count = 0;
+    return Qnil;
+}
+
 static void *
 vm_xmalloc(rb_objspace_t *objspace, size_t size)
 {
@@ -634,6 +835,7 @@
     	assign_heap_slot(objspace);
     }
     heaps_inc = 0;
+    objspace->profile.invoke_time = getrusage_time();
 }
 
 
@@ -1461,6 +1663,7 @@
 	    freed += n;
 	}
     }
+    GC_PROF_SET_MALLOC_INFO;
     if (malloc_increase > malloc_limit) {
 	malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
 	if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
@@ -1474,11 +1677,13 @@
 
     /* clear finalization list */
     if (final_list) {
+	GC_PROF_SET_HEAP_INFO;
 	deferred_final_list = final_list;
 	RUBY_VM_SET_FINALIZER_INTERRUPT(GET_THREAD());
     }
     else{
 	free_unused_heaps(objspace);
+	GC_PROF_SET_HEAP_INFO;
     }
 }
 
@@ -1706,6 +1911,7 @@
 {
     struct gc_list *list;
     rb_thread_t *th = GET_THREAD();
+    INIT_GC_PROF_PARAMS;
 
     if (GC_NOTIFY) printf("start garbage_collect()\n");
 
@@ -1725,6 +1931,8 @@
     during_gc++;
     objspace->count++;
 
+    GC_PROF_TIMER_START;
+    GC_PROF_MARK_TIMER_START;
     SET_STACK_END;
 
     init_mark_stack(objspace);
@@ -1765,9 +1973,13 @@
 	    gc_mark_rest(objspace);
 	}
     }
+    GC_PROF_MARK_TIMER_STOP;
 
+    GC_PROF_SWEEP_TIMER_START;
     gc_sweep(objspace);
+    GC_PROF_SWEEP_TIMER_STOP;
 
+    GC_PROF_TIMER_STOP;
     if (GC_NOTIFY) printf("end garbage_collect()\n");
     return Qtrue;
 }
@@ -2354,6 +2566,7 @@
     }
     rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
     rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
+
     for (i = 0; i <= T_MASK; i++) {
         VALUE type;
         switch (i) {
@@ -2395,6 +2608,88 @@
 
 /*
  *  call-seq:
+ *     ObjectSpace.statistic([result_hash]) -> hash
+ *
+ *  Counts classes for each type.
+ *
+ *  It returns a hash as:
+ *  {:TOTAL=>10000, :FREE=>3011, "String"=>6, "Array"=>404, ...}
+ *
+ *  If the optional argument, result_hash, is given,
+ *  it is overwritten and returned.
+ *  This is intended to avoid probe effect.
+ *
+ *  The contents of the returned hash is implementation defined.
+ */
+
+VALUE
+statistic(int argc, VALUE *argv, VALUE os)
+{
+    VALUE hash;
+    rb_objspace_t *objspace = (&rb_objspace);
+    VALUE syms = rb_ary_new();
+    st_table *table;
+    st_data_t *cnt;
+    size_t free_objs= 0, total_objs = 0, i = 0;
+
+    if (rb_scan_args(argc, argv, "01", &hash) == 1) {
+        if (TYPE(hash) != T_HASH)
+            rb_raise(rb_eTypeError, "non-hash given");
+    }
+
+    table = st_init_numtable();
+    cnt = vm_xmalloc(objspace, sizeof(st_data_t));
+    for (i = 0; i < heaps_used; i++) {
+        RVALUE *p, *pend;
+        ID sym;
+
+        p = heaps[i].slot; pend = p + heaps[i].limit;
+        for (;p < pend; p++) {
+	    *cnt = 1;
+	    if (BUILTIN_TYPE(p) != 0 && BUILTIN_TYPE(p) != T_UNDEF &&
+		RBASIC(p)->klass && RBASIC(p)->flags) {
+		sym = rb_intern(rb_obj_classname((VALUE)p));
+		if (st_lookup(table, (st_data_t)sym, cnt)) {
+		    *cnt = *cnt + 1;
+		    st_insert(table, (st_data_t)sym, *cnt);
+		} else {
+		    rb_ary_push(syms, ID2SYM(sym));
+		    st_add_direct(table, (st_data_t)sym, *cnt);
+		}
+	    }
+	    else {
+		free_objs++;
+	    }
+	}
+	total_objs += heaps[i].limit;
+    }
+
+    if (hash == Qnil) {
+        hash = rb_hash_new();
+    }
+    else if (!RHASH_EMPTY_P(hash)) {
+        st_foreach(RHASH_TBL(hash), set_zero, hash);
+    }
+
+    rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), rb_uint2inum(total_objs));
+    rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), rb_uint2inum(free_objs));
+
+    for (i = 0; i < RARRAY_LEN(syms); i++) {
+	VALUE sym;
+	sym = rb_ary_entry(syms, i);
+	if (st_lookup(table, (VALUE)SYM2ID(sym), cnt)) {
+	    rb_hash_aset(hash, rb_str_new2(rb_id2name(SYM2ID(sym))), rb_uint2inum(*cnt));
+	}
+    }
+
+    vm_xfree(objspace, cnt);
+    st_free_table(table);
+
+    return hash;
+}
+
+/*
+ *  call-seq:
  *     GC.count -> Integer
  *
  *  The number of times GC occured.
@@ -2441,7 +2736,164 @@
 }
 #endif
 
+VALUE
+gc_profile_record_get(void)
+{
+    VALUE prof;
+    VALUE gc_profile = rb_ary_new();
+    size_t i;
+    rb_objspace_t *objspace = (&rb_objspace);
+    
+    if (!objspace->profile.run) {
+	return Qnil;
+    }
+
+    for (i =0; i < objspace->profile.count; i++) {
+	prof = rb_hash_new();
+        rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_time));
+        rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_invoke_time));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), rb_uint2inum(objspace->profile.record[i].heap_use_size));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), rb_uint2inum(objspace->profile.record[i].heap_total_size));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_total_objects));
+#if GC_PROFILE_MORE_DETAIL
+        rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_mark_time));
+	rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DOUBLE2NUM(objspace->profile.record[i].gc_sweep_time));
+        rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), rb_uint2inum(objspace->profile.record[i].allocate_increase));
+        rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), rb_uint2inum(objspace->profile.record[i].allocate_limit));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SLOTS")), rb_uint2inum(objspace->profile.record[i].heap_use_slots));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_live_objects));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), rb_uint2inum(objspace->profile.record[i].heap_free_objects));
+        rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), objspace->profile.record[i].have_finalize);
+#endif
+	rb_ary_push(gc_profile, prof);
+    }
+
+    return gc_profile;
+}
+
+static int
+hash_to_a(VALUE key, VALUE value, VALUE ary)
+{
+    if (key == Qundef) return ST_CONTINUE;
+    rb_ary_push(ary, rb_assoc_new(value, key));
+    return ST_CONTINUE;
+}
+
 /*
+ *  call-seq:
+ *     GC::Profiler.result -> string
+ *
+ *  Report profile data to string.
+ *  
+ *  It returns a string as:
+ *   GC 1 invokes.
+ *   Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC time(ms)
+ *       1               0.012               159240               212940                10647         0.00000000000001530000
+ *
+ *
+ *   2264 used objects 8375 free objects and 10639 total objects in Heap.
+ *   Index                Count                    %          Kind (class)
+ *       1                 1627          15.29279068               String
+ *       2                  417           3.91954131                Class
+ *       3                   81           0.76134975             Encoding
+ *       4                   39           0.36657581             Foo::Bar
+ */
+
+VALUE
+gc_profile_result(void)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    VALUE record = gc_profile_record_get();
+    VALUE stc = statistic(0, 0, 0);
+    VALUE result = rb_str_new2("");
+    int i;
+    char buf[1024];
+    
+    sprintf(buf, "GC %1$d invokes.\n", NUM2INT(gc_count(0)));
+    rb_str_concat(result, rb_str_new2(buf));
+    if(objspace->profile.run && objspace->profile.count) {
+	rb_str_concat(result, rb_str_new2("Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)\n"));
+	for(i = 0; i < (int)RARRAY_LEN(record); i++) {
+	    memset(buf, 0, 1024);
+	    VALUE r = RARRAY_PTR(record)[i];
+	    sprintf(buf, "%1$5d %2$19.3f %3$20d %4$20d %5$20d %6$30.20f\n",
+		    i+1, NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_INVOKE_TIME")))),
+		    NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SIZE")))),
+		    NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")))),
+		    NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")))),
+		    NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_TIME"))))*100);
+	    rb_str_concat(result, rb_str_new2(buf));
+	}
+	rb_str_concat(result, rb_str_new2("\n\n"));
+#if GC_PROFILE_MORE_DETAIL
+	rb_str_concat(result, rb_str_new2("More detail.\n"));
+	rb_str_concat(result, rb_str_new2("Index Allocate Increase    Allocate Limit  Use Slot  Have Finalize             Mark Time(ms)            Sweep Time(ms)\n"));
+	for(i = 0; i < (int)RARRAY_LEN(record); i++) {
+	    memset(buf, 0, 1024);
+	    VALUE r = RARRAY_PTR(record)[i];
+	    sprintf(buf, "%1$5d %2$17d %3$17d %4$9d %5$14s %6$25.20f %7$25.20f\n",
+		    i+1, NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_INCREASE")))),
+		    NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_LIMIT")))),
+		    NUM2INT(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SLOTS")))),
+		    rb_hash_aref(r, ID2SYM(rb_intern("HAVE_FINALIZE")))? "true" : "false",
+		    NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_MARK_TIME"))))*100,
+		    NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_SWEEP_TIME"))))*100);
+	    rb_str_concat(result, rb_str_new2(buf));
+	}
+	rb_str_concat(result, rb_str_new2("\n\n"));
+#endif
+    }
+    memset(buf, 0, 1024);
+    int total = NUM2INT(rb_hash_aref(stc, ID2SYM(rb_intern("TOTAL"))));
+    sprintf(buf, "%1$d used objects %2$d free objects and %3$d total objects in Heap.\n",
+	    total - NUM2INT(rb_hash_aref(stc, ID2SYM(rb_intern("FREE")))),
+	    NUM2INT(rb_hash_aref(stc, ID2SYM(rb_intern("FREE")))), total);
+    rb_str_concat(result, rb_str_new2(buf));
+    rb_str_concat(result, rb_str_new2("Index                Count                    %         Kind (class)\n"));
+    rb_hash_delete(stc, ID2SYM(rb_intern("FREE")));
+    rb_hash_delete(stc, ID2SYM(rb_intern("TOTAL")));
+
+    VALUE stc_ary = rb_ary_new();
+    rb_hash_foreach(stc, hash_to_a, stc_ary);
+    rb_ary_sort_bang(stc_ary);
+    rb_ary_reverse(stc_ary);
+    for(i = 0; i < (int)RARRAY_LEN(stc_ary); i++) {
+	VALUE s = RARRAY_PTR(stc_ary)[i];
+	memset(buf, 0, 1024);
+	sprintf(buf, "%1$5d %2$20d %3$20.8f %4$20s\n", i+1, NUM2INT(RARRAY_PTR(s)[0]),
+		NUM2DBL(RARRAY_PTR(s)[0])/total*100, StringValuePtr(RARRAY_PTR(s)[1]));
+	rb_str_concat(result, rb_str_new2(buf));
+    }
+    return result;
+}
+
+
+/*
+ *  call-seq:
+ *     GC::Profiler.report
+ *
+ *  GC::Profiler.result display
+ *  
+ */
+
+VALUE
+gc_profile_report(int argc, VALUE *argv, VALUE self)
+{
+    VALUE out;
+
+    if (argc == 0) {
+	out = rb_stdout;
+    }
+    else {
+	rb_scan_args(argc, argv, "01", &out);
+    }
+    rb_io_write(out, gc_profile_result());
+
+    return Qnil;
+}
+
+
+/*
  *  The <code>GC</code> module provides an interface to Ruby's mark and
  *  sweep garbage collection mechanism. Some of the underlying methods
  *  are also available via the <code>ObjectSpace</code> module.
@@ -2451,6 +2903,7 @@
 Init_GC(void)
 {
     VALUE rb_mObSpace;
+    VALUE rb_mProfiler;
 
     rb_mGC = rb_define_module("GC");
     rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
@@ -2461,6 +2914,14 @@
     rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
     rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
 
+    rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
+    rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
+    rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
+    rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0);
+    rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
+    rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
+    rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
+
     rb_mObSpace = rb_define_module("ObjectSpace");
     rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
     rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
@@ -2480,6 +2941,7 @@
     rb_define_method(rb_mKernel, "object_id", rb_obj_id, 0);
 
     rb_define_module_function(rb_mObSpace, "count_objects", count_objects, -1);
+    rb_define_module_function(rb_mObSpace, "statistic", statistic, -1);
 
 #if CALC_EXACT_MALLOC_SIZE
     rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);

In This Thread