PL/I に関する基本的なデバッグ・タスクの説明では、下記 の PL/I プログラムを参照します。
ここで述べた内容に関して詳しくは、以下のトピックを参照してください。
下記のプログラムは、デバッグ・タスクを示すために各種のトピックで取り上げられます。
このプログラムは、その入力を文字バッファーから読み取る単純な 計算プログラムです。整数を読み取ると、スタックに 入れます。演算子 (+ - * /) の 1 つが読み取られると、最上位の 2 つのエレメントがスタックから取り出され、それらのエレメントに演算が行われ、結果がスタックに 置かれます。演算子 = は、スタックの一番上のエレメントの値をバッファーに 書き出します。
PLICALC を実行する前に、次のコマンドを入力して SYSPRINT を端末に割り振る必要があります。
ALLOC FI(SYSPRINT) DA(*) REUSE
メインプログラム PLICALC
plicalc: proc options(main);
/*------------------------------------------------------------------*/
/* */
/* A simple calculator that does operations on integers that */
/* are pushed and popped on a stack */
/* */
/*------------------------------------------------------------------*/
dcl index builtin;
dcl length builtin;
dcl substr builtin;
/* */
dcl 1 stack,
2 stkptr fixed bin(15,0) init(0),
2 stknum(50) fixed bin(31,0);
dcl 1 bufin,
2 bufptr fixed bin(15,0) init(0),
2 bufchr char (100) varying;
dcl 1 tok char (100) varying;
dcl 1 tstop char(1) init ('s');
dcl 1 ndx fixed bin(15,0);
dcl num fixed bin(31,0);
dcl i fixed bin(31,0);
dcl push entry external;
dcl pop entry returns (fixed bin(31,0)) external;
dcl readtok entry returns (char (100) varying) external;
/*------------------------------------------------------------------*/
/* input action: */
/* 2 push 2 on stack */
/* 18 push 18 */
/* + pop 2, pop 18, add, push result (20) */
/* = output value on the top of the stack (20) */
/* 5 push 5 */
/* / pop 5, pop 20, divide, push result (4) */
/* = output value on the top of the stack (4) */
/*------------------------------------------------------------------*/
bufchr = '2 18 + = 5 / =';
do while (tok ^= tstop);
tok = readtok(bufin); /* get next 'token' */
select (tok);
when (tstop)
leave;
when ('+') do;
num = pop(stack);
call push(stack,num); /* CALC1 statement */
end;
when ('-') do;
num = pop(stack);
call push(stack,pop(stack)-num);
end;
when ('*')
call push(stack,pop(stack)*pop(stack));
when ('/') do;
num = pop(stack);
call push(stack,pop(stack)/num); /* CALC2 statement */
end;
when ('=') do;
num = pop(stack);
put list ('PLICALC: ', num) skip;
call push(stack,num);
end;
otherwise do;/* must be an integer */
num = atoi(tok);
call push(stack,num);
end;
end;
end;
return;
TOK 関数
atoi: procedure(tok) returns (fixed bin(31,0));
/*------------------------------------------------------------------*/
/* */
/* convert character string to number */
/* (note: string validated by readtok) */
/* */
/*------------------------------------------------------------------*/
dcl 1 tok char (100) varying;
dcl 1 num fixed bin (31,0);
dcl 1 j fixed bin(15,0);
num = 0;
do j = 1 to length(tok);
num = (10 * num) + (index('0123456789',substr(tok,j,1))-1);
end;
return (num);
end atoi;
end plicalc;
PUSH 関数
push: procedure(stack,num);
/*------------------------------------------------------------------*/
/* */
/* a simple push function for a stack of integers */
/* */
/*------------------------------------------------------------------*/
dcl 1 stack connected,
2 stkptr fixed bin(15,0),
2 stknum(50) fixed bin(31,0);
dcl num fixed bin(31,0);
stkptr = stkptr + 1;
stknum(stkptr) = num; /* PUSH1 statement */
return;
end push;
POP 関数
pop: procedure(stack) returns (fixed bin(31,0));
/*------------------------------------------------------------------*/
/* */
/* a simple pop function for a stack of integers */
/* */
/*------------------------------------------------------------------*/
dcl 1 stack connected,
2 stkptr fixed bin(15,0),
2 stknum(50) fixed bin(31,0);
stkptr = stkptr - 1;
return (stknum(stkptr+1));
end pop;
READTOK 関数
readtok: procedure(bufin) returns (char (100) varying);
/*------------------------------------------------------------------*/
/* */
/* a function to read input and tokenize it for a simple calculator */
/* */
/* action: get next input char, update index for next call */
/* return: next input char(s) */
/*------------------------------------------------------------------*/
dcl length builtin;
dcl substr builtin;
dcl verify builtin;
dcl 1 bufin connected,
2 bufptr fixed bin(15,0),
2 bufchr char (100) varying;
dcl 1 tok char (100) varying;
dcl 1 tstop char(1) init ('s');
dcl 1 j fixed bin(15,0);
/* start of processing */
if bufptr > length(bufchr) then do;
tok = tstop;
return ( tok );
end;
bufptr = bufptr + 1;
do while (substr(bufchr,bufptr,1) = ' ');
bufptr = bufptr + 1;
if bufptr > length(bufchr) then do;
tok = tstop;
return ( tok );
end;
end;
tok = substr(bufchr,bufptr,1); /* get ready to return single char */
select (tok);
when ('+','-','/','*','=')
bufptr = bufptr;
otherwise do; /* possibly an integer */
tok = '';
do j = bufptr to length(bufchr);
if verify(substr(bufchr,j,1),'0123456789') ^= 0 then
leave;
end;
if j > bufptr then do;
j = j - 1;
tok = substr(bufchr,bufptr,(j-bufptr+1));
bufptr = j;
end;
else
tok = tstop;
end;
end;
return (tok);
end readtok;
ここで述べた内容に関して詳しくは、以下のトピックを参照してください。
このトピックでは、AT CALL コマンドおよび AT ENTRY コマンドを使用して、ルーチンが呼び出される直前または直後に停止する方法を説明します。 これらのコマンドの説明には、例: デバッグを行うための PL/I プログラムの例 が使用されます。
AT CALL コマンドを使用するには、呼び出すプログラムを TEST コンパイラー・オプションを使用して コンパイルしておく必要があります。
READTOK が呼び出される直前に停止するには、次のコマンドを入力します。
AT CALL READTOK ;
AT ENTRY コマンドを使用するには、呼び出したプログラムを TEST コンパイラー・オプションを使用して コンパイルしておく必要があります。
READTOK が呼び出された直後に停止するには、次のコマンドを入力します。
AT ENTRY READTOK ;
TOK が呼び出された直後に、パラメーター tok が 2 の場合にのみ停止するには、以下のコマンドを入力します。
AT ENTRY TOK WHEN tok='2';
プログラム内に多くのブレークポイントを設定している場合、以下のコマンドを入力して、プログラムが停止した場所を Debug Tool によって特定します。
QUERY LOCATION
Debug Tool ログ・ウィンドウには、次の例と同様なものが表示されます。
QUERY LOCATION ; You are executing commands in the ENTRY READTOK breakpoint. The program is currently entering block READTOK.
単一の変数の内容をリストするには、ソース・ウィンドウ内の変数名の存在箇所にカーソルを移動させ、PF4(LIST) を押します。値はログ・ウィンドウに表示されます。これは、コマンド行に LIST TITLED variable を入力するのと同等です。例えば、PLICALC プログラムを CALC1 というラベルの付いた ステートメントまで実行するには、Debug Tool コマンド行に AT 22 ; GO ; と入力します。カーソルを NUM の上に移動させ、PF4 (LIST) を押します。これにより、ログ・ウィンドウに次のように表示されます。
LIST NUM ; NUM = 18
NUM の値を 22 に変更するには、NUM = 18 の行に NUM = 22 と上書きし、Enter を押してこれをコマンド行に置き、Enter を再度押してこのコマンドを出します。
ほとんどの PL/I の式をコマンド行に入力することができます。
次に、PF2 ( STEP) を押して PUSH の呼び出しに進み、 PUSH1 というラベルのついたステートメントまで進みます。変数 STKNUM の属性を表示するには、次の Debug Tool コマンドを入力します。
DESCRIBE ATTRIBUTES STKNUM;
結果はログ・ウィンドウに次のように表示されます。
ATTRIBUTES FOR STKNUM
ITS ADDRESS IS 0003944C AND ITS LENGTH IS 200
PUSH : STACK.STKNUM(50) FIXED BINARY(31,0) REAL PARAMETER
ITS ADDRESS IS 0003944C AND ITS LENGTH IS 4
次のコマンドにより、STACK により指されている構造体のメンバーの すべての値をリストすることができます。
LIST STACK;
ログ・ウィンドウに、次のような結果が表示されます。
LIST STACK ; STACK.STKPTR = 2 STACK.STKNUM(1) = 2 STACK.STKNUM(2) = 18 STACK.STKNUM(3) = 233864
·
·
·
STACK.STKNUM(50) = 121604
構造体メンバーの値を変更するには、次の例のように、割り当てをコマンドとして出すことにより可能です。
STKNUM(STKPTR) = 33;
ユーザー・プログラムの特定の部分が、最初の数千回は 正常に機能しますが、ある条件のもとでは失敗するということが よくあります。このような場合、GO を入力し続けねばならなくなるので、行ブレークポイントをただ設定することはしたくありません。
例えば、PLICALC で、(例外が起こる前に) 除数が 0 である 場合にのみ除算の選択で停止したいとします。この場合、次のようにブレークポイントをセットします。
AT 31 DO; IF NUM ^= 0 THEN GO; END;
行 31 は CALC2 というラベルの付いたステートメントです。このコマンドにより、Debug Tool は行 31 で停止します。NUM の値が 0 でない場合、プログラムは続行します。このコマンドにより、Debug Tool は NUM の値が 0 の場合にのみ、行 31 で停止するようになります。
サブルーチン PUSH への入り口にブレークポイントを設定したいとします。PUSH は TEST を使用してコンパイルされたが、その他のファイルはそうではありません。Debug Tool は、空のソース・ウィンドウで立ち上がります。コンパイル単位を表示するには、次のコマンドを入力します。
LIST NAMES CUS
LIST NAMES CUS コマンドは、Debug Tool が認識しているすべてのコンパイル単位のリストを表示します。PUSH が後からアプリケーションによりフェッチされる場合、このコンパイル単位は Debug Tool に既知でないことがあります。これが表示された場合、次のコマンドを入力します。
SET QUALIFY CU PUSH AT ENTRY PUSH; GO ;
これが表示されていない場合、次のように APPEARANCE ブレークポイントをセットします。
AT APPEARANCE PUSH ; GO ;
また、次のように、ブレークポイントを結合することもできます。
AT APPEARANCE PUSH AT ENTRY PUSH; GO;
この APPEARANCE ブレークポイントの唯一の目的は、PUSH コンパイル単位内の関数が最初に実行されるときに制御を得ることです。これが起こった時に、次のように、PUSH の入り口に ブレークポイントを設定できます。
AT ENTRY PUSH;
LIST STORAGE コマンドを使用して、変数のストレージを表示することができます。例えば、STACK の最初の 30 文字のストレージを表示するには、次のように入力します。
LIST STORAGE(STACK,30)
ここで述べた内容に関して詳しくは、以下のトピックを参照してください。
プログラミング・エラーの検討中に、どのようにしてこの状態になったのか、また特に、関数呼び出しのトレースバックが何なのかを知りたいことがよくあります。この情報を得るには、次のコマンドを出します。
LIST CALLS ;
例えば、次のコマンドを使用して PLICALC の例を実行すると、
AT ENTRY READTOK ; GO ; LIST CALLS ;
ログ・ウィンドウには、次のような情報が表示されます。
At ENTRY IN PL/I subroutine READTOK. From LINE 17.1 IN PL/I subroutine PLICALC.
これは、呼び出し元のトレースバック情報を示しています。
プログラムを変更せずに、入り口点と出口点を示しながらプログラムをトレースするには、コマンド・ファイルを使用するか、またはコマンドを個別に入力することによって、ステップ 2 で説明しているコマンドを入力します。コマンド・ファイルを使用するには、次のステップを実行します。
SET PROGRAMMING LANGUAGE PLI ; DCL LVLSTR CHARACTER (50); DCL LVL FIXED BINARY (15); LVL = 0; AT ENTRY * DO; LVLSTR = ' ' ; LVL = LVL + 1 ; LVLSTR = 'ENTERING >' || %BLOCK; LIST UNTITLED ( LVLSTR ) ; GO ; END; AT EXIT * DO; LVLSTR = 'EXITING < ' || %BLOCK; LIST UNTITLED ( LVLSTR ) ; LVL = LVL - 1 ; GO ; END;
USE DT.COMMANDS(PLICALL)
例えば、USE コマンドを入力した後に、次のプログラム・シーケンスを実行します。
*PROCESS MACRO,OPT(TIME);
*PROCESS S STMT TEST(ALL);
PLICALL: PROC OPTIONS (MAIN);
DCL PLIXOPT CHAR(60) VAR STATIC EXTERNAL
INIT('STACK(20K,20K),TEST');
CALL PLISUB;
PUT SKIP LIST('DONE WITH PLICALL');
PLISUB: PROC;
DCL PLISUB1 ENTRY ;
CALL PLISUB1;
PUT SKIP LIST('DONE WITH PLISUB ');
END PLISUB;
PLISUB1: PROC;
DCL PLISUB2 ENTRY ;
CALL PLISUB2;
PUT SKIP LIST('DONE WITH PLISUB1');
END PLISUB1;
PLISUB2: PROC;
PUT SKIP LIST('DONE WITH PLISUB2');
END PLISUB2;
END PLICALL; Debug Tool によって、ログ・ウィンドウに次のようなトレースが表示されます。
'ENTERING >PLICALL ' 'ENTERING >PLISUB ' 'ENTERING >PLISUB1 ' 'ENTERING >PLISUB2 ' 'EXITING < PLISUB2 ' 'EXITING < PLISUB1 ' 'EXITING < PLISUB ' 'EXITING < PLICALL '
プログラムの実行時に、あるストレージが不意にその値を変更し、いつ、どこでこのようなことが生じたかを発見したい場合があります。プログラムが、呼び出し元が想定している以上に変更を行う次のような例を考えてみます。
2 FIELD1(2) CHAR(8); 2 FIELD2 CHAR(8); CTR = 3; /* an invalid index value is set */ FIELD1(CTR) = 'TOO MUCH';
次のコマンドにより、FIELD2 のアドレスを見つけます。
DESCRIBE ATTRIBUTES FIELD2
その結果が X'00521D42' であったとします。このアドレスから始まる次の 8 バイトのストレージの値の変化を監視する ブレークポイントを設定するには、次のコマンドを出します。
AT CHANGE %STORAGE('00521D42'px,8)プログラムが実行されるとき、Debug Tool は、このストレージの値が変更されると停止します。
未定義のプログラムや関数を呼び出すと重大エラーになります。このような呼び出しが実行される直前に停止するには、次のブレークポイントをセットします。
AT CALL 0
Debug Tool がこのブレークポイントで停止した場合、GO BYPASS コマンドを入力して CALL をバイパスすることができます。これにより、条件を起こさずにデバッグ・セッションを続行することが できます。