見積依頼

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

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

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