COBOL のファイル処理は、一見すると非常にシンプルです。ENVIRONMENT DIVISION でファイルを宣言し、DATA DIVISION でレコードのレイアウトを記述し、PROCEDURE DIVISION で OPEN、READ または WRITE、CLOSE を使用します。4 つの動詞で、ライフサイクルは 1 つです。問題は、COBOL プログラムはシンプルなままではいられないということです。何十年にもわたる保守の中で、ロジック、例外処理、条件分岐、PERFORM 呼び出しが蓄積されていきます。最初はクリーンな単一のオープンと単一のクローズのパターンだったものが、いつの間にかより複雑なものへと変化していきます。単一の実行パスでファイルが 2 回開かれたり、ファイルが開かれていないときに CLOSE ステートメントが実行されたり、エラーによって CLOSE をはるかに超えた分岐から完全に抜け出したりします。
これらの問題はどれもすぐには明らかになりません。プログラムはコンパイルされ、テストした入力に対してはエラーなく実行されることが多いです。しかし、想定していなかった入力の組み合わせでは、エラーを起こさずにエラーが発生したり、バッチウィンドウのパフォーマンスが徐々に低下したりするため、誰も原因と結果を結びつけることができません。静的解析は、こうした問題が発生する前に発見します。
発見する価値のある4つのファイル状態欠陥
検出メカニズムに入る前に、探すべき対象を明確に分類してみましょう。COBOLファイルの問題は、すべて同じではありません。
| 欠陥 | 実行時に何が起こるか | 重大度 |
|---|---|---|
| ダブルオープン | 既に開いているファイルに対してOPENを実行すると、ファイルステータス41または異常終了が発生します。 | ハイ |
| 事前のオープンなしにクローズする | 一度も開かれていないファイルに対して CLOSE を実行すると、ファイルステータス 42 または異常終了が発生します。 | ハイ |
| 通常出口でCLOSEが欠落しています | STOP RUN でファイルが開いたままになっている → OS はそれを閉じるが、フラッシュされていないバッファはデータ損失のリスクがある | 技法 |
| エラー終了時にCLOSEが欠落しています | ファイルオープン中にプログラムが異常終了 → VSAM ファイルのレコード破損の可能性 | ハイ |
| ループ内の冗長なOPEN/CLOSE | ファイルの開閉がイテレーションごとに行われるため、I/Oオーバーヘッドが深刻です。 | 高いメディア |
重要なポイント:最も深刻な欠陥である、OPENとCLOSEがOPENなしで二重に実行されると、即座に実行時エラーやデータ整合性リスクが発生します。一方、パフォーマンス上の欠陥(ループレベルのopen/close)はエラーを一切発生させず、原因が特定されていないバッチ処理の遅延を引き起こすだけです。
手作業で見つけるのが難しい理由
問題はパターンを特定することではない。どの開発者も、ファイルを二度開いてはいけないことは知っている。問題は、COBOLの制御フローが非線形であるため、手動でのトレースが信頼できない点にある。
次のような構造を持つプログラムを考えてみましょう。
COBOL
PROCEDURE DIVISION.
MAIN-LOGIC.
PERFORM INITIALIZATION
PERFORM PROCESS-RECORDS
PERFORM TERMINATION
STOP RUN.
INITIALIZATION.
OPEN INPUT CUSTOMER-FILE
OPEN OUTPUT REPORT-FILE
MOVE 0 TO WS-ERROR-FLAG.
PROCESS-RECORDS.
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END MOVE 1 TO WS-EOF-FLAG
END-READ
IF WS-ERROR-FLAG = 1
PERFORM ERROR-EXIT
END-IF
PERFORM UNTIL WS-EOF-FLAG = 1
PERFORM PROCESS-ONE-RECORD
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END MOVE 1 TO WS-EOF-FLAG
END-EXEC
END-PERFORM.
ERROR-EXIT.
DISPLAY 'Error in processing'
CLOSE CUSTOMER-FILE *> CLOSE here...
STOP RUN.
TERMINATION.
CLOSE CUSTOMER-FILE *> ...and CLOSE here too
CLOSE REPORT-FILE
STOP RUN.
すべての実行パスをトレースせずに欠陥を見つけることができますか? WS-ERROR-FLAG 内部で1に設定されます PROCESS-ONE-RECORD (おそらく呼び出されたサブプログラムによって)制御は ERROR-EXITファイルを閉じて停止します。それは正しいです。しかし、 WS-ERROR-FLAG is セットすると、ループが完了します。 PROCESS-RECORDS 戻り、そして TERMINATION 実行され、閉じられる CUSTOMER-FILE2 つのクローズ、1 つのオープン。ランタイム ファイル ステータス 42。
これは単純な3セクション構成のプログラムです。実際のプログラムは20セクション構成で、条件付きPERFORM呼び出し、レガシーコード内のGO TO文、そして他のルーチンを呼び出す例外処理ルーチンなどが含まれます。手動トレースはエラーが発生しやすいだけでなく、管理も困難です。
静的解析モデルがファイル状態をどのように捉えるか
静的解析は、プログラムの制御フローグラフを構築し、ファイル状態を各パスに沿って伝播させることで、これらの欠陥を検出します。
分析では、各ファイルに3つの可能な値を持つ状態変数を割り当てます。
CLOSEDファイルが開かれていないか、正常に閉じられましたOPENファイルが開かれましたが、まだ閉じられていません。UNKNOWN状態を静的に決定することはできません(例:完全に解析されていないブランチでの条件付きオープン)。
各OPENステートメントで、分析は、このファイルが既に状態になっているかどうかを確認します。 OPEN?もしそうなら→ダブルオープン欠陥。
各 CLOSE ステートメントで: このファイルはどの状態ですか? CLOSED or UNKNOWN?クローズの場合 → オープン欠陥なしでクローズします。
STOP RUN または EXIT PROGRAM の各パスで、まだ状態になっているファイルはありますか? OPEN?はいの場合 → 欠陥が見つかりません。
forループの場合、解析ではOPENまたはCLOSEステートメントがループヘッダーによって支配されているかどうか、つまり各イテレーションが実行されるかどうかを確認します。
気を付けて: どのアナライザーにとっても最も難しいケースは、共有ルーチンへのPERFORM呼び出しです。
ERROR-HANDLERプログラム内の 12 か所から呼び出され、PERFORM 呼び出し時点でのファイル状態によって、内部で閉じられるかどうかが決まります。ERROR-HANDLER正しいか間違っているかを判断します。PERFORM チェーンを通じて状態を伝播しないアナライザーは、偽陰性(実際の欠陥を見逃す)または偽陽性(正しいコードを誤って検出する)を生成します。
ループレベルのOPEN/CLOSE:エラーコードのないパフォーマンス上の欠陥
このパターンでは、実行時エラーもファイルステータスの異常も発生しません。ただ、実行速度が遅く、場合によっては必要以上に桁違いに遅くなる可能性があります。
COBOL
*> PROBLEMATIC: file opened and closed on every iteration
PROCESS-ALL-REGIONS.
PERFORM VARYING WS-REGION-ID FROM 1 BY 1
UNTIL WS-REGION-ID > 10
OPEN INPUT CUSTOMER-FILE
PERFORM PROCESS-REGION-RECORDS
CLOSE CUSTOMER-FILE
END-PERFORM.
ファイルが物理的に領域ごとに分割されていない限り、この方法は不要なオーバーヘッドを引き起こします。実際には、ファイルを一度開き、すべてのレコードを読み込み、メモリ内またはロジックによってフィルタリングを適用する方が良いでしょう。
VSAMファイルに対するOPEN操作では、カタログ検索、ACBの初期化、バッファプールの割り当てが毎回行われます。CLOSE操作では、バッファのフラッシュ、カタログの更新、ACBの解放が毎回行われます。10個のリージョンを持つファイルの場合、この処理は1回ではなく10回実行されます。10,000レコードと1,000個のリージョンを持つファイルの場合、計算は非常に複雑になります。
COBOL
*> CORRECT: open once, filter inside the loop
PROCESS-ALL-REGIONS.
OPEN INPUT CUSTOMER-FILE
PERFORM VARYING WS-REGION-ID FROM 1 BY 1
UNTIL WS-REGION-ID > 10
PERFORM PROCESS-REGION-RECORDS
END-PERFORM
CLOSE CUSTOMER-FILE.
静的解析では、ループが支配的なOPEN文とCLOSE文を特定することでこれを検出します。つまり、ループ本体の実行ごとにOPEN文またはCLOSE文が実行されます。この検出には、制御フローグラフが単なる文の順序だけでなく、ループ構造を表す必要があります。
ファイルステータス:プログラムのファイル状態シグナル(チェックした場合)
COBOLのファイル操作はすべて、FILE-CONTROLエントリで定義されているFILE STATUSフィールドを設定します。OPEN、READ、WRITE、CLOSEのたびにFILE STATUSをチェックするプログラムは、実行時にあらゆるファイル状態の異常を検出し、対応することができます。FILE STATUSを無視するプログラムは、手探りで操作しているようなものです。
チェックリスト:すべてのCOBOL開発者が知っておくべきファイルステータスコード
00完了10ファイルの終わり(読み取り:これ以上レコードはありません)35ファイルが見つかりません(入力ファイルを開く:ファイルが存在しません)41ファイルは既に開いています(OPEN状態のファイルに対してOPENコマンドを実行した場合)42ファイルが開かれていません(CLOSED状態のファイルに対してCLOSEまたはREADを実行した場合)47未オープンのファイルに対して読み取りが試行されました。INPUT または IO48ファイルが開かれていないため、書き込みが試みられました。出力、入出力、または拡張97ファイルが正常に開かれました(IBM固有の環境)
COBOL
FILE-CONTROL.
SELECT CUSTOMER-FILE
ASSIGN TO CUSTFILE
FILE STATUS IS WS-CUST-FILE-STATUS.
WORKING-STORAGE SECTION.
01 WS-CUST-FILE-STATUS PIC XX.
PROCEDURE DIVISION.
OPEN INPUT CUSTOMER-FILE
IF WS-CUST-FILE-STATUS NOT = '00'
DISPLAY 'OPEN failed: ' WS-CUST-FILE-STATUS
PERFORM ABEND-ROUTINE
END-IF.
気を付けて: よくあるメンテナンスエラーは、FILE-CONTROLエントリでFILE STATUSを宣言しているが、参照していないことです。
WS-CUST-FILE-STATUS手続き部において、このフィールドはファイル操作のたびに値が設定されますが、コードでチェックされていない場合、プログラムはエラーを無視して実行されます。静的解析では、条件ロジック内でFILE STATUSが宣言されているにもかかわらず参照されていないというパターンを検出できます。
COBOLで実際に発生する3つの不具合パターン
パターン1:共通のエラー終了問題
プログラムは複数の処理セクションから構成され、各セクションには独自のエラーハンドラがあります。いくつかのエラーハンドラは、処理を終了する前にファイルを閉じます。通常の終了処理でもファイルは閉じられます。エラーが発生し、エラーハンドラを通過して回復処理が進む場合、ファイルは2回閉じられます。
COBOL
VALIDATE-RECORDS.
READ CUSTOMER-FILE INTO WS-REC
AT END MOVE 1 TO WS-EOF
END-READ
IF WS-CUST-FILE-STATUS NOT = '00' AND '10'
CLOSE CUSTOMER-FILE *> closes here on read error
PERFORM WRITE-ERROR-LOG
GO TO TERMINATION *> skips to close in TERMINATION?
END-IF.
TERMINATION.
CLOSE CUSTOMER-FILE *> double close if GO TO reached here
CLOSE REPORT-FILE
STOP RUN.
その GO TO TERMINATION 制御を直接 TERMINATION独自の CLOSE CUSTOMER-FILEファイルステータス 42。
修正方法:単一の集中型クローズルーチンを使用する。すべての終了パスで同じルーチンを呼び出し、ファイルを閉じる前にファイルが開いているかどうかを確認する。
パターン2:条件付きOPEN
ファイルは、特定の処理モードが有効な場合にのみ条件付きで開かれます。しかし、CLOSE は無条件であり、ファイルが一度も開かれなかった場合も含め、すべての実行パスで発生します。
COBOL
INITIALIZATION.
IF WS-PROCESSING-MODE = 'FULL'
OPEN OUTPUT AUDIT-FILE *> only opened in FULL mode
END-IF.
TERMINATION.
CLOSE AUDIT-FILE *> ALWAYS closes -- FILE STATUS 42 in PARTIAL mode
STOP RUN.
この不具合は、テストを常にFULLモードで実行している場合は検出されません。本番環境では、PARTIALモードでの最初の実行時に顕在化します。
パターン3:コンパイル単位をまたいでのパフォーマンス
CALLを使用してサブルーチンを呼び出すプログラムでは、メインプログラムで開かれたファイルがサブルーチンに渡され、サブルーチンがファイルを閉じ、その後メインプログラムが再びファイルを閉じる場合があります。
COBOL
*> Main program
CALL 'SUBPROG1' USING CUSTOMER-FILE-STATUS
*> SUBPROG1 closes CUSTOMER-FILE internally
CLOSE CUSTOMER-FILE *> double close
このパターンでは、プロシージャ間解析が必要であり、CALL境界を越えてサブルーチンの動作までファイル状態を追跡する必要があります。これは、単純なプログラム内解析では検出できません。
静的解析ツールがこれをうまく行うために必要なもの
すべての静的解析ツールが、COBOLファイルの状態解析を同等の深さで処理できるわけではありません。以下に、機能評価のためのチェックリストを示します。
このツールはPERFORMチェーンを処理できますか? COBOLプログラムはPERFORMを使用して、名前付き段落やセクションを呼び出します。PERFORMターゲット内のファイル操作は、呼び出し元の状態追跡から見える必要があります。
このツールはインラインのPERFORM VARYING(ループ)に対応していますか?ループ検出機能は、ループが支配的なOPEN/CLOSE操作を識別するために必要です。
GO TO を介した状態追跡は可能ですか?従来の COBOL プログラムでは GO TO が多用されています。制御フロー グラフ内の GO TO エッジを追跡できないアナライザーでは、多くの種類の欠陥を見落としてしまう可能性があります。
COPYとREPLACEは処理できますか?ファイル処理コードは多くの場合、COPYメンバに記述されています。解析前にCOPYメンバを展開しないアナライザーでは、インクルードされたコードで定義された操作が見落とされてしまいます。
FILE STATUSの使用状況をチェックしていますか?完全な分析では、FILE STATUSが宣言されているかどうかだけでなく、各操作後に実際にチェックされているかどうかも確認します。
プロシージャ間解析は実行しますか?ファイル操作を実行する呼び出しサブルーチンは、プログラム間の状態依存関係を生み出します。真のカバレッジを実現するには、呼び出しチェーンを追跡する必要があります。
重要なポイント:単一の段落またはセクション内のみをチェックするツールでは、実際の運用環境で発生するほとんどの欠陥を見逃してしまう。本番環境で問題となる欠陥、つまり発生までに何年もかかり、診断に何時間もかかるような欠陥は、複数のセクション、条件分岐、および呼び出されるルーチンにまたがるものである。
結果をどう活用するか:優先順位付けのフレームワーク
静的解析でファイル欠陥が発見された場合、すべての発見が同等に重要というわけではありません。以下に、それらをトリアージする方法を示します。
直ちに修正してください(次回のバッチ実行前):
- ファイルステータス41が異常終了を引き起こす実行パスでは、ダブルOPENを実行する。
- 本番環境で実行されるパスでは、OPEN なしで CLOSE を実行します。
- VSAMファイルのエラー終了パスにCLOSEが欠落している(書き込み整合性リスク)
次回のスプリントのスケジュール:
- ループ処理が中心のOPEN/CLOSE処理で、バッチウィンドウへの影響を測定可能
- 重要なファイル(監査、トランザクション、レポート)に対するファイルステータスチェックの欠落
- マルチモードプログラムにおける条件付きOPENと無条件CLOSE
次回の近代化作業中に追跡および対処すべき事項:
- ファイルステータス宣言が欠落しています
- CLOSEは、通常の終了パスでのみ使用します(OSが処理しますが、推奨されません)。
- 複数のプログラム間で変更を調整して修正する必要がある、プロシージャ間の欠陥
認定条件 SMART TS XL COBOLファイルの状態を分析します
SMART TS XLさん 静的コード分析 環境内のすべての COBOL プログラムの制御フロー モデルを構築し、COPY メンバーを展開し、PERFORM チェーンをたどり、GO TO エッジをトレースし、すべての条件分岐のすべてのブランチを通じてファイル状態を伝播します。これはパターン マッチング スキャンではなく、プログラムが実行中の到達可能なすべてのポイントで実際に何を行うかを示す構造モデルです。
アプリケーション依存関係マッピング機能は、これをプロシージャ間次元にまで拡張します。COBOLプログラムがファイル操作を実行するサブルーチンを呼び出す場合、依存関係マップはその関係を表し、コンパイル単位の境界を越えた分析を可能にします。メインプログラムと呼び出されるサブルーチンにまたがるダブルOPENエラーは、プログラム内分析では見られない方法で構造モデル上で可視化されます。
エンタープライズ検索機能により、大規模な環境でも実用的な結果が得られます。数百万行に及ぶCOBOLコードの中から、特定のデータセットを開くすべてのプログラム、CLOSE文を含むすべてのCOPYメンバー、OPEN文を含むPERFORMターゲットを持つすべてのプログラムを、わずか数秒で検索できます。バッチワークロードをクラウドに移行しようとしているモダナイゼーションチームにとって、この検索機能は、数週間かかる手動監査を、的を絞ったクエリへと変革します。
JCL拡張機能は、運用コンテキストを追加します。つまり、どのJCLジョブステップが各プログラムを呼び出すか、各DDステートメントがどのデータセットを参照するか、そしてCOBOLコード内のファイル処理がJCLで定義された物理データセットにどのように接続されるかといった情報です。JCLで重要な出力データセットとして定義されているファイルに対するCLOSEエラーは、一時作業ファイルに対する同じエラーよりも優先度が高く、この優先順位付けには各COBOLプログラムのJCLコンテキストを把握する必要があります。
より広い教訓:ファイル状態は単なる一側面に過ぎない
冗長なファイル操作は、より一般的な問題の具体的な例です。数十年にわたって進化してきたCOBOLプログラムには、状態依存の動作、ファイル状態、スイッチ状態、カウンタ状態などが含まれており、これらは完全な制御フローグラフを通して考えられるすべての実行パスをトレースすることによってのみ正しく理解できます。
こうしたコードの人間によるレビューは、時間がかかり、不完全で、一貫性に欠ける。開発者によって発見される欠陥も異なり、同じ開発者が同じプログラムを2回レビューしても、異なる欠陥が見つかる。静的解析は体系的であり、疲労や先入観にとらわれることなく、あらゆるプログラムのあらゆるパスに常に同じルールを適用する。
大規模なCOBOLポートフォリオを管理するチームにとって、継続的な保守、レガシーシステムの近代化プログラム、コンプライアンス監査など、どのような目的であれ、体系的なファイル状態分析のメリットは、発見された欠陥だけにとどまりません。ポートフォリオ全体が完全に分析され、残っている欠陥が隠されているのではなく、既知のものであるという確信を得られることが、そのメリットなのです。


