0_0_63、デバッガの使い方

 obake   specification   version 

0_0_62B で見つかった complier から出るエラー警告の修正を試みる。

タイル落下に絡んだ処理に問題があることはバージョンを遡って事前に検証。この結果、問題があるユーザ定義関数は目星が付いている。

今回のデバッグ対象はループ処理部分を含み、変数の値が頻繁に更新され、条件分岐もやや複雑。デバッガを使って検証しないと無理。約100行程度のコード。

Phy_Control ユーザ定義関数

compile window から発せられる Grid 0, index out of bounds writing という警告について、「Grid 0」 というのは 「ev.MG」というデータ構造「グリッド」 のこと。”index out of bounds” はあらかじめ確保されている二次元配列の各要素数よりも大きな配列要素にアクセスがあります、という意味のエラーメッセージ。本来致命的エラーなのだけど、GM:S の「グリッド」はデータ構造扱いなので単なる二次元配列ではなく、不正な値/要素数で配列へアクセスした場合でもエラー警告を発するだけでアプリケーションが落ちたりしないフェイルセーフな仕組みとなっている。

デバッガを利用する場合、コードエディタからブレークポイントを設置する場所が重要。問題が発生している可能性のある範囲が特定困難か、範囲が広いとデバッグも困難、細かくユーザ定義関数化することはデバッグする際にも有効となる。

  1. Breakpoint 設定するユーザ定義関数かコード部分を特定する
  2. 怪しいと思う箇所から少し離れた行を選び Breakpoint を設定
  3. コードエディタのショートカットは F9 で Breakpoint 設置
  4. ショートカット F6 でデバッガを起動
  5. 自分が必要とする情報をデバッガのウィンドウへ追加し表示
  6. 今回は Locals 、Output 、Globals 、 Call Stack 、Source を選択
  7. テスト動作中のアプリケーションを操作してエラーを発生させる
  8. BreakPoint の設置行が実行されたら、そこでアプリが一時停止
  9. 一時停止と同時にデバッガがポップされ、ここからデバッガの出番
    • 一時停止した状態でメモリ情報、つまり変数値と Output を参照
    • BreakPoint 位置でエラーが未発生の場合、Step In で次行へ進む
    • デバッガの Step in はショートカット F11
    • デバッガの Source には現在実行中のソースコードが表示される
    • Step In は F11 を押しっぱなしにすれば一行ずつ自動進行
    • デバッガの Output にエラーが出たらその行がエラー発生箇所
    • 特定したエラー箇所で変数の状態などを確認。前後の処理を確認

このような手順でデバッガと連携してデバッグを行う。

コードをエディタから見ているだけでは分からなかったループ中の不正な配列操作/違反も、デバッガを使った行単位のステップ実行によって正確に検証可能となる。

原因の絞り込みは成功

この関数( Phy_Control ) がエラー警告の大本であろうという推測も運良く当たっていたようだ。

今回はバージョン管理を使って開発していたので、かなり古いバージョンまで遡ってバグを探している。開発中の古いバージョンはデバッグの要所要所で必要に成るケースがあるから、開発が一定の規模になったらバージョン管理は大事。

エラー該当箇所はループ処理中にあり、ここは結構長いループが続く。ループ前半ではなく、ループ後半から発生するバグだったのでデバッガツールの F11 キーをポチポチ押す作業にも飽き、やがて F11 押しっぱなしの半自動ステップ実行でエラー発生までの様子を漫然と観察する。根気よく。

Output にようやくターゲット出現。思ったより速く出てきてくれたためホッとする。

デバッガの Source から見るとコードの Line 35 を実行後 Output にエラーログ発生、このタイミングで Locals から変数値をチェック、Output のログにあった out of bounds writing と変数値は一致する。とりあえずデバッガのおかげで正確なエラー位置とタイミングは分かった

で、その後、どのような処理を行っているのか更にステップインして処理を確認。どうやら配列から値を取得することに失敗しても、その後の条件分岐で影響が無いようルーチン処理を作ってあったようだ。

しかしだ、配列の範囲外を参照したらその時点で致命的エラーにならないってどういうことよ……… つまり GM:S のグリッドってフェイルセーフなの?と気が付く。よく考えたら GM:S における「グリッド」はデータ構造扱いなので、設計がそのようにされていても不思議ではない。

原因も分かったしステップ実行は終了、デバッガを終了してアプリケーションも終了する。そしてコードエディタに設置した Breakpoint も再度 F9 押しで解除。

その後

var d; の値が 7 になって、次回ループ時にインクリメントされた d == 8 となる。この値が配列の参照に利用されるのが問題。

おもしろいことに、GMS の Grid は Out of bounds でもフェイルセーフ、ただしこの場合戻り値はデフォルトで 「noone」 ではなく「undefined」が返って来ている。まぁ当たり前といえば当たり前か。

一行ずつ処理の流れを追ってみたが、位置が正確に分かりさえすれば問題解決は難しくない。それよりもデバッガの重要性とありがたみに感謝。これがなければ特定がこれほど速くならなかった。




次へ

前へ