Rational Developer for System z バージョン 7.6

フルスクリーン・モードでの PL/I プログラムのデバッグ

PL/I に関する基本的なデバッグ・タスクの説明では、下記 の PL/I プログラムを参照します。

例: デバッグを行うための 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;

ここで述べた内容に関して詳しくは、以下のトピックを参照してください。

ある特定の PL/I 関数が呼び出されるときの停止

このトピックでは、AT CALL コマンドおよび AT ENTRY コマンドを使用して、ルーチンが呼び出される直前または直後に停止する方法を説明します。 これらのコマンドの説明には、例: デバッグを行うための PL/I プログラムの例 が使用されます。

AT CALL コマンドを使用するには、呼び出すプログラムを TEST コンパイラー・オプションを使用して コンパイルしておく必要があります。

READTOK が呼び出される直前に停止するには、次のコマンドを入力します。

AT CALL READTOK ;

AT ENTRY コマンドを使用するには、呼び出したプログラムを TEST コンパイラー・オプションを使用して コンパイルしておく必要があります。

READTOK が呼び出された直後に停止するには、次のコマンドを入力します。

AT ENTRY READTOK ;

TOK が呼び出された直後に、パラメーター tok2 の場合にのみ停止するには、以下のコマンドを入力します。

AT ENTRY TOK WHEN tok='2';

PL/I プログラムが停止したステートメントの特定

プログラム内に多くのブレークポイントを設定している場合、以下のコマンドを入力して、プログラムが停止した場所を Debug Tool によって特定します。

QUERY LOCATION

Debug Tool ログ・ウィンドウには、次の例と同様なものが表示されます。

QUERY LOCATION ;
You are executing commands in the ENTRY READTOK breakpoint.
The program is currently entering block READTOK.

PL/I 変数の値の変更

単一の変数の内容をリストするには、ソース・ウィンドウ内の変数名の存在箇所にカーソルを移動させ、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;

条件が真である場合に限った PL/I 行で停止

ユーザー・プログラムの特定の部分が、最初の数千回は 正常に機能しますが、ある条件のもとでは失敗するということが よくあります。このような場合、GO を入力し続けねばならなくなるので、行ブレークポイントをただ設定することはしたくありません。

例: デバッグを行うための PL/I プログラムの例

例えば、PLICALC で、(例外が起こる前に) 除数が 0 である 場合にのみ除算の選択で停止したいとします。この場合、次のようにブレークポイントをセットします。

AT 31 DO; IF NUM ^= 0 THEN GO; END;

行 31 は  CALC2  というラベルの付いたステートメントです。このコマンドにより、Debug Tool は行 31 で停止します。NUM の値が 0 でない場合、プログラムは続行します。このコマンドにより、Debug Tool は NUM の値が 0 の場合にのみ、行 31 で停止するようになります。

一部のみが TEST を使用してコンパイルされている場合の PL/I のデバッグ

例: デバッグを行うための PL/I プログラムの例

サブルーチン PUSH への入り口にブレークポイントを設定したいとします。PUSHTEST を使用してコンパイルされたが、その他のファイルはそうではありません。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;

PL/I でのロー・ストレージの表示

LIST STORAGE コマンドを使用して、変数のストレージを表示することができます。例えば、STACK の最初の 30 文字のストレージを表示するには、次のように入力します。

LIST STORAGE(STACK,30)

ここで述べた内容に関して詳しくは、以下のトピックを参照してください。

PL/I 関数のトレースバックの実施

プログラミング・エラーの検討中に、どのようにしてこの状態になったのか、また特に、関数呼び出しのトレースバックが何なのかを知りたいことがよくあります。この情報を得るには、次のコマンドを出します。

LIST CALLS ;

例: デバッグを行うための PL/I プログラムの例

例えば、次のコマンドを使用して PLICALC の例を実行すると、

AT ENTRY READTOK ;
GO ;
LIST CALLS ;

ログ・ウィンドウには、次のような情報が表示されます。

At ENTRY IN PL/I subroutine READTOK.
From LINE 17.1 IN PL/I subroutine PLICALC.

これは、呼び出し元のトレースバック情報を示しています。

TEST を使用してコンパイルされた PL/I コードの実行時パスのトレース

プログラムを変更せずに、入り口点と出口点を示しながらプログラムをトレースするには、コマンド・ファイルを使用するか、またはコマンドを個別に入力することによって、ステップ 2 で説明しているコマンドを入力します。コマンド・ファイルを使用するには、次のステップを実行します。

  1. userid.DT.COMMANDS(PLICALL) のような名前で PDS メンバーを作成します。
  2. ファイルまたはデータ・セットを編集し、次の Debug Tool コマンドを追加します。
    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;
  3. Debug Tool を始動します。
  4. 次のコマンドを入力します。
    USE DT.COMMANDS(PLICALL)
  5. プログラム・シーケンスを実行します。Debug Tool によってログ・ウィンドウにトレースが表示されます。

例えば、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                                 '

PL/I における予期しないストレージ上書きのエラーの検出

プログラムの実行時に、あるストレージが不意にその値を変更し、いつ、どこでこのようなことが生じたかを発見したい場合があります。プログラムが、呼び出し元が想定している以上に変更を行う次のような例を考えてみます。

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 は、このストレージの値が変更されると停止します。

PL/I における未定義プログラムの呼び出し前の停止

未定義のプログラムや関数を呼び出すと重大エラーになります。このような呼び出しが実行される直前に停止するには、次のブレークポイントをセットします。

AT CALL 0

Debug Tool がこのブレークポイントで停止した場合、GO BYPASS コマンドを入力して CALL をバイパスすることができます。これにより、条件を起こさずにデバッグ・セッションを続行することが できます。


ご利用条件 | フィードバック

このインフォメーション・センターでは Eclipse テクノロジーが採用されています。(http://www.eclipse.org)