多くのプログラムはすべての機能を自前で実装しているわけではなく, ライブラリと呼ばれる外部のプログラムに依存しています. よく使われるライブラリには,
通常,ライブラリ関数の中身まで追跡する必要はないので, このことはあまり問題になりません. ライブラリ関数の呼び出しに差し掛かったら,nextコマンドで 関数呼び出し毎飛ばせば良いだけの話です.
しかし,それではすまない場合というのがあり, その代表例がGUIプログラムです. GUIプログラムは通常,「コールバック」と呼ばれる関数を, GUIライブラリに登録しておき,あるイベント (キーが押されたとか,マウスがクリックされたなど) が起きたら登録してあるコールバック関数が呼ばれる, という作りをしています.そして通常アプリケーションの全ての処理は, コールバックの中でなされます. このような構造のアプリケーションでは, 関数呼び出しのフローが,
ユーザプログラム -----> GUIライブラリ --- (コールバック) ---> ユーザプログラムという流れになるので,ユーザプログラムからGUIライブラリを呼び出すところで, GUIライブラリの内部を追跡していかないと,そこから呼ばれる(コールバックされる)ユーザプログラムを追跡することも出来ません.
ライブラリ内部を追跡するひとつの方法は, そのライブラリ自身も自分でソースからコンパイルすることでありますが, それをやっていくと果てしなく色々なソースをコンパイルをする羽目に陥る可能性があります. 特にGUIライブラリの多くは巨大なので, コンパイルに失敗したり, アプリケーション本体よりもよほど長時間を要し, だんだん何をやっていたのかわからなくなることもあります.
ここではひとつの有用なスキルとして, パッケージとして提供されているライブラリ内部を (自分でコンパイルせずに)追跡する方法を紹介します.
ただし,GUIアプリを理解するのに, 巨大なGUIライブラリの中を本当に追跡したいか, というのは,また別の問題かもしれません.一旦それはおいておいて, ここではライブラリの中を追跡できるようになることは有用であるので, そのやり方を説明します.GUIアプリを追跡するためのいくつかの作戦 については最後に述べます.
例として,C言語のシステム関数であるqsort の中身を追跡することにします. qsortは2つの要素を比較する関数(まさにコールバック)を受け取って, それを呼び出すので, GUIアプリケーションで遭遇する問題の単純ケースになっています.
以下で順に説明しますが,端的に行って次の2つのことを行います.
$ sudo apt-get install libc6-dbg後者は,
$ apt-get source libc6でインストールします.後者は,システムディレクトリにインストールするのではなく, カレントディレクトリに大量のソースフォルダや,付随物をインストールするので, 散らかされたくない場所ではやらないでください (適当な空のサブフォルダを作ってやりましょう).
以下ではそれぞれがある時と無いときで何がおきるのか, また, そもそも何というパッケージをいれればよいのかを突止める方法を紹介します.
qsortを用いた例として,対象プログラムは以下の通りとします.
#include <stdio.h> #include <stdlib.h> int cmp_int(const void * a_, const void * b_) { const int * a = a_, * b = b_; return a[0] - b[0]; } int main() { int i; int a[] = { 7, 6, 3, 8, 5, 4 }; int n = sizeof(a)/sizeof(int); qsort(a, n, sizeof(int), cmp_int); for (i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); return 0; }
単純に上記のソースファイルだけを-g付きでコンパイルした場合の動作は, qsort関数の中にstepコマンドで入っていこうと思っても, 単純に飛ばされてしまいます.これは,qsortという関数のデバッグ情報 (具体的にはqsortのソースコードの各行が, どの命令アドレスに対応しているかという情報)が ないためにおきます.この情報がないと,stepせよと言われても, どの命令で実行をとめていよいのかわからないので,当然の動作と言えます.
Linuxのパッケージ管理システムでは,主要な(全部ではない) ライブラリのデバッグシンボルが別のパッケージとして 提供されていることが多いです.我々が普段,-g をつけて プログラムをコンパイルすると,プログラムの命令列と, デバッグ情報が一つに収まった実行可能ファイルが出来ます. もしこのようにしてコンパイルされたライブラリが最初から 出荷されているのであれば,わざわざ別のパッケージをインストール する必要は最初からなかったのですが, 配布ファイルの大きさを小さくする目的から, 実行命令列と, デバッグ情報が分離されて提供されています. (具体的にどうやって分離するかはここでの本題ではないが, 興味があれば,こちら). デバッグシンボルパッケージとはまさしくこの, 別ファイルに納められたデバッグ情報(だけ)を提供するパッケージです.
たとえば
$ apt-cache search libc6とすると,多数のパッケージが見つかりますがその中に, 以下のようにlibc6-dbg というパッケージが見つかります.
$ apt-cache search libc6 libc6 - GNU C Library: Shared libraries libc6-arm64-cross - GNU C Library: Shared libraries (for cross-compiling) libc6-armhf-cross - GNU C Library: Shared libraries (for cross-compiling) libc6-dbg - GNU C Library: detached debugging symbols libc6-dev - GNU C Library: Development Libraries and Header Files ...Debian, UbuntuなどのLinuxディストリビューションでは, ライブラリの名前-dbg という名前のパッケージがあれば, それが目当てのものでしょう. 分離されたデバッグシンボルのパッケージをインストールする手順は, 他のパッケージと同じです.
$ sudo apt-get install libc6-dbg
これを入れた状態でgdbを用いて qsort に step を試みる(qsortの行にデバッガのカーソルがある状態で,stepコマンドを実行する)と, 以下のような動作になります.
(gdb) step __GI_qsort (b=0x7fffffffe390, n=6, s=4, cmp=0x400626 <cmp_int>) at msort.c:308 308 msort.c: そのようなファイルやディレクトリはありません. (gdb)
これの意味するところは,以下の通り.
この状態でも,(各行に対応する命令アドレスがわかるため),step, next などのコマンドでqsort関数の内部を実行していくことは可能ですが, おそらくここで終わらせたくはないでしょう. ソースコードを表示したければ,ソースファイルを入手する必要があります.
$ apt-get source libc6小さな注意として,まず,sudoで実行する必要はありません(しないほうが良い). ソースファイルはシステムディレクトリ(/usrとか)ではなく, コマンドを実行したディレクトリ(カレントディレクトリ)にダウンロードされます. もうひとつの注意は,一つのディレクトリにきれいにダウンロードされるとは 限らず,複数のファイルがカレントディレクトリにぶちまけられることです. 多すぎて, どのファイルが実際にダウンロードされたのかわからない,ということもあります. ので,これをやる際は,以下のように空のフォルダを作ってそこで実行することをおすすめです.
$ mkdir libc6-src $ cd libc6-src $ apt-get source libc6 パッケージリストを読み込んでいます... 完了 'libc6' の代わりに 'glibc' をソースパッケージとして選出しています ... ... ... $ ls glibc-2.23/ glibc_2.23-0ubuntu3.dsc glibc_2.23-0ubuntu3.debian.tar.xz glibc_2.23.orig.tar.xz
無事にデバッガがソースファイルを勝手に表示してくれるようになるためには, どこにソースがありますよ,ということをデバッガに教えるステップが必要です. それは,GDBでは,directory というコマンドでおこないます. 例えば,qsortに入った時の以下のメッセージを思い出して下さい.
(gdb) step __GI_qsort (b=0x7fffffffe390, n=6, s=4, cmp=0x400626 <cmp_int>) at msort.c:308 308 msort.c: そのようなファイルやディレクトリはありません.これが無事見つかるためには, msort.c というファイルが存在するフォルダを指定する必要があります.そのために, そもそも msort.c がどこにあるのかを突き止めましょう. 先ほどソースをダウンロードしたフォルダで, 以下のようにして見つけます.
$ find glibc-2.23 -name msort.c glibc-2.23/stdlib/msort.cということでこの glibc-2.23/stdlib/ というフォルダを,directoryコマンドに指定してやればOKです(実際のパス名はどこで作業をしているかで異なります.以下はたまたまこれを書いている時のパス名です).
(gdb) directory ~/tmp/dbg/libc6-src/glibc-2.23/stdlib Source directories searched: /home/tau/tmp/dbg/libc6-src/glibc-2.23/stdlib:$cdir:$cwd
これで,無事ソースファイルを表示しながら実行を追っていくことが出来ます.
void qsort (void *b, size_t n, size_t s, __compar_fn_t cmp) { => return __qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL); } libc_hidden_def (qsort)
今の場合,qsortという関数を提供しているのが, libc6というパッケージであるということは,既知として話をすすめました. GUIライブラリを使っている時も, なんとなく当てずっぽうパッケージ名がわかっている時もあるでしょうが, 一般には,もう少し確実に突き止める方法が必要です. 関数名からパッケージ名へたどり着くには以下の二つの関連を追う必要があります.
(gdb) b qsort Breakpoint 1 at 0x4004e0 (gdb) r Starting program: /home/tau/tmp/dbg/a.out Breakpoint 1, 0x00007ffff7a47750 in qsort () from /lib/x86_64-linux-gnu/libc.so.6ここで表示されるファイル名(/lib/x86_64-linux-gnu/libc.so.6)が求めるもの. デバッグシンボルがない状態でも, 各関数名の先頭アドレスはわかります. 従って,関数名でブレークポイントをかければ,そこで実行を止め, その関数がなんというファイルにあるのかを表示させることが出来ます.
$ ldd a.out linux-vdso.so.1 => (0x00007ffd0e3f5000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb08e2e9000) /lib64/ld-linux-x86-64.so.2 (0x0000564998139000)もちろんある程度知識がないと, この3つのうちのどれなのかということがわからないかもしれませんが, ライブラリの一般的な名称(gtkとか,qtとか)が判っているなら, お手軽な方法です.
dpkg -Sというコマンドで, あるファイルを提供しているパッケージ名を得ることが出来ます (ただしそのパッケージがインストールされていることが条件です). 今既に,/lib/x86_64-linux-gnu/libc.so.6 というファイル名がわかっていますので,
$ dpkg -S /lib/x86_64-linux-gnu/libc.so.6 libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6とします.左側に表示されているのがパッケージ名.このうち:amd64 部分は アーキテクチャ名なので,libc6というのがパッケージ名になっていると 理解すればおそらく良いのでしょう.これができたら,libc6, libc6-dbgという パッケージが実際に存在するかを以下で確かめて下さい.
$ apt-cache search libc6 libc6 - GNU C Library: Shared libraries libc6-arm64-cross - GNU C Library: Shared libraries (for cross-compiling) libc6-armhf-cross - GNU C Library: Shared libraries (for cross-compiling) libc6-dbg - GNU C Library: detached debugging symbols libc6-dev - GNU C Library: Development Libraries and Header Files ...
これら(デバッグ情報のパッケージ,ソースファイルのダウンロード, 種々のケースでのデバッガの動作,ldd, dpkgなど) はすべて有用な知識とはおもいます (この演習では,その場その場の場当たり的な 方法で答えを見つけることよりも,一般的に有用な考え方や 手法,ツールの使い方を習得して欲しいので,その意味からも これは有用なexerciseです).
しかし,果たしてでかいGUIライブラリを用いて書かれた でかいGUIアプリを追跡するのに, 本当にGUIライブラリの中を一歩一歩追跡していくのが よいかというのは別問題かもしれません. 多くの場合,GUIライブラリの中が見たいわけではなく, 追跡したいのは(GUIからコールバックされた), アプリ内の関数です.GUIライブラリには, コールバックを登録するAPIがきちんと定義されているわけですから, その登録APIを呼び出している場所を突き止める, またはソース全体を見て,コールバックらしき関数を見つければ, その関数に直接ブレークポイントを設定することが出来ます. このやり方では, 自分が怪しいと思った関数にブレークポイントをかけているので, 知らないうちに重要な関数がコールバックされていてもわからず, 実行の正確な順番が把握できない(例えば知らぬ間に変数の値が, 見逃したコールバックによって書き換わっている), というリスクもあります. しかし,所望の動作を実現するのに必要な関数が 少なく,関係する関数を把握できるとおもうのであれば良い方法です.
当てずっぽうでいじってみて動かしてみる,という方法で動けばそれは めでたいですが,裏を返せば設定課題が簡単ということかもしれません. ぜひ,複雑なアプリ内部を変更できるようになる汎用的・強力な知識, スキルを身につけるという気持ちで臨んでみて下さい.
(gdb) set auto-solib-add offrunをした時や,その他のタイミングで,自動的にすべてのライブラリのシンボルを読み込むという動作をオフにする
(gdb) share ライブラリ名必要だと思ったライブラリのシンボルを明示的に読み込みます.
(gdb) set verboseライブラリのシンボルをロードするたびにメッセージを表示します. set auto-solib-add on/off することの効果を目で見られます.
(gdb) thread apply all コマンド全部のスレッドに同じコマンドを適用します.例えば,
(gdb) thread apply all btで全部のスレッドのスタックトレースを一気に表示できます. スタックトレースを一気に表示することで, どのファイルのシンボルをロードすればよいかがわかります.
以下のように, runを実行した時に多数のライブラリがロードされます. ちなみにですが,できるかぎり多数のライブラリのデバッグシンボル パッケージを インストールしており,なるべく多くのシンボルが 読み込まれるようにしています. それでも一部のライブラリに対しては,no debugging symbols found と表示されます.通常の環境ではもっと多くのライブラリが, no debugging symbols found となることでしょう. 皮肉なことに,そうであるほうが,立ち上がるのは速くなります.
さらに余談. それでもgnumericの起動にかかる時間はせいぜい,5秒程度でした. Chromiumとは桁が違います.おそらく, Chromiumは,サイズが大きいだけでなく, 多くのファイルを,外部のライブラリではなく 自分自身の中に抱えているせいで, ほぼすべてのファイルにデバッグシンボルが存在しているのでしょう. ここで紹介している手法は(おそらく)そういう場合にこそ力を 発揮します.
Reading symbols from gnumeric...done. (gdb) set verbose (gdb) run Starting program: /home/tau/install/gnumeric/bin/gnumeric Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so...done. done. Reading symbols from system-supplied DSO at 0x7ffff7ffa000...(no debugging symbols found)...done. Reading in symbols for rtld.c...done. Reading symbols from /home/tau/install/gnumeric/lib/libspreadsheet-1.12.9.so...done. ... この間約100行 ... Reading symbols from /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.so...(no debugging symbols found)...done. Reading symbols from /usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx.so...(no debugging symbols found)...done. Reading symbols from /usr/lib/x86_64-linux-gnu/libfcitx-gclient.so.0...(no debugging symbols found)...done. Reading symbols from /usr/lib/x86_64-linux-gnu/libfcitx-utils.so.0...(no debugging symbols found)...done. [New Thread 0x7fffe088f700 (LWP 13127)] Reading in symbols for dl-close.c...done.
Type "apropos word" to search for commands related to "word"... Reading symbols from gnumeric...done. (gdb) set verbose (gdb) set auto-solib-add off (gdb) run Starting program: /home/tau/install/gnumeric/bin/gnumeric Reading symbols from system-supplied DSO at 0x7ffff7ffa000...(no debugging symbols found)...done. Reading symbols from /lib/x86_64-linux-gnu/libpthread.so.0...Reading symbols from /usr/lib/debug/.build-id/b7/7847cc9cacbca3b5753d0d25a32e5795afe75b.debug...done. done. [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7fffe95e8700 (LWP 15718)] [New Thread 0x7fffe8d97700 (LWP 15719)] [New Thread 0x7fffe3fff700 (LWP 15720)] [New Thread 0x7fffe088f700 (LWP 15729)]ただしこれだけではもちろん,ソースファイル上でプログラムの実行を追っていくことはできません.肝心なことはここから,「必要に応じて」ファイルをロードしていくことです.そのためには,プログラムが現在止まっているライブラリファイルを突き止め,そのライブラリファイルのデバッグ情報をロードします.現在どこで止まっているかは,バックトレースを見るとわかります.
(gdb) bt #0 0x00007ffff5dd1e8d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x0000000000000000 in ?? ()現在,/lib/x86_64-linux-gnu/libc.so.6 というファイルの中の どこかで止まっているということだけがわかります.それ以外の情報, はすべて?? になっていて,要するにデバッグシンボルがないので わからないということです. なお,これは プログラム中に多数存在する1スレッドの情報です.すべてのスレッドの情報を 一気に表示させるには,thread apply allを使います.
(gdb) thread apply all bt Thread 4 (Thread 0x7fffe3fff700 (LWP 15720)): #0 0x00007ffff5dd1e8d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x0000000000000000 in ?? () Thread 3 (Thread 0x7fffe8d97700 (LWP 15719)): #0 0x00007ffff5dd1e8d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x0000000000000000 in ?? () Thread 2 (Thread 0x7fffe95e8700 (LWP 15718)): #0 0x00007ffff5dd1e8d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x0000000000000000 in ?? () Thread 1 (Thread 0x7ffff7f24a40 (LWP 15712)): #0 0x00007ffff5dd1e8d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x0000000000000000 in ?? () (gdb)ここでとりあえず唯一のわかっている情報である, /lib/x86_64-linux-gnu/libc.so.6 のデバッグ情報を以下のコマンドで読み込みます.ちなみにこういうコマンドを打ち込む時も,Emacsでマウス無しのコピペなどが使えるようになっておくと,作業が何倍も高速になります.
(gdb) share /lib/x86_64-linux-gnu/libc.so.6 Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so...done. done. Reading in symbols for ../sysdeps/unix/syscall-template.S...done. Current language: auto The current source language is "auto; currently asm".その後でまたバックトレースを表示してみます. するとどうでしょう,何やら情報が増えていることがわかります.
(gdb) thread apply all bt Thread 4 (Thread 0x7fffe3fff700 (LWP 15720)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #2 0x0000000000000020 in ?? () #3 0x0000000100682d00 in ?? () #4 0xffffffff7fffffff in ?? () #5 0xe315e2a68ed3d400 in ?? () #6 0x0000000000000000 in ?? () Thread 3 (Thread 0x7fffe8d97700 (LWP 15719)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #2 0x0000000000000000 in ?? () Thread 2 (Thread 0x7fffe95e8700 (LWP 15718)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #2 0x000000000000000f in ?? () #3 0x00000001f65cc900 in ?? () #4 0xffffffff7fffffff in ?? () #5 0xe315e2a68ed3d400 in ?? () #6 0x000000000065eca0 in ?? () #7 0x0000000000661380 in ?? () #8 0x0000000000000001 in ?? () #9 0x0000000000000000 in ?? () Thread 1 (Thread 0x7ffff7f24a40 (LWP 15712)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #2 0x0000000000000010 in ?? () #3 0x00000001f632390d in ?? () #4 0xffffffff7fffffff in ?? () #5 0xe315e2a68ed3d400 in ?? () #6 0x0000000000000000 in ?? () (gdb)ここで新しく発見されたファイル /lib/x86_64-linux-gnu/libglib-2.0.so.0 のシンボルもロードします.どんどん情報が増えていきます.
(gdb) thread apply all bt Thread 4 (Thread 0x7fffe3fff700 (LWP 15720)): Reading in symbols for /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c...done. Reading in symbols for /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gthread.c...done. Reading in symbols for pthread_create.c...done. Reading in symbols for ../sysdeps/unix/sysv/linux/x86_64/clone.S...done. #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in g_main_context_poll (priority=2147483647, n_fds=3, fds=0x7fffd80010c0, timeout=<optimized out>, context=0x7fffe400e0e0) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4135 #2 g_main_context_iterate (context=0x7fffe400e0e0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3835 #3 0x00007ffff6307722 in g_main_loop_run (loop=0x7fffe400e070) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4034 #4 0x00007ffff467c916 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0 #5 0x000000000064f720 in ?? () #6 0x00007ffff632dbc5 in g_thread_proxy (data=0x7fffe400e0b0) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gthread.c:780 #7 0x00007ffff60a76fa in start_thread (arg=0x7fffe3fff700) at pthread_create.c:333 #8 0x00007ffff5dddb5d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 Thread 3 (Thread 0x7fffe8d97700 (LWP 15719)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in g_main_context_poll (priority=2147483647, n_fds=2, fds=0x7fffdc0008c0, timeout=, context=0x7fffe400d920) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4135 #2 g_main_context_iterate (context=context@entry=0x7fffe400d920, block=block@entry=1, dispatch=dispatch@entry=1, self= ) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3835 #3 0x00007ffff63074ac in g_main_context_iteration ( context=0x7fffe400d920, may_block=may_block@entry=1) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3901 #4 0x00007ffff63074e9 in glib_worker_main (data= ) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:5672 #5 0x00007ffff632dbc5 in g_thread_proxy (data=0x64f6d0) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gthread.c:780 #6 0x00007ffff60a76fa in start_thread (arg=0x7fffe8d97700) at pthread_create.c:333 #7 0x00007ffff5dddb5d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 Thread 2 (Thread 0x7fffe95e8700 (LWP 15718)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in g_main_context_poll (priority=2147483647, n_fds=1, fds=0x7fffe40010e0, timeout= , context=0x661380) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4135 #2 g_main_context_iterate (context=context@entry=0x661380, block=block@entry=1, dispatch=dispatch@entry=1, self= ) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3835 #3 0x00007ffff63074ac in g_main_context_iteration ( context=0x661380, may_block=1) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3901 #4 0x00007fffe95f028d in ?? () from /usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so #5 0x000000000064f140 in ?? () #6 0x00007ffff632dbc5 in g_thread_proxy (data=0x661380) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gthread.c:780 #7 0x00007ffff60a76fa in start_thread (arg=0x7fffe95e8700) at pthread_create.c:333 #8 0x00007ffff5dddb5d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 Thread 1 (Thread 0x7ffff7f24a40 (LWP 15712)): #0 0x00007ffff5dd1e8d in poll () at ../sysdeps/unix/syscall-template.S:84 #1 0x00007ffff630739c in g_main_context_poll (priority=2147483647, n_fds=3, fds=0x8e0020, timeout= , context=0x656260) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4135 #2 g_main_context_iterate (context=0x656260, block=block@entry=1, dispatch=dispatch@entry=1, self= ) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:3835 #3 0x00007ffff6307722 in g_main_loop_run (loop=0x8e0000) at /build/glib2.0-7IO_Yw/glib2.0-2.48.1/./glib/gmain.c:4034 #4 0x00007ffff6d0d395 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 #5 0x0000000000000000 in ?? () (gdb)
あとはこれを,自分がソース上で追跡したい所まで 表示される状態 (さしあたりスタックの上から数段分が表示されていればよいでしょう) にしてから, プログラムの実行を(おそらく, finコマンドで)開始して下さい.
繰り返しますが,どうやって該当箇所を突き止めるのが最善手かは, ケースバイケースです.ソースを検索した方が速い (あとは当てずっぽうでなんとかなる)という場合もあるでしょう. ソースを検索するのが絨毯爆撃なら, デバッガで追いかけるのは,精密誘導ミサイルのようなものです. どちらが良いかはケースバイケースです. ここでは,本当はデバッガで追いかけたいのだがこのせいで諦める, ということがないように(+ こちらも皆さんを見ながらon demandに学んで いることのアピールとして)紹介しておきます.
また当然のことながらデバッガは,大きな,未知のコードを探るための手段です. ある程度全体像が把握できたところで,徐々にいらなくなってくる(この機能を入れるには, これをいじればいい,ということが,手探りではなく,確かな知識として蓄積されてくる) ものです.いくら規模が大きくても,自分で全て書いたプログラムの拡張をするのに, デバッガでどこをいじるべきかを見つける人はいないでしょう.この演習で, デバッガで追跡することを妙に推していると感じたとしたら,その理由は,
printf挿入方式とて,おおよそどこに 挿入したらいいかということがわかっていないとできません.当てずっぽうで絞り込めても, 10箇所あったら全てに挿入する気はなかなかしないでしょう.さらに,コードを眺めるだけでは, 関数呼び出しの先を追うのは一苦労(デバッガなら勝手に関数呼び出し先も開いてくれる)ですし, if文のどちらを追っていけばいいのかもわかりません(デバッガなら,その時選んだ枝を教えてくれる).
printfが良いのは例えば,あるいくつかの関数が多数回呼ばれていてそれら全体を流れでおいたいとか, ある変数の値が多数回書き換わり,それらの変遷を見たい,のような,時間方向の変化を一望したい, というような場合でしょう.これを1ステップごとに追いながら把握するのは難しいです. 一方,「ボタンが押されたらどの関数が呼ばれるの?」とか,「これをやると窓が出てくるけど, その窓を出しているのはどの関数?」みたいな場合に,簡単なソースコード検索(grep, htags) でそれらしいのが絞りきれなかったとしたら,そこでprintfが何かの助けになるとはあまり期待できません. また,自分がソースから理解している挙動と何故か違う動作をしてしまうという時に, その原因追求を,デバッガではなく,printfをひたすら挟む(しかできない)というのは, ぜひこのたび卒業してほしい子供の手法です.
c 30でcontinueコマンド30回,
n 30でnextコマンド 30回,のように,何度も同じコマンドを打たずにすむということを覚えておくと良いかも知れません. そうすると,たとえばクリックしてからその効果が出るまでにだいたい何回, ループを回ればよいのか,などということもわかるようになるでしょう.
cat chrome > /dev/nullおそらくですが,こうすると(このコマンド自体に多少時間がかかりますが), 1回目から,2回目以降の速度と同じくらい(1分程度)になります.