080625gdbでXS on mod_perlをデバッグ

080625gdbでXS on mod_perlをデバッグ

プレゼンテーション

自己紹介

  • はてな 田中慎司(id:stanaka)
    • インフラやってます
    • 昨夜も、はてブを落としたりして、すんません

gdbとは

gdb = Gnu DeBugger

  • バイナリアンのたしなみ

CPUOSのデバッガ向けの機能を使って、任意のタイミングでプロセスを止めたり、再開したり、メモリの中身を読んだり、書き換えたりできます。

Perlレベルでは、手の届かない部分を自由に触れます。

xs on mod_perlの苦労

パターン1

  • たまに動作がおかしいと言われる
  • エラーログには「segmentation fault」
  • 原因はまったく不明
  • はまる

パターン2

  • たまにhttpdが暴走する
  • サービスダウン
  • 原因はまったく不明
  • はまる

2分で分かるgdbの使い方(mod_perl編)

gdb /usr/sbin/httpd
(gdb) run -X -f httpd_test.conf

「-X」でシングルプロセスモードで起動する

実行

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1215505888 (LWP 7191)]
XS_TestXS2_consume (my_perl=0x8015cae0, cv=0x80119dc8) at TestXS2.xs:29
29              *((int*)value) = 0;

Cレベルのバックトレース

(gdb) bt
#0  XS_TestXS2_consume (my_perl=0x8015cae0, cv=0x80119dc8) at TestXS2.xs:29
#1  0xb776f33d in Perl_pp_entersub () from /usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so
#2  0xb776879f in Perl_runops_standard () from /usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so
#3  0xb7708ffe in ?? () from /usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so
#4  0xb770d806 in Perl_call_sv () from /usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so
#5  0xb782a8bf in modperl_callback () from /usr/lib/httpd/modules/mod_perl.so
#6  0xb782afba in modperl_callback_run_handlers () from /usr/lib/httpd/modules/mod_perl.so
#7  0xb782b62a in modperl_callback_per_dir () from /usr/lib/httpd/modules/mod_perl.so
#8  0xb78246ef in ?? () from /usr/lib/httpd/modules/mod_perl.so
#9  0xb78248bb in modperl_response_handler_cgi () from /usr/lib/httpd/modules/mod_perl.so
#10 0x80024e4d in ap_run_handler () from /usr/sbin/httpd
#11 0x80028817 in ap_invoke_handler () from /usr/sbin/httpd
#12 0x80034c6e in ap_process_request () from /usr/sbin/httpd
#13 0x800319ff in ?? () from /usr/sbin/httpd
#14 0x8002cf8d in ap_run_process_connection () from /usr/sbin/httpd
#15 0x8002d08c in ap_process_connection () from /usr/sbin/httpd
#16 0x800396f2 in ?? () from /usr/sbin/httpd
#17 0x80039964 in ?? () from /usr/sbin/httpd
#18 0x8003a872 in ap_mpm_run () from /usr/sbin/httpd
#19 0x8001045d in main () from /usr/sbin/httpd

Perl_call_svがメソッド呼出し


Perlレベルのバックトレース

define curinfo
printf "%d:%s\n", my_perl->Tcurcop->cop_line, my_perl->Tcurcop->cop_file
end

my_perlがperlインタプリタを示す構造体

(gdb) curinfo
23:/home/stanaka/070615gdb_perl/lib/Test.pm
define longmess
  set $sv = Perl_eval_pv(my_perl, "Carp::longmess()", 1)
  printf "%s\n", ((XPV*) ($sv)->sv_any )->xpv_pv
end

Perl_eval_pvで、perlのeval相当構文を実行できる。

結果を$svから取り出す。

たまに、うまく取れないこともあるので注意。

Perl_eval_pv以外にもperl処理系を制御するメソッドはたくさんあります。ref. perlembed

暴走したら即attachするスクリプト

  • catch.sh
#!/bin/bash

TARGET=httpd
while [ 1 ];
do
    psline=`ps -C $TARGET u --sort=-vsz | head -2 | tail -1`
    maxvsz=`echo $psline | awk '{print $4 * 10}'`
    pid=`echo $psline | awk '{print $2}'`
    if [ $maxvsz -gt $1 ] ;
    then
        gdb $TARGET $pid
        exit
    fi
    sleep 1
done
  • %MEMが10%を越えたら、gdbでattach
# ./catch.sh 100

デモ

まとめ

  • 2分で分かるgdbの使い方
  • これは設定しておけ的gdb初期化マクロ
$ cat .gdbinit
define curinfo
printf "%d:%s\n", my_perl->Tcurcop->cop_line, my_perl->Tcurcop->cop_file
end

define longmess
  set $sv = Perl_eval_pv(my_perl, "Carp::longmess()", 1)
  printf "%s\n", ((XPV*) ($sv)->sv_any )->xpv_pv
end

define dumperany
  set $sv = Perl_eval_pv(my_perl, "use Data::Dumper; Dumper $arg0",0)
  printf "$arg0 = `%s'\n", $sv ? ((XPV*) ($sv)->sv_any )->xpv_pv : "cannot dump"
end