サポート

技術情報

-インテルコンパイラ・レシピ-

インテルFortranデバッグ入門

株式会社HPCソリューションズ
2013年1月9日

C言語のプログラマーはデバッガーを使う方が多いようですが、Fortranプログラマーは「デバッグWRITE」、つまり、プログラム途中にWRITE文を入れてデバッグを行う悪しき習慣があります。 デバッグWRITEはそろそろ卒業して、円滑なデバッグをしたいところですが、デバッガーを使うのはFortranプログラマーには敷居が少々高いかもしれません。

インテルFortranではコンパイラオプションを使うことで必要なデバッグ情報を得ることが出来ます。 デバッガを使うよりはお手軽なデバッグ方法ですので、ぜひお試し下さい。


トレースバック

プログラムが実行途中で異常終了してしまうが問題の個所がどこかわからない、という場合に有効なのが「-traceback」オプションです。 異常終了時にソースコードをさかのぼり原因箇所を特定することが出来ます。 例えば、異常終了時に以下の様なエラーが出る場合を考えましょう。

[korito@poaro01 debug]$ ./a.out
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source      
a.out              0000000000446A90  Unknown               Unknown  Unknown
a.out              0000000000426B87  Unknown               Unknown  Unknown
a.out              000000000040AE74  Unknown               Unknown  Unknown
a.out              0000000000407BFF  Unknown               Unknown  Unknown
a.out              0000000000402C74  Unknown               Unknown  Unknown
a.out              0000000000402A7C  Unknown               Unknown  Unknown
libc.so.6          000000394081ECDD  Unknown               Unknown  Unknown
a.out              0000000000402979  Unknown               Unknown  Unknown

このエラーメッセージでは致命的なエラーが起きたことしか分かりません。 そこで「-traceback」オプションを付けて実行しなおすと、以下の様なエラーメッセージに変わりました。

[korito@poaro01 debug]$ ifort -traceback mallo.f90
[korito@poaro01 debug]$ ./a.out
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source      
a.out              0000000000446AE0  Unknown               Unknown  Unknown
a.out              0000000000426BD7  Unknown               Unknown  Unknown
a.out              000000000040AEC4  Unknown               Unknown  Unknown
a.out              0000000000407C4F  Unknown               Unknown  Unknown
a.out              0000000000402CB3  MAIN__                      7  mallo.f90
a.out              0000000000402A7C  Unknown               Unknown  Unknown
libc.so.6          000000394081ECDD  Unknown               Unknown  Unknown
a.out              0000000000402979  Unknown               Unknown  Unknown

Routine項目に「MAIN__ 7 mallo.f90」という表示が現れました。 つまり、mallo.f90の7行目で異常終了していることが分かります。 トレースバックは非常に有効なデバッグ方法です。

-traceback specify whether the compiler generates PC correlation data used to
display a symbolic traceback rather than a hexadecimal traceback at
runtime failure

ランタイムチェック

プログラムは実行できたけど、どう考えても結果がおかしい、ということがよくあります。
例えば、次のサンプルプログラムを見て下さい。

  サンプルプログラム: array.f90
 
program array
real, allocatable :: a(:)
allocate(a(4))
a=17
print *,a(0)
deallocate(a)
end

配列aを宣言して、17を代入しています。 その後のprint分では17が表示されるはずです。 このプログラムを念のため「-traceback」オプションを付けて実行してみます。 すると以下の様になりました。

[korito@poaro01 debug]$ ifort -traceback array.f90
[korito@poaro01 debug]$ ./a.out
  0.0000000E+00

出力結果は0になってしまいました。 実行は出来たのですが、結果がおかしい。 エラーが起きないのでトレースバックもしてくれません。 この様な場合、プログラムを文法チェックだけでなく変数のアドレスやポインタ制御、出力形式設定などを厳しくチェックしてくれる「-check (keyword)」オプションを付けて実行してみてください。 (keyword)はいくつか選択できますが、全てチェックする「all」でコンパイルしてみましょう。

[korito@poaro01 debug]$ ifort -traceback -check all array.f90
[korito@poaro01 debug]$ ./a.out
forrtl: severe (408): fort: (3): Subscript #1 of the array A has value 0 which 
is less than the lower bound of 1

Image              PC                Routine            Line        Source      
a.out              0000000000469F9E  Unknown               Unknown  Unknown
a.out              0000000000468A36  Unknown               Unknown  Unknown
a.out              0000000000421A12  Unknown               Unknown  Unknown
a.out              000000000040442B  Unknown               Unknown  Unknown
a.out              0000000000404941  Unknown               Unknown  Unknown
a.out              0000000000402E38  MAIN__                      5  array.f90
a.out              0000000000402ACC  Unknown               Unknown  Unknown
libc.so.6          000000394081ECDD  Unknown               Unknown  Unknown
a.out              00000000004029C9  Unknown               Unknown  Unknown

今度は実行途中で異常終了し、トレースバックが取れました。 メッセージによると「array.f90の5行目で、配列Aの#1のインデックスが下限の1よりも小さい0を使っている」、ということが分かりました。 Fortranの配列はインデックスの下限を指定しなければ1からになります。 C言語などは0からですので、よくある間違いです。

この様に配列宣言したインデックスの範囲外の値を使用することを領域外参照と言います。 上記のサンプルプログラムでは値を標準出力しただけですが、数値を代入してしまったりすると、隣のアドレスを書き換えてしまい、全く間違った計算をしていることがあります。 新たにプログラムを作成した際には「-check all」オプションをつけてコンパイルし、こういったバグがないか調べることをお勧めします。

-check (keyword) check run-time conditions. Default is -nocheck
keywords: all (same as -C), none (same as -nocheck),
[no]arg_temp_created, [no]bounds (same as -CB),
[no]format, [no]output_conversion,
[no]pointer (same as -CA),
[no]uninit (same as -CU), [no]stack

浮動小数点エラー

実行時のエラーで0で割り算をしてしまったりして、結果がおかしくなることがあります。 次のサンプルプログラムを例に考えてみましょう。

  サンプルプログラム: ovf.f90
 
program ovf
real*4 x(5),y(5)
integer*4 i

x(1) = -1e32
x(2) = 1e38
x(3) = 1e38
x(4) = 1e38
x(5) = -36.0

do i=1,5
y(i) = 100.0*(x(i))
print *, 'x = ', x(i), ' x*100.0 = ',y(i)
end do
end

このプログラムを念のため「-traceback -check all」オプションを付けてコンパイルし実行してみます。
すると以下の様になりました。

[korito@poaro01 debug]$ ifort -traceback -check all ovf.f90
[korito@poaro01 debug]$ ./a.out
 x =  -1.0000000E+32  x*100.0 =  -1.0000000E+34
 x =   9.9999997E+37  x*100.0 =        Infinity
 x =   9.9999997E+37  x*100.0 =        Infinity
 x =   9.9999997E+37  x*100.0 =        Infinity
 x =   -36.00000      x*100.0 =   -3600.000

「Infinity」という出力が出てしまいました。 これは単精度実数(REAL*4)の上限値を超えてしまったこと(オーバーフロー)を意味します。 Fortranのプログラムでは値が「Infinity」になってしまってもそのまま実行を続けてしまいます。 「Infinity」が出た箇所で実行を止めてトレースバックを取る場合は「-fpe0」オプションを付けてコンパイルして下さい。

[korito@poaro01 debug]$ ifort -traceback -fpe0 ovf.f90
[korito@poaro01 debug]$ ./a.out
 x =  -1.0000000E+32  x*100.0 =  -1.0000000E+34
forrtl: error (72): floating overflow
Image              PC                Routine            Line        Source      
a.out              0000000000402BA8  MAIN__                     13  ovf.f90
a.out              0000000000402A7C  Unknown               Unknown  Unknown
libc.so.6          000000394081ECDD  Unknown               Unknown  Unknown
a.out              0000000000402979  Unknown               Unknown  Unknown
Aborted

今度はトレースバックが取れました。 10の38乗は単精度実数のほぼ上限です。 このプログラムの場合、変数を倍精度実数(REAL*8)で宣言しなおせば問題解消できるでしょう。

-fpe{0|1|3} specifies program-wide behavior on floating point exceptions




go top