CRubyでオブジェクトが生成された位置を突き止める - GC Advent Calendar

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

おそらく一人でけっこう長い日数を書くと思うので、軽いものからボチボチと…。
しかもGCとはあまり関係ないですが…。

とあるオブジェクトの生成位置を調べたいとき、どうするのか。

object_idを1bit左シフトするとだいたいそのオブジェクトのRVALUEのアドレスになるので

p (hoge.object_id << 1).to_s(16) # => fa83a4

上記のようにobject_idを出力するコードを混ぜておきましょう。

その後にgdbrubyを動かしてみます。
この時、GC_DEBUG付きでビルドしたrubyなら実はソースコードとラインがわかります。
デバッグ用にGC_DEBUG付きのrubyがビルドできるようならそうしてみましょう。
rvmだったら.rvmrcに以下の設定するとできるんじゃないでしょうか(たぶんね!)

export optflags="-DGC_DEBUG -O0 -ggdb" rvm install 1.9.3

テキトーな位置で止めてから以下のようにすると見えます。

$ gdb ruby
(gdb) b rb_gc # テキトーな位置にbreakpointを入れておく。ここではGC.start…。
(gdb) r /tmp/t.rb
93824997489608 # object_id << 1 が出力される

(gdb) p ((RVALUE*)(93824997489608))->line
$1 = 1
(gdb) p ((RVALUE*)(93824997489608))->file
$2 = 0x555555a59b90 "/tmp/t.rb"

GC_DEBUGなし・最適化バリバリのrubyの場合ですが、RVALUEのアドレスをキーにオブジェクトを生成する関数付近でbreakpointやwatchを貼っておいてから、backtraceを辿り、T_NODEのデータを発見し、そこからファイル名・行番号を搾り出すというテクニックもあります。
http://www.loveruby.net/ja/rhg/book/syntree.html
面倒なので省略。

Ruby2.0.0ではDTraceサポートが入るので上記のような面倒なことはしなくてもいいかな、と思ったんですが、
DTraceProbes - Ruby - Ruby Issue Tracking System
クラス名までしか絞れないんですねえ。まあそれでも一歩前進かなあ。