× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.




I am calling the program from the Series i.
I am debugging it from there, because we a live 24/7

Yes, I understand that.

In technical terms:

The problem that you've described is a result of invalid/incorrect memory allocation. The program that allocates the memory for the parameters hasn't allocated enough. The program that's referencing the memory is trying to read beyond the end of the allocation, and is reading data from a different allocation. That's why one parameter appears to be inside another one, because the first one is trying to address beyond the limit of the parameter.

This is how variables work:

When a program starts up, it needs memory ("main storage") to store it's variables in. The compiler has generated code in your program that asks the operating system for memory. If you've declared a variable that's 30000 bytes long, the compiler says "hey operating system, I need 30000 bytes of main storage". The operating system says "sure, program! You can use 30000 bytes starting at address 12345."

The exception to this is input parameters. A program doesn't ask the operating system for memory for it's input parameters. Instead, it asks the prgoram that called it for memory for them. The program that called it provides the memory of the parameter that it passed -- thus the caller and the callee put their variables in the same spot in the computer's memory. This way, the caller can have data in that variable, and it'll be read by the callee. And the callee can change that data, and the caller will be able to see the new value when it regains control.

So that's how parameters work.  Here's a trivial example:

PGM
       DCL VAR(&PARM1) TYPE(*CHAR) LEN(10)

       CALL OTHERPGM PARM(&PARM1)
ENDPGM

When this CL program starts, it asks the operating system for 10 bytes of memory because &PARM1 is not an input parameter. The operating system might respond with (for example) "okay, you can have 10 bytes at address 1001". Now, anytime that the preceding CL program tries to access the &PARM1 variable, it'll reference the memory in positions 1001 - 1010 because that's where the operating system told it to put it's variable.

Indeed, a compiled program has no concept of variables. It's actually working directly with memory, because that's how computers think. Variables are just an abstraction to make it easier for human beings to work with memory.

Okay, so that CL program called OTHERPGM. OTHERPGM was an RPG program that's defined like this:

 C     *ENTRY        PLIST
 C                   PARM                    PARMA            10

When this RPG program runs, it does NOT ask the operating system for memory. Instead, it gets the address from the caller. Since the caller's variable was in position 1001, all the CL program really passed to the RPG was that number (1001). The RPG program put's it's PARMA variable in position 1001, and since it's also defined to be 10 long, it'll read positions 1001 - 1010 -- causing it to see the exact same data as the CL variable. In effect, the two variables "overlay" each other. Changes to one affect the other -- just like two overlaid variables in a data structure.

     D                 ds
     D  PARM1               1001   1010
     D  PARMA               1001   1010

Except, of course, that your computer's main storage is probably billions of bytes long, and has lots and lots and lots of allocations, not just these two. But hopefully you get the idea.

Now let's look at your example:

PGM        (&usr &tnbr &pro &bol &po &emsg &rcnt +
            &lst1 &lst2 &lst3 &lst4)

   DCL        &usr *CHAR 10
   DCL        &tnbr *CHAR 7
   DCL        &pro *CHAR 8
   DCL        &bol *CHAR 11
   DCL        &po  *CHAR 12
   DCL        &emsg *CHAR 80
   DCL        &rcnt *CHAR 3
   DCL        &lst1 *CHAR 30000
   DCL        &lst2 *CHAR 30000
   DCL        &lst3 *CHAR 30000
   DCL        &lst4 *CHAR 30000

   CALL    INROLLXML   (&usr &tnbr &pro &bol &po &emsg &rcnt +
                       &lst1 &lst2 &lst3 &lst4)
ENDPGM

Now this CL program, (unlike my previous example) does NOT ask the operatign system for memory. All of it's variables are INPUT PARAMETERS, and as such, it uses the memory passed into it from the caller.

The same is true for the RPG program (presumably INROLLXML). It never asks the operating system for memory because the variables are all input parameters:

C     *Entry        Plist
 * User Name (Passed In)
C                   Parm                    @@USR            10
 * Transaction Number (Passed In)
C                   Parm                    @@TNBR            7
 * Probill Number (Passed In)
C                   Parm                    @@PRO             8
 * BOL Number (Passed Out)
C                   Parm                    @@BOL            11
 * PO Number (Passed Out)
C                   Parm                    @@PO             12
 * Error Message (Passed Out)
C                   Parm                    @@EMSG           80
 * Number of HTML Rows (Returned to Calling Pgm)
C                   Parm                    @@RCNT            3
C                   Parm                    @@LST1        30000
C                   Parm                    @@LST2        30000
C                   Parm                    @@LST3        30000
C                   Parm                    @@LST4        30000

Your complaint is that the @@EMSG field is overlaying the @@RCNT field. That means that whichever program allocated the memory for the @@EMSG field didn't allocate the full 80 bytes. It's not the RPG program here, because @@EMSG is an INPUT PARAMETER, so the program doesn't ask for memory.

It's not the CL program that called it, since that CL program doesn't ask for memory for the &emsg variable. It gets the memory address from IT'S CALLER.

Now, I'm sure you're not to call the CL program from the command-line with the CALL command. After all, you've already been asked if that's what's happening, and been pointed to the FAQ which talks about this. But, since you won't tell me how you're calling it, let's use the command line as an example so you can see how the pieces fit together. For example, let's say you typed the following:

CALL THECLPGM PARM('RICHARD' '1234567' '87654321' 'ABCDEFGHIJK'
                   '210987654321' 'Error Message' 'CBA' 'ListVal1'
                   'ListVal2' 'ListVal3' 'ListVal4')

In this situation, you're not passing variables to the call command. You're just typing values as part of one big field (the command-line field) and it's going into the operating system into a program named QCMD that interprets and executes commands.

QCMD will scan through the command, and figure out where the program name is. It'll figure out where each parameter starts and stops in the command string. For each parameter, it'll ask the operating system for memory to store the value of each parameter so that it can pass the addresses to the CL program.

Since it's the command line, not a CL program, there are no variables. How does it know how much memory to ask the operating system for? It has no clue what this memory will be used for later, and it has nothing to tell it how big each string can eventually be! So how does it know?

It has a set of rules. For character strings, if the value passed from the user is shorter than 32 bytes, it always allocates 32 bytes, put's the value at the start of those 32 bytes, and sets the rest of the space to blanks. If the value passed by the user is higher than 32 bytes, it uses the exact size that the user passed in.

Looking at the way I called your program, none of the parameters were longer than 32 bytes. So it allocates 32 bytes for all of them. It asks the operating system for 11 allocations, each 32 bytes long. The operating system finds free memory (most of the time when working with allocations this small, it'll find spaces that are consecutive in memory, since the system will have blocks of memory larger than 32 bytes available.)

So the operating system provides addresses 1001, 1033, 1065, 1097, 1129, 1161, 1193, 1125, 1257, 1289, 1321... all 32 bytes apart, and all have 32 bytes reserved to them. (These aren't real addresses, they're just examples I made up -- a real one is actually a 128-bit, very large, number, but the same principle applies).

If you go back to my data structure analogy, the command line (which doesn't know the name of the various parameters) has request this memory:

      D                 ds
      D  CMDLINE1            1001   1032
      D  CMDLINE2            1033   1064
      D  CMDLINE3            1065   1096
      D  CMDLINE4            1097   1128
      D  CMDLINE5            1129   1160
      D  CMDLINE6            1161   1192
      D  CMDLINE7            1193   1224
      D  CMDLINE8            1225   1256
      D  CMDLINE9            1257   1288
      D  CMDLINE10           1289   1320
      D  CMDLINE11           1321   1352

Now it calls your CL program -- the one you posted. The CL program receives all of it's variables as input parameters. It doesn't ask the operating system for any memory. Instead, it asks it's called (the command line) for some memory addresses. It gets the ones I listed in my data structure, above.

However, the CL program does NOT define all of it's parameters as 32 chars like the command-line did. So even though they start at the same addresses, they address different bytes (sometimes less, sometimes more) within that range. Back to the data structure analogy, you have this:

Again, if you think of your computer's main storage as a data structure, and you understand that each CL variable uses the memory passed by the command line (it does not ask the operating system for memory or calculate it's own) but uses it's own internal definition for the data type and length, then you arrive at this data structure:

     D                 ds
     D  CMDLINE1            1001   1032
     D  &usr                1001   1010
     D  CMDLINE2            1033   1064
     D  &tnbr               1033   1039
     D  CMDLINE3            1065   1096
     D  &pro                1065   1072
     D  CMDLINE4            1097   1128
     D  &bol                1097   1107
     D  CMDLINE5            1129   1160
     D  &po                 1129   1140
     D  CMDLINE6            1161   1192
     D  &emsg               1161   1240
     D  CMDLINE7            1193   1224
     D  &rcnt               1193   1195
     D  CMDLINE8            1225   1256
     D  &lst1               1225  31224
     D  CMDLINE9            1257   1288
     D  &lst2               1257  31256
     D  CMDLINE10           1289   1320
     D  &lst3               1289  31288
     D  CMDLINE11           1321   1352
     D  &lst4               1321  31319

The first few parameters aren't a problem. But look at the &emsg and &rcnt ones that you said you were having problems with. Notice that &emsg goes from 1161-1240 (because the caller provided 1161 as the starting address, and it's 80 long). and &rcnt goes from 1193 to 1195 (again, because the caller passed 1193, and it's 3 long.) The problem is, 1193 and 1195 are WITHIN the range of 1161-1240, so now &rcnt appears to be WITHIN &emsg. This isn't &rcnt's fault, of course. &rcnt is doing nothing wrong -- but &emsg is reading more memory than the 32 bytes that were allocated to it. It's reading an extra 48 bytes which happen to contain &rcnt because the system happened to use the space after CMDLINE6 for &rcnt -- just by luck. (It doesn't HAVE to provide consecutive addresses, but it's very common.)

You'll also note similar problems with the &lst1-4 parameters. The system allocated 32 bytes for them, but they try to address 30000! That'll really cause problems if you try to use them, since the memory they overwrite won't be another variable, but'll be some other part of your program or will be unused memory, or might even be something used by the operating system, causing really dire consequences. (But only sometimes! Because the memory addresses can be different every time the OS allocates them! That means that you can test you program and all will be will, but it might cause some completely strange and unrelated result later, maybe even in production.)

Very very bad.

But the CL program doesn't actually USE these variables, so you don't even see the problem in the CL program itself. It just passes them on to the RPG program. The RPG program will not ask for new memory for it's variables, but once again, it'll just use the addresses passed into it. These addresses will be the same ones that QCMD allocagted way back at the start... (That's why I need to understand how your CL program is called -- since it didn't allocate the memory, whomever called it did.)

The RPG program does the same thing the CL program did. It has it's own data types and definitions, but uses the starting addresses passed in as parmaeters. So that makes our data structure analogy look like this:

     D                 ds
     D  CMDLINE1            1001   1032
     D  &usr                1001   1010
     D  @@USR               1001   1010
     D  CMDLINE2            1033   1064
     D  &tnbr               1033   1039
     D  @@TNBR              1033   1039
     D  CMDLINE3            1065   1096
     D  &pro                1065   1072
     D  @@PRO               1065   1072
     D  CMDLINE4            1097   1128
     D  &bol                1097   1107
     D  @@BOL               1097   1107
     D  CMDLINE5            1129   1160
     D  &po                 1129   1140
     D  @@PO                1129   1140
     D  CMDLINE6            1161   1192
     D  &emsg               1161   1220
     D  @@EMSG              1161   1220
     D  CMDLINE7            1193   1224
     D  &rcnt               1193   1195
     D  CMDLINE8            1225   1256
     D  &lst1               1225  31224
     D  @@LST               1225  31224
     D  CMDLINE9            1257   1288
     D  &lst2               1257  31256
     D  @@LST2              1257  31256
     D  CMDLINE10           1289   1320
     D  &lst3               1289  31288
     D  @@LST3              1289  31288
     D  CMDLINE11           1321   1352
     D  &lst4               1321  31319
     D  @@LST4              1321  31319

It doesn't really change anything. @@RCNT will appear to be within @@EMSG because CMDLINE6 wasn't large enough for hold @@EMSG. Same is true of @@LST1 - 4. The program that actually allocated the memory (in this example, QCMD) didn't allocate enough memory to store the strings, so they ended up extending beyond their allocations into other areas of memory.

How can you fix the problem? Simple. you have to FORCE IT to allocate enough memory for each parameter. The way you do that is by calling the program (either the CL or the RPG directly) from another program that has variables that are at LEAST as long as the largest definition that will share that memory.

One easy way is to copy your CL program to another member. Change the variables so that they're NOT input parms. Instead, set each value within the CL program using a CHGVAR command. That way, the CL program will take care of allocating the memory -- since they're not input parms, and when it calls the other CL program (or the RPG directly) it'll pass large enough blocks of memory that nothing will exceed the amount allocated.

Note that this is how parameters have always worked -- even back on the S/38! The fact that your original RPG/400 program worked was just blind luck. The system HAPPENED to (by chance) allocate the memory so that the extra memory that it addressed happened to be unused memory instead of the memory where &RCNT is stored. You just got lucky -- there were no guarantees that it would happen that way.


As an Amazon Associate we earn from qualifying purchases.

This thread ...

Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.