保護違反エラーへの対処

この章では、プログラムが無効コードを生成する仕組みと、生成されたエラーを識別して対処する方法について説明します。Visual COBOL の使用時に発生する可能性がある各種の保護違反について説明し、その識別および解決方法を示します。

概要

Visual COBOL は、無効コードを検出して問題を未然に防ぐように設計されていますが、プログラムが生成しうるあらゆる無効コードに対処できるわけではありません。その結果、プログラムが無効コードを生成した場合には、オペレーティング システムによって一般保護違反エラーが生成される、または Visual COBOL によって保護違反エラーが生成されます。

保護違反の定義

保護違反は、実行を試みられたマシン命令が、実行中のプロセスで不正と判明したときに発生します。この状況は、次の原因によって生じます。

保護違反には次の 2 種類があります。

一般保護違反

オペレーティング システムは通常、各プロセスにコンピューターリソースの一部分のみを割り当て、この割り当てを各プロセスが超えていないかを監視します。割り当てを超過しているプロセスを検出すると、オペレーティング システムはなんらかのエラーを生成します。このエラーが一般保護違反 (例外) です。なお、オペレーティング システムによっては、その他のエラーが生成されることもあります。

一般保護違反を検出すると、オペレーティング システムは該当するプロセスを終了する前に、例外ハンドラーと呼ばれる特殊なプログラムを実行します。例外の原因になったプログラムによっては、プログラム自体と関連付けられた専用の例外ハンドラーが実行されることもありますが、そのような例外ハンドラーが存在しない場合には、オペレーティング システムのデフォルト例外ハンドラーが実行されます。

Visual COBOL は、UNIX 用の COBOL 例外ハンドラーを備えています。詳細については、次の COBOL 保護違反の説明を参照してください。

COBOL 保護違反

オペレーティング システムには、保護違反検出機能が十分でないものもあります。Visual COBOL は、必要に応じて、 COBOL アプリケーション (および COBOL ランタイム制御下の非 COBOL アプリケーション) を監視し、 COBOL ランタイム システムによるリソース割り当てを超過しないようにしています。ランタイム システムが、リソースの境界を超過しているプログラムを検出すると、 COBOL 保護違反が発生します。

COBOL システムが保護違反を検出するかは、監視対象のリソース、監視方法、およびリソースの割り当て方法に依存します。また、COBOL システムによる COBOL 保護違反の監視効率は、ハードウェアとオペレーティング システムによって異なります。正しく動作している COBOL プログラムでも、利用可能なリソースやオペレーティング システムが異なると、保護違反エラーの原因になることがあります。

COBOL プログラムによって保護違反エラーが発生した場合に提示される情報と、その情報を補完するために利用できるツールは、オペレーティング システムごとに異なります。そのため、プログラムのデバッグが困難な場合は、他の環境でデバッグを試みると、より詳しい情報が得られる場合があります。

保護違反による主な影響

UNIX で一般保護違反が検出されると、その種類に応じてランタイム システム エラー 114 または 115 が生成されます。エラー 114 (メモリ領域外の項目にアクセスしようとしている) は割り当てられたメモリ領域外の項目にアクセスが試みられたこと、エラー 115 (予測しない信号を検出した) は予期しないシグナルが検出されたことを示します。

保護違反は通常、プロセスがアクセスを試みたメモリ上の位置が、そのアクセス用に割り当てられたメモリ領域から外れている場合に発生します。この状況は、プロセスがアクセスすべきメモリ上の位置を正しく計算できなかったことを示します。

ただし、プロセスの移動やメモリ上での配置の変更、またはサイズの増加によって、アクセスを試みたメモリ上の位置が割り当て範囲内に入る可能性もあります。その場合には保護違反は発生しませんが、次のような問題が生じます。

保護違反への対処

保護違反および COBOL 保護違反に関連するランタイム システム エラーは通常、次の原因によって発生します。

次では、これらの原因によって生じる問題とその解決方法を、原因ごとに説明します。

アプリケーションの非 COBOL 部分のエラー

Visual COBOL は優れた COBOL 保護違反検出メカニズムを備えており、COBOL のみで開発したアプリケーションで一般保護違反が発生することはほとんどありません。これに対し、他の言語、特に C では、ポインターが多用されることが多いため、一般保護違反が発生する可能性も高くなります。

非 COBOL プログラムを含むアプリケーションで一般保護違反が発生する場合は、非 COBOL プログラムを重点的にチェックすることをお奨めします。ほとんどの場合、一般保護違反の原因は非 COBOL プログラム内で見つかります。

CALL 文内のパラメーターの不整合

CALL 文は、正しく指定しないと一般保護違反の原因になる部分がいくつかあります。

一般保護違反の原因が CALL 文にあると思われる場合には、次の点をチェックしてください。

呼び出し元や呼び出し対象が非 COBOL プログラムの場合は、これらの要因の管理はさらに難しくなり、結果的に保護違反が生じる可能性も高くなります。非 COBOL プログラムを含むアプリケーションを開発する際は、非 COBOL プログラム内の呼び出しを COBOL 標準形式に従わせるか、または COBOL プログラム内の呼び出しを (CALL-CONVENTION 句を使用して) 非 COBOL プログラムの言語の呼び出し規則に従わせる必要があります。CALL-CONVENTION 句の詳細については、『混合言語間インターフェイスの呼び出し規則COBOL インターフェイス環境』の章を参照してください。

呼び出しインターフェイスの不整合による影響は、一致していないパラメーターでやり取りされるデータや、使用する呼び出し規則によって大幅に異なります。

スタック オーバーフロー

より目立ちにくい一般保護違反の原因に、スタック オーバーフローがあります。スタックとは、CALL 文で呼び出されたパラメーターと、呼び出し元プログラムのアドレスが一時的に格納されるシステム領域のことです。呼び出されたサブプログラムのネスト階層数や、渡されたパラメーターのサイズまたは数がスタックの容量を超えると、スタック オーバーフローが発生します。

スタック オーバーフローは、スタックを過度に利用したり、スタックのデフォルトサイズ (または要求されたサイズ) が不十分な場合に発生します。スタックは、オペレーティング システムの各種ルーチンでも使用されることがあり、すべての容量が利用できるとは限りません。また、同じオペレーティング システムでもバージョンによりスタックの使用方法が異なるため、利用できるサイズが変化するという点に注意してください。

正常に動作していたCOBOL アプリケーション内のプログラムが、同じオペレーティング システムの別のバージョンでは一般保護違反を生成するようであれば、デフォルトのスタックサイズを拡張してください。UNIX 環境では、スタック オーバーフローの発生はきわめて稀です。UNIX 上でスタックのサイズを変更する方法については、使用している UNIX システムのマニュアルを参照してください。

無効な部分参照

部分参照を使用すると、指定されたデータ項目外のメモリ領域を容易に参照できます。ただし、無効な参照整合は、プログラムがアクセスしているメモリ領域の位置によって次のような結果につながります。

コード例

次に示すコード例は無効な部分参照を含んでおり、ランタイム システム エラー 114 を生成します。具体的には、b の最大長より長いデータを a に設定しています。1 つめの部分参照は転記元を対象としており、c に無効なデータが渡されます。この問題は Visual COBOL により検出されます。

2 つめの部分参照は転記先が対象であるため、より深刻な問題になる可能性があります。この場合も、Visual COBOL によって問題が検出され、ランタイムエラー 114 が生成されます。

このコード例は以降の説明でも、さまざまな環境における保護違反のデバッグ方法を説明するために使用します。

 program-id. "buggy".
 working-storage section.
 01 a   pic 9(6).
 procedure division.
     move 999999 to a
     call "bug" using a
     stop run.
 end program "buggy".
 program-id. "bug".
 working-storage section.
 01 b        pic x(20).
 01 c        pic x.
 linkage section.
 01 a    pic 9(6).
 procedure division using a.
     move b(a:1) to c
     move "1" to b(a:1)
     exit program.

無効なポインター値

POINTER または PROCEDURE-POINTER として定義したデータ項目はポインターであり、使用時には有効なアドレスを格納している必要があります。無効な値を持つポインターを使用すると、ランタイム システム エラー 114 (メモリ領域外の項目にアクセスしようとしている) が生成されます。一般保護違反エラーが生成されないのは、Visual COBOL で保護違反が検出されるためです。

コード例

次のコード例では、undefined-pointer データ項目をプロシージャへのポインターとして定義していますが、有効なアドレスを格納せずに使用しているため CALL 文が失敗し、ランタイム システム エラー 114 が生成されます。

 working-storage section.
 01 undefined-pointer    usage procedure-pointer.
 procedure division.
     call undefined-pointer
     stop run.
         

有効範囲外の添字

添字や指標として使用される項目は Visual COBOL によってチェックされ、その値が有効範囲を外れているとランタイム システム エラー 153 (添字が指定範囲外になっている) が生成されます。添字項目や指標項目の値をチェックすると、アプリケーションのパフォーマンスが多少低下するため、NOBOUND 指令で無効化することも可能です。ただし、NOBOUND でチェックを無効化した場合は、表の参照時に添字や指標の値が有効範囲を外れ、保護違反が生成される可能性があります。

NOBOUND 指令を指定してコンパイルまたは生成したアプリケーションで予期しない結果や保護違反が発生するようであれば、NOBOUND を指定せずに再コンパイルして実行し、問題が添字の値によって発生しているかを調べる必要があります。

コード例

無効な添字の値を使用するコードの例を次に示します。このコードを NOBOUND 指令とともにコンパイルすると、生成されたコードは予期できない動作を示します。bound-main から build-sub に不適切な値を渡しているため、build-sub 内では 20 個の要素を持つ表 (b) に対して、21 番めの要素に値を転記する処理が試みられます。NOBOUND 指令を指定してコンパイルした場合は、この処理がそのまま実行され、次のデータ項目 (c) が上書きされてしまいます。NOBOUND 指令を指定せずにコンパイルした場合は、MOVE 文の処理でランタイム システム エラー 153 (Subscript out of range) が生成されます。

 program-id. "bound-main".
 working-storage section.
 01 a   pic 99.
 procedure division.
     move 21 to a
     call "bound-sub" using a
     stop run.
 end program "bound-main".

$set align(2) nobound
 program-id. "bound-sub".
 working-storage section.
 01 b    pic x occurs 20.
 01 c    pic xx.
 linkage section.
 01 a    pic 99.
 procedure division using a.
     move (1) to b(a)
     exit program.

無効なリンク オプションやプロシージャへの対処

リンク オプションの多くは、正しく使用しないと一般保護違反の原因になる場合があります。不適切なオブジェクトやライブラリのリンクによって問題が生じたり、オブジェクトやライブラリは適切でも、バージョンが不適切なために問題が生じる場合があります。

オブジェクトやライブラリのバージョンによって問題が発生したと思われる場合は、検索対象のディレクトリ内に旧バージョンのオブジェクトやライブラリが残っていない、および最新版のオブジェクトやライブラリを正しくインストールしているかを確認してください。

デバッグ手法

プログラムのデバッグ手法は、プログラムのロード方法や開発方法によって異なります。

Animator を使用すると、中間コードファイル、生成コードファイル、呼び出し可能な共有オブジェクト、またはシステム実行可能ファイルで保護違反エラーが発生した場合に、その原因になったソースファイル内の行を見つけることができます。ただし、この方法でデバッグを行うには、Animator 情報ファイル (.idy ファイル) とソースファイルが必要です。

開発段階のデバッグ

開発段階で JIT (Just-In-Time) デバッグを利用すると、プログラムで保護違反エラーが発生した場合に、その原因となったソースファイル内の行を容易に見つけることができます。JIT デバッグを行うには、debug_on_error ランタイム チューナーを設定します。

debug_on_error チューナーを設定してプログラムを実行すると、保護違反エラーが発生した時点で Animator が自動的にロードされ、エラーの原因となったソースファイル内の行がハイライト表示されます。Animator では対象アプリケーションの動作を完全に制御でき、あらゆる機能を使用して問題を特定することができます。

保護違反の原因になっているソースコード内の行がわかっている場合には、JIT デバッグを使用する必要はありません。プログラムを Animator に直接ロードして、その行をズームします。

本番環境のアプリケーションのデバッグ

本番環境やユーザーサイトで使用されているアプリケーションで保護違反エラーが発生する場合は、コアダンプデバッグを実施します。コアダンプデバッグを行うには、core_on_error ランタイム チューナーを設定します。

コアダンプデバッグを有効化した状態でアプリケーションを実行すると、保護違反が検出された時点でランタイム システムがコアダンプを生成します (この場合は、ランタイム システム エラー 114 または 115 は通知されません)。生成されたコアダンプのコピーを開発用システムに移し、Animator を使用して内容を調べます。

Animator でコアダンプを表示するには、次のように、コアダンプファイルを Animator の入力ファイルとして指定します。

anim corefile

Animator がコアダンプファイルとソースファイルをロードして、保護違反の原因になったソースファイル内の行をハイライト表示します。コアダンプファイルを使用する場合は、Animator の一部の機能は利用できません。つまり、コアダンプは保護違反が発生した時点におけるアプリケーションのスナップショットであるため、アプリケーションを実行する機能が無効になります。ただし、クエリ機能によるデータ項目の値の確認や、スタック表示の実行や呼び出しを通じたスタックコンテキストの切り替えは可能です。これらの機能を使用してアプリケーションの実行経路をさかのぼり、アプリケーションフローを制御する各データ項目の値を確認できます。


注: コアダンプファイルの生成時には、ランタイム システムが COBOL ファイル バッファーをクリアできず、システムリソースが解放されません。その結果、データファイルが破損する可能性があります。