TSOには、TESTコマンドというMVSが標準で提供するコマンド・ベースの対話型デバッガーがあります(*1)。MVSでは、バッチ処理プログラムであってもそのままTSOで実行することができます。プログラムの実行中は端末からのキー入力ができませんが、TSOのTMPであってもイニシエーターであっても、プログラム側で特別な対応をすることなくバッチ処理プログラムのロード・モジュールをそのままTSOで実行できます。I/Oを行うプログラムの場合は、JCLのDDステートメントに定義するデータセットをDDステートメントの代わりにTSOのALLOCATEコマンドを使用して割り振ります。
なお、TESTコマンドはISPFのコマンド・シェルでは実行できないので一旦READYプロンプトに戻る必要があります(*2)。そのため、対話型でデバッグするには事前に対象プログラムのアセンブリー・リストを出力して手元に用意しておかないと効率的な作業が行えません。確認すべき点が予めわかっているのであれば、対話ではなくバッチ処理での実行も便利です。
*1 HLASM(高水準アセンブラー)のToolkit Featureには、IDF(Interactive Debug Facility)というフル・スクリーンによる対話型デバッガーもあり、z/OSでは標準的にインストールされIFAPRDxxメンバーで有効にできるが、有償オプションなので使用するには追加の契約が必要になる。(zD&TであればADCDに含まれる)
*2 REXXからであればISPF内からでもTESTコマンドを実行できる。但し、画面はフル・スクリーン・モードに切り替わってしまうのでPF2キーで画面を分割してPF9キーでSDSFパネルを見ながらのような作業はできない。
実行準備:アセンブルとバインド時にTESTオプションを追加する
TESTコマンドを使用すれば、わざわざダンプを取らなくてもプログラムの実行中にレジスターやデータ領域の内容を簡単なコマンドでディスプレイに表示させて確認することができます。その際、表示させたい命令やデータ領域をアドレスではなくプログラムのソース・コードに定義した名前で指定することもできますが、そのためにはロード・モジュールにSYMレコードと呼ばれるプログラム内に定義した領域などの名前とアドレスの対応表であるSymbolテーブルが格納されたデータ・レコードを出力しておく必要があります。このSYMレコードを作成するのがTESTオプションです。
SYMレコードはアセンブラーによってオブジェクト・モジュール内に書き出され、さらにそれがバインダーによってロード・モジュールに書き出されます。したがって、TESTオプションはアセンブラーとバインダーの両方に指定する必要があります。
1 2 3 4 5 |
//ASMACL EXEC ASMACL,COND.L=(8,LE,C), // PARM.C='ASA,TEST',PARM.L='MAP,LET,LIST,TEST' ~~~~ ~~~~ //C.SYSIN DD * : |
デバッグするプログラムをTESTコマンドによって実行する
プログラムがアクセスするデータセットの割り振り
TSOで対話型デバッグを行うのであれば、TESTコマンド実行前にALLOCATEコマンドによってデータセットを割り振ります。バッチ処理で実行するのであれば、JCLにDDステートメントを追加します。データセットをアクセスしないのであれば追加で割り振る必要はありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
【対話型】 ALLOC DD(OUTLIST) SYSOUT(A) ALLOC DD(INDATA) DSN(PS.DATA1) SHR 【バッチ】 //TSOBATCH EXEC PGM=IKJEFT01 //STEPLIB DD DISP=SHR,DSN=&SYSUID..LOAD //SYSTSPRT DD SYSOUT=* //OUTLIST DD SYSOUT=* //INDATA DD DISP=SHR,DSN=&SYSUID..PS.DATA1 //SYSTSIN DD * TEST *(PROG1) 'PARM STRING' : // // |
TESTコマンドの実行
最初のパラメーターで実行するプログラムのロード・モジュール・ライブラリーとモジュール・メンバー名を指定します。デフォルトDSNは userid.LOAD です。DSNを省略して(メンバー名)だけを括弧で括って指定した場合は userid.LOAD が暗黙のDSNです。DSNを指定する場合は完全修飾名で指定します。クォーテーション記号を省略した場合、プレフィックス userid、サフィックス LOADが付加されます。LOAD(member) と指定した場合、userid.LOAD.LOAD となってしまいます。
バッチ・セッションで実行する場合は *(member) と指定して、STEPLIB/JOBLIBライブラリーからmemberが探されるようにすればいいでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
【対話型】 TEST (PROG1) 'PARM STRING' ※ロード・モジュール・ライブラリーがDSN=userid.LOADの場合 TEST 'dsname(PROG1)' 'PARM STRING' ※ロード・モジュール・ライブラリーがDSN=userid.LOADではない場合 【バッチ】 //TSOBATCH EXEC PGM=IKJEFT01 //STEPLIB DD DISP=SHR,DSN=&SYSUID..LOAD //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * TEST *(PROG1) 'PARM STRING' : // // |
HLASM標準提供のアセンブル・プロシージャーASMACLでは、ロード・モジュールはDSN=&&GOSET(GO)という一時データセットに出力されます。その場合は、STEPLIBにDSN=&&GOSETを定義し、TESTコマンドのプログラム名にGOを指定します。
1 2 3 4 5 6 7 8 |
//TSOBATCH EXEC PGM=IKJEFT01 //STEPLIB DD DISP=(OLD,DELETE),DSN=&&GOSET //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * TEST *(GO) 'PARM STRING' : // // |
REXXでのTESTコマンドの実行
今日ではISPFを使わずにTSO端末を利用することはほぼありません。対話型でTESTコマンドを使う場合、TESTコマンドを実行するためにいちいちISPFを終わらせてREADYプロンプトに戻るのは面倒なので、下記のようなREXX execを作ってTESTコマンドを起動する方が便利です。データセットをアクセスするプログラムであれば、READYプロンプトから個々にALLOCATEなどのコマンドを実行する必要も無くなります。
REXXからであれば、ISPF内からでもTESTコマンドを実行できます。但し、画面はフル・スクリーン・モードに切り替わるのでPF2キーで画面を分割してPF9キーでSDSFパネルを見ながらのような作業はできません。円滑な作業の為には、予めアセンブリー・リストをプリントしておくかダウンロードしておくといいです。
1 2 3 4 5 6 7 8 9 |
/* REXX */ /* SAMPLE REXX EXEC: */ /* DO TSO TEST COMMAND. */ /* */ SAY "ENTER MODULE LIBRARY DSNAME(MEMBER)" PULL dsname SAY "ENTER PARM TEXT PASSING TO THE PROGRAM" PULL parmtext ADDRESS TSO "TEST "dsname" '"parmtext"'" |
汎用的なものなので、プログラム・モジュールの格納データセット名やメンバー名は実行時に入力するようにしてあります。決まった名前であれば適宜修正して下さい。また、データセットをアクセスするプログラムであれば、ALLOCATEコマンドなどを適宜追加して下さい。
ISPFコマンド・シェルもしくは任意のISPFパネルでのTSOコマンドによってTSOのEXコマンドを実行できます。以下に任意のパネルからの実行例を示します(REXX execの格納DSNはuserid.CLIST、メンバー名はDOTESTとします)。
TSO△EX△(DOTEST)
TSO△EX△'dsname(DOTEST)'
REXXによってTESTコマンドが起動されると、「ENTER MODULE LIBRARY DSNAME(MEMBER)」と「ENTER PARM TEXT PASSING TO THE PROGRAM」の2つのプロンプト・メッセージが出力されます。それぞれ、テストするプログラムの格納DSN(メンバー名)とプログラムに渡すパラメーター文字列を応答します。ロード・モジュールの格納先DSNがuserid.LOADであれば、(メンバー名)だけを入力できます。その他のDSNであれば’DSN(MEMBER)’のように全体を引用符で囲みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
ENTER MODULE LIBRARY DSNAME(MEMBER) (ASMLEARN) ENTER PARM TEXT PASSING TO THE PROGRAM ABCDEFGHIJKL TEST AT MAINPROC (L 0R:15R;L 1R%-2 XC L(64)) TEST GO IKJ57024I AT MAINPROC 0R 0000000C 1R 000C4F56 2R FFFFFFFF 3R FFFFFFFF 4R FFFFFFFF 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF 10R FFFFFFFF 11R FFFFFFFF 12R 000810C0 13R 00081184 14R 00B86B6C 15R 000C4F68 000C4F54. 000CC1C2 C3C4C5C6 C7C8C9D1 D2D30000 *..ABCDEFGHIJKL..* 00000000 00000000 00000000 00000000 00081184 *...............d* 000C4F74. 00B86B6C 000810C0 000C3C4C 000C4F50 *..,%.......<..|&* FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF *................* TEST END█ |
REXXからのプロンプトへの応答が終わると、TESTコマンドが起動されて指定したプログラム・モジュールが読み込まれ、プログラムの最初の命令の直前で停まります。「TEST」プロンプトに対して各種のTESTサブコマンドを入力してプログラムの実行(デバッグ作業)を進めて行きます。
主なサブ・コマンド
TESTコマンドには数多くのサブ・コマンドがありますが、覚えておくとよい基本的なものは数種類です。プログラムの実行を開始するGOとRUN、現在のPSW内容を表示するLISTPSW、レジスターや記憶域の内容を表示するLIST、それらの内容を変更する値割り当て機能、ブレーク・ポイントの設定と解除を行うATとOFFなどを使えば基本的なデバッグを行うことができます。
コマンド一覧
サブ・コマンド | 機能 | 備考 |
---|---|---|
RUN | プログラムの実行を開始して最後まで実行してTESTセッションを終了する。ブレーク・ポイントが設定されていても停止しない。 | |
END | TESTコマンド・セッションを終了する。(実行の打ち切り) | |
AT | ブレーク・ポイントを設定する。 | |
OFF | ブレーク・ポイントを解除する。 | |
GO | プログラムの実行を開始/再開する。 | ブレーク・ポイントがあればそこで停止する。 |
LIST | レジスターや記憶域の内容を表示する。 | |
COPY | レジスターや記憶域の内容を別のレジスターや記憶域にコピーする。 | |
LISTPSW | 現PSWの内容を編集して表示する。 | |
WHERE | 指定したアドレスやシンボルもしくはCSECTが、プログラム内のどの位置にあるかを表示する。 | プログラムのABEND時などに使用すれば、どの命令でエラーを起こしたかがわかる。 |
LISTMAP | テスト中プログラムの仮想記憶マップを表示する。 | REGION開始アドレスとサイズ、TCBアドレスとRBタイプ、モジュール名と長さ、ロード・アドレス、割当て済み記憶域ページのサブプール番号、開始アドレスとサイズ。 |
ATサブコマンド
1 2 3 4 |
AT address [COUNT(num)] [(subcmd)|(subcmd1;subcmd2;subcmd3 ...)] [TITLE('message text')] AT [module][.csect.]address AT addr1:addr2 AT (addr1 addr2 addr3 ...) |
addressはブレーク・ポイントを設定する命令アドレス。ラベル名、CSECT先頭からの変位等で指定できる。変位で指定する場合、+16進数(例:+6C、+86)のみを指定すればモジュールの先頭CSECT内の指定された変位の命令を示す。:記号で命令範囲を指定すればその間をステップ走行させることができる(GOコマンドを実行する毎に1命令ずつ実行される)。設定するブレーク・ポイントには有効な命令コードが含まれている必要がある。範囲指定する場合、指定範囲でマクロ命令が使われていると、そのマクロ命令内でパラメーター用データなどが展開されることがある。その場合、そのデータ部分以降にはブレーク・ポイントは設定されない(その旨のエラー・メッセージが出力される)。
COUNTは、その命令の実行が指定された回数に達するまでブレークしないことを示す。ループ内にブレーク・ポイントを設定するような場合に利用できる。COUNT(100)と指定した場合、ループ内で命令が100回実行される毎に停止する。
subcmdはブレークした際に自動実行させるサブコマンドの定義。(LISTPSW;L 0R:15R)と指定すれば、ブレーク時にPSWとGPR0~15の内容が自動表示される。
TITLEは、そのブレーク・ポイントで停止する際に端末に出力するメッセージ・テキストを指定するもの。省略時は、addressで指定した名前や値が示される。
1 2 3 4 5 6 7 |
AT SUBRTN1 (LIST 0R:1R) AT SUBRTN1-8 AT SUBRTN1+1C AT .MAINPROG.+FC CSECT名のみを指定する場合は前後に.ピリオドを付ける。 AT PROG2.MAINPROG.+1FA AT PROC1:ENDPROC (LISTPSW;LIST 0R:1R) AT (+6A +84 +EC) (LISTPSW;LIST 0R:15R;GO) |
ブレーク時に実行するサブコマンドの最後にGOを指定すると、subcmdで指定したサブコマンドを実行して停まらずに実行を再開する。実行中のレジスターや決まった記憶域内容の変化を実行を停めずに確認したい場合に利用できる。バッチTSOのTESTコマンドでステップ走行する場合は、ATサブコマンド内でGOサブコマンドを実行させればダイナミック・ステップ数に合わせたGOサブコマンドを並べる必要が無くなる。
設定したブレーク・ポイントを解除するにはOFFサブコマンドを使用する。ATサブコマンドで指定したブレーク・ポイントを指定したOFFサブコマンドを実行すればよい。
1 2 3 4 |
OFF address OFF [module][.csect.]address OFF addr1:addr2 OFF (addr1 addr2 addr3 ...) |
解除するブレーク・ポイントは必ずしもATサブコマンドの指定と合わせる必要はなく、順不同で指定してもいいし、1部のブレーク・ポイントだけを指定して解除してもいい。
LISTサブコマンド
1 2 3 4 |
LIST nR|nR:mR LIST abs-addr.|±offset|symbol|symbol±offset [type] [LENGTH(len)] [MULTIPLE(num)] LIST abs-addr.:abs-addr2.|±offset:±offset2|symbol:symbol2 [type] LIST symbol±offset:symbol2±offset [type] |
nRとnR:mRはレジスター内容の表示。nRで特定のレジスター番号を指定するかnR:mRで範囲指定のレジスター番号を指定する。記憶域は、絶対アドレス(末尾に.ピリオド記号を付ける)、±オフセット値(モジュールの先頭CSECT内の指定された変位)、シンボル名、シンボル名±オフセット値で表示開始アドレスを指定するか記憶域アドレスを:記号で繋げて範囲で指定する。記憶域データに応じた表示形式(type)も指定できる。
開始アドレスだけを指定した場合、表示する長さはL[ENGTH](長さ)で示すかM[ULTIPLE](個数)で、もしくはLENGTHとMULTIPLEを組み合わせて示す。MULTIPLEを併用すれば同じ型のデータを並べて表示できる。
表示形式
type | 最大長 | 表示形式 |
---|---|---|
C | 256 | 文字 |
X | 256 | 16進数 |
B | 256 | 2進数 |
H | 8 | ハーフワード整数 |
F | 8 | フルワード整数 |
P | 16 | パック10進数 |
Z | 16 | ゾーン10進数 |
E | 8 | 浮動小数点(単精度) |
D | 8 | 浮動小数点(倍精度) |
A | 4 | アドレス定数(フルワード) |
Y | 2 | アドレス定数(ハーフワード) |
S | 2 | 相対アドレス(基底+変位) |
I | 256 | 命令(逆アセンブル) |
XC | 256 | ダンプ(16進数+文字) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
LIST nR レジスターの内容を表示する(nにレジスター番号を指定する) L 1R レジスター1の内容を表示する L 0R:15R レジスター0~15の内容を表示する L 14R:12R レジスター14~15、0~12の内容を表示する LIST symbol 名前が示す領域の内容を表示する(symbolにプログラムで書いた名前を指定する) L FWORD データ領域FWORDの内容 L CBYTE+1 X L(1) データ領域CBYTE+1番地の1バイトの内容を16進数で表示 L MAINPROC-0A I M(3) ラベルMAINPROC-x0A番地からの3命令を逆アセンブルして表示 L LISTREC XC L(200) データ領域LISTRECから200バイトをダンプ形式で表示 L FWORD X L(4) M(3) データ領域FWORDから4バイトずつに区切って3ワード分を16進数で表示 L .MAINPROG 実行中モジュールのセクション(CSECT)MAINPROG L PROG2.MAINENTR モジュールPROG2のセクション(CSECT)MAINENTR LIST nR% レジスターnが示すアドレスの記憶域内容を表示する(間接アドレス指定) %と?記号は、そのシンボル(又はレジスター)の内容がアドレス(間接アドレス)であることを示す。 %は24ビット・アドレス、?は31ビット・アドレスを示す。 L 1R% XC L(32) GR1が示すアドレスから32バイト分ダンプ形式で表示する L 15R% F M(3) GR15が示すアドレスからのフルワード整数を3個分表示する L 3R?+1A X L(8) M(4) GR3が示すアドレス+1A番地からの8バイトを16進数で4個分表示する L ADDR1? X L(16) ADDR1領域が示すアドレスからの16バイトを16進数で表示する (間接アドレスはポインター・フィールド等の領域内アドレスでもよい) アドレス・ポインターがさらに別の領域を示すポインター・フィールドを指すような場合、 間接アドレス記号を複数個繋げて指定すれば1回のLISTサブコマンドで目的領域の内容を表示できる。 L 1R%% XC L(100) TESTコマンドで指定されたパラメーター文字列の長さと内容を表示する L 1R%% XC L(100) 0002CF54. 000BD7C1 D9D440E2 E3D9C9D5 C7000000 *..PARM STRING...* 00000000 長さ 文字列 00000000 00000000 00000000 00BABDBC *................* 0002CF74. 80FD8E28 8A330838 00000000 00BAAA80 *................* FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF *................* 0002CF94. FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF *................* FFFFFFFF FFFFFFFF FFFFFFFF 00000000 *................* 0002CFB4. 00000000 *.... * LIST +xxx 相対アドレス指定で領域の内容を表示する(xxxは基準点からの変位) L +B6 現在の基準点から+B6番地の内容を表示する L +1A0 I M(8) 現在の基準点+1A0番地からの命令を8個分表示する L +2E0 XC L(16) 現在の基準点から+2E0番地から16バイトの内容をダンプ形式で表示する Q address|module.csect 相対アドレス指定のための基準点(ベース・アドレス)の設定 (基準点の初期値はモジュール名.CSECT名になっている。基準点を変更したい場合に使用すればよい) Q TABLE1 基準点をTABLE1領域の先頭アドレスに変更する LIST xxxxxxxx. 絶対アドレス指定で領域の内容を表示する(アドレス値の直後にピリオド記号を付ける) L 2AF10. x2AF10番地の内容を16進数で表示する L 7520F68. XC L(72) x7520F68番地から72バイトの内容をダンプ形式で表示する |
WHEREサブコマンド
1 |
WHERE [abs-addr.|±offset|symbol|module name|.csect name] (省略時の暗黙値は現PSWの命令アドレス部) |
値の割当て
レジスターや記憶域の内容は、レジスターやアドレスを示すシンボルに値を割り当てれば変更することができる。レジスターや記憶域内容を変更すれば、「もし、ここの値が〇〇〇だったら動きはどう変わるのか?」といったテストが簡単に行える。
1 2 3 4 5 |
nR=type'value' (右辺はDC命令の書き方に合わせればよい) nR=(type1'value1',type2'value2',type3'value3'...) abs-addr.|±offset|symbol|symbol±offset=type'value' COPY org_addr targ_addr LENGTH(len) [POINTER|NOPOINTER] |
左辺にnRで特定のレジスター番号を指定するか、記憶域を絶対アドレス(末尾に.ピリオド記号を付ける)、±オフセット値(モジュールの先頭CSECT内の指定された変位)、シンボル名、シンボル名±オフセット値で指定し、右辺にデータ型と値を type文字’値’の形式で指定すれば値を割り当てられる。
複数のレジスターに値を割り当てる場合は、左辺に開始レジスター番号を指定し、右辺に開始レジスターから順に割り当てる型と値の組み合わせをカンマで区切って指定し右辺全体を括弧で括る。
値の代入ではなく、COPYコマンドを使用して別のレジスターや記憶域内の値をコピーすることもできる。COPYコマンドのorg_addrにコピー元のレジスターや記憶域を、targ_addrにコピー先のレジスターや記憶域を指定する。レジスターと記憶域の指定方法はLISTサブコマンドと同様である。コピーするデータの長さはLENGTHパラメーターで指定できる。POINTERオプションを付けると、org_addrにアドレスではなく即値を指定することができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
nR=X'xxxxxxxx' レジスターnの内容を変更する 1R=X'ABCDEF34' レジスター1の内容をxABCDEF34に変更する 10R=F'1234' レジスター10に整数1234(x000004D2)をロードする nR=(X'xxxxxxxx',X'xxxxxxxx') レジスターnからの複数レジスターの内容を変更する 2R=(X'ABCDEF34',F'1234') レジスター2をxABCDEF34に、レジスター3を整数1234(x000004D2)に変更する symbol=type'new value' FWORD=F'567890' FWORD領域の内容を567890(x0008AA52)に変更する DWORD=X'0123456789ABCDEF' DWORD領域の内容をx0123456789ABCDEFに変更する WORKAREA=C'ABCDEFGHIJKL' WORKAREA領域に文字ABCDEFGHIJKLを書き込む COPY org_addr targ_addr データをコピーする C FWORD 7R FWORDの内容をレジスター7にコピーする C 80. 15R L(1) POINTER レジスター15の先頭バイトにx80を読み込む C 0R DOUBLE L(8) レジスター0と1をDOUBLE領域に保管する |
TESTコマンドによるデバッグ例
ブレーク・ポイントで実行を停める
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
READY TEST (PROG1) TEST AT BRK1 ブレーク・ポイント設定 TEST GO 実行開始 IKJ57024I AT BRK1 TEST LIST ANSWER XC L(32) ANSWER 00000000 +0 00000000 00000000 00000000 00000000 *................* 00000000 00000000 00000000 00000000 *................* TEST GO 実行再開 IKJ57023I PROGRAM UNDER TEST HAS TERMINATED NORMALLY+ (プログラムの最後迄実行された) TEST █ |
最後のブレーク・ポイントでサブコマンドを使った後、再度GOまたはRUNコマンドを実行しないとプログラムの最後まで実行されません。ENDサブコマンドはTESTコマンドの終了なのでブレークした状態でプログラム実行が打ち切られます(バッチ処理でTESTコマンドを実行する際に最後まで実行させるGOやRUNを書かずにSYSTSINを//で終了させた場合も同様で、ブレークした状態でプログラム実行が打ち切られる)。デバッグなので実行を途中で打ち切っても(恐らく)実害は無いでしょうが、そのような動きをすることは覚えておきます。
ステップ走行を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
READY TEST (PROG1) TEST AT BEGIN:END (L 2R:9R;L WORKAREA X L(16)) TEST GO IKJ57024I AT BEGIN 2R 12290221 3R 12290321 4R FFFFFFFF 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000000 00000000 0002DE84 00003039 TEST GO IKJ57024I AT +4 FROM BEGIN 2R 00052DE0 3R 12290321 4R FFFFFFFF 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000000 00000000 0002DE84 00003039 TEST GO IKJ57024I AT +8 FROM BEGIN 2R 00052DE0 3R 12290321 4R 00052DE0 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000000 00000000 0002DE84 00003039 TEST GO IKJ57024I AT +E FROM BEGIN 2R 00052DE0 3R 12290321 4R 00052DE0 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000001 2345678C 0002DE84 00003039 TEST GO IKJ57024I AT +12 FROM BEGIN 2R 00052DE0 3R 00BC614E 4R 00052DE0 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000001 2345678C 0002DE84 00003039 TEST GO IKJ57024I AT +16 FROM BEGIN 2R 00052DE0 3R 00BC614E 4R 00052DE0 5R FFFFFFFF 6R FFFFFFFF 7R FFFFFFFF 8R FFFFFFFF 9R FFFFFFFF WORKAREA 00000000 +0 00000001 2345678C 0002DE84 00003039 TEST █ |
ATで指定した範囲の命令が、GOが入力される毎にレジスター2~9とWORKAREA領域の内容を表示しながら1命令ずつ実行されます。
プログラム実行中にABENDする
1 2 3 4 5 6 7 8 |
READY TEST (PROG1) TEST GO IKJ56641I PROG1 ENDED DUE TO ERROR IKJ56640I SYSTEM ABEND CODE 0C7 REASON CODE 00000000 TEST █ |
GOで実行させます(RUNではデバッグできないので、GOで実行します)。ABENDした状態で停止するので、その状態のままLISTPSWやWHEREでABENDした命令を確認して、LISTを使用して関連するレジスターや記憶域の内容を表示します。
【PSWがABENDした次の命令を指すケース】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
TEST W 2DE8A. LOCATED AT +D2 IN PROG1 .MAINPROG UNDER TCB LOCATED AT BCC400. TEST PSWアドレス近辺の命令を確認するためWHEREで示されたアドレス-16番地からの32バイト分をダンプする。 L 2DE8A.-10 LEN(32) XC 0002DE7A. 0A0B1830 5820C244 41002000 4F20C110 *......B.....|.A.* 00000000 ~~~~~~~~ エラーを起こした命令(PSWが示す1つ前の命令) 9130C11C 4780C108 5810C118 1F335820 *j.A...A...A.....* ^^^^^^^^ PSWが示すアドレス TEST L 2DE8A.-10 LEN(32) I 0002DE7A. SVC 11 00000000 0002DE7C. LR R3,R0 0002DE7E. L R2,580(,R12) 0002DE82. LA R0,0(,R2) 0002DE86. CVB R2,272(,R12) <--- エラーを起こした命令 0002DE8A. TM 284(R12),48 <--- PSWが示すアドレス 0002DE8E. BC 8,264(,R12) 0002DE92. L R1,280(,R12) 0002DE96. SLR R3,R3 0002DE98. L R2,272(,R12) TEST L 12R?+110 LEN(8) XC 0002DEC8. 00000000 00000000 *........ * 00000000 TEST █ |
WHEREでABENDした時のPSWが示す命令アドレスを表示させ、その16バイト前から表示させています(PSWは基本的にABENDした命令の次の命令を指す)。さらに同じ範囲を逆アセンブルして具体的な命令も表示させています(デバッグ時に具体的な命令を確認する場合は、アセンブリー・リストを見るか機械語コードを解読することになるが、TESTコマンドのLISTサブコマンドには逆アセンブルの機能もあるので必要に応じて利用することができる)。
ABENDした命令はCVB命令。第2オペランドが示す記憶域内容は、x0000000000000000。正しい10進数ではないことがわかります。
【PSWがABENDした命令を指すケース】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
TEST GO IKJ56641I PROG1 ENDED DUE TO ERROR IKJ56640I SYSTEM ABEND CODE 0C4 REASON CODE 00000011 TEST ABENDS0C4で理由(割込み)コードがx10かx11の時はPSWはABENDした命令を指す(次の命令ではない)。 W 2DE82. LOCATED AT +CA IN PROG1 .MAINPROG UNDER TCB LOCATED AT BA74B8. TEST LISTPSW IKJ57652I PSW LOCATED AT BA7B98 XRXXXTIE KEY XMWP AS CC PROGMASK EA BA INSTR ADDR 00000111 8 1101 00 00 0000 0 1 0002DE82 TEST L 2DEBA.-10 LEN(32) XC 0002DE72. 00180A2F 41100002 0A0B1830 5820C244 *..............B.* 00000000 58002000 4F20C110 9130C11C 4780C108 *....|.A.j.A...A.* ~~~~~~~~ TEST L 2DE82.-8 LEN(20) I 0002DE7A. SVC 11 00000000 0002DE7C. LR R3,R0 0002DE7E. L R2,580(,R12) 0002DE82. L R0,0(,R2) <--- PSWが示すアドレス&エラーを起こした命令 0002DE86. CVB R2,272(,R12) 0002DE8A. TM 284(R12),48 TEST L 2R 2R 00052DE0 GR2内のアドレスが誤っているので確認する。 TEST LISTMAP REGION SIZE 00BFA000 AT ADDRESS 00006000 REGION SIZE 75D00000 AT ADDRESS 0A300000 UNDER TCB AT 00BA74B8 PROGRAM NAME LENGTH LOCATION PROG1 00000248 0002DDB8 ACTIVE RBS: TYPE PROGRAM-ID PRB PROG1 SUBPOOL INFORMATION: NUMBER LOCATION LENGTH 0 0002C000 00001000 アドレス誤りの時は、LISTMAPで割当て済み記憶域のマップを出す。 78 0000F000 00001000 命令オペランドで指定されたGR2が示すアドレスはREGION内に無い。 78 00010000 00008000 誤ったアドレスをGR2にロードしてL命令を実行したのでABENDした。 78 0A308000 00001000 78 0A309000 00001000 78 0A30A000 00001000 78 0A30B000 00001000 78 0A365000 0000A000 78 0A36F000 00001000 IKJ57395I MAP COMPLETE TEST █ |
期待した実行結果にならない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
READY TEST (PROG1) TEST AT +D2 TEST GO IKJ57024I AT +D2 TEST LISTPSW IKJ57652I PSW LOCATED AT BA7B88 XRXXXTIE KEY XMWP AS CC PROGMASK EA BA INSTR ADDR 00000111 8 1101 00 01 0000 0 1 0002DE8A TEST L FLAG1 FLAG1 58 00000000 TEST L 2DE8A.-4 LEN(8) 0002DE86. 9130C11C 47E0C104 00000000 TEST L 2DE8A.-4 LEN(8) I 0002DE86. TM 284(R12),48 00000000 0002DE8A. BC 14,260(,R12) TEST █ |
ATでブレーク・ポイントを設定してGOで実行します。ブレークしたら、関連するレジスターや記憶域内容を表示して、プログラム・ロジックで想定している内容通りかを調べます。条件分岐命令の動きが想定通りでないような場合は、その条件分岐命令でブレークさせてLISTPSWでCC(条件コード)の現在値を確認します。
ループあるいはウェイト(ハングアップ、フリーズ)してしまう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
READY TEST (PROG1) TEST GO 実行後ループなどプログラムが終わらないことに気付いたらATTNキーを押す。 どのキーがATTNなのかわかならなければキーパッドを表示させればよい。 | ←(ATTNが受け付けられると縦棒のプロンプトが出る) TEST LISTPSW ATTNによってプログラムの実行は中断される。 IKJ57652I PSW LOCATED AT BA7B88 LISTPSWやWHEREで中断点のアドレスや位置を求め、 XRXXXTIE KEY XMWP AS CC PROGMASK EA BA INSTR ADDR レジスターや関連する記憶域の内容等から原因を。 00000111 8 1101 00 10 0000 0 1 0002DEA4 さぐって行く。 TEST W 2DEA4. LOCATED AT +DC IN PROG1 .MAINPROG UNDER TCB LOCATED AT BA74B8. TEST L 0R:15R 0R 10361877 1R 00003039 2R F600C5BC 3R 09FF3A45 4R 13FE7487 5R 1DFDAECB 6R 27FCE90F 7R 31FC2353 8R 3BFB5D97 9R FFFFFFFF 10R FFFFFFFF 11R FFFFFFFF 12R 8002DDC8 13R 0002CF68 14R 00BAAB6C 15R 00000000 TEST END ENDで実行を打ち切ることができる。(GOだとまたループしてしまう) READY █ |
TESTコマンド実行中に、ブレークを設定していない範囲でループしてしまった場合やPOSTされないWAITで停まってしまった場合、バッチ・ジョブと違って自分で自分をキャンセルすることはできません。その場合は、慌てずにATTNキーを押下します。アテンション割込みが受け付けられると、TESTコマンドはプロンプトを出して次のサブコマンド入力待ちになります。PSWやレジスターの内容などを表示してデバッグを進めます。あるいはENDサブコマンドで一旦処理を打ち切って、再度TESTコマンドを起動してから改めてブレーク・ポイントを設定し直します。
※STIMERのWAIT時間誤りなど、MVSのサービス・ルーチン側で停止しているような場合はATTN割込みは効きません。(ATTNは受け付けられるがTESTのプロンプトは表示されない)
アテンション割込みを行うには、SNA端末ではATTNキーを、非SNA端末ではRESETキーでキーボード・ロックを解除してからPA1キーを押します。今日では、TN3270接続ではSNA端末として設定されていることが多いです。ATTNキーは普段あまり使わないので、エミュレーターのキーボード設定を事前に確認しておくかキーパッドを表示させるといいでしょう。なお、むやみにATTNやPAnキーを連打すると無用なアテンションが何度も通知されTESTコマンド自体が終了してしまうことがあります。