通常、IEEE 例外について、次のことを知っておく必要があります。
例外が発生するときのシステムの動作。
例外ハンドラとして使用できる関数を作成する方法。
例外の発生場所を調べる方法。
ユーザールーチンへの例外トラップは、システムの浮動小数点例外に対するシグナルの生成から始まります。「浮動小数点例外のシグナル」を表す UNIX の公式名は SIGFPE です。 SPARC プラットフォームは、デフォルトでは、例外が発生しても SIGFPE を生成しません。システムが SIGFPE を生成するためには、まず、例外トラップを有効にしなければいけません。これは通常は、ieee_handler() への呼び出しによって行います。
例外ハンドラとして関数を設定するときは、監視する例外や対応動作といっしょに、関数の名前を ieee_handler() に渡します。 ハンドラを一度設定すると、特定の浮動小数点の例外が発生するたびに、SIGFPE シグナルが生成され、指定した関数が呼び出されます。
ieee_handler() を起動する形式を次の表に示します。
表 6–4 ieee_handler( action , exception , handler) の引数|
引数 |
種類 |
可能な値 |
|---|---|---|
|
action |
文字列 |
get、set、clear のいずれか |
|
exception |
文字列 |
invalid、division、overflow、underflow、inexact のいずれか |
|
handler |
関数名 |
ユーザーハンドラ関数の名前、または、SIGFPE_DEFAULT、SIGFPE_IGNORE、SIGFPE_ABORT のいずれか |
|
戻り値 |
整数 |
0 =OK |
f95 でコンパイルする Fortran 95 ルーチンで ieee_handler() を呼び出す場合は、次の宣言も必要です。
#include 'floatingpoint.h'
特別な引数 SIGFPE_DEFAULT、SIGFPE_IGNORE、および SIGFPE_ABORT は、これらのインクルードファイルで定義され、特定の例外に対するプログラムの動作を変更するのに使用できます。
|
SIGFPE_DEFAULT または SIGFPE_IGNORE |
指定した例外が発生しても何も動作しない。 |
|
SIGFPE_ABORT |
例外発生時にはプログラムが異常終了し、おそらくダンプファイルを生成する。 |
ユーザーの例外ハンドラが行う動作はユーザーが自由に設定できます。しかし、ルーチンは、次に示す 3 つの引数を取る、整数型関数でなければいけません。
handler_name( sig, sip, uap )
handler_name は整数関数の名前です。
sig は整数です。
sip は構造体 siginfo を持つレコードです。
uap は使用されません。
例: 例外ハンドラ関数
INTEGER FUNCTION hand( sig, sip, uap )
INTEGER sig, location
STRUCTURE /fault/
INTEGER address
INTEGER trapno
END STRUCTURE
STRUCTURE /siginfo/
INTEGER si_signo
INTEGER si_code
INTEGER si_errno
RECORD /fault/ fault
END STRUCTURE
RECORD /siginfo/ sip
location = sip.fault.address
... ユーザーの行う処理 ...
END
|
64 ビット SPARC アーキテクチャーで実行するには、STRUCTURE 内のすべての INTEGER 宣言を INTEGER*8 で置き換えて、この例を変更する必要があります。
ieee_handler() によって有効にされるハンドラルーチンが例で示すように Fortran で書かれている場合、ハンドラルーチンは 1 番目の引数 (sig) に対しては、一切の参照を行なってはいけません。1 番目の引数は値でルーチンに渡され、loc(sig) としてのみ参照できます。この値はシグナル番号です。この値はシグナル番号です。
次の例では、浮動小数点の例外を検出するためのハンドラルーチンを作成する方法を示します。
SIGFPE は、浮動小数点演算の例外が発生すると必ず生成されます。SIGFPE が検出されると、制御が myhandler 関数に渡され、即座に異常終了します。-g を付けてコンパイルし、dbx を使用すると、例外箇所を突き止めることができます。
例: 例外箇所を突き止め (アドレスを出力して)、異常終了します。
demo% cat LocExcHan.F
#include "floatingpoint.h"
EXTERNAL Exhandler
INTEGER Exhandler, i, ieee_handler
REAL:: r = 14.2 , s = 0.0 , t
C ゼロ除算の検出
i = ieee_handler( 'set', 'division', Exhandler )
t = r/s
END
INTEGER FUNCTION Exhandler( sig, sip, uap)
INTEGER sig
STRUCTURE /fault/
INTEGER address
END STRUCTURE
STRUCTURE /siginfo/
INTEGER si_signo
INTEGER si_code
INTEGER si_errno
RECORD /fault/ fault
END STRUCTURE
RECORD /siginfo/ sip
WRITE (*,10) sip.si_signo, sip.si_code, sip.fault.address
10 FORMAT(' シグナル ',i4,' コード ',i4,' 16 進アドレス ', Z8 )
Exhandler=1
CALL abort()
END
demo% f95 -g LocExcHan.F
demo% a.out
シグナル 8 コード 3 16 進アドレス 11230
異常終了
demo%
|
64 ビット SPARC 環境では、各 STRUCTURE 内の INTEGER 宣言を INTEGER*8 で置き換えて、書式中の i4 を i8 で置き換えます。この例では、f95 コンパイラへの拡張により、VAX Fortran の STRUCTURE 文を受け付けられるようにしています。
ほとんどの場合、例外の実際のアドレスを知るということは、dbx だけに意味があります。
demo% dbx a.out
(dbx) stopi at 0x11230 ブレークポイントをアドレスに設定する
(2) stopi at &MAIN+0x68
(dbx) run プログラムを実行する
実行中: a.out
(プロセス id 18803)
MAIN で 0x11230 で停止しました
MAIN+0x68: fdivs %f3, %f2, %f2
(dbx) where 例外が発生した行番号を表示する
=>[1] MAIN()、"LocExcHan.F" の 7 行目
(dbx) list 7 ソースコード行を表示する
7 t = r/s
(dbx) cont ブレークポイント後、継続してハンドラルーチンに入る
シグナル 8 コード 3 16 進アドレス 11230
異常終了:
_kill 0xef6e18a4 でシグナル ABRT (異常終了) が呼び出されました
_kill+0x8: bgeu _kill+0x30
現関数: exhandler
24 CALL abort()
(dbx) quit
demo%
|
もちろん、エラーの原因となるソース行を決定するためのより簡単な方法があります。しかし、この例は、例外処理の基本を示すのが目的です。