読者です 読者をやめる 読者になる 読者になる

最近追加したCRubyのGCデバッグ用関数 - GC Advent Calendar

Garbage Collection Advent Calendarの4日目の記事です。

最近のコミットでGCデバッグ用の関数を追加しました。
どちらもGC_DEBUGマクロが定義されているときに有効になる関数です。

  • rb_gcdebug_print_obj_condition
  • rb_gcdebug_sentinal

それぞれ紹介してみましょう。

rb_gcdebug_print_obj_condition

以下のような感じでrb_gcdebug_print_obj_conditionには引数にVALUEを渡します。

(gdb) call rb_gcdebug_print_obj_condition($1)
pointer to heap?: true
marked?: true
lazy sweeing?: true
swept?: false

それぞれ以下のような意味です。

  • 引数に渡したVALUEがオブジェクトヒープの中のものか?
  • それがマークされているか?
  • 現在はLazySweep中?
  • 引数のものはSweep後?

Ruby2.0.0からビットマップマーキングが入ったのであるオブジェクトがマークされているか確認するのが結構面倒になり、上記のような関数を追加しています。
わかる方には便利に使えるかなと思います。printfデバッグのお供に…。

rb_gcdebug_sentinal

こちらの関数は引数にVALUEとconst char*を受け取ります。

(gdb) call rb_gcdebug_sentinal($1, "obj1")

これは引数に渡されたVALUEが回収された時に以下のような文字列を出力します。

WARNING: object obj1(0x0391230) is inadvertently collected

これをどうやって実現しているかというと、実はファイナライザを登録しているだけです。

static VALUE
gcdebug_sential(VALUE obj, VALUE name)
{
    fprintf(stderr, "WARNING: object %s(%p) is inadvertently collected\n", (char *)name, (void *)obj);
    return Qnil;
}

void
rb_gcdebug_sentinel(VALUE obj, const char *name)
{
    rb_define_final(obj, rb_proc_new(gcdebug_sential, (VALUE)name));
}

これはGaucheのScm_GCSentinel()を真似したものですが、
http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3A%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%AF
CRubyの場合、ファイナライズRubyプロセス終了時にすべて実行する仕様になっていますので、実際にはcollectされていない場合にも*プロセス終了時には*メッセージが出力されてしまいますので要注意です。