(第17回)データ構造をグラフィカルに表示してみる

2007/11/14

あなたは 人目のお客様です.

ここまでで,デバッガとしての機能はだいたい動くことが確認できた. 今回はちょっと毛色を変えて,デバッガのGUIについて.

まあ最近はデバッガもコンソール上での原始的なものではなく, GUIを使ったきらびやかなものもあるというよりもむしろそっちのほうが 今となっては主流のような気もする. まあそうはいっても emacs 上でも十分であったり,むしろそちらのほうが 使いやすい場合も多かったりするのでどちらも一長一短あり,というか好きずきな ような気もするが,GUIを利用したデバッガについてちょっと説明しよう.

gdbはコマンド操作によるデバッガなので,実はGUI上でボタンを操作したら gdbのコマンドを発行するだけ(そしてその応答を見て,GUI上にきれいに表示する) ということをすれば,GUIは作成できる.つまりGUIは基本的にはgdbとは独立して, ボタン押下などのイベントをgdbコマンドに変換してコマンド発行するだけのツール, ということもできる. こーいうのをよく 「gdbの上にかぶせてある」とか 「GUIの皮をかぶせてある」とか 「単なるラッパ(wrapper)」なんていうふうに表現する.

で,gdbのGUIとしては,dddというものがある.これは Data Display Debugger の略で,その名の通り,データ構造を表示するのに 威力を発揮する. まあ実際のところ,単にコマンド発行するだけならばCUIでもGUIでもたいして 変わりはなく, 使う本人が操作しやすいかどうか,あとはGUIならばコマンド名を覚えなくても済む, くらいの違いしか無いような気もするが,データ構造の表示に関していえば, GUIによるグラフィカル表示は非常にわかりやすく,圧倒的に便利だと思う. (まあCUIでもそーいう表示はやってできなくはないとは思うが)

ほかにも GUI としては kdbg とか kdevelop とか insight とか いろいろあるようなのだが,今回はdddをちょっと使ってみよう.

まず ddd の準備なのだが,FreeBSD の ports になっているので, ふつうにパッケージインストールできる.ports のカテゴリは devel だ. ちなみに ddd は gdb に対する純粋なラッパなので, たとえばクロス開発でターゲット機器用のクロスgdbを利用している場合にも, 通常の ddd を使う (そして起動時に --debugger オプションで使いたい gdb を指定する) ことができる.つまり,ddd自身はクロスコンパイルの必要は無い. (そもそもdddはgdbに対する「皮」だけなので,クロスコンパイルという概念が無い)

で,ddd をインストールしたら,実行形式 koz を起動する. KOZOSのソースコードは前回と同じものを使用する.

% ./koz 
(この状態で停止)
koz の起動時には,デバッガからの接続待ちで止まっている. いつもならばここで gdb から target コマンドでリモート接続することになるが, 今回は ddd を起動しよう.引数に実行形式 koz を指定して起動する.
% ddd koz
Creating "/home/hiroaki/.ddd/"...
Creating "/home/hiroaki/.ddd/"...done.
Creating "/home/hiroaki/.ddd/sessions/"...
Creating "/home/hiroaki/.ddd/sessions/"...done.
Creating "/home/hiroaki/.ddd/themes/"...
Creating "/home/hiroaki/.ddd/themes/"...done.

tipsとかヘルプがいろいろ開いているのでとりあえずクローズして, メインウインドウにする.

で,まずはターゲットへの接続なのだが, この接続の設定のしかたがよくわからん. まあいざとなったら .gdbinit に target remote を書いておく, というのでもいいのだが,今回はコマンド実行用のウインドウから target remote を 実行してみる.

まず,メニューバーから Commands - Clear Window を実行して コマンド実行ウインドウをクリアする.なぜかこれをやらないと コマンド実行できない.

一番下のコマンド実行用ウインドウがクリアされていることに注意.

で,コマンド実行用ウインドウで target remote コマンドを実行し, スタブに接続する.

おー,ブレーク位置のソースコードが出てきた.どうやら接続できたようだ.

ちなみに簡単な操作用に

のようなツールバーが出ているが,ここで Cont を押下して continue を行う.

コマンド実行用ウインドウで continue が行われていることに注意.

Wed Nov 14 21:45:21 2007
Wed Nov 14 21:45:22 2007
Wed Nov 14 21:45:23 2007
Wed Nov 14 21:45:24 2007
Wed Nov 14 21:45:25 2007
Wed Nov 14 21:45:26 2007
...
時刻表示が開始される.KOZOSが動作を開始したわけだ.

さてここで,いままでどおりブレークポイントの設定やステップ実行もできるのだが, それではあまり面白くない. まあこのへんのことはやればわかるだろうし, せっかくGUIを使っているので,データ構造の表示をしてみたいところだ.

まずツールバーで Interrupt を押下して,実行を停止する. どうやらこれは Ctrl-C に相当するようだ.

Wed Nov 14 21:48:36 2007
Wed Nov 14 21:48:37 2007
Wed Nov 14 21:48:38 2007
($T054:58e60e08;5:58e60e08;8:c5a80408;#c0)[+]
[$M8048088,1:55#c2](+)($OK#9a)[+]
(この状態で停止)
時刻表示が停止し,gdbとスタブ間の通信が始まっている.

例として,KOZOSが持っている優先度キュー情報を見てみよう. 優先度キューの配列名は readyque なので, 左上の検索バーに「readyque」と入力してみる.

おー,readyque の定義場所が表示された.

ここでソースコード上の「readyque」をおもむろにダブルクリックすると...

なんか上のほうにウインドウができているね.ちょっと広げてみよう.

おー,配列が表示されている.

ここで readyque の配列に値が入っている部分は, 優先度キューでスレッドが存在するプライオリティだ. で,そこをダブルクリックする.

なんと,リンクしているスレッド構造体が表示される. ほかのところもクリックしてみよう.

こんな感じで,構造体のリンク構造とかを知ることができる. 上の例だと 優先度2のレディーキューに stubd, 優先度31のレディーキューに idle スレッドが存在しているようだ. telnetd とか httpd も存在はしているが,メッセージ受信待ち状態のため レディーキューには接続されていないので,ここでは表示されない.

たとえば他にもタイマ管理なら,timers をダブルクリックして

みたいにして,タイマのリンク構造とさらにそこからリンクされているスレッドの 構造体を見ることができる. こんなふうに構造体のリンク構造を追いかけていくことができるわけだ. まあこの例だとタイマがひとつしか登録されていないのだが, タイマが複数登録されている場合には,タイマ構造体の next をクリックしていけば, リンクリストを追いかけて表示することができる.

このへんの値は,実はまあgdb上でも

(gdb) print readyque[2]
(gdb) print *(readyque[2])
(gdb) print *(readyque[2]->next)
(gdb) print *timers
(gdb) print *(timers->next)
(gdb) print *(timers->next->next)
とかやれば表示させることはできる.gdb上では,データの表示に C言語の演算子がほぼそのまま使えるので, ポインタのリンク先などをC言語と同様の書き方で追うことができる. *p とか p->next とか &p とか sizeof(*p) とかいった書き方もそのままできる. で,表示対象が構造体の場合にはそのメンバ値を全部表示してくれるし, 文字列の場合にはその内容をきちんと表示してくれる.

しかし ddd のように,リンク構造を図示してくれるというのは非常にありがたい. ハッシュや二分木,2重リンク,リンクのリンク,もしくは共用体が 入り組んでいたりして,複雑になっているデータ構造を追う場合にはとてもべんりだ. プログラムは機能追加につれてどんどん新しいポインタが追加され,データ構造は 複雑になっていきがちなものだからだ.個人的な感想としては, このためだけに ddd を使ったとしても十分な価値があると思う.

う〜ん,現代的だ.やっぱし今の時代,こうでなくっちゃあねえ.


メールは kozos(アットマーク)kozos.jp まで