2011年12月6日火曜日

debuginfo RPM パッケージで、ソースレベル・デバッグをする

お元気ですか? あいにく風邪気味の、サイオス 那賀です。

最近の RPM ベースの Linux ディストリビューションであれば、バイナリ RPM と共に、debuginfo パッケージが提供されているのが普通だと思います。この debuginfo パッケージをインストールすることで、従来であればバイナリパッケージ作成の際に strip で落とされてしまっていたデバッグ情報やソース情報を別パッケージとして利用することができるようになるため、本体パッケージを肥大化させることなく、本体パッケージのバイナリそのものを用いて、ソースレベルでのデバッグを行うことができます (GDB の分離デバッグ情報についての詳細は「Separate Debug Files - Debugging with GDB」あたりを、RPM のビルド中の処理については /usr/lib/rpm/find-debuginfo.sh の debugedit のあたりを参照してください)。

しかし現在の提供形態だと、いくつか問題があります。

  • バイナリは "-g -O2" でコンパイルされているので、最適化がかかっており、デバッグがしづらい
  • 自前でパラメータを変えてビルドしたら、既存の debuginfo は使用できない
  • 最新のバイナリのアップデートは公開されているのに、対応する debuginfo が出ていないことがある (特に CentOS)
  • debuginfo パッケージは大きいので、SRPM ファイルともども、ミラーから削除されがちで入手できなかったりする (特に CentOS)

そこで、自前でパッケージをビルドして、debuginfo を活用する方法をご紹介します。以下は、おそらく RHEL4~ の系列では動きますが、それ以外はよく知りません。合わせて以前の記事も参考にしてください → 「SIOS OSS Tech: ソースの入手と再ビルド ~ CentOS / Scientific Linux 編」。

まずは、redhat-rpm-config パッケージをインストールします。debuginfo パッケージ生成関連のマクロはこのパッケージに含まれているため、これをインストールしておかないと、RPM パッケージのビルドはできますが、debuginfo パッケージが生成されません。

[root@centos4 ~]# yum install redhat-rpm-config

次にビルドです。"rpmbuild --rebuild <SRPM ファイル>" なり "rpmbuild -ba <SPEC ファイル>" なり (渋いところでは、"rpmbuild -ta <tar.gz ファイル>" なり) でビルドをするところですが、ここで、デバッグ用のオプションを指定することができます。デフォルトは "-g -O2" になっていると思いますが、特にこの "-O2" のおかげでかなり最適化がされてしまい、ソースレベルでのデバッグがしづらくなります。これを "-O0" にして最適化を切ると同時に、"-pg", "-g3" で、付加的なデバッグ情報も付けておきます。

注意: 最適化オプションやコンパイル環境の変更によって、調査するはずだった不具合が解消してしまうことも、よくあります。困ります

以下は、CentOS 4 での例です。ソースの入手からビルド~ソースレベル・デバッグまでを見てみます。

[root@centos4 ~]# wget http://ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os/i386/SRPMS/bash-3.0-19.2.src.rpm
(中略)
[root@centos4 ~]# rpmbuild --rebuild \
 --define="optflags -pg -g3 -O0" bash-3.0-19.2.src.rpm
(中略)
Wrote: /usr/src/redhat/RPMS/i386/bash-3.0-19.2.i386.rpm
Wrote: /usr/src/redhat/RPMS/i386/bash-debuginfo-3.0-19.2.i386.rpm
(中略)
[root@centos4 ~]# rpm -Uvh --force \
 /usr/src/redhat/RPMS/i386/bash-3.0-19.2.i386.rpm \
 /usr/src/redhat/RPMS/i386/bash-debuginfo-3.0-19.2.i386.rpm
(中略)
[root@centos4 ~]# bash -c "while true; do sleep 1; done" &
[1] 32108
[root@centos4 ~]# gdb -p 32108
(中略)
Attaching to process 32108
Reading symbols from /bin/bash...Reading symbols from
 /usr/lib/debug/bin/bash.debug...done.
Using host libthread_db library "/lib/tls/libthread_db.so.1".
done.
Reading symbols from /lib/libtermcap.so.2...done.
Loaded symbols for /lib/libtermcap.so.2
Reading symbols from /lib/libdl.so.2...done.
Loaded symbols for /lib/libdl.so.2
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0x0027f7a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
(gdb) bt
#0  0x0027f7a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
#1  0x00324533 in __waitpid_nocancel () from /lib/tls/libc.so.6
#2  0x0807d03f in waitchld (wpid=32134, block=1) at jobs.c:2497
#3  0x0807be1d in wait_for (pid=32134) at jobs.c:1904
#4  0x0806b901 in execute_command_internal (command=0x9c73280, asynchronous=0, pipe_in=-1, pipe_out=-1, fds_to_close=0x9c73378) at execute_cmd.c:704
#5  0x0806b279 in execute_command (command=0x9c73280) at execute_cmd.c:351
#6  0x0806e2db in execute_while_or_until (while_command=0x9c732d0, type=0) at execute_cmd.c:2327
#7  0x0806e1ef in execute_while_command (while_command=0x9c732d0) at execute_cmd.c:2273
#8  0x0806ba79 in execute_command_internal (command=0x9c732e0, asynchronous=0, pipe_in=-1, pipe_out=-1, fds_to_close=0x9c732f8) at execute_cmd.c:764
#9  0x080a7187 in parse_and_execute (string=0x9c72d00 "while true; do sleep 1; done", from_file=0x80d9392 "-c", flags=4) at evalstring.c:267
#10 0x0805cb6f in run_one_command (command=0xbff9bc18 "while true; do sleep 1; done") at shell.c:1269
#11 0x0805bd7a in main (argc=3, argv=0xbff378e4, env=0xbff378f4) at shell.c:653
(gdb) frame 11
#11 0x0805bd7a in main (argc=3, argv=0xbff378e4, env=0xbff378f4) at shell.c:653
653           run_one_command (command_execution_string);
(gdb) l
648           if (debugging_mode)
649             start_debugger ();
650
651     #if defined (ONESHOT)
652           executing = 1;
653           run_one_command (command_execution_string);
654           exit_shell (last_command_exit_value);
655     #else /* ONESHOT */
656           with_input_from_string (command_execution_string, "-c");
657           goto read_and_execute;
(gdb) p command_execution_string
$1 = 0xbff9bc18 "while true; do sleep 1; done"

ソースもシンボルも見えています。良いようですね。

では。

話は逸れますが、上記で、上書きでインストールをする際に "rpm" コマンドに "-U" と "--force" を組み合わせています。"--force" は依存を壊しませんので、それほど問題ではありません。一方、"--nodeps" はいけません。RPM の本質は依存関係の解決だと思いますので、それを破壊する "--nodeps" は悪です。"--nodeps" を付けなければならない状況に陥ったら、開発者 or 管理者として負けだと思ってください。

0 件のコメント:

コメントを投稿