Advent calendar の途中ですが、昨日デバッグするためにやったこと。Ruby 2.0 から使えるテクニックです(同じようなことは、もっと前から入っている set_trace_func を使っても出来ました)。
tracer というライブラリがあり、実行過程を dump することが出来ますが、TracePoint を使うことで簡単に似たようなことをやることが出来ます。
n = 0 trace = TracePoint.new(:a_call, :a_return, :raise){|tp| case tp.event.to_s # 手抜き when /call/ puts ' ' * n + tp.inspect n+=1 when /return/ n-=1 puts ' ' * n + tp.inspect when /raise/ puts ' ' * n + '!!!' + tp.inspect end } trace.enable{ # 興味のあるコード }
こんなふうにすると、興味のあるコードだけ追うことが出来ます。好きなようにフィルタリングもできる(例えば、tp.path を使って、興味のあるファイルのイベントだけ見ることができる)。WEBrick でよくわからんエラーが出たので、これを使って追いました([ruby-core:59088] [ruby-trunk - Bug #9247][Open] Bugs in socket.rb (exception retrieval))。
■Ruby 2.1 internal advent calendar 7 日目
さて、7 日目は ObjectSpace.reachable_objects_from_root メソッドです。
Ruby 2.0 から、ObjectSpace.reachable_objects_from(obj) というメソッドが導入されました。これは、obj から参照されるオブジェクトの一覧配列を返します。
Ruby 2.1 から導入された ObjectSpace.reachable_objects_from_root メソッドは、GC 時に「ルート」と言われる、マークの起点となるオブジェクトの集合を返します。
require 'pp' require 'objspace' pp ObjectSpace.reachable_objects_from_root #=> ... 3000 行くらいの出力
これで何が出来るかというと、ObjectSpace.reachable_objects_from_root と ObjectSpace.reachable_objects_from を組み合わせることで、GC のまねごとみたいなことが出来ます。
■Ruby 2.1 internal advent calendar 6 日目
今日は、ObjectSpace の新メソッド・2.1 の目玉の 1 つと思って居る ObjectSpace.trace_object_allocations についてご紹介します。
* ObjectSpace.trace_object_allocations * ObjectSpace.trace_object_allocations_start * ObjectSpace.trace_object_allocations_stop * ObjectSpace.trace_object_allocations_clear * ObjectSpace.allocation_sourcefile * ObjectSpace.allocation_sourceline * ObjectSpace.allocation_class_path * ObjectSpace.allocation_method_id * ObjectSpace.allocation_generation
この辺がひとまとまりなんですが、とりあえず、標準的な話を書いてみます。
ObjectSpace.trace_object_allocations に渡すブロック中で生成したオブジェクトの生成タイミングの情報をとっておくことが出来ます。
現在、取っておくことが出来るのは、次の「どこで、いつ」生成されたか、という情報です。
論より証拠として、何が出来るかを紹介します。
require 'objspace' def where_are_you_from? obj file = ObjectSpace.allocation_sourcefile(obj) line = ObjectSpace.allocation_sourceline(obj) "#{file}:#{line}" if file && line end ObjectSpace.trace_object_allocations{ $a = a = Object.new # t.rb:10 行目で作成 b = Object.new # t.rb:11 行目で作成 p where_are_you_from?(a) #=> "t.rb:10" p where_are_you_from?(b) #=> "t.rb:11" } c = Object.new # t.rb: 16 行目で作成 p where_are_you_from?($a) #=> "t.rb:10" p where_are_you_from?(c) #=> nil ObjectSpace.trace_object_allocations_clear p where_are_you_from?($a) #=> nil
ObjectSpace.trace_object_allocations に渡したブロック中で、a ($a), b というオブジェクトを作成しています。
作成したオブジェクトに対して where_are_you_from?() を呼び出すと、"t.rb:10" のように、作成された場所のファイル名、および行番号の文字列を返すことが出来ます。
ブロックを出た後では、オブジェクト生成時情報は保存されませんので、ブロックを出た後作成した c を見ても、場所を返してくれません(単に nil になっています)。
このブロックを出た後も、$a を見ると、正しい場所を返しています。これは、保存している情報が残っているからです。
しかし、ObjectSpace.trace_object_allocations_clear を呼び出すと、保存しているすべての情報を破棄するので $a の情報は見えなくなります。これ以降情報は不要だ、と判断できるタイミングで、適切に破棄すると良いと思います。
情報は次の API で取得することが出来ます。
ちなみに、ObjectSpace.trace_object_allocations は、渡されたブロックの中で取得を有効にします。他にも、ObjectSpace.trace_object_allocations_start で取得スタート、ObjectSpace.trace_object_allocations_stop でストップ、のように使うことが出来ます。
つまり、ObjectSpace.trace_object_allocations は次の定義されます。
def ObjectSpace.trace_object_allocations begin ObjectSpace.trace_object_allocations_start yield ensure ObjectSpace.trace_object_allocations_stop end end
とても便利な機能だと思うんですが、デメリットとして、オブジェクトの生成・廃棄がとても遅くなってしまいます。なので、デバッグ用途・プロファイリング用途で利用されるといいんじゃないかと思います。
もう 1 つ大事な点は、この新機能は、実はサンプル機能として追加した、という点です。後日ちゃんと紹介しますが新しい TracePoint のイベントを利用しています。というわけで、似たような感じで、もっといろいろ作れるんじゃ無いかと思います。
■Ruby 2.1 internal advent calendar 5 日目
ObjectSpace のメソッドがいろいろ増えています。
NEWS によると、
* objspace * new method: * ObjectSpace.trace_object_allocations * ObjectSpace.trace_object_allocations_start * ObjectSpace.trace_object_allocations_stop * ObjectSpace.trace_object_allocations_clear * ObjectSpace.allocation_sourcefile * ObjectSpace.allocation_sourceline * ObjectSpace.allocation_class_path * ObjectSpace.allocation_method_id * ObjectSpace.allocation_generation * ObjectSpace.reachable_objects_from_root * ObjectSpace.dump * ObjectSpace.dump_all
ワオ、こんなに増えてる。
明日からは、各メソッドの紹介を書きますかね。
■Ruby 2.1 internal advent calendar 4 日目
GC が世代別 GC に対応しました。
ただし、よくある、「若い世代に限定した copy GC / 古い世代も含めた M&S」ではなくて、「若い世代に限定した M&S(の mark)/ 古い世代も含めた M&S」になっています。若い世代は mark だけ世代別、sweep のコストは変わらない、キャッシュ効率改善は期待できない、という実装です。
過去の拡張ライブラリとの互換性を取るためにこのような実装になっています。
技術的な詳細は、私の発表資料などを見て下さい (笹田耕一の研究業績リスト の中の rubyconf 2013 の資料など)。
■Ruby 2.1 internal advent calendar 3 日目
GC.start() がキーワード引数を受け取るようになりました。
読めば意味はわかるよね。上記がデフォルトです。
■Ruby 2.1 internal advent calendar 2 日目
GC.stat で返るハッシュ値の値がだいぶ変わりました。
ruby 1.9.3p469 (2013-08-19) [i386-mswin32_110] {:count=>1, :heap_used=>21, :heap_length=>21, :heap_increment=>0, :heap_live_num=>7089, :heap_free_num=>10076, :heap_final_num=>0}
ruby 2.0.0p317 (2013-09-15 revision 42947) [i386-mswin32_110] {:count=>3, :heap_used=>37, :heap_length=>37, :heap_increment=>0, :heap_live_num=>26713, :heap_free_num=>4113, :heap_final_num=>9, :total_allocated_object=>40468, :total_freed_object=>13755}
ruby 2.1.0dev (2013-12-02 trunk 43956) [i386-mswin32_110] {:count=>6, :heap_used=>32, :heap_length=>32, :heap_increment=>0, :heap_live_slot=>25482, :heap_free_slot=>668, :heap_final_slot=>0, :heap_swept_slot=>1291, :heap_eden_page_length=>32, :heap_tomb_page_length=>0, :total_allocated_object=>47216, :total_freed_object=>21734, :malloc_increase=>60991, :malloc_limit=>16777216, :last_collection_flags=>260, :minor_gc_count=>4, :major_gc_count=>2, :remembered_shady_object=>600, :remembered_shady_object_limit=>1200, :old_object=>6578, :old_object_limit=>13156, :oldmalloc_increase=>0, :oldmalloc_limit=>16777216}
1.9 から比べて、だいぶ増えたよねぇ。
ええと、:nantoka_num というハッシュ値から :nantoka_slot と変わったところに注意。
ポリシーとして、「インターナルな情報は、まぁ見られる分には見られてもいいかな」という感じで変わりました。
要望があれば、(要望のあった)各要素の意味について解説します。
ちなみに、まだ変わるかもしんないです。この辺、だいぶいろんな人の要望を取り入れてる感じです。
■Ruby 2.1 internal advent calendar 1 日目
TracePoint で a_call, a_return というイベントをフック出来るようになりました。
というのも、b_call/b_return, c_call/c_return があって、a_call/a_return は無いのな、と言われたからです。
どういう意味かというと、a_call は TracePoint.new(call, b_call, c_call)、a_return も同様に TracePoint.new(return, b_return, c_return) の意味です。
all とか a の意味で付けました。
発作的に付けたので、後で後悔しそうな気がします。
reachableという名前ということは、「直接に参照されてる」だけでなく「いくつもポインタを経由して到達できるものすべて」を返すんですか?
indirection は含みません。ObjectSpace.reachable_objects_from(obj) は、obj から direct に辿ることができるオブジェクトの集合を返すので、これを利用することですべて辿ることが出来ます。しかし、確かに reachable だけだと、indirect 入りそうですね...。
そうですね.
かと言って,directly_reachable_objects_from(obj) だと長いですねえ.objects_referenced_from(obj) とかがいいんじゃないかなあ.または, objects_pointed_to_by(obj),referents(obj), descendant(obj), offspring(obj) とか…
あと,reachable_objects_from_root も間接参照を含むように読めちゃう.こっちはroot_objects がいいんじゃないかなあ.
その辺の用語は 2.0 で導入しちゃったんですよね...。