hacking ruby with python
DESCRIPTION
Short introduction of GDB/Python, with actual examples for debugging and (sort of) running Ruby-as-a-debugger-of-oneself on top of it. This was prepared for YamiRubyKaigi 2011 (Darkness of RubyKaigi2011), but unfortunately gave up doing actual presentation due to too many pages.TRANSCRIPT
Hacking Rubywith
Python
@tyamadajp
光ある所、影があり
コードある所、闇がある
闇の名は、バグ。
そしてバグを殺す者、
Ruby Debugger
~僕と契約して闇デバッガになろうよ!~
みなさんオススメのデバッガは?
0. p+? (?!)1. debug.rb? (旧型)2. ruby-debug? (普通)3. rb-trepanning? (新型)4. others?
#ベースの機能を生かした# JRuby, Rubinius 用のとかも#ありますよね
・どこまでも覗ける・動いている状態を覗ける・あろうことか、手まで出せる
「言語仕様?そんなの関係ねぇ!」
「もし闇の住人がデバッガーの 解説ページを読んだら」
デバッガのいい所
さて今宵のデバッガは・・・
さて今宵のデバッガは・・・
GDB
さて今宵のデバッガは・・・
TheGNUDebugger
さて今宵のデバッガは・・・
… の Python 拡張
さて今宵のデバッガは・・・
さて今宵のデバッガは・・・
GDB/Python
さて今宵のデバッガは・・・
明日の Ruby のために、今夜は Python
なにそれこわい
こわくないよ!> GDB/Python
1. GDB を 100% API control→ ブレークポイントの操作とか
2. GDB そのものも拡張 →新コマンド追加 / (gdb)...
→簡易関数の追加 / $func(...)
→ print ハイジャック / pretty printer
こんな感じ(実装例)
(gdb) p self$299 = (struct RTypedData *) 0x64b2c0, type=iseq ($300), data=0x731ee0 ($301)
(gdb) pp self$302 = (struct RTypedData *) 0x64b2c0, type=iseq ($303), data=0x731ee0 ($304)$305 = (const rb_data_type_t *) 0x7ffff7dbe780$306 = (rb_iseq_t *) 0x731ee0, <compiled>:3, type=ISEQ_TYPE_TOP
・・・まあ色々デコードしてくれます
それ $RUBY/.gdbinit でできるよ?
ええ、あるのですが・・・
・主力は内蔵 eval や dump 関数?
・なので展開能力が控えめ ( ぬるぽ避け? )
(gdb) rp recv T_CLASS: $1156 = (struct RClass *) 0x655310
→ソース読解の道具が欲しかった →自分でデコードするのがよい練習
ぬるぽを恐れず何でも展開
(gdb) pp recv$1158 = (struct RClass *) 0x655310 [Time]dump: {basic = {flags = 2, klass = 6640360}, ptr=0x6e4e20, m_tbl=0x6e4e40, iv_index_tbl=0x0}m_tbl: +, -, <=>, _dump, asctime, ctime, …
(gdb) pp ruby_current_thread->cfp->iseq 2$1072 = (rb_iseq_t *) 0x75c9c0, MAIN, /home/tai/tarai.rb:1(p=0x70a260/$1073,
l=0x70a260/$1074)line#0003: $1075 putstring $1076 getclassvariable …
VALUE ラップもの、 ID 、NODE 、 rb_iseq_t 、 rb_vm_t...自動で型判定し p or pp で中身を徹底表示
GDB script ではダメですか?
・ まともな制御構文がない → if else if else if else end end end
・ スコープ概念が(ほとんど)ない
・ 遅い。当人比で最大 100+倍以上
・ 限りなく貧弱なデータ操作機能とライブラリ
「 GDB script がやられたか・・・」 「所詮あやつは言語以前の存在」
GDB Python API - basicimport gdb
# vmは gdb.Value オブジェクトだが rb_vm_t* の型を保持vm = gdb.parse_and_eval(“ruby_current_vm”)
# これも gdb.Value オブジェクトだが、 rb_thread_t* の型を維持th = vm['running_thread']
# いわゆる構造体ダンプ :(gdb) p *ruby_current_vmprint(vm.dereference())
# ポインタ演算したいときには char* にしたりpointer_op(th.cast(gdb.lookup_type(“char”).pointer()))
# CLI側で参照できるよう $ や $N に戻したりもできるgdb.execute(“p (%s)%ld” % (th.type, long(th)))gdb.execute(“set $foo = %ld” % long(th))
GDB Command extending gdb–import gdb
class HelloCommand(gdb.Command): """Sample GDB command in Python""" def __init__(self): super(self.__class__, self).__init__( "hello-cmd", gdb.COMMAND_OBSCURE)
def invoke(self, arg, from_tty): args = gdb.string_to_argv(arg) print("arg is [%s]" % ", ".join(args))
HelloCommand()
※ロードは (gdb) python execfile(“hello.py”) などで
拡張1つにクラスを1つ
GDB Command easy way–import gdb
@gdbcommand(“hello-cmd“)def hello(*args): """ Sample GDB command in Python Usage: hello-cmd args """ print("arg is [%s]" % ", ".join(args))
バイト数 50%カット!
GDB Command easy way, impl–def gdbcommand(*args): """Turns decorated function into GDB command""" opts = [args[0], gdb.COMMAND_OBSCURE] #FIXME
def wrap(func): name = opts[0] or func.func_name def init(self): super(self.__class__, self).__init__(name, *opts[1:]) def invoke(self, arg, from_tty): func(*gdb.string_to_argv(arg)) type("", (gdb.Command,), { '__doc__': func.__doc__, '__init__': init, 'invoke': invoke, })() return func return wrap デコレータ、 Ruby にも
本気で欲しくなったり
※スライドに収めるため機能とコードをカットしています。バグってたらごめんなさい
GDB Pretty Printerclass HelloPrinter(object): """Print (hello_t *) type""" def __init__(self, val): self.val = val
# 表示する文字列又は gdb.Value を返す。後者の場合は # 再度 pretty-printing 処理が試みられる。以下はダミー。 def to_string(self): return "hogehoge"
# 表示ヒント。 "array", "map", "string" のいずれかを返す def display_hint(self): return "string"
# カスタムプリンタが反応できるようチェッカを登録def ckval(gv): if gv.type == gdb.lookup_type("hello_t").pointer(): return HelloPrinter(gv) return Nonegdb.pretty_printers.append(ckval)
print以外にも、あらゆる表示処理に適用される
おまけ: Ruby と Python の狂演$ rlwrap gdb -q -readnow --args ./ruby1.9.1 tarai.rb(gdb) b vm_exec(gdb) run(gdb) python sys.argv = [“gdb”] # GDB側の漏れ対応(gdb) python execfile("/usr/bin/ipython")IPython 0.10.2 -- An enhanced Interactive Python.In [1]: from gdb import *In [2]: vm = parse_and_eval("ruby_current_vm")In [3]: vmOut[3]: <gdb.Value object at 0x207bb70>In [4]: p vm['main_thread'].typestruct rb_thread_struct *
補完処理が壊れたり、単に GDB/Python スクリプト書くよりバグ率高いですが、変態的な可能性を感じる
おまけ: gdb.rb GDB/Ruby in Py*–https://github.com/tmm1/gdb.rb
「 gdb hooks for MRI/REE (and some for YARV) 」
(gdb) b vm_exec if $interactive == 0(gdb) run(gdb) set $interactive = 1 #自己呼び出しのブロック防止(gdb) python execfile("ruby-gdb.py")(gdb) ruby eval Thread.list[#<Thread:0x00000000650cc0 run>](gdb) ruby objects HEAPS 24 SLOTS 9816 LIVE 2717 (27.68%) FREE 7099 (72.32%)
complex 1 (0.04%) bignum 2 (0.07%)
実は金曜日に発見してかなりへこんだ。Ruby 内部の機能をフルに使って自分自身をデバッグ
まとめ
1. Ruby のデバッグ・学習に使ってみた2. GDB/Python は強力なツール3.正直わけがわからないよ!
「明日の Ruby のために、今夜は Python 」
参考文献:・ sourceware.org/gdb/onlinedocs/gdb/Python-API.html・ sourceware.org/gdb/wiki/PythonGdbTutorial・ gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python/