- 「メインフレーム・コンピューター」で遊ぼう - http://www.arteceed.net -

TSO/TESTコマンドによるデバッグ

TSOのTESTコマンドはMVSにおける標準デバッガーです。WindowsやUnixのビジュアル開発ツールのような多機能なデバッガーではありませんが、アセンブラー言語のプログラムのちょっとしたデバッグには結構役に立ちます。レジスターやメモリー領域の表示、ブレークポイントの設定など基本的なデバッグができます。
※TESTコマンドはISPFのコマンドシェル・パネル(オプション6)では実行できません。ネイティブなTSOコマンドプロンプトから実行します。


TESTコマンドを使ってプログラムを起動する

    TEST 'dsname(member)'
    TEST 'dsname(member)' 'parm string'
    

TESTコマンドで実行したいプログラムを、’格納されているロードモジュールライブラリー(メンバー名)’で指定します。起動後、プログラムは入口点(先頭の命令)でブレークした状態になり、TESTのサブコマンド入力待ちになります。


サブコマンド(プログラムを実行する)

    GO
    WHERE
    END
    

GOコマンドはブレークしている場所からプログラムの実行を行います。次のブレークポイントを見つけるか途中でABENDするとそこで停止します。最後の命令を実行するとプログラムは終了します。WHEREコマンドで現在どこでブレークしているかがわかります。ENDコマンドでTESTコマンドを終了します。
プログラムの中でWTOマクロを使っている場合、メッセージはコンソールだけでなくTSO端末にもエコーされます。


サブコマンド(レジスターやメモリー内容を表示する)

    LISTMAP
    LISTPSW
    
    LIST 1R
    LIST 0R:15R
    
    LIST 20F48. LENGTH(64) XC
    LIST member.csect LENGTH(128) XC
    LIST +20 LENGTH(64) XC
    LIST +40:+8A XC
    LIST LABEL1 LENGTH(128) XC
    LIST 20ED0.:20FF0. XC
    LIST LABEL1:LABEL2 XC
    
    
    LIST 1R% XC
    LIST 12R% LENGTH(128) XC
    

LISTMAPは仮想記憶の割り当て状態のサマリーを表示します。プログラム自身がGETMAINしていなくても、TSOおよびTESTコマンドが制御のために割り当てた領域も含まれます。LISTPSWは現在のPSW内容を編集して表示します。

LISTコマンドはレジスターやメモリー領域の内容を表示します。
1Rはレジスター1の内容を、0R:15Rは全てのレジスターの内容を表示します。メモリーの内容はアドレスまたはラベル名で表示できます。アドレスは16進数で指定し末尾に.(ピリオド)を付けます。LENGTHパラメーターで長さを指定できます。省略すると4バイトだけしか表示されません。XCは表示タイプで16進数(HEX)と文字(CHAR)の両方が表示されるダンプ形式です。LENGTHパラメーターに代えてアドレスとアドレスを:(コロン)で繋げば、そのアドレス範囲のメモリー内容を表示できます。メンバー名.CSECT名とすればプログラムの先頭から表示できます。+xxxxのようにプログラムの先頭からの相対アドレスで表示することもできます。この場合は末尾にピリオドは付けません。
ラベル名はソースプログラム内に書いたラベル名です。ラベル名を使う場合はアセンブルおよびバインド(リンクエディット)両方のパラメーターにTESTオプションを指定する必要があります。TESTオプションによってソースプログラム中のラベル名などのシンボル情報がオブジェクトとロードモジュールに出力されます。

アドレスは直接指定の他にレジスターを使った間接アドレスでも指定できます。12R%のようにRの後ろに%を置くと、そのレジスターの内容をアドレスとするメモリーの内容を表示します。


サブコマンド(レジスターやメモリー内容を変更する)

    2R=X'00123456'
    +20=X'8888'
    20EF4.=X'0123456789ABCDEF'
    

レジスターやメモリーの内容は表示するだけでなく、変更することもできます。もちろん変更できるのは自分のプログラムまたはGETMAINした領域です。割り当てられている領域はLISTMAPコマンドで確認できます。
nR=X’xxxxxxxx’とすれば、n番レジスターに値xxxxxxxxをロードします。+20=はプログラムの先頭からx20バイト離れたアドレス、20EF4.=はx20EF4番地のアドレスのメモリー内容を変更することになります。デバッグ中にレジスターやメモリーの内容を変えてプログラムの動きがどう変わるかを確認することができます。


ブレークポイントの設定

    AT ENDLOOP
    AT ENDLOOP (LISTPSW;LIST 0R:15R)
    AT +8C C(8)
    AT member.csect.+8C
    

ブレークポイントはATで設定できます。ブレークする場所をラベル名、プログラムの先頭からの相対アドレスで指定できます。Cパラメーターは通過回数カウンター値です。3と指定すれば3回目にそこを通過する時ブレークします。ここでカウンターがリセットされるのでループが続くと、さらに3回目の通過で再びブレークします。つまり3,6,9と3の倍数回目で止まります。2であれば偶数回数目、10であれば10,20,30回目になります。Cパラメーターを使うとループの中にブレークポイントを設定する時、いちいちプログラムが止まらなくなるのでデバッグ効率が上がります。なおブレークは設定できますが、1命令ずつ止めるステップ実行はできないので、必要に応じてブレークポイントを細かく設定します。
ブレークする箇所の後ろに、ブレークと同時に実行するコマンドを指定することもできます。2番目のサンプルではブレークしたら同時にその時点のPSWとレジスター0から15の内容を表示します。

TESTコマンドはブレークする箇所にSVC 97と言う命令(x0A61)を埋め込みます。そのため命令コードやオペランドそのものをデータとして処理しているようなプログラムは正しく動かなくなりますから注意が必要です。

     ----+----1----+----2----+----3----+----4----+----5----+----6----+----7--
              WAIT  ECB=ecbaddr,LONG=YES
    +         LA    0,1(0,0)                    COUNT OMITTED,1 USED
    +         LA    1,ecbaddr                         LOAD PARAMETER REG 1
    +        BCR   8,0                     GIVES AN INLINE '80'   
    +         ICM   0,8,*-1                     INSERT INTO HI BYTE
    +         SVC   1                           LINK TO WAIT ROUTINE
    

太字箇所が命令オペランドがデータとして参照されているものです。サンプルのようにWAITマクロを書いてアセンブルして見ればよりわかりやすいでしょう。