> Tech > Boîte à outils System iNews – Déboguer avec *Source

Boîte à outils System iNews – Déboguer avec *Source

Tech - Par Systeme iNEWS - Publié le 18 juin 2012
email

Toutes les réponses aux questions des administrateurs d'environnements IBM i.

Boîte à outils System iNews – Déboguer avec *Source

Au sommaire de cette édition :

Déboguer avec *SOURCE
JOBQ pendant un IPL
Utiliser SAV sur des objets verrouillés
Message Internal Server Error
Extraire des résultats avec QShell

 

Déboguer avec *SOURCE

Q. J’essaie de déboguer un programme CL en utilisant le mode de débogage *SOURCE qui, je crois, fait une rétroréférence au membre du fichier source pour afficher les instructions HLL originales pendant le débogage. Pour invoquer cela, je règle ainsi l’option Debugging View :

Debugging view . . . . . . . . . *SOURCE

Pour certains programmes CL, tout se passe bien : je peux voir les instructions du programme source lors de l’exécution avec STRDBG. Mais pour d’autres programmes CL, *SOURCE ne fonctionne pas. Cependant, si je spécifie *LIST au lieu de *SOURCE, je peux à nouveau voir le code source pendant le débogage. Quelle est la différence entre les options *SOURCE et *LIST ?

R. Scott Klement a expliqué les différences dans l’édition du 27 janvier 2005 de cette newsletter, qui est reproduite ici :

A partir de la V3 de l’OS/400, IBM a mis à niveau la commande Start Debug (STRDBG ) pour qu’elle soit aussi un débogueur interactif. Contrairement à ISDB, elle fonctionne avec tous les langages que l’on rencontre sur l’iSeries, y compris tous les langages ILE, les langages OPM (le original program model qui précédait ILE), et même Java. Qui plus est, elle peut travailler avec tous ces langages à la fois.

Supposons un programme OPM CL qui appelle un programme ILE. La procédure principale du programme ILE est écrite en RPG IV, mais elle appelle des modules supplémentaires écrits en C et COBOL. Avec la touche de fonction “step into”, F22, vous pouvez entrer directement dans chacun des programmes et modules au moment où ils sont appelés, en passant de l’un à l’autre de ces langages différents au fil de l’exécution du code. C’est une fonction très utile !

Afin de valider le débogage interactif de vos programmes, vous devez spécifier une vue de débogage au moment de la compilation. Pour cela, utilisez le paramètre DBGVIEW sur l’un des compilateurs ILE. La liste suivante explique les valeurs disponibles :

  • DBGVIEW(*STMT) = C’est le paramétrage par défaut, lequel n’inclut que les numéros d’instruction dans la vue. Avec cette option, vous ne pourrez pas déboguer le programme interactivement.
  • DBGVIEW(*SOURCE) = Cela permet au débogueur de visualiser le membre source du programme quand vous le déboguez. Le résultat obtenu est semblable à celui de ISDB. Cette option présente un inconvénient : en cas de changement du code source, les résultats risquent d’être incorrects. Par ailleurs, quand votre objet programme est déplacé dans un système de production, le membre source risque de ne pas être disponible comme vous le souhaiteriez. 
  • DBGVIEW(*LIST) = Une copie du listing de compilation se trouve imbriquée dans votre objet programme. Quand le débogueur visualise votre programme, il utilise cette liste de compilation au lieu de visualiser le membre source. De sorte que si le source est modifié ou indisponible, vous pourrez quand même déboguer le programme. C’est l’option que j’utilise et que je recommande.

Après avoir compilé votre programme avec la vue de débogage appropriée, vous pouvez lancer le débogueur en tapant la commande

===> STRDBG PGM(myProgram)
….

K2R400 et Scott Klement

JOBQ pendant un IPL


Q. Les jobs dans JOBQ qui sont en attente (held) seront-ils perdus pendant un IPL ? Les jobs seront-ils là dans le JOBQ en état d’attente après l’activation du système ?

R. Ce comportement dépend du paramètre CLRJOBQ de la commande Change IPL Attributes (CHGIPLA). La valeur par défaut de ce paramètre est *NO, auquel cas les jobs JOBQ sont conservés après un IPL pourvu qu’ils aient été en suspens au moment du démarrage de l’IPL. Si vous changez le paramètre en *YES, les jobs JOBQ en suspens seront libérés au prochain IPL, mais le système remet la valeur CLRJOBQ à *NO, selon la documentation de l’IBM i Information Center.

Satid Singkorapoom

Utiliser SAV sur des objets verrouillés

Q. partie de notre sauvegarde nocturne consiste à sauvegarder certains répertoires de l’IFS. Notre commande SAV se présente ainsi
 
SAV        DEV(‘/QSYS.LIB/TAP01.DEVD’) +                
              OBJ((‘/CREATE!FORM2/*’) (‘/HTTP/*’) +      
              (‘/WWW/*’) (‘/NETDATA/*’) (‘/INFOR/*’) +   
              (‘/ETC/*’) (‘/HOME/*’) (‘/CREATE!SEND/*’) +
              (‘/QIBM/USERDATA/*’)) SAVACT(*YES) +       
              ENDOPT(*LEAVE)

Malgré la spécification de SAVACT(*YES) pour forcer la sauvegarde des objets actifs, il arrive que le job « plante » sur cette commande SAV et que nous devions y mettre fin. Le job log présente un message d’erreur Object in use juste avant que nous ne terminions le job. Pouvons-nous ou ne pouvons-nous pas sauvegarder des objets qui sont en service ?

R. SAVACT *YES a encore besoin d’un verrou pour sauvegarder un objet ; donc, s’il est toujours verrouillé, il ne peut pas être sauvegardé. SAVACT *YES fait simplement que la commande SAV attende que le verrou d’un objet actif soit libéré ; à ce moment-là, elle verrouille l’objet, le sauvegarde puis le déverrouille. La plupart des objets passent constamment de l’état verrouillé à déverrouillé, et donc le processus SAV sera suspendu pendant un certain temps en espérant obtenir un verrou quand SAVACT *YES est utilisé. Sans SAVACT *YES, la commande SAV n’attend pas un verrou : dès qu’elle détecte qu’un objet est verrouillé, elle abandonne et passe à l’objet suivant. Cela est très fréquent avec des objets *USRSPC et d’autres objets IFS utilisés constamment. Il vaut mieux les écarter des sauvegardes quotidiennes et les confier aux sauvegardes système intégrales.

Chris Hird

Message Internal Server Error

Q. Alors que j’essayais de me connecter à ma console d’administration web  (http://myas400:2001) sous V6R1M0, j’ai reçu un message HTTP 500 Internal Server Error :
 
500 Internal Server Error

The server encountered an internal error or misconfiguration
and was unable to complete your request.

Please contact the server administrator, [no address given] and inform
them of the time the error occurred, and anything you might have done
that may have caused the error.

More information about this error may be available in the server error log.

L’instance QTMHHTTP ADMIN est active, avant et après la connexion, donc l’erreur “500” ne semble pas mettre le programme QTMHHTTP à genoux. Les jobs QLWISVR/ADMIN – ADMIN4, qui, selon les documents IBM, doivent être actifs, le sont normalement, aussi sans message d’erreur. J’ai l’IBM Developer Kit for Java (5761JV1 *BASE,6,7) installé avec l’état *COMPATIBLE. Qu’est-ce qui ne va pas ?

R. J’ai rencontré un problème semblable, mais avec la V5R4. Après avoir passé d’innombrables d’heures avec IBM à chercher la solution, il s’est avéré que je n’avais pas un nom d’hôte configuré pour correspondre à mon nom de système. Votre problème devrait être réglé en ajoutant le nom de votre serveur au fichier HOSTS (CFGTCP option 10).

Dan Devoe

Extraire des résultats avec QShell

Q. Je sais que je peux transférer le résultat de la commande DB2 de QShell dans une variable d’environnement.  Mais comment puis-je extraire cette variable d’un programme CL ? Par exemple :

ADDENVVAR ENVVAR(result) VALUE(‘ ‘)
QSH CMD(’result=db2 “select field from UserLib/UserFile +
 where RRN(UserFile) = 1″‘)

R. Tout simplement, vous ne pouvez pas. Les variables d’environnement fonctionnent bien pour envoyer des données d’un programme CL à une commande QShell que vous appelez. Mais cela ne marche pas dans l’autre sens. Heureusement, le même but peut être atteint par des zones de données, des files d’attente de données, ou des fichiers. Ou, mieux encore, servez-vous de mon utilitaire qui vous permet d’extraire vos résultats de requêtes directement dans le programme CL.

Pourquoi une variable d’environnement ne fonctionnera pas.

Dans UNIX, les variables d’environnement sont copiées dans chaque nouveau programme ajouté à la pile d’appel. Si un programme appelé change une variable d’environnement, il change sa propre copie. L’appelant ne voit jamais le changement.
On peut comparer cela à la manière dont le LDA fonctionne quand vous soumettez un job avec la commande SBMJOB. Vous pouvez définir le LDA, et il sera copié dans le job soumis. Mais si ce job soumis effectue un changement, il change sa propre copie. Le soumettant ne verra jamais la mise à jour. Les variables d’environnement fonctionnent de la même manière : elles sont copiées dans le nouveau job, mais les éventuels changements ne sont reflétés que dans ce nouveau job. Dans UNIX, chaque nouvelle entrée dans la pile d’appels se comporte comme un job entièrement nouveau : par conséquent, les variables sont copiées et l’appelant ne voit pas les changements éventuels.

Sur l’IBM i, QShell a été conçu pour fonctionner comme UNIX. De ce fait, les points suivants sont vrais :

  • QShell a ses propres variables d’environnement, séparées des variables CL que vous définissez avec ADDENVVAR/CHGENVVAR.
  • Quand vous exécutez STRQSH (ou QSH), elle copie les variables CL dans QShell. 
  • Quand QShell effectue un changement, celui-ci n’a lieu que dans la copie QShell. Votre programme CL ne peut pas le voir.

Approfondissons cela : quand un script QShell démarre, il copie toutes les variables d’environnement au niveau du job à partir de l’environnement CL. Il en fait une copie, puis travaille avec elle. Les éventuels changements effectués ne sont pas visibles par le programme CL (parce qu’il ne les recopie pas vers l’appelant).

De plus, chaque commande ou programme que QShell exécute obtient sa propre copie des variables. Les éventuels changements apportés à ces variables ne sont pas visibles par l’appelant, même si celui-ci est dans l’environnement QShell. Pourquoi ? Parce que c’est ainsi qu’UNIX travaille : il donne à chaque niveau d’appel sa propre copie des variables. Qshell  s’efforce de travailler autant que possible comme UNIX. Disons-le tout net, les variables d’environnement ne feront pas l’affaire dans ce cas.

Cependant, si vous vouliez extraire la valeur d’une variable d’environnement d’un programme CL (peut-être que votre appelant a défini une variable que vous voulez extraire), vous pourriez être intéressé par l’article à tinyurl.com/2aekfqa.

Utiliser une zone de données pour obtenir une valeur unique

Votre exemple renvoie la valeur d’un seul champ à partir d’un seul enregistrement—autrement dit, une valeur scalaire unique. (Ici, scalaire est le contraire de matrice : ce n’est qu’une valeur). Si c’est tout ce dont vous avez besoin, une zone de données pourrait bien faire l’affaire.

QShell fournit un utilitaire nommé datarea, capable de lire ou d’écrire dans des zones de données. Il prendra la première ligne des données fournies et les écrira dans la zone de données. Comme vous le savez probablement, CL peut lire des zones de données à l’aide de la commande RTVDTAARA, et il sera donc facile d’obtenir le résultat. Les zones de données nommées sont des objets disque dans une bibliothèque et par conséquent n’ont pas le même problème d’étendue que les variables d’environnement.

Votre exemple a extrait un champ nommé field. Pour notre exemple, supposons que c’est un champ alphanumérique de 25 caractères. Vous pouvez donc espérer pouvoir faire ce qui suit (cela ne marchera pas, mais qu’importe) :

PGM                                                      
    DCL VAR(&RESULT) TYPE(*CHAR) LEN(25)                 
                                                         
    DLTDTAARA DTAARA(result)                             
    MONMSG CPF2105                                       
                                                         
    CRTDTAARA DTAARA(result) TYPE(*CHAR) LEN(25)         
                                                         
    QSH CMD(’db2 “select field from userLib/userFile where +
             rrn(userFile)=1″ | datarea -lw result’)      
     
    /* ADDED THE FOLLOWING SO WE CAN SEE THE OUTPUT: */                                                   
    RTVDTAARA DTAARA(RESULT) RTNVAR(&RESULT)             
    SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGTYPE(*COMP)
              MSGDTA(&RESULT)                            
                                                         
ENDPGM                                                  

Cela résout le problème de la variable d’environnement : les données sont maintenant bien renvoyées au programme. Mais cela ne marchera pas parce que la commande db2 ne reconnaît pas la syntaxe LIB/OBJ que vous avez utilisée dans notre exemple ! Elle n’accepte que la syntaxe de nommage SQL, où se trouve LIB.OBJ. Pis encore, la commande db2 produit toujours des surplus inutiles comme des en-têtes, des lignes vierges, etc. et il faudra donc du code pour les éliminer. Essayez donc ceci :

PGM                                                              
   DCL VAR(&RESULT) TYPE(*CHAR) LEN(25)                          
                                                                 
   DLTDTAARA DTAARA(result)                                      
   MONMSG CPF2105                                                
                                                                 
   CRTDTAARA DTAARA(result) TYPE(*CHAR) LEN(50)                
                                                                 
   QSH CMD(’db2 “select ”XX”,field from userLib.userFile where +
            rrn(userLib.userFile)=1″ | grep XX | datarea -lw result’
     
    /* ADDED THE FOLLOWING SO WE CAN SEE THE OUTPUT: */                                                   
                                                                 
   RTVDTAARA DTAARA(RESULT (8 25)) RTNVAR(&RESULT)               
   SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGTYPE(*COMP) +       
             MSGDTA(&RESULT)                                     
                                                                 
endpgm                                                           

J’ai ajouté à la sortie une colonne supplémentaire qui contient toujours XX. Comme XX ne figure dans aucun des en-têtes et des autres messages imprimés par la commande db2, toute ligne qui contient XX est constituée de données réelles. J’utilise l’outil grep (qui fait aussi partie de QShell) pour filtrer la sortie de db2, afin qu’elle ne contienne que les lignes contenant XX. Ce faisant, j’élimine tous les en-têtes et les autres messages. La sortie est placée dans la zone de données.

Maintenant, les sept premiers caractères de ma zone de données contiendront XX suivi de cinq blancs. Il me faut donc enlever cela de mon résultat, mais c’est facile en spécifiant une position de départ sur la commande RTVDTAARA. Le code résultant n’est pas joli joli, mais il fonctionne !

Je vous avoue que l’outil db2 m’agace beaucoup. La suppression des en-têtes de colonne supplémentaire que je viens juste de montrer est l’un de mes griefs, mais pas le seul ! Il ne me permet pas d’utiliser la liste de bibliothèques, il ne signale pas bien les erreurs . . . Bon je préfère m’arrêter là.

J’ai donc écrit mon propre outil SQL pour Qshell, que vous pouvez vous procurer à tinyurl.com/mepv5g. Avec lui, je peux éliminer le code superflu qui me servait à pallier les faiblesses de db2.

Voici comment :

pgm                                                      
   DCL VAR(&RESULT) TYPE(*CHAR) LEN(25)                  
                                                         
   DLTDTAARA DTAARA(result)                              
   MONMSG CPF2105                                        
                                                         
   CRTDTAARA DTAARA(result) TYPE(*CHAR) LEN(25)          
                                                         
   QSH CMD(’sql -QqS “select field from userFile where + 
            rrn(userFile)=1″ | datarea -lw result’)       
     
    /* ADDED THE FOLLOWING SO WE CAN SEE THE OUTPUT: */                                                   
                                                         
   RTVDTAARA DTAARA(RESULT) RTNVAR(&RESULT)              
   SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGTYPE(*COMP) +
             MSGDTA(&RESULT)                             
                                                         
endpgm                                                   

Extraire des enregistrements multiples

Si vous voulez extraire plus d’un enregistrement de la sortie SQL, une zone de données n’y suffira pas. Cependant, QShell peut écrire dans d’autres objets outre les zones de données. Voici deux idées :

  • Employer l’utilitaire rfile de Qshell pour écrire les données dans un fichier.
  • Employer l’utilitaire dataq de Qshell pour écrire les données dans une file d’attente.

Le code correspondant à ces suggestions ressemblerait beaucoup à la solution zone de données ci-dessus, à cela près que l’outil rfile ou dataq remplacerait l’outil datarea. Et, bien entendu, il faudrait réécrire le programme CL chargé de lire le résultat.

Personnellement, je l’ai fait avec des files d’attente de données pendant plusieurs années. J’ai choisi cette méthode parce que CL traite bien mieux les files d’attente de données que les fichiers. Il est vrai que ce support des fichiers s’est amélioré au fil des ans, mais, à l’époque, nous étions limités à un seul fichier, lequel ne pouvait être lu qu’une fois. Les files d’attente données ne souffraient pas d’une telle restriction !

Mais je me suis également lassé des files d’attente de données : bien trop de code pour une chose aussi simple ! Aussi je n’en montrerai même pas un exemple dans cet article. Je préfère passer directement à la nouvelle solution, bien meilleure à mes yeux.

Quelques problèmes non résolus

Les exemples zone de données ci-dessus présentent un gros inconvénient que je n’ai pas mentionné jusqu’ici : la zone de données ne peut pas être créée dans QTEMP—elle doit l’être dans une bibliothèque « réelle ». Conséquence : il y aura conflit entre plusieurs jobs exécutant ce même programme CL.

De plus, même la simple solution zone de données semble comporter trop de code ! Si je vous montrais la solution file d’attente ou fichier, vous verriez qu’il y en a encore plus. Bref, ces solutions m’ont beaucoup déçu. En effet, nous semblions tout près d’avoir un bon outil pour exécuter des commandes QShell (pas simplement exécuter des instructions SQL, mais aussi zipper des fichiers, lire des répertoires IFS, exécuter des programmes PHP, etc.), et pourtant nous n’avons pas pu franchir le dernier obstacle : restituer les résultats à CL !

Quelle est la cause du problème ? Ce n’est pas UNIX lui-même, car il permet parfaitement de renvoyer des données via le flux de sortie standard d’un programme. Figurez-vous que le problème vient de la commande STRQSH (ou QSH qui est un alias) ! Cette commande est conçue pour prendre une commande, puis l’exécuter, sans permettre à votre programme d’interagir avec elle pendant qu’elle s’exécute ! Et sans vous donner aucun moyen (autre que d’écrire le résultat dans une sorte d’objet temporaire) pour obtenir la sortie de la commande QShell. C’est vraiment frustrant !

J’ai donc inventé le remplaçant de STRQSH. Avec lui, au lieu de balancer simplement une commande QShell à QShell et d’attendre qu’elle finisse, j’interagis avec elle. Je peux connecter mon programme CL à QShell et lire la sortie directement dans les variables d’un programme CL. En passant par un « pipe » ou tuyau.

PGM                                                        
                                                           
     DCL VAR(&RESULT) TYPE(*CHAR) LEN(25)                  
                                                           
     OPNPIPE CMD(’sql -QqS “select field from userFile +   
                             where rrn(userFile)=1″‘)       
     RCVPIPE RCD(&RESULT)                                  
     CLOPIPE                                               

    /* ADDED THE FOLLOWING SO WE CAN SEE THE OUTPUT: */     

     SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGTYPE(*COMP) +
               MSGDTA(&RESULT)                             
                                                           
ENDPGM                                                     

Certes, il ne s’agit plus d’un simple appel adressé à STRQSH, mais voyez combien il est plus facile de renvoyer le résultat. L’exécution de QShell prend maintenant trois lignes de code au lieu d’une, mais je n’ai plus besoin d’exécuter trois autres lignes de code pour extraire la réponse. Pour cet exemple simple, on peut noter deux avantages : le code est raccourci d’une ligne et tout est beaucoup plus simple du fait de l’élimination de l’objet disque temporaire, source potentielle de conflits.

Si vous devez obtenir des enregistrements multiples, plus besoin d’appeler une API de file d’attente complexe et d’utiliser des objets disque temporaires :

PGM                                                           
                                                              
     DCL VAR(&RESULT) TYPE(*CHAR) LEN(50)                     
     DCL VAR(&EOF)    TYPE(*LGL)  VALUE(‘0’)                  
                                                              
     OPNPIPE CMD(’sql -QqS “select field from userFile”‘)     
                                                              
     RCVPIPE RCD(&RESULT) EOF(&EOF)                           
     DOWHILE (&EOF *EQ ‘0’)                                   
        /* do something with the record, here… */
        RCVPIPE RCD(&RESULT) EOF(&EOF)                        
     ENDDO                                                    
                                                              
     CLOPIPE                                                  
ENDPGM  

Ces commandes OPNPIPE, RCVPIPE et CLOPIPE font partie d’un projet open-source que j’ai lancé sous le nom de UNIXCmd. Vous pouvez obtenir le code correspondant et lire mes articles précédents à son sujet (dont un qui explique comment l’utiliser à partir de CL) à www.scottklement.com/unixcmd.

Scott Klement

Téléchargez gratuitement cette ressource

Le Guide Atlassian Data Center

Le Guide Atlassian Data Center

Face aux nombre croissants d’utilisateurs qui accèdent à vos apps, le Guide Atlassian Data Center présente les meilleures pratiques et solution d'évolutivité, de sécurité et de conformité en vous appuyant sur les normes du secteur et les logiciels présents dans votre stack technique, ce Guide Ultime répond à vos principales préoccupations pour assurer la conformité et surveiller tous les problèmes de sécurité potentiels de vos environnements applicatifs d’entreprise.

Tech - Par Systeme iNEWS - Publié le 18 juin 2012