• 検索結果がありません。

大統一Debian勉強会 gdb+python拡張を使ったデバッグ手法

N/A
N/A
Protected

Academic year: 2021

シェア "大統一Debian勉強会 gdb+python拡張を使ったデバッグ手法"

Copied!
45
0
0

読み込み中.... (全文を見る)

全文

(1)

大統一

Debian

勉強会

2013

gdb+python拡張を使ったデバッグ手法

野島 貴英 [email protected]

(2)

Level

Up

Debian

(3)

質問タイィィィィーム

皆さん Debianって使ってますかー?

debian sidで unstable な生活してますかー?

*-dbgパッケージって知ってますかー?

gdbって使ったことありますかー?

(4)

質問タイィィィィーム

皆さん Debianって使ってますかー?

debian sidで unstable な生活してますかー?

*-dbgパッケージって知ってますかー?

gdbって使ったことありますかー?

(5)

質問タイィィィィーム

皆さん Debianって使ってますかー?

debian sidで unstable な生活してますかー?

*-dbgパッケージって知ってますかー?

gdbって使ったことありますかー?

(6)

質問タイィィィィーム

皆さん Debianって使ってますかー?

debian sidで unstable な生活してますかー?

*-dbgパッケージって知ってますかー?

gdbって使ったことありますかー?

(7)

質問タイィィィィーム

皆さん Debianって使ってますかー?

debian sidで unstable な生活してますかー?

*-dbgパッケージって知ってますかー?

gdbって使ったことありますかー?

(8)

回答

皆さん Debianって使ってますかー? →使ってない人はぜひ使ってみましょう!

debian sidで unstable 生活してますかー?

→level upを目指す人はぜひ debian sid へアップグレー ドして unstable な生活を送りましょう!楽しいよ! *-dbgパッケージって知ってますかー? →使ったことない人は、家に帰ったら aptitude install XXXX-dbg!(XXXX は何か好きなパッケージで) gdbって使ったことありますかー?/gdb の python 拡張 使ったことありますかー? →使ったことない人は、本資料公開してますので、ぜ ひ真似をしてみてー。

(9)

回答

皆さん Debianって使ってますかー? →使ってない人はぜひ使ってみましょう!

debian sidで unstable 生活してますかー?

→level upを目指す人はぜひ debian sid へアップグレー ドして unstable な生活を送りましょう!楽しいよ! *-dbgパッケージって知ってますかー? →使ったことない人は、家に帰ったら aptitude install XXXX-dbg!(XXXX は何か好きなパッケージで) gdbって使ったことありますかー?/gdb の python 拡張 使ったことありますかー? →使ったことない人は、本資料公開してますので、ぜ ひ真似をしてみてー。

(10)

回答

皆さん Debianって使ってますかー? →使ってない人はぜひ使ってみましょう!

debian sidで unstable 生活してますかー?

→level upを目指す人はぜひ debian sid へアップグレー ドして unstable な生活を送りましょう!楽しいよ! *-dbgパッケージって知ってますかー? →使ったことない人は、家に帰ったら aptitude install XXXX-dbg!(XXXX は何か好きなパッケージで) gdbって使ったことありますかー?/gdb の python 拡張 使ったことありますかー? →使ったことない人は、本資料公開してますので、ぜ ひ真似をしてみてー。

(11)

回答

皆さん Debianって使ってますかー? →使ってない人はぜひ使ってみましょう!

debian sidで unstable 生活してますかー?

→level upを目指す人はぜひ debian sid へアップグレー ドして unstable な生活を送りましょう!楽しいよ! *-dbgパッケージって知ってますかー? →使ったことない人は、家に帰ったら aptitude install XXXX-dbg!(XXXX は何か好きなパッケージで) gdbって使ったことありますかー?/gdb の python 拡張 使ったことありますかー? →使ったことない人は、本資料公開してますので、ぜ ひ真似をしてみてー。

(12)

今 回 発 表

Debian

(13)

今回の

Debian

環境

項番 種類 内容

1 Debianバージョン debian sid (jessie/sid)

2 CPU amd64 (多分 i386 も一緒かな?)

3 OS種別 linux

4 php version 5.5.0 (debian sidから)

5 gdb version 7.6 (debian sidから)

kFreeBSDとか、Hurd 環境とか、ARM とかの他の CPU の人

(14)

ちょ

っと

だ け お さ

らい

(15)

*-dbg

パッケージ今までの

Debian

勉強会での発

*-dbgパッケージの技術的な話と、debian の*-dbg パッケー ジの現状については、 2012年の大統一 Debian 勉強会の岩松さんの発表 「debug.debian.net」↓

http: // gum. debian. or. jp/ 2012/

に掲載されているプレゼン資料が大変参考になりますので、 こちらもあわせてご覧くださいませ。

(今回の発表はこちらの資料読んでなくても大丈夫なように 調整してます!)

(16)

gdb+python

の今までの

Debian

勉強会での発表

gdb+pythonについては、

第 98 回東京エリア Debian 勉強会↓

http: // tokyodebian. alioth. debian. org/ 2013-03. html に掲載されている勉強会資料に内部構造、コマンド拡張、 ブレークポイントの応用など載せてますので、こちらもあ わせてご覧くださいませ。 (今回の発表はこちらの資料読んでなくても大丈夫なように 調整してます!)

(17)

*-dbg

パッ

ケ ー ジ に

ついてちょ

っと

(18)

*-dbg

パッケージとは

バイナリパッケージに対応して、デバッガが利用するデバッ グシンボルを別のファイルに分離してパッケージにまとめ たパッケージとなります。Debian に含まれているバイナリ をデバッグするときに大変便利です。 項番 種類 内容 1 *-dbgパッケージ名 バイナリパッケージ名-dbg 2 シンボルファイル名 COMPATIBILITY LEVEL 9未満のパッケージの場合: バイナリと同じファイル名 COMPATIBILITY LEVEL 9以上のパッケージの場合: バイナリに埋め込まれた BuildID に対応したファイル名 3 インストール先 COMPATIBILITY LEVEL 9未満のパッケージの場合: /usr/lib/debug/¡バイナリのインストール先のディレクトリ名¿ 例:/usr/bin/php5 だったら、/usr/lib/debug/usr/bin/php5 COMPATIBILITY LEVEL 9以上のパッケージの場合: /usr/lib/debug/.build-id/以下

(19)

*-dbg

パッケージとは

試しにどんな*-dbg パッケージがあるかみてみます。

$ cat /etc/debian_version jessie/sid

$ aptitude search ’.*-dbg’

p 0ad-dbg - Real-time strategy game of p 389-ds-base-dbg - 389 Directory Server suite p 389-ds-base-libs-dbg - 389 Directory Server suite p 7kaa-dbg - Seven Kingdoms Ancient ...中略...

$ aptitude search ’.*-dbg’ | wc -l 2272

$

(20)

*-dbg

パッケージを早速使ってみる

php5をデバッグしてみます。

$ aptitude install php5-dbg $ apt-get source php5/sid $ ls php5-5.5.0+dfsg php5_5.5.0+dfsg-1.dsc php5_5.5.0+dfsg-1.debian.tar.gz php5_5.5.0+dfsg.orig.tar.xz $ pwd /home/yours/php5-src/

$ gdb --args /usr/bin/php5 -r ’phpinfo()’

(gdb) set substitute-path /tmp/buildd/ /home/yours/php5-src/ (gdb) b main

(gdb) run (gdb) l

1197 #else

1198 int main(int argc, char *argv[]) 1199 #endif

1200 {

1201 #ifdef ZTS

(21)

COMPATIBILITY LEVEL

Debianのソースパッケージは、debhelper の

COMPATIBILITY LEVELにより構築に使われる debian/以下

のファイルのフォーマットや、実際にビルドされる時の挙 動がちょっと異なっています。現在の debian sid でも様々な LEVELを持つパッケージが混在している状況です。確認に はソースパッケージを取ってきて debian/compat ファイルを 見るとわかります。   COMPATIBILITY LEVEL の見方↓ $ apt-get source php5 $ cat php5-5.5.0+dfsg/debian/compat 5

$ apt-get source gstreamer0.10

$ cat gstreamer0.10-0.10.36/debian/compat 9

(22)

COMPATIBILITY LEVEL 9

未満の

*-dbg

debhelperの COMPATIBILITY LEVEL 9 未満の形式で構築さ れているパッケージの*-dbg の中身: 例えば php5-dbg が該当 $ dpkg -L php5-dbg ...中略... /usr/lib/debug /usr/lib/debug/usr /usr/lib/debug/usr/bin /usr/lib/debug/usr/bin/php5 /usr/lib/debug/usr/bin/php5-cgi /usr/lib/debug/usr/sbin /usr/lib/debug/usr/sbin/php5-fpm ...中略... $ php5コマンドは/usr/bin/php5 にあるので、インストール先 は/usr/lib/debug/usr/bin/php5 に、デバッグシンボルがイン ストールされています。

(23)

COMPATIBILITY LEVEL 9

以上の

*-dbg

debhelperの COMPATIBILITY LEVEL 9 以上の形式で構築さ れているパッケージの*-dbg の中身: 例えば libgstreamer0.10-0-dbg が該当 $ dpkg -L libgstreamer0.10-0-dbg ...中略... /usr/lib/debug/.build-id/3f/bdb94562139d00e153a2fc6c 720772ca28acad.debug /usr/lib/debug/.build-id/86 /usr/lib/debug/.build-id/86/cc80bb6f2bdb31a2ed02973d 54530b3d99846f.debug /usr/lib/debug/.build-id/44 /usr/lib/debug/.build-id/44/ff321f11ffd750f8c351ffa3 f5d20028d2f6a6.debug /usr/share /usr/share/doc ...中略... $

(24)

COMPATIBILITY LEVEL 9

以上の

*-dbg

どのシンボルファイルがどのバイナリに対応するかを確認 してみます↓ $ file /usr/lib/x86_64-linux-gnu/libgstreamer-0.10. so.0.30.0 /usr/lib/x86_64-linux-gnu/libgstreamer-0.10.so.0.30.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked,

BuildID[sha1]=44ff321f11ffd750f8c351ffa3f5d20028d2f6a6, stripped $ BuildID[sha1]の値から /usr/lib/debug/.build-id/44/ff321f11ffd750f8c351ffa3f5d20028d2f6a6.debugが libgstreamer-0.10.so.0.30.0のシンボルファイルとなります。

(25)

ところで

BuildID

って何?

バイナリを一意に指定できるように埋め込んだ ID。バイナ リの構築時に、リンカがバイナリのハッシュ値を計算して 埋め込んでいます。 $ readelf -n /usr/lib/x86_64-linux-gnu/libgstreamer-0.10.so.0

Notes at offset 0x000001c8 with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID

Build ID: 44ff321f11ffd750f8c351ffa3f5d20028d2f6a6

BuildIDについて詳しくは、 BuildID解説 http://fedoraproject.org/wiki/RolandMcGrath/ BuildID BuildIDに関して binutils へ寄贈されたパッチ↓ http://sourceware.org/ml/binutils/2007-07/ msg00012.html

(26)

どうやってシンボルファイルを見つける?

gdbはバイナリに埋め込まれている .gnu_debuglink セク

ションを見てシンボルファイルのファイル名を判断します。

$ readelf -x .gnu_debuglink /usr/bin/php5 Hex dump of section ’.gnu_debuglink’:

0x00000000 70687035 ..中略..0dd10 php5...‘..

$ readelf -x .gnu_debuglink /usr/lib/x86_64-linux-gnu/ libgstreamer-0.10.so.0.30.0

Hex dump of section ’.gnu_debuglink’:

0x00000000 66663332 ..中略..06638 ff321f11ffd750f8 0x00000010 63333531 ..中略..03238 c351ffa3f5d20028 0x00000020 64326636 ..中略..00000 d2f6a6.debug.... 0x00000030 0da0b0f5 .... つまり、バイナリ php5 のシンボルファイル名は php5 とい うファイル名、バイナリ libgstreamer-0.10.so.0.30.0 のシンボ ルファイル名は、 ff321f11ffd750f8c351ffa3f5d20028d2f6a6.debugという事がわ かります。

(27)

ところでソースデバッグ出来ないんだけど?

先ほどの/usr/bin/php5 は gdb から’set substitute-path’ で、 ソースのありかを一発で指定できましたが、いつもうまく いくとは限りません。

うまくいかない例↓

$ gdb --args /usr/bin/gst-launch

(gdb) set substitute-path /tmp/buildd/ /home/yours/gstreamer/ (gdb) b main

(gdb) run

Breakpoint 1, main ...中略... at gst-run.c:318

318 gst-run.c: そのようなファイルやディレクトリはありません.

(28)

ところでソースのデバッグ出来ないんだけど?

gst-launchのソースである gstreamer0.10-0.10.36 を実際にビ ルドするとわかるのですが、gcc に渡されるソースファイル の指定にソースがフルパスで指定されていません。 (php5は gcc にソースファイルが指定されるときフルパスで 指定されています)  こういうときは、gdb の dir コマンドを利用してソースの 入っている可能性のある場所をすべて指定するとソースの デバッグができるようになります。

(29)

gdb

dir

コマンドによるソースの指定

できた。 $ gdb --args /usr/bin/gst-launch (gdb) dir /home/yours/gstreamer/gstreamer0.10-0.10.36/ tools (gdb) b main (gdb) run (gdb) l 314 } 315 316 int

317 main (int argc, char **argv) 318 {

319 GHashTable *candidates; 320 gchar *dir;

(30)

dir

コマンド注意点

gdbの dir でソースファイルを指定している時は、gdb は dir で登録されたパスをすべて検索して該当のソースファイル を見つけようとするため、万一同じファイル名のファイル が別のディレクトリで先に発見されると、間違ったソース ファイルを表示してしまうので注意がいります。

(31)

補足:

2013/7/22(

追記

)

2013/7/20(土) の東京エリア Debian 勉強会の月刊 Debhelper の発表にて、dir コマンドで指定する場合でも set substitue-pathを使ってソースコードを一発指定する方法を 見つけており、紹介させていただきましたので、ご参照く ださい。資料は以下から↓ http://tokyodebian.alioth.debian.org/2013-07.html

(32)

gdb+python

gdb.Value

(33)

gdb+python

動かす

  gdb 内臓の python を動作させてみます。  なお、今回は、gdb 7.6 から搭載された1python-interactive コマンドを使って、python をインタラクティブモードで利 用してみます。 $ gdb (gdb) pi >>> import sys >>> print sys.version_info

sys.version_info(major=2, minor=7, micro=5, releaselevel=’final’, serial=0)

>>>

見ての通り、gdb を python で制御できるわけです。

(34)

gdb.Value

クラス

gdbはブレークポイントの他に、バイナリのソースコード側 で定義した変数を参照できます。  こちらを python 側で参照したり、応用きかせたりするの には、gdb.Value クラスがとても便利です。 便利な機能一覧: 1 デバッグ対象の変数が structure の場合、gdb.Value 型オ ブジェクトは structure のメンバ変数をそのまま連想配 列にマップしてくれます。 2 デバッグ対象の変数が配列なら、python 上も配列とし てアクセスできます。 3 ポインタか即値かは自動で判定されて適宜問題無いよ うに扱われるため、structure のポインタを辿ったりす るのが非常に簡潔に書けます。 4 関数ポインタの呼び出しもサポート済みです。

(35)

gdb.Value

クラスで変数アクセス

値の参照の例:

$ sudo aptitude install php5-dbg $ cat ./phpinfo.php

<?php phpinfo() ?>

$ gdb --args /usr/bin/php5 ./phpinfo.php (gdb) b zend_vm_execute.h:356 (gdb) run (gdb) pi >>> edata=gdb.parse_and_eval( ... "executor_globals.current_execute_data") >>> print edata.address 0xe60920 <executor_globals+1120> >>> print edata.dereference() {opline = 0x7ffff7fbd778, function_state = {function = 0x7ffff7fbd498, arguments = 0x0}, ...中略...,call = 0x0} >>> print edata[’op_array’][’filename’] 0x7ffff7fbd640 "/home/yours/phpinfo.php" >>> [Ctrl+D]

(36)

gdb.Value

クラスで関数ポインタ

関数ポインタ呼び出しの例:

$ gdb --args /usr/bin/php5 ./phpinfo.php (gdb) b zend_vm_execute.h:356 (gdb) run (gdb) pi >>> phpfile=(gdb.parse_and_eval( ... "zend_get_executed_filename")).dereference() >>> print phpfile

{const char *(void)} 0x6c7600 <zend_get_executed_filename> >>> print phpfile()

0x7ffff7fc4418 "/home/yours/phpinfo.php >>> [Ctrl+D]

(37)

gdb.Value

クラスに文字列格納時の注意点

gdb.Valueクラスに文字列へのポインタが代入された場合、 こちらの文字列を str() で取り出そうとすると、どうしても gdbで”p pointer” した時とまったく同じフォーマットで文字 列に変換されてしまいます。  このとき、” アドレス 文字列” というフォーマットにされ てしまうので、str.partition(" ") 等を使って、欲しい文 字列だけ取り出す必要があります。

$ gdb --args /usr/bin/php5 ./phpinfo.php (gdb) b zend_vm_execute.h:356 (gdb) run (gdb) pi >>> phpfile=(gdb.parse_and_eval( ... "zend_get_executed_filename")).dereference() >>> filename=phpfile() >>> print str(filename) 0x7ffff7fc4418 "/home/yours/phpinfo.php" >>> print (str(filename).partition(" "))[2] "/home/yours/phpinfo.php"

(38)

php5.5.0

部構造

(39)

php5.5.0

の中間コード実行部分

php5.5.0では、中間コードを実行する部分が2つあります。 execute_ex()(Zend/zend_vm_execue.h) ユーザが定義した関数を実行するときに呼ばれる execute_internal()(Zend/zend_execute.c) php本体に C 等で実装されている関数を実行するとき に呼ばれる

(40)

今回

gdb

を仕掛ける場所

今回、ユーザ定義の関数の実行トレースを取ってみたいの で、execute_ex() の中間コード実行部分直前に、gdb を仕 掛け、どこを実行しているかを表示してみます。

  Zend/zend_vm_execute.h

330 ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC) 331 { 332 DCL_OPLINE 333 zend_bool original_in_execution; ...中略... 356 if ((ret = OPLINE->handler( <---ココ execute_data TSRMLS_CC)) > 0) { 357 switch (ret) {

(41)

php5.5.0

の便利な

API

php5.5.0から、Zend Engine は現在どこを実行中なのかにつ いて情報を得る為に、便利な API が用意されています。 zend_get_executed_filename() (Zend/zend_execute_API.c) 現在実行中の php ファイル名を得る get_active_function_name() (Zend/zend_execute_API.c) 現在実行中の関数スコープ名を得る zend_get_executed_lineno() (Zend/zend_execute_API.c) 現在実行中の行番号を得る

(42)

php

のソー

ス の 実 行

ト レ ー ス

取って

(43)

gdb

を使って

php

のソースをデバッグする事に

ついて

  通常、php のデバッグをするのであれば、xdebug を導入 して、vim-nox+debugger plugin/netbean/eclipse 等でデバッ グするのが手軽ですし、強力なデバッグが可能です。  しかしながら、バックグラウンドで動作するような php プログラムを書いた際に、運用中、突然暴走するなどが あったとき、何が起きているのかをデバッグするには、 xdebugの環境だけでは厳しいものがあります。  ここで、gdb が使えれば、暴走中の php プロセスに直接ア タッチしてデバッグする事が可能も可能になるので、デバッ グの方法が広がります。

(44)

実行トレースを取ってみた

今回の勉強会資料には、今まで説明したものを組み合わせ て、php ソースレベルでの実行トレースを取った例を載せて いますので、お試しあれ! 7/22(追記): 勉強会資料は↓からダウンロードできます。 http://tokyodebian.alioth.debian.org/pdf/ debianmeetingresume2013-gum.pdf

(45)

今回紹介の方法の問題点

今回の gdb+python で、php ソースレベルのトレースを取る やり方は、実は、以下の問題点があります。 中間コードが1つ実行される度に gdb 側に逐一ブレー クが入り python スクリプトが動くため、php の実行速 度が極端に遅くなります。大きい php プログラムにつ いて、gdb を用いたデバッグをするにはもっと別のアプ ローチを検討する必要があるかもしれません。 phpの変数の値を参照するには、php の内部変数である   executor_globals  に登録されているシンボル テーブルからデータを引っ張ってくる必要があります。 しかしながら、現行の php5.5.0 の Zend エンジンの API には、 php の変数の値をデバッガ側から簡単に参照で きるような API が存在しない為、gdb から php の変数を 参照するにはそれなりの工夫がいると思われます。

参照

関連したドキュメント

はありますが、これまでの 40 人から 35

基本目標2 一 人 ひとり が いきいきと活 動するに ぎわいのあるま ち づくり1.

父親が入会されることも多くなっています。月に 1 回の頻度で、交流会を SEED テラスに

海なし県なので海の仕事についてよく知らなかったけど、この体験を通して海で楽しむ人のかげで、海を

何人も、その日常生活に伴う揮発性有機 化合物の大気中への排出又は飛散を抑制

何人も、その日常生活に伴う揮発性有機 化合物の大気中への排出又は飛散を抑制

○齋藤部会長 ありがとうございました。..

人間は科学技術を発達させ、より大きな力を獲得してきました。しかし、現代の科学技術によっても、自然の世界は人間にとって未知なことが