Wednesday, February 18, 2026

Find all the services programs used by a program

Most of the time it is my curiosity, but there have been occasions, where I needed to know if a program calls a particular service program. It is easy to discover which service a programs call a service program, but what about the service programs that that those service programs calls? Now there is way to do that.

A new SQL Table function, PROGRAM_RESOLVED_ACTIVATIONS, has been added to IBM i as part of latest Technology Refreshes, IBM i 7.6 TR1 and IBM i 7.5 TR7.

PROGRAM_RESOLVED_ACTIVATIONS has five parameters:

  1. PROGRAM_LIBRARY:  Library that contains the object
  2. PROGRAM_NAME:  Name of the program or service program
  3. OBJECT_TYPE*PGM if the object is a program, *SRVPGM if it is a service program
  4. DEFERRED_SERVICE_PROGRAMSNO do not follow deferred activations, these are service programs that had *DEFER in the BNDSRVPGM parameter of the CRTPGM or CRTSRVPGM command. YES follow the deferred activations, this is the default
  5. IGNORE_ERRORSNO if an error is found an error happens. YES a warning is returned; this is the default

I have only used the first three.

I am going to need some programs for examples. The first is the simplest RPG I could write. I called it TESTRPG:

01  **free
02  *inlr = *on ;

Before this new Table function I used the BOUND_SRVPGM_INFO View to see which service programs are called by this program:

01  SELECT BOUND_SERVICE_PROGRAM_LIBRARY AS "SrvPgm lib",
02         BOUND_SERVICE_PROGRAM AS "SrvPgm name"
03    FROM QSYS2.BOUND_SRVPGM_INFO
04   WHERE PROGRAM_LIBRARY = 'MYLIB'
05     AND PROGRAM_NAME = 'TESTRPG'
06     AND OBJECT_TYPE = '*PGM'

This gives me the three service programs that are called by this program:

SrvPgm lib   SrvPgm name
----------   -----------
QSYS         QRNXIE
QSYS         QRNXUTIL
QSYS         QLEAWI

That is just the "first level". What service programs do those service programs call?

That is where PROGRAM_RESOLVED_ACTIVATIONS comes into its own:

01  SELECT * 
02    FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
03                 PROGRAM_LIBRARY => 'MYLIB', 
04                 PROGRAM_NAME    => 'TESTRPG', 
05                 OBJECT_TYPE     => '*PGM'))

Whoa, there are so many results, way too many to show here. Below are the first few columns and rows, and the last row.

                                         BOUND_
                                         SERVICE_    BOUND_
        PROGRAM_   PROGRAM     OBJECT    PROGRAM_    SERVICE_
LEVEL   LIBRARY    _NAME       _TYPE     LIBRARY     PROGRAM
-----   --------   ---------   --------  ---------   --------
    1   MYLIB      TESTRPG     *PGM      QSYS        QRNXIE
    1   MYLIB      TESTRPG     *PGM      QSYS        QRNXUTIL
    1   MYLIB      TESTRPG     *PGM      QSYS        QLEAWI
    2   QSYS       QRNXIE      *SRVPGM   QSYS        QRNXDUMP
    2   QSYS       QRNXIE      *SRVPGM   QSYS        QLECWI
    2   QSYS       QRNXIE      *SRVPGM   QSYS        QC2UTIL1
    2   QSYS       QRNXIE      *SRVPGM   QSYS        QLEAWI

   15   QSYS       QCLGASSP    *SRVPGM   QSYS        QLEAWI

The LEVEL column denotes how deep this service program was called. Service programs will be listed more than once if they are called by more than one service program.

How many calls are there to service programs that are called by this RPG program, or by the service programs it calls:

01  SELECT COUNT(*) 
02    FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
03                 PROGRAM_LIBRARY => 'MYLIB', 
04                 PROGRAM_NAME    => 'TESTRPG', 
05                 OBJECT_TYPE     => '*PGM'))

The number of calls:

00001
------
  3447

That is a lot of calls!

The next example program I wrote, includes a service program I created, I called the program TESTRPG1:

01  **free
02  ctl-opt bnddir('TESTBNDDIR') dftactgrp(*no) ;

04  dcl-pr Procedure1 char(20) ;
05  end-pr ;
                                             
06  dcl-pr Procedure2 char(20) ;
07  end-pr ;

08  dsply Procedure1() ;
09  dsply Procedure2() ;

10  *inlr = *on ;

Line 1: The binding directory TESTBNDDIR lists the service programs and modules to be included in this program.

Lines 4 – 7: Procedure definitions for the two procedures, in the service program, that I will be calling.

I can see what service program I added to the binding directory using the BINDING_DIRECTORY_INFO View:

01  SELECT ENTRY,ENTRY_LIBRARY,ENTRY_TYPE,ENTRY_ACTIVATION
02    FROM QSYS2.BINDING_DIRECTORY_INFO 
03   WHERE BINDING_DIRECTORY_LIBRARY = 'MYLIB'
04     AND BINDING_DIRECTORY = 'TESTBNDDIR'

Line 1: I am only selecting the columns that appear on the screen when I add binding directories entries.

There is only one result as only one service program was added to the binding directory:

             ENTRY_   ENTRY     ENTRY_
ENTRY        LIBRARY  _TYPE     ACTIVATION
----------   -------  -------   ----------
TESTSRVPGM   *LIBL    *SRVPGM   *IMMED

BOUND_SRVPGM_INFO shows the service programs directly called by TESTRPG1:

01  SELECT BOUND_SERVICE_PROGRAM_LIBRARY AS "SrvPgm lib",
02         BOUND_SERVICE_PROGRAM AS "SrvPgm name"
03    FROM QSYS2.BOUND_SRVPGM_INFO
04   WHERE PROGRAM_LIBRARY = 'MYLIB'
05     AND PROGRAM_NAME = 'MYLIB'
06     AND OBJECT_TYPE = '*PGM'

Which returns:

SrvPgm lib   SrvPgm name
----------   -----------
*LIBL        TESTSRVPGM
QSYS         QRNXIE
QSYS         QRNXUTIL
QSYS         QLEAWI

I see my service program, followed by the same service programs TESTRRPG called.

Rather than show a few of the service programs that are called, I am just going to show the count of those called:

SELECT COUNT(*) FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
                        PROGRAM_LIBRARY => 'MYLIB', 
                        PROGRAM_NAME    => 'TESTRPG1', 
                        OBJECT_TYPE     => '*PGM'))

Which returns the number:

00001
------
  3451

That number is different from the number for TESTRPG. I can use the following statement to return the rows in the results for TESTRPG1, that are not in TESTRPG.

01  SELECT * FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
02                        PROGRAM_LIBRARY => 'MYLIB', 
03                        PROGRAM_NAME    => 'TESTRPG1', 
04                        OBJECT_TYPE     => '*PGM'))
05  EXCEPT
06  SELECT * FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
07                        PROGRAM_LIBRARY => 'MYLIB', 
08                        PROGRAM_NAME    => 'TESTRPG', 
09                        OBJECT_TYPE     => '*PGM'))

The above joins the two statements for the programs into one statement with an EXCEPT. The Except will return all the rows in the results for TESTRPG1 that are not in the results for TESTRPG.

The results are:

                                          BOUND_
                                          SERVICE_    BOUND_
        PROGRAM_   PROGRAM      OBJECT    PROGRAM_    SERVICE_
LEVEL   LIBRARY    _NAME        _TYPE     LIBRARY     PROGRAM
-----   --------   ----------   --------  ---------   --------
    2   MYLIB      TESTSRVPGM   *SRVPGM   QSYS        QLEAWI
    2   MYLIB      TESTSRVPGM   *SRVPGM   QSYS        QRNXIE 
    1   MYLIB      TESTRPG1     *PGM      QSYS        QLEAWI
    1   MYLIB      TESTRPG1     *PGM      QSYS        QRNXUTIL
    2   MYLIB      TESTSRVPGM   *SRVPGM   QSYS        QRNXUTIL
    1   MYLIB      TESTRPG1     *PGM      *LIBL       TESTSRVPGM
    1   MYLIB      TESTRPG1     *PGM      QSYS        QRNXIE

The third, fourth, and last result are the equivalent of ones for TESTRPG. First, second, fifth, and sixth are the four additional results for TESTRPG1, which are due to the service program TESTSRVPGM.

If I just want a list of service programs, without having repeats, I can use the Select Distinct statement:

01  SELECT DISTINCT PROGRAM_LIBRARY, PROGRAM_NAME, OBJECT_TYPE
02    FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
03                 PROGRAM_LIBRARY => 'MYLIB', 
04                 PROGRAM_NAME    => 'TESTRPG', 
05                 OBJECT_TYPE     => '*PGM'))
06   ORDER BY PROGRAM_LIBRARY, PROGRAM_NAME, OBJECT_TYPE

Line 1: Select Distinct returns one result for each combination of program library, program name, and object type.

Line 6: I am sorting the results in the same order as the unique selection.

I am not going to show the results of this statement.

The Select Distinct reduces the number of results for TESTRPG from 3447 to 377, which is a easier number of results to analyze.

Having shown examples of discovering all the service programs called by a program, I can do the same for any service program:

01  SELECT *
02    FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 
03                 PROGRAM_LIBRARY => 'QSYS2', 
04                 PROGRAM_NAME    => 'JOB_NAME', 
05                 OBJECT_TYPE     => '*SRVPGM'))

Line 4: JOB_NAME is a SQL global variable. Global variables are service programs so I can use PROGRAM_RESOLVED_ACTIVATIONS to discover what other service programs they call.

 

You can learn more about the PROGRAM_RESOLVED_ACTIVATIONS Table function from the IBM website here.

 

This article was written for IBM i 7.6 TR1 and 7.5 TR7.

No comments:

Post a Comment

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.