Recommended CPANの依存モジュールをもう少し正しく検出したい
php and sapi and zendengine2 and...
PHP と SAPI と ZendEngine3 と
Apacheの展望とmod_perlの超絶技巧 #yapcasia
PHPカンファレンス2014の懇親会飛び込みLT資料
Serverspecを自分好みにアレンジ スクリーンショットで証跡保存を撲滅-
C#次世代非同期処理概観 - Task vs Reactive Extensions
Deep Dive async/await in Unity with UniTask(UniRx.Async)
StackExchangeで見たシステムプログラミング案件
More Related Content CPANの依存モジュールをもう少し正しく検出したい
php and sapi and zendengine2 and...
What's hot (20)
PHP と SAPI と ZendEngine3 と
Apacheの展望とmod_perlの超絶技巧 #yapcasia
PHPカンファレンス2014の懇親会飛び込みLT資料
Serverspecを自分好みにアレンジ スクリーンショットで証跡保存を撲滅-
C#次世代非同期処理概観 - Task vs Reactive Extensions
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Similar to How to debug a perl script using gdb (19)
StackExchangeで見たシステムプログラミング案件
PHP で実行中のスクリプトの動作を下から覗き見る
Write good parser in perl
clu2cは64ビットOSでも使えます (OSC 2012 Hiroshima LT用資料)
debugging server with strace
2011.06.11 v7から始めるunix まとめ
Programming camp 2010 debug hacks
More from akirahiguchi (6) ライブストリーミング低遅延化の取り組み @ DeNA
Handlersocket etc. 20110906
HandlerSocket plugin for MySQL (English)
HandlerSocket plugin for MySQL
How to debug a perl script using gdb1. Perl スクリプトを gdb でデバッグ 2011/10/15 YAPC::Asia Tokyo 2011 @ 大岡山 株式会社ディー・エヌ・エー 樋口 証 3. 作った https:// github.com/ahiguti/gdbperl 使いかた : 実行中プロセスのプロセス id を指定して実行 $ gdbperl.pl 24113 コアファイルを指定して実行 $ gdbperl.pl core.24113 /usr/bin/perl 実行すると perl の呼出し履歴などを表示する 呼出し元のファイル名と行番号 呼出し先の関数名と引数 4. 使用例 use IO::Socket::INET; sub baz { while (1) { my $sd = new IO::Socket::INET( PeerAddr => '10.1.0.0:80'); # ここで固まる sleep(1); } } sub bar { baz($_[0]); } sub foo { bar(35, 5.98, "xyz", @_); } foo("abc"); 5. gdbperl.pl による呼出し履歴出力例 [10] IO::Socket::connect() <- /home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket/INET.pm:257(IO::Socket::INET) [9] IO::Socket::INET::connect() <- /home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket/INET.pm:232(IO::Socket::INET) [8] (loop) <- /home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket/INET.pm:178(IO::Socket::INET) [7] IO::Socket::INET::configure(ref, ref) <- /home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket.pm:48(IO::Socket) [6] IO::Socket::new("IO::Socket::INET", "PeerAddr", "10.1.0.0:80") <- /home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket/INET.pm:37(IO::Socket::INET) [5] IO::Socket::INET::new("PeerAddr", "10.1.0.0:80") <- ./t.pl:7(main) [4] (loop) <- ./t.pl:6(main) [3] main::baz(35) <- ./t.pl:13(main) [2] main::bar(35, 5.9800000000000004, "xyz", "abc") <- ./t.pl:17(main) [1] main::foo("abc") <- ./t.pl:20(main) 6. どうやっているか? gdb -p でプロセスにアタッチ gdb にコマンドを送って perl インタプリタの内部状態を調べる gdbperl.pl 24113 verbose_gdb=1 のようにやると gdb との会話内容が見れる 7. gdbperl.pl の動作条件 gdb が必要 perl が「 -g 」付きでコンパイルされている C のソースレベルデバッグできないといけないから perlbrew install 5.15.3 -D ccflags='-g‘ perl 5.8 ~ 5.13 少なくとも GNU/Linux と MacOSX で動作 8. perl の caller() 関数 呼出し履歴を取得する関数 引数として、呼出しの深さを指定 pp_ctl.c に定義されている。 C の関数名は pp_caller この関数と同等のことを gdb 上からやれば、実行中のプロセスの呼出し履歴が調べられる 9. pp_caller のキモ cx = caller_cx(count, &dbcx); 指定された深さの呼出し履歴のポインタ stashname = CopSTASHPV(cx->blk_oldcop); 実行中の命令のパッケージ名 mPUSHs(newSVpv(OutCopFILE(cx->blk_oldcop), 0)); 実行中の命令のファイル名 mPUSHi((I32)CopLINE(cx->blk_oldcop)); 行番号 GV * const cvgv = CvGV(dbcx->blk_sub.cv); gv_efullname3(sv, cvgv, NULL); 呼出された関数の名前 AV * const ary = cx->blk_sub.argarray; 呼出しの引数 11. perl インタプリタインスタンスの取得 perl がスレッド付きでコンパイルされているとインタプリタが複数インスタンスに my_perl という名前なので、 C の呼出し履歴から探す スレッド無しのときはインタプリタがグローバルに定義されているので my_perl は不要 (gdb) bt #0 0xffffe410 in __kernel_vsyscall () #1 0xb7de2021 in connect () at ../sysdeps/unix/sysv/linux #2 0x0810f623 in Perl_pp_bind () at pp_sys.c:2475 #3 0x080ce533 in Perl_runops_standard () at run.c:41 #4 0x08073c40 in S_run_body ( my_perl=0x816b008 ) at perl.c #5 perl_run (my_perl=0x816b008) at perl.c:2252 #6 0x0805e99d in main (argc=2, argv=0xbf9fe104, env=0xbf9 (gdb) frame 4 #4 0x08073c40 in S_run_body (my_perl=0x816b008) at perl.c 2334 CALLRUNOPS(aTHX); 13. 現在実行中の命令 ( スレッド付き ) (gdb) p *my_perl->Icurcop $2 = {op_next = 0x823c930, op_sibling = 0x8260d38, op_ppaddr = 0x80dd030 <Perl_pp_nextstate>, op_targ = 0, op_type = 181, op_opt = 1, op_latefree = 0, op_latefreed = 0, op_attached = 0, op_spare = 0, op_flags = 1 '\001', op_private = 0 '\000', cop_line = 114 , cop_stashpv = 0x8260df0 " IO::Socket ", cop_file = 0x8260d90 " /home/user/perl5/perlbrew/perls/5.14.2-thr/lib/5.14.2/i686-linux-thread-multi/IO/Socket.pm ", cop_hints = 1794, cop_seq = 719, cop_warnings = 0x0, cop_hints_hash = 0x0} 14. 現在実行中の命令 ( スレッド無し ) (gdb) p *PL_curcop $2 = {op_next = 0x8184fe8, op_sibling = 0x81908f8, op_ppaddr = 0x80d00d0 <Perl_pp_nextstate>, op_targ = 0, op_type = 183, op_opt = 1, op_latefree = 0, op_latefreed = 0, op_attached = 0, op_spare = 0, op_flags = 1 '\001', op_private = 0 '\000', cop_line = 8, cop_stash = 0x816df58, cop_filegv = 0x816e168, cop_hints = 0, cop_seq = 1058, cop_warnings = 0x0, cop_hints_hash = 0x0} 15. ファイル名と行番号 (gdb) p PL_curcop->cop_filegv->sv_u.svu_gp->gp_sv->sv_u.svu_pv $3 = 0x818acd0 "/home/user/perl5/perlbrew/perls/perl-5.15.3/lib/5.15.3/i686-linux/IO/Socket.pm" (gdb) p PL_curcop->cop_line $4 = 114 16. 呼出し履歴 スレッド付き : my_perl->Icurstackinfo->si_cxstack[ 深さ ] スレッド無し : PL_curstackinfo->si_cxstack[ 深さ ] 履歴の最大長 : *cur_stackinfo->si_cxix 17. 呼出し元ファイル名と行番号 (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blku_oldcop->cop_filegv->sv_u.svu_gp->gp_sv->sv_u.svu_pv $86 = 0x817a238 "./t.pl" (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blku_oldcop->cop_line $87 = 17 18. 呼出し元パッケージ名 (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blku_oldcop->cop_stash->sv_any->xhv_max $88 = 511 (gdb) p (char *)((struct xpvhv_aux *)(PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blku_oldcop->cop_stash->sv_u.svu_hash+511+1))->xhv_name_u.xhvnameu_name->hek_key $89 = 0x817686c "main" 19. 呼出しの種類 (sub, eval, loop 等 ) (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_subst.sbu_type & 0xf $90 = 8 #define CXt_NULL 0 #define CXt_WHEN 1 #define CXt_BLOCK 2 #define CXt_GIVEN 3 #define CXt_LOOP_FOR 4 #define CXt_LOOP_PLAIN 5 #define CXt_LOOP_LAZYSV 6 #define CXt_LOOP_LAZYIV 7 #define CXt_SUB 8 #define CXt_FORMAT 9 #define CXt_EVAL 10 #define CXt_SUBST 11 20. 呼出し先パッケージ名 (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.cv->sv_any->xcv_gv->sv_any.xnv_u.xgv_stash->sv_any->xhv_max $91 = 511 (gdb) p (char *)((struct xpvhv_aux *)(PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.cv->sv_any->xcv_gv->sv_any.xnv_u.xgv_stash->sv_u.svu_hash+511+1))->xhv_name_u.xhvnameu_name->hek_key $92 = 0x817686c "main" 21. 呼出し先関数名 (gdb) p (char *)PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.cv->sv_any->xcv_gv->sv_any.xiv_u.xivu_namehek->hek_key $93 = 0x822e2dc "bar“ 23. 引数の数 (gdb) p PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.argarray->sv_any->xav_fill $94 = 3 これを更に +1 した値が引数の数 24. スカラ値 (SV) の型 (gdb) p (PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.argarray->sv_u.svu_array[0]).sv_flags $95 = 134222082 この値を 0xf でマスクする typedef enum { SVt_NULL, /* 0 */ SVt_BIND, /* 1 */ SVt_IV, /* 2 */ SVt_NV, /* 3 */ SVt_PV, /* 4 */ ... } svtype; 25. 整数型 (IV) のときの値取得 (gdb) p ((XPVIV*)(PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.argarray->sv_u.svu_array[0]).sv_any)->xiv_u.xivu_iv $96 = 35 26. 浮動小数点数 (NV) のときの値取得 (gdb) p ((XPVNV*)(PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.argarray->sv_u.svu_array[1]).sv_any)->xnv_u.xnv_nv $98 = 5.9800000000000004 27. 文字列 (PV) のときの値取得 (gdb) p (PL_curstackinfo->si_cxstack[2].cx_u.cx_blk.blk_u.blku_sub.argarray->sv_u.svu_array[2]).sv_u.svu_pv $100 = 0x818ea88 "xyz" 28. まとめ等 perl のコードも頑張れば gdb でデバッグできる 実行中プロセスもデバッグ可 ただし自動化しないときつい gdbperl.pl perl のバージョンとスレッド有無によって内部が大幅に異なる 実運用環境でもデバッグシンボルは付けておきましょう