CLOSE

みなさんは、 PowerBuilder で開発をするとき、デバッグ機能を 120 % 活用できていますか?
「バグの原因を探すだけで、気づいたら半日終わってた … 」
「いつもなんとなくブレークポイントを置いて、F8 キーを連打してるだけかも … 」
このようなことを経験された方も多いかと思います。
今回は、ただプログラムを止めるだけではない、実務で今すぐ使える強力なデバッグテクニックを厳選してご紹介します。
もっとスマートに、もっと効率よく原因を突き止められるスキルを、一緒に身につけていきましょう!

そもそもデバッグとは

デバッグとは一言で言えば、プログラムに潜む不具合の発生原因を見つけ出し、それを取り除く一連の作業のことです。
よくテストと混合されがちですが、この 2 つは役割が違います。
テストがバグが存在するかどうかを見つける作業(現象の発見)であるのに対して、デバッグはなぜそのバグが起きたのかを突き止め、修正する作業(原因の特定と解決)を指します。
どれだけ完璧に設計したつもりでも、開発にバグは付きものです。
だからこそ、ただ動かないと頭を抱えるのではなく、ツールの力を借りてスマートに原因を突き詰められることが、プロジェクトの成功を左右する極めて重要なスキルです。

実行中のピンチを救う! Just-in-Time デバッグとは?

アプリケーションの実行時にエラーが発生した際、その原因を調査するために「一度アプリを終了してデバッガを起動し、再度同じ操作をやり直す」というのは、二度手間ですよね。
特に、再現手順が複雑なバグだと、もう一度同じ状態を作るだけで一苦労です。
そんな時に大活躍するのが Just-in-Time デバッグ (以下、 JIT デバッグ) です!

JIT デバッグの使用方法は簡単で、システムオプションで「Just-In-Time デバッグ」のチェックボックスを ON にするだけです!

実際に、JIT デバッグをしてみましょう。
数値を 0 で割るサンプルソースを作成しました。
0 で割るという計算は、多くのプログラミング言語でエラーが返され、数学的にも成立しない不能な処理です。

// 0で割るためエラーが発生する
Integer li_a, li_b, li_c
li_a =  10
li_b =  0
li_c = li_a / li_b

JIT デバッグを有効にしてから、上記のソースを実行してみます。

数値を 0 で割ったことでエラーが発生し、自動的にデバッガが起動しました。
そこから、後述する「ウォッチウィンドウ」で変数の中身を確認したり、ステップ実行でプログラムを 1 行ずつ処理することができます。
このように、今まさに起きたエラーを即座に調査することで、開発効率の向上が期待できます!

PB Debug でアプリの動きをログに全記録する

開発環境 (IDE) で利用する場合も、やり方は JIT デバッグとほぼ同じです。
システムオプションを開いて「PBDebug トレースを有効にする」のチェックボックスを ON にするだけで OK。
これだけで手軽に利用できるようになります。
下記のように、アプリケーションのログを任意のフォルダーにテキストファイル (.pbg) で出力します。

実行環境 (EXE) は下記のコマンドを入力し、PBDebug を有効化して実行します。

[アプリケーション名].exe /PBDEBUG

下記に PBDebug で出力されたログファイルの一部をピックアップします。

C:\Program Files (x86)\Appeon\PowerBuilder 22.0\IDE\pbdevproxy.dll,   22.2.0.3418,   192KB

Executing object function +CREATE for class SAMPLE, lib entry SAMPLE
  Executing instruction at line 2
  Executing instruction at line 3
// 中略 //
End class function +CREATE for class SAMPLE, lib entry SAMPLE
Executing event +OPEN for class SAMPLE, lib entry SAMPLE
// 中略 //
End event +OPEN for class SAMPLE, lib entry SAMPLE
// 中略 //
Executing event +CLICKED for class CB_1, lib entry W_SAMPLE
  Executing instruction at line 1
  Executing object function SETTRANSOBJECT for class DW_1, lib entry W_SAMPLE
    Executing system dll function
  End class function SETTRANSOBJECT for class DW_1, lib entry W_SAMPLE
  Executing instruction at line 2
  Executing object function RETRIEVE for class DW_1, lib entry W_SAMPLE
    Executing system dll function
  End class function RETRIEVE for class DW_1, lib entry W_SAMPLE
  Executing instruction at line 8
  Executing object function ROWCOUNT for class DW_1, lib entry W_SAMPLE
    Executing system dll function
  End class function ROWCOUNT for class DW_1, lib entry W_SAMPLE
  Executing instruction at line 9
  Executing instruction at line 10
  Executing object function GETITEMNUMBER for class DW_1, lib entry W_SAMPLE
    Executing system dll function
// 中略 //
  End class function GETITEMNUMBER for class DW_1, lib entry W_SAMPLE
  Executing instruction at line 11
  Executing instruction at line 9
  Executing instruction at line 15
  Executing instruction at line 16
  Executing instruction at line 17
  Executing object function +CREATE for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
    Executing instruction at line 12308
    Executing object function RUNTIMEERRORCREATE for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
      Executing system dll function
    End class function RUNTIMEERRORCREATE for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
    Executing instruction at line 12309
  End class function +CREATE for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
  Executing system dll function  Executing object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  End object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  Executing system dll function  Executing object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  End object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  Executing system dll function  Executing object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  End object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  Executing system dll function  Executing object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  End object function +CREATE for class CLASSDEFINITION, lib entry _TYPEDEF
  Executing object function +DESTROY for class DWOBJECT, lib entry _TYPEDEF
    Executing instruction at line 2668
    stack size changed during a routine call
  End class function +DESTROY for class DWOBJECT, lib entry _TYPEDEF
  Executing object function +DESTROY for class W_SAMPLE, lib entry W_SAMPLE
    Executing instruction at line 2
    stack size changed during a routine call
  End class function +DESTROY for class W_SAMPLE, lib entry W_SAMPLE
  Executing object function +DESTROY for class DW_1, lib entry W_SAMPLE
  End object function +DESTROY for class DW_1, lib entry W_SAMPLE
  stack size changed during a routine call
End event +CLICKED for class CB_1, lib entry W_SAMPLE
Executing object function +DESTROY for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
End object function +DESTROY for class DIVIDEBYZEROERROR, lib entry _TYPEDEF
Executing object function +DESTROY for class CB_1, lib entry W_SAMPLE
End object function +DESTROY for class CB_1, lib entry W_SAMPLE
Executing object function +DESTROY for class SAMPLE, lib entry SAMPLE
// 中略 //
End class function +DESTROY for class SAMPLE, lib entry SAMPLE

なお、ログファイルは実行したプログラムを 1 行ごとに出力するため、サイズが非常に大きくなります。
処理速度が劣化する可能性があるため、PBDebugは調査のときだけ使うのが無難です。

クイックウォッチでデータウィンドウの中身を丸裸にする

デバッグにおいてデータウィンドウの中身が今どのような状態になっているか、IDE のウォッチタブを見れば、関数の実行結果がリアルタイムに表示されます。
下記の画像では、データウィンドウの Describe 関数を入力し、その結果を表示しています。

このように、関数の結果をウォッチタブに固定表示することで、値をリアルタイムで追跡するため大変便利です。
もちろんデータウィンドウだけでなく、各種コントロールや変数の中身を同様にウォッチに追加し、値を追跡することができます。

ただ置くだけじゃない!ブレークポイントの編集

IDE でコードの左側をポチッとクリックして配置するブレークポイント。 (赤丸)
通常はそこを通ったら必ず止まるという動きをしますが、実はこのブレークポイントは、右クリックから編集して条件をつけることができるんです。
今回は「繰り返し」「条件」について見ていきましょう。
ブレークポイントの編集を使いこなせば、ステップインを連打する作業から解放されますよ!

繰り返し

ループが 5 回目のときに処理がおかしくなると分かっている場合、普通にブレークポイントを置くと、1 回目、2 回目、3 回目 …… と、ステップインし続けるのは大変な手間になります。
そんなときは、ブレークポイントの編集画面にある「繰り返し」欄に 5 と入力します。


「繰り返し」に回数を入力した状態で、アプリを起動してみます。

「繰り返し」に設定した通り、5 回目のループ処理でデバッガが起動しました。
ステップインのボタンを連打する手間が、大幅に短縮されるかと思います!

条件

「繰り返し」よりもさらに柔軟で、実務での出番が最も多いのがこの「条件」の設定です。
特定の変数やデータウィンドウの値が、ある条件を満たしたときにだけ止められます。
例えば、ld_emp_id が 3 の時だけなぜか挙動がおかしくなるという謎の現象があるとします。
不具合の原因調査をするのに、 1 行ずつステップインしていては日が暮れてしまいます。

対策は簡単です。ブレークポイントの編集画面にある「条件」の欄に、論理式をそのまま入力するだけです。
不具合時のデータの中身が分かっていれば、それを条件に設定することで開発効率が向上します!


対象の変数に特定の値が保持されたときに、デバッガが起動します。

「条件」に設定した通り、ld_emp_id が 3 のときにデバッガが起動しました。
この時点からステップインを続行すれば、不具合の原因特定を効率よく行えます!

まとめ

今回は、PowerBuilder の強力なデバッグテクニックをご紹介しました。
条件付きのブレークポイントやクイックウォッチ、PBDebug など、意外と知ってはいたけど使っていなかった… という機能もあったのではないでしょうか?
バグが出るとどうしてもテンションが下がってしまいますが、ツールの力を 120% 引き出せば、謎の不具合をスマートに解決する謎解きのような楽しさも見えてくるはずです。
不具合をサクッと解決して、快適な開発ライフを送りましょう!

x instagram facebook youtube