CL

136. CLPでのエラー・モニター解決

今回のテクニックの紹介は日常に潜む問題を解決してくれるテクニックであり
非常に有効なものとして公開するに至ったのでよく理解して欲しい。

さてCLPでエラーを最初にMONMSG CPF0000 でモニターしておくと
どのようなエラーが発生してもモニターすることができると
60. CLP でのエラー・モニター (1)」で解説した。

例えば

0009.00              MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))           
                      :
  (CLPでの実行)
                      :
                     RETURN

0016.00  ERROR:      RCVMSG     MSGTYPE(*LAST) RMV(*NO) MSG(&MSG)
                      :

という具合であり事実 MONMSG CPF0000 は有効に働いている。
これまでどのCLPでも最初に MONMSG CPF0000 を宣言するのは
非常に有効であるとして海外サイトでも半ば常識になっている。
一方では

124. デバッグするにはMONMSGははずして」で紹介したように
MONMSG CPF0000 ではエラーが発生したときにはエラー行がわからないので
デバッグ中やまだ開発中であるときは MONMSG を一時的に除去するように
解説した。
ところが実際は MONMSG CPF0000 を記述したものをエンド・ユーザーに
リリースするわけなのでそこで実際の運用中にエラーが発生すると
エラーはわかるが発生箇所がわからないという矛盾を生じてしまう。
そこでエラー・メッセージもエラー発生場所の特定もわかる処理が
必要になってくる。

(1) SENDER の情報をさぐる

  0026.00              DCL        VAR(&SENDER) TYPE(*CHAR) LEN(80)
   :
    0029.00              MONMSG     MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR))
      :
    
    0050.00  ERROR:      RCVMSG     MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) +  
    0051.00                           MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) +
    0052.00                           SENDER(&SENDER) MSGF(&MSGF) +            
    0053.00                           SNDMSGFLIB(&MSGFLIB)    

のように SENDER の CL 変数 として 80バイトまたは 720バイトの &SENDER という
文字変数を定義することができる。(SENDER は *SHORTの80バイトまたは *LONGを指定すると720バイトの
情報を取得することができる)
この SENDER という変数にはメッセージの送り手の情報が戻され
どのプログラムのどのような命令でエラーになったのかの情報が収められている。
もちろん発生場所の情報もある。
それではこの SENDER を解析すればようことになるのだがいろいろ調べてみると
発生場所としては MI(=Machine Interface)の場所はわかるものの
CLPのステートメント番号は入っていないことがわかった。
しかし ステートメントXXXXでエラーが発生しました、という情報は
よく目にするのでどこかにCLPのステートメント場所の情報はあるはずである。

(2) CPF9999 による解決

そこでIBM SWMA(=プログラム相談室)に相談して調べてもらったところ
意外な方法が判明した。
MONMSG CPF0000 ではなく MONMSG CPF9999 でモニターすると
エラーのステートメント番号が判明するということである。
なるほど試してみると確かに受信したCPF9999 の中にエラー行が
あってエラー行も報告されていることがわかった。
そこで MONMSG CPF9999 に変更したCLPを作ってみると次のようにすれば
エラー場所とエラー・メッセージの両方を取得できることがわかった。

[サンプルCLP: TESTSNDR ]

ソースはこちらから

0001.00              PGM                                                       
0002.00 /*-------------------------------------------------------------------*/
0003.00 /*   TESTSNDR   :   エラー場所を表示するテスト                       */
0004.00 /*                                                                   */
0005.00 /*   2021/03/28  作成                                                */
0006.00 /*-------------------------------------------------------------------*/
0007.00              DCL        VAR(&MSG) TYPE(*CHAR) LEN(132)                 
0008.00              DCL        VAR(&STMMSG) TYPE(*CHAR) LEN(132)              
0009.00              DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)                 
0010.00              DCL        VAR(&MSGF) TYPE(*CHAR) LEN(10)                 
0011.00              DCL        VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)              
0012.00              DCL        VAR(&MSGKEY) TYPE(*CHAR) LEN(4)                
0013.00              DCL        VAR(&MSGDTA) TYPE(*CHAR) LEN(132)              
0014.00              DCL        VAR(&ERRDTA) TYPE(*CHAR) LEN(132)              
0015.00              DCL        VAR(&TYPE) TYPE(*CHAR) LEN(1)                  
0016.00              DCL        VAR(&TOPGMQ) TYPE(*CHAR) LEN(10)               
0017.00              DCL        VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) +            
0018.00                           VALUE('*ESCAPE   ')                          
0019.00              DCL        VAR(&RCVTYPE) TYPE(*CHAR) LEN(10) +            
0020.00                           VALUE('*LAST')                               
0021.00              DCL        VAR(&APIERR) TYPE(*CHAR) LEN(116) +            
0022.00                           VALUE(X'000074') /* 2 進数  */               
0023.00              DCL        VAR(&ERR) TYPE(*CHAR) LEN(1)                   
0024.00              DCL        VAR(&NULL4) TYPE(*CHAR) LEN(4) +            
0025.00                           VALUE(X'00000000')                        
0026.00              DCL        VAR(&SNDPGM) TYPE(*CHAR) LEN(10)            
0027.00              DCL        VAR(&STMT) TYPE(*CHAR) LEN(4)               
0028.00              MONMSG     MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR))     
0029.00                                                                     
0030.00 /*( 環境の取得 )*/                                                  
0031.00              RTVJOBA    TYPE(&TYPE)                                 
0032.00              IF         COND(&TYPE *EQ '0') THEN(DO) /*  バッチ  */ 
0033.00              CHGVAR     VAR(&TOPGMQ) VALUE('*SYSOPR   ')            
0034.00              ENDDO      /*  バッチ  */                              
0035.00              ELSE       CMD(DO) /*  対話式  */                      
0036.00              CHGVAR     VAR(&TOPGMQ) VALUE('*TOPGMQ   ')            
0037.00              ENDDO      /*  対話式  */                              
0038.00                                                                     
0039.00              DLTF       FILE(QTEMP/TESTF)                           
0040.00              RETURN                                                 
0041.00                                                                     
0042.00  APIERR:                                                            
0043.00              CHGVAR     VAR(&MSGID) VALUE(%SST(&APIERR 9 7))        
0044.00              CHGVAR     VAR(&MSGDTA) VALUE(%SST(&APIERR 17 100))    
0045.00              CHGVAR     VAR(&MSGF) VALUE('QCPFMSG   ')              
0046.00              CHGVAR     VAR(&MSGFLIB) VALUE('QSYS      ')           
0047.00              GOTO       SNDMSG                                      
0048.00                                                                         
0049.00  ERROR:      RCVMSG     MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) +       
0050.00                           MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) +     
0051.00                           MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB)              
0052.00              IF         COND(&MSGID *EQ 'CPF9999') THEN(DO)             
0053.00              CHGVAR     VAR(&ERRDTA) VALUE(&MSGDTA)                     
0054.00              RCVMSG     MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) +        
0055.00                           MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) +     
0056.00                           MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB)              
0057.00              ENDDO                                                      
0058.00              CHGVAR     VAR(&STMMSG) VALUE(' プログラム ' *CAT +        
0059.00                           %SST(&ERRDTA 8 10) *TCAT +                    
0060.00                           ' のステートメント ' *CAT %SST(&ERRDTA +      
0061.00                           24 4) *CAT ' で次のエラーが発生しました。 ')  
0062.00              SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) +  
0063.00                           TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG)                
0064.00  SNDMSG:     IF         COND(&MSGID *EQ ' ') THEN(DO)                   
0065.00              SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) +     
0066.00                           TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE)             
0067.00              MONMSG     MSGID(CPF2400) EXEC(RETURN)                     
0068.00              ENDDO                                                      
0069.00              ELSE       CMD(DO)                                         
0070.00              SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +            
0071.00                           MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) +             
0072.00                           MSGTYPE(&MSGTYPE)        
0073.00              MONMSG     MSGID(CPF2400) EXEC(RETURN)
0074.00              ENDDO                                 
0075.00              ENDPGM  


                              

[コンパイル]

CRTCLPGM OBJLIB/TESTSNDR SRCFILE(MYSRCLIB/QCLSRC9 AUT(*ALL)

[解説]

最初のエラー・モニターを

0028.00              MONMSG     MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR))

として定義しておいて

0049.00  ERROR:      RCVMSG     MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) +       
0050.00                           MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) +     
0051.00                           MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB)

でエラー・メッセージを受信するがこれはほとんどが MSGID=CPF9999 が受信されることになる。
しかしここで欲しいのは &MSGDTA なので

0052.00              IF         COND(&MSGID *EQ 'CPF9999') THEN(DO)             
0053.00              CHGVAR     VAR(&ERRDTA) VALUE(&MSGDTA)                     
0054.00              RCVMSG     MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) +        
0055.00                           MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) +     
0056.00                           MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB)              
0057.00              ENDDO

として直前のメッセージを受信するがこれが本当のエラー・メッセージである。
先に受信した CPF9999のメッセージ・データ: &ERRDTA を使って

0058.00              CHGVAR     VAR(&STMMSG) VALUE(' プログラム ' *CAT +        
0059.00                           %SST(&ERRDTA 8 10) *TCAT +                    
0060.00                           ' のステートメント ' *CAT %SST(&ERRDTA +      
0061.00                           24 4) *CAT ' で次のエラーが発生しました。 ')  
0062.00              SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) +  
0063.00                           TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG)

としてから本当のエラーも次のようにして報告する。

0064.00  SNDMSG:     IF         COND(&MSGID *EQ ' ') THEN(DO)                   
0065.00              SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) +     
0066.00                           TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE)             
0067.00              MONMSG     MSGID(CPF2400) EXEC(RETURN)                     
0068.00              ENDDO                                                      
0069.00              ELSE       CMD(DO)                                         
0070.00              SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +            
0071.00                           MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) +             
0072.00                           MSGTYPE(&MSGTYPE)        
0073.00              MONMSG     MSGID(CPF2400) EXEC(RETURN)
0074.00              ENDDO    

このプログラムを実行してみると

                          コマンド入力
> call test.com/testsndr                                                
   プログラム TESTSNDR のステートメント 3900 で次のエラーが発生しました 
  QTEMP に,タイプ *FILE のオブジェクト TESTF が見つからない。 
         

というようにエラー箇所とエラー・メッセージが同時に報告されるようになった。
この方法でリリースしておけばエラーが発生したとしても
エラー・メッセージだけでなくエラー箇所も同時に報告されるようになった。
これは開発上大きな進歩である。
ぜひ活用して頂きたい。
次にこの方法を組み込んだCLPテンプレートを紹介していく予定である。