書き物

技術とか作った物の話とか愚痴文句感想など

fdbを使ってみた

Flex2SDKには、fdbというデバッガも付随しています。デバッガとは、要はバグ取りの手助けとなるツールのことです。コードを一つ一つ処理の順に追って確認しながら実行してくことによって、誤動作をしているポイントを見つけるのに役に立ちます。

今回はこのfdbの基本的な使い方を書いておきます。

はじめに

とりあえず今回は以下のようなプログラムを動かしてみます。

メインとなるTest.as

package
{
import flash.display.Sprite;
public class Test extends Sprite
{
public function Test()
{
super();
var j:Jo = new Jo();
var d:Da = new Da();
for (; ; )
{
if (j.question(d.getAnswer()))
{
break;
}
}
for (; ; )
{
if (d.body(j.getOra()))
{
break;
}
}
trace("やれやれ");
}
}
}

Jo.as

package
{
public class Jo
{
private var _punch:String;
public function Jo()
{
_punch = "りょうほー";
}
public function question(answer:String):Boolean
{
trace(answer);
if (answer != _punch)
{
trace("NO! NO! NO! NO! NO!");
return false;
}
trace("YES! YES! YES! YES! Y E S !");
return true;
}
public function getOra():String
{
return "オラ";
}
}
}

Da.as

package
{
public class Da
{
private var _aCount:uint;
private var _oCount:uint;
public function Da()
{
_aCount = 0;
_oCount = 0;
}
public function getAnswer():String
{
++_aCount;
if (_aCount == 1)
{
return "右";
}
else if (_aCount == 2)
{
return "左";
}
return "りょうほー";
}
public function body(ora:String):Boolean
{
if (++_oCount > 11)
{
trace("ド――z___ン");
return true;
}
trace(ora);
return false;
}
}
}

つづいてこれらをデバッグオプションを付けてコンパイルします。

mxmlc Test.as -compiler.debug

これで準備OK。

デバッグする

先ほどコンパイルして出力されたswfを渡してfdbを起動します。

fdb Test.swf

すると、fdbが起動してなんちゃらかんちゃらとメッセージが出ます。

D:\Test\src>fdb Test.swf
Adobe fdb (Flash Player Debugger) [ビルド 155542]
Copyright (c) 2004-2006 Adobe, Inc. All rights reserved.
URL を使用して Player を起動し、接続しようとしています
Test.swf
Player が接続されました。セッションを開始しています。
ブレークポイントを設定して「continue」と入力し、セッションを再開してください。
[SWF] D:\Test\src\Test.swf - 1,949 バイト (解凍後)
(fdb)

あとはこの一番下の(fdb)の後にコマンドを入力していくことでデバッグを行います。

ブレークポイント設定

まずはじめに、「info source」を入力して、ファイルの一覧を表示します。

(fdb) info sources
Da.as#2
Jo.as#3
Test.as#1

それぞれのファイルに番号がついてますね。

続いて処理を一時停止するブレークポイントを設定します。設定するには「break」コマンドを使用します。今回はTest.asのコンストラクタから即行停止させたいので、コンストラクタの最初である7行目にブレークポイントを設定したいと思います。ファイルの指定には上記の番号を、「:」で挟んで行番号を指定します。

(fdb) break #1:7
ブレークポイント 1 : ファイル Test.as、行番号 7

これで実行する準備ができました。

ということで早速「continue」を入力して、処理を開始させます。

(fdb) continue
理由 : ブレークポイント 1、関数名 : Test$iinit()、場所 : Test.as:7
7              public function Test()

予定通り、コンストラクタの最初で処理が一時停止しました。

ついでに、「list」を入力すると、現在の処理の地点の周辺のソースを表示してくれます。今の状態だとご覧のようになります。

(fdb) list
2     {
3      import flash.display.Sprite;
4
5      public class Test extends Sprite
6      {
=7              public function Test()
8              {
9                      super();
10
11                     var j:Jo = new Jo();

「=」の部分が処理地点ですね。

step、next、finish

それでは、停止させた地点から1つずつ処理を実行していきましょう。

デバッガでの処理の進め方には3種類あります。

  • step  関数内部に進んでストップ。
  • next  関数内部には進まず、そのまま関数を通り抜けてストップ。
  • finish 現在処理を行っている関数の処理終了まで進んでストップ。

まあ、意味は実際に見てもらいたいと思います。

まずはstep。先ほどの状態で、stepを9回ほど実行します。

(fdb) step
9                      super();
(fdb) step
11                     var j:Jo = new Jo();
(fdb) step
理由 : 実行が中止されました、関数名 : global$init()、場所 : Jo.as:3
3      public class Jo
(fdb) step
理由 : 実行が中止されました、関数名 : Test$iinit()、場所 : Test.as:11
11                     var j:Jo = new Jo();
(fdb) step
理由 : 実行が中止されました、関数名 : Jo$iinit()、場所 : Jo.as:7
7              public function Jo()
(fdb) step
8              {
(fdb) step
9                      _punch = "りょうほー";
(fdb) step
10             }
(fdb) step
理由 : 実行が中止されました、関数名 : Test$iinit()、場所 : Test.as:11
11                     var j:Jo = new Jo();

super()は無視してもらって、クラスインスタンスの生成部分に注目してください。「Joクラスのインスタンス生成」、「コンストラクタ実行」、「Joのプロパティ設定」の流れが分かるでしょうか。これがstepの動作です。関数内部に渡って、すべてたどって1つずつ実行していきます。

といってもまだいまいち分かりにくいと思いますので、次のnextを見てください。stepの動作の時と比べてください。

(fdb) step
12                     var d:Da = new Da();
(fdb) next
16                             if (j.question(d.getAnswer()))
(fdb) list
11                     var j:Jo = new Jo();
12                     var d:Da = new Da();
13
14                     for (; ; )
15                     {
=16                             if (j.question(d.getAnswer()))
17                             {
18                                     break;
19                             }
20                     }

一度stepを実行して、Daクラスのインスタンス生成部分まで移動しています。次にnextを実行しています。listの結果で分かるとおり、new Da()の行を飛ばして、次の処理部分であるifのところへさっさと移動してしまいました。これがnextの動作です。stepは関数内部へ入りましたが、nextは内部へは入らず、次の処理部分まで移動しました。

finishは後ほどでてきます。

続いて、少々処理がややこしそうなこのif(j.question(d.getAnswer())部分です。ここもstepを連打すると、処理の順序が分かりやすいと思います。

(fdb) step
理由 : 実行が中止されました、関数名 : getAnswer()、場所 : Da.as:14
14             public function getAnswer():String
(fdb) step
16                     ++_aCount;
(fdb) step
17                     if (_aCount == 1)
(fdb) step
19                             return "右";
(fdb) step
理由 : 実行が中止されました、関数名 : Test$iinit()、場所 : Test.as:16
16                             if (j.question(d.getAnswer()))
(fdb) step
理由 : 実行が中止されました、関数名 : question()、場所 : Jo.as:12
12             public function question(answer:String):Boolean
(fdb) step
14                     trace(answer);
(fdb) step
[trace] 右
15                     if (answer != _punch)
(fdb) step
17                             trace("NO! NO! NO! NO! NO!");
(fdb) step
[trace] NO! NO! NO! NO! NO!
18                             return false;

まずはd.getAnswer()が実行され、続いてj.question()が実行されることが分かります。

あと、traceの結果が「[trace] 右」という風に表示されています。これもデバッグの基本ですね。

つづいてfinishを使ってみます。

さきほどの状態からstepを2回実行して、getAnswer()内に入ってください。

(fdb) step
理由 : 実行が中止されました、関数名 : Test$iinit()、場所 : Test.as:16
16                             if (j.question(d.getAnswer()))
(fdb) step
理由 : 実行が中止されました、関数名 : getAnswer()、場所 : Da.as:14
14             public function getAnswer():String

この状態で、またstepやnextでgetAnswer内を走査するのはちょっと面倒...というとき、finishを使えば関数からさっさと脱出できます。

(fdb) finish
理由 : 実行が中止されました、関数名 : Test$iinit()、場所 : Test.as:16
16                             if (j.question(d.getAnswer()))

ちょっと分かりにくいですが、getAnswerが実行し終わって、またif文のところへ戻ってきました。ここで処理が停止しています。これがfinishの動作です。

あとは適当にstepなりnextなりを入力して一通り処理を終えてみてください。このTestクラスのコンストラクタが実行し終わると、FlashPlayerのウインドウが出現すると思います。このタイミングでウインドウが表示されてたんですねぇ。

おわりに

デバッグの基本はこの程度です。処理の流れを追うだけで、結構難解なバグの原因が特定できたりします。また、変数の内容を表示したり、設定したりもできるので、またhelpコマンドを実行して一覧をみてみると良いかもしれません。

(fdb) help
fdb を初めて使用する場合は、'tutorial' を実行し、基本情報を確認してください。
fdb コマンドの一覧 :
bt (bt)             すべてのスタックフレームのバックトレースをプリントします。
break (b)           指定された行または関数にブレークポイントを設定します。
cf (cf)             現在のファイルの名前と番号を表示します。
clear (cl)          指定された行または関数のブレークポイントをクリアします。
condition (cond)    ブレークポイントに対する条件式を適用または削除します。
continue (c)        ブレークポイントで停止した後に実行を続行します。
commands (com)      ブレークポイントに達したときに実行するコマンドを設定します。
delete (d)          ブレークポイントまたは auto-display 式を削除します。
directory (dir)     ソースファイルの検索パスにディレクトリを追加します。
disable (disab)     ブレークポイントまたは auto-display 式を無効にします。
disassemble (disas) ソース行または関数を逆アセンブルします。
display (disp)      auto-display 式を追加します。
enable (e)          ブレークポイントまたは auto-display 式を有効にします。
file (fil)          デバッグするアプリケーションを指定します。
finish (f)          現在の関数が返されるまで実行します。
handle (han)        失敗の処理方法を指定します。
help (h)            fdb コマンドに関するヘルプを表示します。
home (ho)           実行を中止する箇所にリストの場所を設定します。
info (i)            デバッグ中のプログラムに関する情報を表示します。
kill (k)            デバッグ中のプログラムの実行を強制終了します。
list (l)            指定した関数または行を一覧表示します。
next (n)            プログラムを次の段階に進めます。
print (p)           変数 EXP の値をプリントします。
pwd (pw)            作業ディレクトリをプリントします。
quit (q)            fdb を終了します。
run (r)             デバッグしたプログラムを起動します。
set (se)            変数の値を設定します。
source (so)         ファイルから fdb コマンドを読み取ります。
step (s)            別のソース行に達するまで、プログラムを進めます。
tutorial (t)        fdb の使用方法に関するチュートリアルを表示します。
undisplay (u)       auto-display 式を削除します。
viewswf (v)         swf に基づいて、ファイルの一覧表示のフィルタを設定またはクリ
アします。
what (wh)           変数のコンテキストを表示します。
where (w)           bt と同じです。
詳細を確認するには、'help' の後にコマンド名を入力します。

ちなみに今回のプログラムの出力は以下の通り。

NO! NO! NO! NO! NO!

NO! NO! NO! NO! NO!

りょうほー

YES! YES! YES! YES! Y E S !

オラ

オラ

オラ

オラ

オラ

オラ

オラ

オラ

オラ

オラ

オラ

ド――z___ン

やれやれ

追記(2007/7/10)

fdbのオブジェクトの表示の仕方はこちら。

「fdb」オブジェクト出力メモ