プログラムの作成やメンテナンスを行う際にどうしても避けて通れないのがデバッグです。デバッグは対象となるプログラムの誤り(エラー)を修正し、意図したとおりに動くようにするための一連の作業です。 デバッグには「こうやれば必ず上手く行く」というような方法論はありませんが、ある程度のパターンや、利用可能なツールをあらかじめ知っておくと役立ちます。
本資料はnAG Fortranコンパイラを用いて、Fortranプログラムのデバッグを行う方々向けの参考資料です。
この資料が皆様のお役に立てれば幸いです。
エラーの種類
プログラムの誤り(エラー)は大凡以下の3種類に分けることができます。
- コンパイル/リンク時のエラー スペルミス、引用符が閉じていない、括弧が対応していない、ライブラリが指定されていない等々
- 実行時のエラー 算術例外、範囲外アクセス、入力ファイルが存在しない、その他の異常終了等々
- 論理エラー 意図しない手順を指定してしまっていた、想定し忘れていたパターンがあった、
一般的に、下へ行くほどデバッグが困難になる傾向があります。
コンパイル/リンク時のエラー
コンパイル/リンク時のエラーの原因で多いものに、スペルミス、括弧や引用符の非対応、全角半角の誤り等が挙げられます。
エラーが存在する場合 「文法エラー」 等のエラーメッセージが出力されますが、この際に事細かに「スペルミスをしています」や「全角文字が半角文字の代わりに使われています」等、 原因まで示してくれる事はまれです。
しかし、多くの場合エラーメッセージにはエラー発生箇所(ファイル名、行、ルーチン名、etc.)や、その他、何らかの手がかりを得る事ができます。
コンパイル/リンク時のエラーへの対処は、基本的にエラーメッセージをたよりにプログラムの修正を行う作業となります。
以下に一例としてリンク時に発生する参照エラーメッセージの一例とその原因となりそうな理由を示します。
undefined reference to `MYSUB'
このような参照エラーは、リンク対象となるサブルーチンや関数(上記例ではサブルーチンmysub)が見つからない場合に出力されます。 主な原因としては以下のようなものがあります。
- サブルーチン/関数の綴りを間違えていた。
- サブルーチン/関数を定義していなかった。
- サブルーチン/関数を定義しているソースファイルを(コンパイル/リンク対象として)指定し忘れていた。
- サブルーチン/関数が定義されているライブラリをリンクし忘れていた。
- サブルーチン/関数が定義されているライブラリのリンク方法(コンパイル/リンクオプションの指定等)に誤りがあった。
実行時のエラー
実行時のエラーはコンパイル/リンク時のエラーと比較して、一般的にエラー箇所や原因の特定がより困難となります。
実行時のエラーはSegmentation Fault(セグメンテーションフォルト)やArithematic Exception(算術例外)等、何らかのエラーメッセージと共にプログラムが終了してしまう場合もありますし、何もエラーが表示されずにいきなりプログラムが終了してしまうようなケースもあります。
実行時エラーが発生した場合ですぐに原因がわからない場合は、プログラムの実行時診断機能を有効にするコンパイルオプションを指定してプログラムを再ビルドしてから現象の再現する方法が有効です。
以下に配列範囲外アクセスを行うプログラム例(main.f90)を示します。
Program main
Implicit None
Integer i
Double Precision x(10)
Do i = 1, 100
x(i) = i ! ここで(意図的に)配列範囲外アクセスを発生させる
End Do
Print *, sum(x)
End Program
このプログラムを弊社テスト環境で以下のようにコンパイル・実行するとヒントになりそうなエラーメッセージが表示されることもなく、異常終了を示すダイアログウィンドウが表示されました。
$ nagfor main.f90 $ a.exe次に今度は実行時診断機能を有効にするためのオプションを以下のように指定してコンパイルした後に実行を行いました。
$ nagfor -C=all -gline main.f90
$ a.exe
すると今度は以下のようなプログラム上の誤りを示す実行時エラーメッセージが表示されるようになり、エラーの原因と場所の特定が行えました。
実行時エラー: main.f90, line 6: Xの第1添字(値 11)が範囲外です(1:10)
致命的なエラーでプログラムが終了しました
別のプログラムコードでも実行時診断を有効にした場合とそうでない場合の比較を行ってみます。
以下のプログラムは意図的にゼロ除算を行うプログラムです。
Program main
Implicit None
Double Precision y
y = 1D0
y = y - 1D0
Print *, 99D0/y ! ここでゼロ除算が行われる
End Program
診断機能を有効にしていない場合(算術例外が表示されるが場所が特定されていない):
$ nagfor main.f90 $ a.exe 実行時エラー: *** 算術例外: 浮動小数ゼロ除算 - 終了します診断機能を有効にするコンパイラオプション指定した場合:
$ nagfor -C=all -gline main.f90 $ a.exe 実行時エラー: *** 算術例外: 浮動小数ゼロ除算 - 終了します main.f90, line 6: エラーがMAINで発生しました
実行時診断を行うオプションを指定する事により、エラーの原因や場所の特定が上手く行える場合があります。
実行診断オプションはデバッグ時に非常に役立つオプションですが、そのトレードオフとして、実行時間が長くなったり、実行形式ファイルが大きくなったり、実行時のメモリ使用量が増えたりしますので、デバッグが終わって最終実行形式を作成する際にはこれらのオプションを指定しない事を推奨します。
おすすめの指定例その1
(未定義変数参照以外の全ての実行時診断を有効にする)
nagfor myprog.f90 -C=all -gline
おすすめの指定例その2
(未定義変数参照も含め全ての実行時診断を有効にする)
nagfor myprog.f90 -C=all -C=undefined -gline
-gline 実行時エラー発生時にトレースバック情報を表示します。エラー発生箇所を特定しやすくなります。 -C=all 未定義変数の参照以外の全ての実行時診断機能を有効にする -C=undefined 未定義変数の参照を検出する以外の全ての実行時診断機能を有効にする。 (利用するライブラリを含め全てがこのオプションでコンパイルされていなければならない)
論理エラー
すべてのコンパイル/リンク時のエラーと実行時エラーに対処したにもかかわらず、計算結果がおかしい等、意図したとおりにプログラムが動かない場合もあります。 このようなエラーについての特効薬は残念ながらありませんが、一般的には write文等で途中結果を出力させながらおかしい箇所をさがす方法や、デバッガで少しづつ処理を進めながら流れの確認や変数値の確認などを行う方法などが使われます。