ダンプ・リスト解析入門 ③:ABENDの原因を調べる①

モジュール内のどこでプログラムがABENDしたのかが特定できたら、エラーの原因を究明していきます。まずは、ABENDコードによってABENDさせられた直接の理由を確認します。その後、どうしてそのようなエラーを引き起こしたのかという原因を調べていきます。

ABENDコード

ABENDコードはプログラムの異常終了の理由を示すもので、大きくシステムABENDコードとユーザーABENDコードに大別されます。ユーザーABENDコードは「Unnnn(nnnnは0から4095の10進数)」の形式で、ABENDマクロを発行したユーザー・プログラムで指定した値です。ユーザーABENDは、入力データなどに誤りがあった場合や想定していないデータが渡されたような場合に、そのまま処理しては正しい結果を出すことができないことをユーザー・プログラム自らが検知してプログラムの処理をキャンセルするような場合に使用されます。
ユーザーABENDの場合のABENDコードはユーザー・プログラムの仕様によって決まるので、エラーの原因や対処方法などはユーザー・プログラムのマニュアルなどで明記されます。ユーザー自らが作ったアプリケーション・プログラムではなく、メーカーやベンダーのソフトウェア製品などでは、それらの製品のマニュアルにABENDコードの意味や対処方法が記載されています。

システムABENDコードは「Sxxx(xxxは001からFFFの16進数)」の形式で、OSが割り当てたコードです。システムABENDは、プログラムのエラーを検知したOSによって強制的にABENDさせられるものです。その原因にはプログラムのバグもありますが、入出力エラーやハードウェア・エラーなどアプリケーション・プログラムのせいではないものもあります。
システムABENDの場合のABENDコードは、システム・コードあるいはメッセージのマニュアルなどにコードの意味や対処方法が解説されています。

S0Cx

S0Cxはプログラム割込みによるABENDです。プログラム割込みはCPUの割込みの1つで、存在しない命令を実行しようとしたり、数値を0で割ろうとする(0除算)など演算不可能な計算をしようとした場合などに、CPUが命令の実行を抑止したことを通知するものです。この割込みが発生すると、MVSはプログラム割込みルーチンによってエラー処理を行います。プログラム・エラーの原因は割込みコードによって通知されますが、これはS0CxABENDの理由を示します。0Cxのxと理由コード(プログラム割込みコード)は、ほとんどの場合同じですがS0C4ABENDのように複数の理由で構成されるものもあります。例えば、同じS0C4ABENDでもPSWキーと異なる領域にアクセスした記憶保護例外(理由コード4)と存在しないアドレスにアクセスした(理由コード10や11)では原因が異なります。

どちらもABENDS0C4だが、上はINTC=4で記憶保護例外(仮想記憶として正しい領域であるがキーが異なるためアクセスできない)、下はINTC=11でページ変換例外(仮想アドレス自体が誤っている、存在しない仮想記憶域をアクセスしようとした)とABENDの理由は異なっている。S0C4ABENDのように複数の理由を持つABENDコードでは、割込みコードをきちんと確認することが正しいデバッグに繋がる。

S0CxABENDは、ほとんどの場合プログラムのバグです。中には入力データが誤っていたため(例えば、数値なのに文字列が入っていた)に、そのデータで演算命令を実行した結果S0C7などを引き起こすこともあります。コーディング・ミスではありませんが、データの正当性をチェックしていれば防げる類のものでプログラム・デザインの甘さに起因するものです。これらも広義のバグと言えるでしょう。

S1xx、S2xx、S3xx、S4xx、S5xx、S6xx、S7xx、S8xx、S9xx、SAxx、SBxx…

S100以上のコードの場合、システムABENDコードの下2桁はエラーを検知したSVC処理のSVC番号を示します。例えば、S80AはSVC10でGETMAIN、S213はSVC19でOPENの各サービスでのエラーです。
コードの下2桁がSVC番号ということは覚えておくと便利です。ABENDコードを見ればどのSVCがエラーになったかがわかるため、エラーの箇所や原因を特定しやすくなるからです。
GETMAINやOPENなど様々なマクロ命令がありますが、どのマクロが何番のSVCを出すかはアセンブリー・リストを見ればわかります。1つのプログラム・モジュール内で何十ものマクロ命令を使うことなどほとんどありません。せいぜい数種類でしょう。最初のうちはアセンブルした後にリストを見て展開された命令列を確認したり、翻訳された命令コードを見ることで自分がプログラムで書いたマクロがどのSVCを出すかなどはすぐに覚えるものです。発行したマクロの処理を行うSVC内でABENDするのは、ほとんどのケースが誤ったパラメーターでマクロを発行したことによります。そういう意味でも多くはプログラムのバグ、ということになります。

レジスター

レジスター(ここでは汎用レジスターを指す)の内容は、ABEND原因を調べる上でとても重要です。特に、S0C4やS0C7といったアドレスを指定してメモリーにアクセスしたり演算を行ったりする命令でABENDした場合は、命令が指しているレジスターの内容は重要です。また、誤ったアドレスに分岐してしまったような場合も、レジスターの内容はプログラムがABENDに至るまで処理がどこまで行われていたのかをトレースする重要な手がかりにもなります。

ABEND時のレジスター内容は、徴候(SYMPTOM)ダンプに表示されます。あるいはダンプ・リストのレジスター表示部「GPR VALUES」に出力されています。しかし、SVCルーチンの中でABENDしたような場合、徴候ダンプやダンプ・リストのレジスター表示部は実際にABENDを起こした時点のレジスター内容なので、アプリケーション・プログラムがマクロ命令などを発行した後のOS側の処理で内容が変わってしまっています。PSWでABEND箇所を特定した時と同様に、ABEND時点ではなく最後に割込みを起こした時点のレジスター内容を求める必要が出てくることがあります。
マクロ命令発行によるSVC呼び出しの場合、アプリケーション・プログラムが最後に起こした割込みはSVC割込みとなり、SVC命令を実行した時点(最後に割込みを起こした時点)のレジスター内容はPSWが保管されているPRBの次に表示されているSVRB内に退避されています。

PSWとレジスターは同じRB内に退避されるのではない。SVC命令実行(マクロ命令発行)の場合は、PRBの次のSVRBにあるGPR表示部を見る。この例ではSVC19を実行した時点のGR1は87500F78となっているが、これはSVCルーチン内でABENDした時のPSWが示す命令 ICM R2,B’0111′,0(R1) (xBF271001) が使用しているGR1の内容00500F78と酷似している。このことからSVC19発行時のGR1の値がエラーを引き起こした要因と考えられる。

自分のプログラム内でABENDしたのであれば、徴候ダンプに表示されているPSWとレジスター内容でアセンブリー・リストとダンプ・リストのメモリー内容を照らし合わせればいいのですが、自分のプログラム外で(例えば、発行したマクロの延長で呼ばれるSVCルーチン等)のABENDであれば、単に徴候ダンプの内容だけではなく、ダンプ・リストのPRBとSVRBから自分のプログラムが最後に起こした割込み(SVCの発行)時点のPSWとレジスターを求めて、アセンブリー・リストとダンプ・リストのメモリー内容を照らし合わせます。もちろん、SVCルーチンの処理エラーなどABENDコードのマニュアルで原因が判明できるのであれば、対応するプログラム・コードを見直して正しく修正すればいいのです。