
The latest Technology Refresh for IBM i 7.3, TR6, has seen two new subfields added to RPG's Program Data Structure. This data strucutre provides me with a wealth information about the status of the program while it is running, and when it errors.
I always add the Program Status Data Structure, PSDS, to all of my RPG programs. I can dump the program and learn a lot of what happened from the information contains within the PSDS.
Rather than manually entering the same data structure into every program, I have my PSDS in a member I just copy, or include, it into the source members of others.
Below is my definition for the PSDS. If you are creating your own you can give the data structure and subfields whatever names you like.
**free dcl-ds Pgm psds qualified ; Proc char(10) ; // Module or main procedure name StsCde zoned(5) ; // Status code PrvStsCde zoned(5) ; // Previous status SrcLineNbr char(8) ; // Source line number Routine char(8) ; // Name of the RPG routine Parms zoned(3) ; // Number of parms passed to program ExceptionType char(3) ; // Exception type ExceptionNbr char(4) ; // Exception number Exception char(7) samepos(ExceptionType) ; Reserved1 char(4) ; // Reserved MsgWrkArea char(30) ; // Message work area PgmLib char(10) ; // Program library ExceptionData char(80) ; // Retrieved exception data Rnx9001Exception char(4) ; // Id of exception that caused RNX9001 LastFile1 char(10) ; // Last file operation occurred on Unused1 char(6) ; // Unused DteEntered char(8) ; // Date entered system StrDteCentury zoned(2) ; // Century of job started date LastFile2 char(8) ; // Last file operation occurred on LastFileSts char(35) ; // Last file used status information JobName char(10) ; // Job name JobUser char(10) ; // Job user JobNbr zoned(6) ; // Job number StrDte zoned(6) ; // Job started date PgmDte zoned(6) ; // Date of program running PgmTime zoned(6) ; // Time of program running CompileDte char(6) ; // Date program was compiled CompileTime char(6) ; // Time program was compiled CompilerLevel char(4) ; // Level of compiler SrcFile char(10) ; // Source file name SrcLib char(10) ; // Source file library SrcMbr char(10) ; // Source member name ProcPgm char(10) ; // Program containing procedure ProcMod char(10) ; // Module containing procedure SrcLineNbrBin bindec(2) ; // Source line number as binary LastFileStsBin bindec(2) ; // Source id matching positions 228-235 User char(10) ; // Current user ExtErrCode int(10) ; // External error code IntoElements int(20) ; // Elements set by XML-INTO or DATA-INTO (7.3) InternalJobId char(16) ; // Internal job id (7.3 TR6) SysName char(8) ; // System name (7.3 TR6) end-ds ; |
Two things to note are:
- As the data structure has been written in totally free format RPG I have to start the source member with **FREE. If I don't do so when the member is copied into the other source member by the RPG compiler it copies the member starting in the seventh column of the source member.
- I have used the SAMEPOS keyword to combine the Exception Type and Exception Number subfields.
The two new subfields are the last two in the data structure. I have to admit I am unsure what the "Internal job id" is.
Just to give you a glimpse of the information I can see in the PSDS I wrote this program of just three lines:
01 **free 02 /include cpysrc,rpg_psds 03 *inlr = *on ; |
Lines 2: I have used the /INCLUDE compiler directive to copy the contents of the PSDS into this program.
After compiling the program I started debug and put added a break point on the last line of the program. I see the following in the PSDS:
EVAL pgm PGM.PROC = 'TESTPGM ' PGM.STSCDE = 00000. PGM.PRVSTSCDE = 00000. PGM.SRCLINENBR = '00000000' PGM.ROUTINE = '*DETC ' PGM.PARMS = 000. PGM.EXCEPTION = ' ' PGM.EXCEPTIONTYPE = ' ' PGM.EXCEPTIONNBR = ' ' PGM.RESERVED1 = '0000' PGM.MSGWRKAREA = ' ' PGM.PGMLIB = 'MYLIB ' PGM.EXCEPTIONDATA = ....5...10...15...20...25...30...35...40...45...50...55...60 1 ' ' 61 ' ' PGM.RNX9001EXCEPTION = ' ' PGM.LASTFILE1 = ' ' PGM.UNUSED1 = ' ' PGM.DTEENTERED = '06122019' PGM.STRDTECENTURY = 20. PGM.LASTFILE2 = ' ' PGM.LASTFILESTS = ' ' PGM.JOBNAME = 'QPADEV0001' PGM.JOBUSER = 'SIMON ' PGM.JOBNBR = 526346. PGM.STRDTE = 061219. PGM.PGMDTE = 061219. PGM.PGMTIME = 032844. PGM.COMPILEDTE = '061219' PGM.COMPILETIME = '032826' PGM.COMPILERLEVEL = '0001' PGM.SRCFILE = 'DEVSRC ' PGM.SRCLIB = 'MYLIB ' PGM.SRCMBR = 'TESTPGM ' PGM.PROCPGM = 'TESTPGM ' PGM.PROCMOD = 'TESTPGM ' PGM.SRCLINENBRBIN = 48. PGM.LASTFILESTSBIN = 48. PGM.USER = 'SIMON ' PGM.EXTERRCODE = 0 PGM.INTOELEMENTS = 0 PGM.INTERNALJOBID = ' ? ? ?" sM´Þmǵ?' PGM.SYSNAME = 'DEV730 ' |
As I work in a multiple partition world being able to retrieve the System Name is going to help me to be able to condition code depending upon which partition the program is running on, without retrieving the network name using a CL command.
You can learn more about the Program Status Data Structure from the IBM website here.
This article was written for IBM i 7.4 and 7.3 TR6.
Simon. Love your work. I learn a lot. What is your opinion on using an externally described PF for the SDS definition? I have been using the same one for decades.
ReplyDeleteFor many years (possibly decades too), starting with RPG3, I used a file as you described.
DeleteMy opinion is if you prefer to use a file that is fine. If you want to make a source member and /INCLUDE it into your source that is fine too.
Use whichever one you feel comfortable with.
You can mix and match **FREE source members with /COPY. If the main source is not **FREE, you can copy a source member with *FREE, and if you main source is **FREE, you can copy a source member without **FREE.
ReplyDeleteThe "Internal Job ID" is used for the Work Management APIs.
ReplyDeleteThe APIs can locate a job from either the name/user/number or the internal job ID, but the RFE that requested this said that they sometimes have duplicate jobs on their system, so the name/user/number isn't sufficient to locate the job. The documentation for the Work Management APIs also says that the internal job ID can locate the job faster than name/user/number.
Here's the RFE: http://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=103671
When using the Internal Job ID, it's important to remember that this ID is reassigned to all currently registered jobs during an IPL. That is, the Internal ID does not persist following an IPL, and any given job will have a different Internal ID after the IPL. To avoid constant lookups of a job's Internal ID, I store the current IPL version of it in my own table of jobs I care about, and I also store the current IPL date along with the Internal ID. As long as the IPL date of the stored Internal ID is the same as the start date/time of the SCPF job, I know I can trust the Internal ID. Otherwise, I know I have to do a fresh fetch of the job's Internal ID (using either a QSYS2 Table inquiry or an IBM i API call). Careful planning of when to check the IPL date can reduce unnecessary function or API calls, thus taking best advantage of the speed of a reliable Internal ID search.
ReplyDeletecreate table cartnels1.ds_psds (
ReplyDeleteProc char(10) not null default ' ', -- Module or main procedure
StsCde numeric(5) not null default 0, -- Status code
PrvStsCde numeric(5) not null default 0 , -- Previous status
SrcLineNbr char(8) not null default ' ', -- Source line number
"Routine" char(8) not null default ' ', -- Name of the RPG routine
Parms numeric(3) not null default 0, -- Number of parms passed t
ExceptionType char(3) not null default ' ', -- Exception type
ExceptionNbr char(4) not null default ' ', -- Exception number
-- Exception char(7) samepos(ExceptionType) ,
Reserved1 char(4)not null default ' ', -- Reserved
MsgWrkArea char(30)not null default ' ', -- Message work area
PgmLib char(10) not null default ' ', -- Program library
Rnx9001Exception char(4) not null default ' ', -- Id of exception that cau
LastFile1 char(10) not null default ' ', -- Last file operation occu
Unused1 char(6) not null default ' ', -- Unused
DteEntered char(8) not null default ' ', -- Date entered system
StrDteCentury numeric(2) , -- Century of job started d
LastFile2 char(8) not null default ' ', -- Last file operation occu
LastFileSts char(35) not null default ' ', -- Last file used status in
JobName char(10) not null default ' ', -- Job name
JobUser char(10) not null default ' ', -- Job user
JobNbr numeric(6) not null default 0 , -- Job number
StrDte numeric(6) not null default 0, -- Job started date
PgmDte numeric(6) not null default 0 , -- Date of program running
PgmTime numeric(6)not null default 0 , -- Time of program running
CompileDte char(6) not null default ' ', -- Date program was compile
CompileTime char(6) not null default ' ', -- Time program was compile
CompilerLevel char(4) not null default ' ', -- Level of compiler
SrcFile char(10) not null default ' ', -- Source file name
SrcLib char(10) not null default ' ', -- Source file library
SrcMbr char(10) not null default ' ', -- Source member name
ProcPgm char(10) not null default ' ', -- Program containing proce
ProcMod char(10) not null default ' ', -- Module containing proced
SrcLineNbrBin binary (2) not null , -- Source line number as bi
LastFileStsBin binary(2) not null , -- Source id matching posit
User char(10) not null default ' ', -- Current user
ExtErrCode numeric(10) not null default 0, -- External error code
IntoElements numeric(20) not null default 0 , -- Elements set by XML-INTO
SysName char(8) not null default ' ' -- System name (7.3 TR6)
)
rcdfmt rc_psds;
-- drop table cartnels1.ds_psds;