gdbで動作中のプロセスをデバッグしてみる
gdbを使ってLinux上で既に動作しているアプリケーションをattachしてデバッグしてみる。
今回は例としてsnmpdをほんの少しだけ解析します。環境はLinux(CentOS7)です。
[rtoc_mokuji title=”” title_display=”” heading=”h3″ list_h2_type=”” list_h3_type=”” display=”” frame_design=”” animation=””]
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 ###
...(以降繰り返しのため省略)...
これで期待通りの動作を確認できました。キチンと作ればテストの自動化も出来そうですね。
コメント