ダンプリスト解析入門③
ダンプリスト解析入門③:ABENDの原因を調べる①
モジュール内のどこでプログラムがABENDしたのかが特定できたら、エラーの原因を究明していきます。まずはABENDコードによってABENDさせられた直接の理由を確認します。その後で、どうしてそのようなエラーを引き起こしたのか、という原因を調べていきます。
ABENDコード
ABENDコードは、プログラムの異常終了の理由を示しますもので、大きくシステムABENDコードとユーザーABENDコードに大別されます。ユーザーABENDコードは「Unnnn(nnnnは0から4095の10進数)」の形式で、ABENDマクロを発行したユーザー・プログラムで指定した値です。ユーザーABENDは、入力データなどに誤りがあった場合、想定していないデータが渡されたような場合、そのまま処理しては正しい結果を出すことができないことをユーザー・プログラム自らが検知して、プログラムの処理をキャンセルするような場合に使用されます。
ユーザーABENDの場合のABENDコードは、ユーザー・プログラムの仕様によって決まるので、エラーの原因や対処方法などは、ユーザー・プログラムのマニュアルなどで明記されます。ユーザー自らが作ったアプリケーション・プログラムではなく、メーカーのPP製品やベンダーのISV製品などでは、それらの製品のマニュアルにABENDコードの意味や対処方法が記載されています。
システムABENDコードは「Sxxx(xxxは001からFFFの16進数)」の形式で、OSが割り当てたコードです。システムABENDは、プログラムのエラーを検知したOSによって強制的にABENDさせられるものです。その原因には、プログラムのバグもありますが、入出力エラーやハードウェア・エラーなど、アプリケーション・プログラムのせいではないものもあります。
システムABENDの場合のABENDコードは、システム・コードあるいはメッセージのマニュアルなどにコードの意味や対処方法が解説されています。
S0Cx
S0Cxは、プログラム割込み(またはプログラム・チェック割込み)によるABENDです。プログラム割込みは、CPUの割込みの1つで、存在しない命令を実行しようとしたり、数値を0で割ろうとする(0除算)など演算不可能な計算をしたり、などといった場合に、CPUが命令の実行を抑止したことを通知するものです。MVS(MSP、VOS3)は、この割込みが発生すると、プログラム割込みルーチンによってエラー処理を行います。プログラム・エラーの原因は割込みコードによって通知されますが、これはS0CxABENDの理由を示します。0Cxのxと理由コード(プログラム割込みコード)は、ほとんどの場合同じですが、S0C4ABENDのように複数の理由で構成されるものもあります。例えば、同じS0C4ABENDでも、PSWキーと異なる領域にアクセスした記憶保護例外(理由コード4)と存在しないアドレスにアクセスした(理由コード10や11)では原因が異なります。
同じS0C4ABENDでも... PSW AT TIME OF ERROR 078D0000 80007F20 ILC 4 INTC 04 ~~~~ 割込みコードは4で記憶保護例外 ACTIVE LOAD MODULE ADDRESS=00007EB8 OFFSET=00000068 NAME=TEMPNAM0 DATA AT PSW 00007F1A - 00645010 20004130 00C84110 : PSW AT TIME OF ERROR 078D0000 80007F1C ILC 4 INTC 11 ~~~~ 割込みコードは11でページ変換例外 ACTIVE LOAD MODULE ADDRESS=00007EB8 OFFSET=00000064 DATA AT PSW 00007F16 - F0085820 C1405010 20004130 : どちらもS0C4だが、上は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に至るまで処理がどこまで行われていたのかをトレースする重要な手がかりにもなります。
SYSTEM COMPLETION CODE=0C4 REASON CODE=00000010 TIME=14.14.54 SEQ=00011 CPU=0000 ASID=0020 PSW AT TIME OF ERROR 078C0000 00EA0C76 ILC 4 INTC 10 NO ACTIVE MODULE FOUND NAME=UNKNOWN DATA AT PSW 00EA0C70 - 1B224111 0000BF27 10014770 GR 0: FD000008 1: 00500F78 2: 00000000 3: 00EA030A 4: 009D0E88 5: 009FDB90 6: 87500F78 7: 00F98B80 8: 00000000 9: 80FFF01C A: 009FDC50 B: 009D0E88 C: 07500F10 D: 87500F20 E: 90EA0350 F: 009D0E88 END OF SYMPTOM DUMP
ABEND時のレジスター内容は、徴候(SYMPTOM)ダンプに表示されます。あるいは、ダンプリストのレジスター表示部「REGS AT ENTRY TO ABEND」(MVSのダンプでは、「REGS AT ENTRY TO ABEND」または「GPR VALUES」と表示されている)に出力されています。しかしSVCルーチンの中でABENDしたような場合、徴候ダンプやダンプリストのレジスター表示部は、実際にABENDを起こした時点のレジスター内容なので、アプリケーション・プログラムがマクロ命令などを発行した後のOS側の処理で内容が変わってしまっています。PSWでABEND箇所を特定した時と同様に、ABEND時点ではなく最後に割込みを起こした時点のレジスター内容を求める必要が出てくることがあります。
マクロ命令発行によるSVC呼び出しの場合、アプリケーション・プログラムが最後に起こした割込みはSVC割込みとなり、SVC命令を実行した時点(最後に割込みを起こした時点)のレジスター内容は、PSWが保管されているPRBの次に表示されているSVRB内に退避されています。
PRB: 009EC9B8 -0020 XSB...... 7FFFEF08 FLAGS2... 00 RTPSW1... 00000000 …… -000C 00000000 FLAGS1... 00000000 WLIC..... 00020013 ~~~~~~~~ +0000 RSV...... 00000000 00000000 SZSTAB... 00110082 …… +0010 OPSW..... 078D0000 87500F86 SQE...... 00000000 …… ~~~~~~~~~~~~~~~~~~ SVRB: 009FDB90 -0020 XSB...... 7FFFC638 FLAGS2... 00 RTPSW1... 078C0000 …… -000C 00500000 FLAGS1... 00000000 WLIC..... 00040010 …… +0000 RSV...... 00000000 00000000 SZSTAB... 001ED022 …… +0010 OPSW..... 078C0000 00EA0C76 Q........ 00000000 …… +0020 GPR0-3... FD000008 87500F78 07500F9C 009D29D4 ~~~~~~~~ +0030 GPR4-7... 009D29B0 009EC818 009C1FE0 FD000000 +0040 GPR8-11.. 009EC600 009ECAD8 00000000 009EC818 +0050 GPR12-15. 07500F10 87500F20 80FC8308 00006F60 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もレジスター内容も徴候(SYMPTOM)ダンプに表示されているもので、アセンブル・リストとダンプリストのメモリー内容を照らし合わせればいいのですが、自分のプログラム外で(例えば発行したマクロの延長で呼ばれるSVCルーチンなど)のABENDであれば、単に徴候ダンプの内容だけではなく、ダンプリストのPRBとSVRBから自分のプログラムが最後に起こした割込み(SVCの発行)時点のPSWとレジスターを求めて、アセンブル・リストとダンプリストのメモリー内容を照らし合わせます。もちろん、SVCルーチンの処理エラーなどABENDコードのマニュアルで原因が判明できるのであれば、対応するプログラム・コードを見直して正しく修正すればいいのです。