Segmentation Faultぐ

Segmentation Fault

コアダンプの数だけ強くなれるよ。

gdbで動作中のプロセスをデバッグしてみる

gdbを使ってLinux上で既に動作しているアプリケーションをattachしてデバッグしてみる。

今回は例としてsnmpdをほんの少しだけ解析します。環境はLinux(CentOS7)です。

gdbのインストール

gdbが無いと始まらないのでyumでインストールします。

[user@localhost gdb]$ sudo yum install gdb


SNMPエージェントのビルド

解析用のsnmpdをソースコードからインストールします。インストール方法はこちらを参照。

www.segmentation-fault.xyz

[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   ###

...(以降繰り返しのため省略)...



これで期待通りの動作を確認できました。キチンと作ればテストの自動化も出来そうですね。