gdbで動作中のプロセスをデバッグしてみる
gdbを使ってLinux上で既に動作しているアプリケーションをattachしてデバッグしてみる。
今回は例としてsnmpdをほんの少しだけ解析します。環境はLinux(CentOS7)です。
gdbのインストール
gdbが無いと始まらないのでyumでインストールします。
[user@localhost gdb]$ sudo yum install gdb
SNMPエージェントのビルド
解析用のsnmpdをソースコードからインストールします。インストール方法はこちらを参照。
[user@localhost net-snmp-5.7.3]$ /usr/local/sbin/snmpd --version NET-SNMP version: 5.7.3 Web: http://www.net-snmp.org/ Email: net-snmp-coders@lists.sourceforge.net
シンボルの抽出と削除
インストールしたsnmpdをfileコマンドで調べるとシンボルがそのまま残っているのでstripコマンドでシンボルを削除します。
[user@localhost net-snmp-5.7.3]$ file /usr/local/sbin/snmpd /usr/local/sbin/snmpd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=e0b96f955caa14b8ad97c44cbc2f292f31e72caf, not stripped [user@localhost net-snmp-5.7.3]$ [user@localhost net-snmp-5.7.3]$ sudo strip /usr/local/sbin/snmpd [user@localhost net-snmp-5.7.3]$ [user@localhost net-snmp-5.7.3]$ file /usr/local/sbin/snmpd /usr/local/sbin/snmpd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=e0b96f955caa14b8ad97c44cbc2f292f31e72caf, stripped [user@localhost net-snmp-5.7.3]$
また、ビルドしたsnmpdからデバッグ用のシンボルファイルを取り出しておきます。
[user@localhost net-snmp-5.7.3]$ objcopy --only-keep-debug agent/.libs/snmpd snmpd.debug
SNMPエージェント起動してgdbでattach
準備が出来たのでSNMPエージェントを起動してgdbで補足してみます。
attach後にdirectoryでソースコードを場所、symbol-fileでシンボル情報をgdbに教えてあげます。
そうしたら、snmpdが周期的に実行するループの処理にブレークポイントを張って動きを見てみましょう。
[user@localhost net-snmp-5.7.3]$ sudo /usr/local/sbin/snmpd -c /usr/local/etc/snmpd.conf -p /var/run/snmpd.pid -M /usr/local/share/snmp/mibs [user@localhost net-snmp-5.7.3]$ [user@localhost net-snmp-5.7.3]$ ps aux |grep snmpd root 11089 0.0 0.3 132640 4000 ? S 09:47 0:00 /usr/local/sbin/snmpd -c /usr/local/etc/snmpd.conf -p /var/run/snmpd.pid -M /usr/local/share/snmp/mibs [user@localhost net-snmp-5.7.3]$ [user@localhost net-snmp-5.7.3]$ sudo gdb --pid=11089 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7 Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> ...(途中略)... (gdb) (gdb) directory agent/ Source directories searched: /home/user/gdb/net-snmp-5.7.3/agent:$cdir:$cwd (gdb) (gdb) symbol-file snmpd.debug Load new symbol table from "/home/user/gdb/net-snmp-5.7.3/snmpd.debug"? (y or n) y Reading symbols from /home/user/gdb/net-snmp-5.7.3/snmpd.debug...done. (gdb) (gdb) break snmpd.c:1217 Breakpoint 1 at 0x40383c: file snmpd.c, line 1217. (gdb) (gdb) continue Continuing. Breakpoint 1, receive () at snmpd.c:1217 (gdb) p reconfig $1 = 0 (gdb) l 1212 1213 /* 1214 * Loop-forever: execute message handlers for sockets with data 1215 */ 1216 while (netsnmp_running) { 1217 if (reconfig) { 1218 #if HAVE_SIGHOLD 1219 sighold(SIGHUP); 1220 #endif 1221 reconfig = 0; (gdb) p netsnmp_running $2 = 1 (gdb) (gdb) detach Detaching from program: /usr/local/sbin/snmpd, process 11089 (gdb) q
ちゃんと期待した箇所でブレークしてますね。detachするとsnmpdがgdbの補足から解放されます。 これで起動中のプロセスをgdbでデバッグできることが分かりました。
自動化してみる
実際にやってみるとgdbを起動してから実際に確認するまでにコマンドを打つ操作が多いことが分かります。1回だけなら良いですが何回も同じことをするのはとても面倒です。こういうところは自動化していきましょう。
gdbは-xオプジョンで指定したファイルからgdbのコマンドを入力することができるのでこれを利用します。 外部入力のファイルとしてsnmp_gdb.shを作成します。
snmp_gdb.sh
# # snmpdをgdbでデバッグしてみる # # ログ出力 set logging file gdb.log set logging on # ページャー機能をOFF set pagenation off # ソースコードの所在を指定 directory agent/ # デバッグの為のシンボルファイルの読み込み symbol-file snmpd.debug # ブレークポイントの設定 break snmpd.c:1217 commands # ブレークポイント到達時に実行するコマンド printf "#### Break Point Start ###\n" list printf "reconfig = %d\n", reconfig printf "netsnmp_running = %d\n", netsnmp_running continue printf "#### Break Point End ###\n" printf "\n" end
commands~endの句はbreakポイントに到達した際に自動で実行するコマンドを定義しています。 commandsの書式はcommands <ブレークポイント番号>ですが、引数を省略すると直前に張ったブレークポイントのさ番号になります。
ファイルが作成できたら実際にやってみましょう。
[user@localhost net-snmp-5.7.3]$ sudo gdb --pid=11089 -x snmp_gdb.sh ...(略) Breakpoint 1 at 0x40383c: file snmpd.c, line 1217. Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7_3.5.x86_64 openssl-libs-1.0.1e-60.el7_3.1.x86_64 zlib-1.2.7-17.el7.x86_64 (gdb) (gdb) continue Continuing. Breakpoint 1, receive () at snmpd.c:1217 1217 if (reconfig) { #### Break Point Start ### 1212 1213 /* 1214 * Loop-forever: execute message handlers for sockets with data 1215 */ 1216 while (netsnmp_running) { 1217 if (reconfig) { 1218 #if HAVE_SIGHOLD 1219 sighold(SIGHUP); 1220 #endif 1221 reconfig = 0; reconfig = 0 netsnmp_running = 1 #### Break Point End ### ...(以降繰り返しのため省略)...
これで期待通りの動作を確認できました。キチンと作ればテストの自動化も出来そうですね。