Thursday, June 13, 2019

New subfields added to Program Status Data Structure

two new subfields added to the new program status data structure

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.

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:

  1. 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.
  2. 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.STSCDE = 00000.
PGM.SRCLINENBR = '00000000'
PGM.PARMS = 000.
PGM.EXCEPTION = '       '
PGM.RESERVED1 = '0000'
PGM.MSGWRKAREA = '                              '
  1 '                                                            '
 61 '                    '
PGM.LASTFILE1 = '          '
PGM.UNUSED1 = '      '
PGM.DTEENTERED = '06122019'
PGM.LASTFILE2 = '        '
PGM.LASTFILESTS = '                                   '
PGM.JOBNBR = 526346.
PGM.STRDTE = 061219.
PGM.PGMDTE = 061219.
PGM.PGMTIME = 032844.
PGM.INTERNALJOBID = ' ? ? ?" sM´Þmǵ?'


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.


  1. 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.

    1. For many years (possibly decades too), starting with RPG3, I used a file as you described.

      My 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.

  2. 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.

  3. The "Internal Job ID" is used for the Work Management APIs.

    The 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:

  4. 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.


To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.