|
Code and concept by Bryan Meyers
Original text by Gary Guthrie and Bryan Meyers
It's no secret that the Integrated Language Environment helps us all write
more modular applications. Modules and service programs make it easy to
construct programs from several small, easily maintained, reusable chunks
of code. We can even create programs composed of several different languages.
Although development using modular chunks is easy, there is
a bit of trouble in paradise. You sometimes need to know which application
programs use particular modules and service programs. This is especially true
when you modify modules and service programs, because you must rebind them to
your application programs for the changes to take effect. In the past, there
has been no convenient way to determine the modules and service programs your
applications use. But now, you can have the AnzILEPgm
utility do the research and present the information to you.
Overview
Let's take a quick look at the utility's components.
Command AnzILEPgm (Figure 1) lets
you specify the library whose programs you want to cross-reference. You can
also specify one of three special values for the library-*ALLUSR to analyze all
user libraries, *CURLIB to analyze the job's current library, or *USRLIBL to
analyze the libraries in the user portion of the job's
library list. This command is so simple that it needs no further explanation.
SQL source member AnzCrtDb contains SQL statements
that create the cross-reference files AnzIlePgm uses.
This member is of type Text.
Figure 1. Command ANZILEPGM
/* ======================================================== */
/* Command: ANZILEPGM - Analyze ILE programs */
/* ======================================================== */
ANZILEPGM: CMD PROMPT('Analyze ILE programs' )
PARM KWD(LIB ) +
TYPE(*NAME) +
LEN(10) +
SPCVAL((*ALLUSR) +
(*CURLIB) +
(*USRLIBL)) +
MIN(1) +
PROMPT('Library to analyze')
ILE program AnzILEPgm consists of
two modules, CL module AnzILEPgm1 and RPG module AnzILEPgm2. AnzILEPgm1 serves
as the driver for the utility, and AnzILEPgm2 builds the cross-reference
database.
The Cross-Reference Database
To create its database, AnzILEPgm
uses the RUNSQLSTM (Run SQL Statement) command to execute the SQL statements in
source member AnzCrtDb (Figure 2). These statements
produce two cross-reference files- AnzILEMod, which
contains information about modules, and AnzILESvc,
which contains information about service programs. The CREATE TABLE statements
actually create the physical files, while the LABEL ON statements
add text descriptions for the fields.
Figure 2 - Creating the cross-reference database
CREATE TABLE QTEMP/ANZILEMOD (PGMNAME CHAR(10),
LIBNAME CHAR(10),
MODNAME CHAR(10),
MODLIB CHAR(10),
MODSRC CHAR(10),
MODSRCLIB CHAR(10),
MODSRCMBR CHAR(10),
MODATR CHAR(10),
MODCRT CHAR(13),
MODSRCUPD CHAR(13));
LABEL ON QTEMP/ANZILEMOD (PGMNAME IS 'Prog Name',
LIBNAME IS 'Libr Name',
MODNAME IS 'Module Name',
MODLIB IS 'Module Libr',
MODSRC IS 'Mod Src File',
MODSRCLIB IS 'Mod Src Libr',
MODSRCMBR IS 'Mod Src Mbr',
MODATR IS 'Mod Attrib',
MODCRT IS 'Mod Created',
MODSRCUPD IS 'Src Changed');
CREATE TABLE QTEMP/ANZILESVC (PGMNAME CHAR(10),
LIBNAME CHAR(10),
SVCNAME CHAR(10),
SVCLIB CHAR(10),
SVCSIG CHAR(16));
LABEL ON QTEMP/ANZILESVC (PGMNAME IS 'Program Name',
LIBNAME IS 'Library Name',
SVCNAME IS 'Srv Pgm Name',
SVCLIB IS 'Srv Pgm Libr',
SVCSIG IS 'Srv Pgm Signature');
Because AnzILEPGM creates the
files in library QTEMP, the information is no longer available after your job
ends. You could keep the information in permanent files, but we don't advise
it. The temporary files always reflect the state of your objects at the time
you analyze them, but permanent files run the risk of not being synchronized
with your objects should you make changes without refreshing the database.
After the utility loads these files, you can query their
contents however you'd like. You can simply print a report showing the
information, search for information about how a particular object is used, or
even use the information as input to a utility to compile programs.
The CL Driver
AnzILEPgm1 functions much like an OPM CL program that acts
as a driver for an OPM RPG program. That is, AnzILEPgm1 acts as the entry
point, setting overrides and other environmental conditions necessary for the
RPG program. But unlike an OPM environment, in which the CL driver and RPG
program are two separate programs, AnzILEPgm1 is a module bound with RPG module
AnzILEPgm2 to form a single program. AnzILEPgm1 (Figure 3) first executes the
DSPOBJD (Display Object Description) command to create a file containing a list
of the program and service program objects to be analyzed. This file serves as
input to module AnzIlePgm2.
Figure 3 - CL module AnzILEPgm1
/* ========================================================= */
/* Module: ANZILEPGM1 - Analyze ILE programs - Main driver */
/* ========================================================= */
PGM PARM( &Library )
DCL &Library *CHAR 10
DCL &MsgID *CHAR 7
DCL &MsgF *CHAR 10
DCL &MsgFLib *CHAR 10
DCL &Msgdta *CHAR 100
MONMSG (CPF0000 MCH0000) EXEC(GOTO Error)
DSPOBJD &Library/*ALL +
OBJTYPE(*PGM *SRVPGM) +
OUTPUT(*OUTFILE) +
OUTFILE(QTEMP/QADSPOBJ)
DLTF QTEMP/ANZILEMOD
MONMSG CPF0000
DLTF QTEMP/ANZILESVC
MONMSG CPF0000
RUNSQLSTM SRCFILE(ANZILEPGM) +
SRCMBR(ANZCRTDB)
OVRDBF QADSPOBJ QTEMP/QADSPOBJ
OVRDBF ANZILEMOD QTEMP/ANZILEMOD
OVRDBF ANZILESVC QTEMP/ANZILESVC
CALLPRC ANZILEPGM2
DLTOVR *ALL
RETURN
Error:
RCVMSG MSGTYPE(*LAST) +
MSGDTA(&MsgDta) +
MSGID(&MsgID) +
MSGF(&MsgF) +
MSGFLIB(&MsgFLib)
MONMSG (CPF0000 MCH0000)
SNDPGMMSG MSGID(&MsgID) +
MSGF(&MsgFLib/&MsgF) +
MSGDTA(&MsgDta) +
MSGTYPE(*ESCAPE)
MONMSG (CPF0000 MCH0000)
ENDPGM
Next, AnzILEPgm1 creates the cross-reference files by
issuing the RUNSQLSTM command for source member AnzCrtDB.
Finally, after issuing file overrides, the CL module invokes main procedure
AnzILEPgm2 with the CALLPRC (Call Procedure) command.
The Utility's Workhorse
RPG module AnzILEPgm2 (Figure 4) does the real work of
AnzILEPgm. This module uses several procedures and APIs to
gather the cross-reference information. The main procedure reads a record from
the file produced by the DSPOBJD command. When the object is a service program
or an ILE program, the procedure invokes procedure BldModLst
and BldSvcLst to build a list of modules and service
programs the object uses.
Figure 4 - RPG module AnzILEPgm2
// ===============================================================
// Module: ANZILEPGM2 - Analyze ILE programs - Build database
// ===============================================================
FQadspobj IPE E Disk
FAnzilemod O E Disk Prefix(Mod_)
F Rename(Anzilemod:Modrec)
FAnzilesvc O E Disk Prefix(Svc_)
F Rename(Anzilesvc:Svcrec)
// ------------------------------------------- Procedure prototypes
D Bldmodlst PR
D 20 Value
D 8 Value
D Bldsvclst PR
D 20 Value
D 8 Value
D Crtusrspc PR
D 20 Value
D Getlstinf PR
D 20 Value
D 10U 0
D 10U 0
D 10U 0
D Pgmisile PR N
D 20 Value
// ------------------------------------------------- API prototypes
D Qbnlpgmi PR Extpgm('QBNLPGMI')
D Apiusrspc 20 Const
D Apilstfmt 8 Const
D Qpgmname 20 Const
D Apierr 16
D Qbnlspgm PR Extpgm('QBNLSPGM')
D Apiusrspc 20 Const
D Apilstfmt 8 Const
D Qpgmname 20 Const
D Apierr 16
D Qclrpgmi PR Extpgm('QCLRPGMI')
D Pgmi0100 161
D Pgmilen 10U 0 Const
D Pgmifmt 8 Const
D Qpgmname 20 Const
D Apierr 16
D Quscrtus PR Extpgm('QUSCRTUS')
D Uspcname 20 Const
D Uspcextatr 10 Const
D Uspcsiz 10U 0 Const
D Uspcinzval 1 Const
D Uspcpubaut 10 Const
D Uspctext 50 Const
D Uspcrpl 10 Const
D Apierr 16
D Qgetlstmod PR Extpgm('QUSRTVUS')
D Apiusrspc 20 Const
D Apilstpos 10U 0 Const
D Apisizent 10U 0 Const
D Apirtnvar 508
D Qgetlstspg PR Extpgm('QUSRTVUS')
D Apiusrspc 20 Const
D Apilstpos 10U 0 Const
D Apisizent 10U 0 Const
D Apirtnvar 56
D Qgetlstinf PR Extpgm('QUSRTVUS')
D Apiusrspc 20 Const
D Apilstpos 10U 0 Const
D Apisizent 10U 0 Const
D Apirtnvar 16
// ----------------------------------------------- Global variables
D Modfmt E DS Extname(Anzilemod)
D Based(Modfmtptr)
D Prefix(Mod_)
D Svcfmt E DS Extname(Anzilesvc)
D Based(Svcfmtptr)
D Prefix(Svc_)
D Modfmtptr S * Inz(*NULL)
D Svcfmtptr S * Inz(*NULL)
// ---------------------------------------------------------------
// - Main procedure
// ---------------------------------------------------------------
/free
If Odobtp = '*SRVPGM' Or Pgmisile(Odobnm + Odlbnm);
Bldmodlst(Odobnm + Odlbnm:Odobtp);
Bldsvclst(Odobnm + Odlbnm:Odobtp);
Endif;
/end-free
// ---------------------------------------------------------------
// Procedure: BldModLst - Populate database with modules used
// ---------------------------------------------------------------
P Bldmodlst B
D PI
D Qpgmname 20 Value
D Objtype 8 Value
D Apiusrspc C 'PGML0100 QTEMP '
D Apierr S 16
D Apilstpos S 10U 0
D Apinbrent S 10U 0
D Apirtnvar S 508
D Apisizent S 10U 0
D X S 10U 0
/free
Crtusrspc(Apiusrspc); // Create User Space To Hold Module List
If Objtype = '*SRVPGM'; // Build Module List
Qbnlspgm(Apiusrspc:'SPGL0100':Qpgmname:Apierr);
Else;
Qbnlpgmi(Apiusrspc:'PGML0100':Qpgmname:Apierr);
Endif;
Getlstinf(Apiusrspc:Apilstpos:Apinbrent:Apisizent); // Get List Hdr
If Apinbrent < 1; // Process List
Return;
Endif;
Modfmtptr = %ADDR(Apirtnvar);
For X = 1 To Apinbrent;
Qgetlstmod(Apiusrspc:Apilstpos:Apisizent:Apirtnvar);
Write Modrec;
Apilstpos = Apilstpos + Apisizent;
Endfor;
Return;
/end-free
P Bldmodlst E
// ---------------------------------------------------------------
// Procedure: BldSvcLst - Populate database with service
// ---------------------------------------------------------------
P Bldsvclst B
D PI
D Qpgmname 20 Value
D Objtype 8 Value
D Apiusrspc C 'PGML0200 QTEMP '
D Apierr S 16
D Apilstfmt S 8
D Apilstpos S 10U 0
D Apinbrent S 10U 0
D Apirtnvar S 56
D Apisizent S 10U 0
D X S 10U 0
/free
Crtusrspc(Apiusrspc); // Create User Space For Service Program List
If Objtype = '*SRVPGM'; // Build Service Program List
Qbnlspgm(Apiusrspc:'SPGL0200':Qpgmname:Apierr);
Else;
Qbnlpgmi(Apiusrspc:'PGML0200':Qpgmname:Apierr);
Endif;
Getlstinf(Apiusrspc:Apilstpos:Apinbrent:Apisizent); // Get List Hdr
If Apinbrent < 1; // Process List
Return;
Endif;
Svcfmtptr = %ADDR(Apirtnvar);
For X = 1 To Apinbrent;
Qgetlstspg(Apiusrspc:Apilstpos:Apisizent:Apirtnvar);
If %SUBST(Apirtnvar:31:10) <> 'QSYS ';
Write Svcrec;
Endif;
Apilstpos = Apilstpos + Apisizent;
Endfor;
Return;
/end-free
P Bldsvclst E
// ---------------------------------------------------------------
// Procedure: CrtUsrSpc - Creates a user space
// ---------------------------------------------------------------
P Crtusrspc B
D PI
D Uspcname 20 Value
D Apierr S 16
/free
Quscrtus(Uspcname:
'ANZILEPGM':4096:X'00':'*ALL':*BLANKS:'*YES':Apierr);
Return;
/end-free
P Crtusrspc E
// ---------------------------------------------------------------
// Procedure: GetLstInf - Retrieves generic header list format
// ---------------------------------------------------------------
P Getlstinf B
D PI
D Uspcname 20 Value
D Lstpos 10U 0
D Lstnbrent 10U 0
D Lstsizent 10U 0
D Uspcrtnvar DS
D Uspclstpos 10U 0
D 10U 0
D Uspcnbrent 10U 0
D Uspcsizent 10U 0
/free
Qgetlstinf(Uspcname:125:16:Uspcrtnvar); // Get list position
Lstpos = Uspclstpos + 1;
Lstnbrent = Uspcnbrent;
Lstsizent = Uspcsizent;
Return;
/end-free
P Getlstinf E
// ---------------------------------------------------------------
// Procedure: PgmIsILE - Returns indicator for program type
// *ON = ILE program
// *OFF = Not ILE program
// ---------------------------------------------------------------
P Pgmisile B
D PI N
D Qpgmname 20 Value
D Apierr S 16
D Pgmi0100 DS
D Pgmipgmtyp 161 161
/free
Qclrpgmi(Pgmi0100:%SIZE(Pgmi0100):'PGMI0100':Qpgmname:Apierr);
Return (Pgmipgmtyp = 'B'); // *ON=Ile Pgm, *OFF=Not Ile Pgm
/end-free
P Pgmisile E
BldModLst populates the
cross-reference database with modules used by the object. The procedure uses
APIs QBNLPGMI to retrieve information for modules and QBNLSPGM to retrieve
information for service programs. Procedure BldSvcLst
populates the cross-reference database with service programs used by the
object. This procedure's workings are, for all practical purposes, identical to
those of BldModLst. Because these procedures are so
similar, we discuss only BldModLst.
BldModLst first creates a user space to contain the module information
retrieved by the APIs. Next, it retrieves the information by invoking the
appropriate API. The procedure then extracts (from the generic header portion
of the user space) the entry size and the number of entries in the module
list. Finally, the procedure extracts the information from the user space
and writes it to the cross-reference database.
Conclusion
The next time you modify a module or service program, you
can use AnzILEPgm to find all application programs that
reference it. AnzILEPgm makes the job of rebinding
modified modules and service programs to your applications easier, faster, and
more reliable.
Installing AnzILEPgm
You should create source physical file ANZILEPGM to
contain all source from the AnzILEPgm utility.
If you opt to use your own source file, be sure to change references to
source file AnzILEPgm in the utility and
installation process below.
RUNSQLSTM SRCFILE(YourLib/ANZILEPGM) + SRCMBR(ANZCRTDB)
DSPOBJD YourLib/*ALL +
OBJTYPE(*PGM *SRVPGM) +
OUTPUT(*OUTFILE) +
OUTFILE(QTEMP/QADSPOBJ)
CRTCMD CMD(YourLib/ANZILEPGM) + PGM(ANZILEPGM) + SRCFILE(YourLib/AnzILEPgm)
CRTCLMOD MODULE(ANZILEPGM1) + SRCFILE(YourLib/AnzILEPgm)
CRTRPGMOD MODULE(ANZILEPGM2) + SRCFILE(YourLib/AnzILEPGM)
CRTPGM PGM(ANZILEPGM) + MODULE(ANZILEPGM1 ANZILEPGM2) + ACTGRP(QILE)

|