Determining Where a Fault Occurred

You can use the STACKHISTORY task attribute to determine the statement that was being executed and the procedures that had been entered when a process terminated abnormally. To understand the value returned by this attribute, you must have compiled the program that was being executed with one or both of the following compiler options set: LINEINFO and LIST.

Setting LINEINFO causes the STACKHISTORY value to include the sequence number for each of the relevant statements in the source program. Setting LIST causes the compiler to produce a printout of the source program that includes code addresses for each line. The code addresses are needed to interpret the STACKHISTORY value if LINEINFO was not set.

The following is an example of the source program printout for a program that was compiled with the LIST and LINEINFO options set. (The example has been condensed horizontally to fit on the page.)

BEGIN                                   00000200  000:0000:0
                                      BLOCK#1 IS SEGMENT 0003
                                   1    00000300  003:0000:1
                                        00000400  003:0000:1
REAL X, Y;                              00000500  003:0000:1
PROCEDURE ONE;                          00000600  003:0000:1
BEGIN                                   00000700  003:0000:1
  PROCEDURE TWO;                        00000800  003:0000:1
                                          ONE IS SEGMENT 0004
                                    2   00000900  004:0000:1
  BEGIN                                 00001000  004:0000:1
     Y:= X DIV 0;                       00001100  004:0000:1
  END;                              3   00001200  004:0001:2
                                    3   00001300  004:0001:3
  TWO;                                  00001400  004:0001:3
END;                                    00001500  004:0002:1
                               ONE(004) LENGTH IN WORDS IS 0005
                                    2   00001600  003:0000:1
                                        00001700  003:0000:1
ONE;                                    00001800  003:0000:1
                                        00001900  003:0000:5
END.                                 00002000  003:0000:5
                           BLOCK#1(003) LENGTH IN WORDS IS 0006

In this example, each line of source code ends with the sequence number and code address of the line. The code address is divided into three parts by colons; the first part is the code segment number, the second is the word number, and the third is the syllable number. The numbers in the code address are in hexadecimal format.

If a process terminates normally, the STACKHISTORY value is a null string. However, if the process terminates abnormally, STACKHISTORY returns a value such as the following:

004:0000:5 (00001100), 004:0002:1 (00001400), 003:0000:5 (00001800).

This value gives the code address and sequence number for the statement that was being executed when the process terminated and for each procedure invocation statement that was in effect when the process terminated. Thus, the value in this example indicates that the statement at line 1100 was being executed when the process terminated, and that procedure invocation statements at lines 1400 and 1800 were in effect. The sequence numbers are included in the STACKHISTORY value because the program was compiled with LINEINFO set.

If LINEINFO is not set, then STACKHISTORY returns the following value:

004:0000:5, 004:0002:1, 003:0000:5.

The numbers in this example give a somewhat less exact idea of the locations of the statements that were being executed when the process terminated. Each statement usually occurs on the line preceding the specified code address. The address 004:0000:5 does not appear in the printout, but the statement occurs on the next lower-numbered line: 004:0000:1.

If the Boolean task attribute FULLSTACKHISTORY is set for the faulting task, then a segment dictionary stack number and associated code file name is contained in the STACKHISTORY data. For example, if the segment dictionary number in the above example was 053A and the code file was an unsaved program code file run in a CANDE session, then the above RCW/LINEINFO STACKHISTORY is modified to show the segment dictionary stack number for each RCW/LINEINFO pair listed and the code file name for each unique segment dictionary stack number; that is, the above example’s STACKHISTORY becomes

053A @ 004:0000:5 (00001100), 053A @ 004:0002:1 (00001400),
053A @ 003:0000:5 (00001800), 053A (USRCODE)CANDE/CODE160.

Alternatively, when LINEINFO is not present, then the STACKHISTORY is

053A @ 004:0000:5, 053A @ 004:0002:1, 053A @ 003:0000:5,
053A (USRCODE)CANDE/CODE160.

Note that if the object code file was produced by the Binder, you must use some extra care in interpreting the code addresses or sequence numbers returned in the STACKHISTORY value. When the Binder produces a bound object code file, the Binder changes the code segment numbers for statements in the subprogram. Fortunately, if you use the Binder option LIST, the Binder produces a printout that lists such changes. The following is an example of such a printout:

              O B J E C T / A L G O L / B I N D   O N   D I S K
              = = = = = = = = = = = = = = = = = = = = = = = = =

HOST IS OBJECT/ALGOL/HOST;                                      00001270
BIND PRINTIT FROM OBJECT/ALGOL/SUB;                             00001272
STOP;                                                           00001274
BEGIN BINDING PRINTIT OF BLOCK#1 FROM OBJECT/ALGOL/SUB
      PRINTIT  (02,0006) CHANGED TO (02,0006)
K  <-- NEW GLOBAL ADDED TO HOST - WARNING ONLY
      K  (02,0004) CHANGED TO (02,0008)
      LINE  (02,0005) CHANGED TO (02,0003)
      J  (02,0003) CHANGED TO (02,0005)
      BUFFER (02,0002) CHANGED TO (02,0004)
      ?010  (01,0006) CHANGED TO (01,0006)
      <SEG DICT ITEM>   (01,0002) CHANGED TO (01,0005) = 03 000001300001
      <SEG DICT ITEM>   (01,0003) CHANGED TO (01,0007) = 05 07000000005F
      <SEG DICT ITEM>   (01,0004) CHANGED TO (01,0008) = 05 080000540002
END OF BINDING PRINTIT

Note the three lines near the bottom of the list that begin with “<SEG DICT ITEM>.” These list changes to the address couples for code segments in the subprogram. The second number in each address couple is the offset, which is the same as the code segment number for that code segment. The list informs us that code segment number 2 was changed to 5, 3 was changed to 7, and 4 was changed to 8. Therefore, you should look at the STACKHISTORY value for code addresses that begin with 5, 7, or 8, and make a note that they really begin with 2, 3, or 4, respectively. Then you can look for the code addresses in the compiler listing that was created when you originally compiled the subprogram. Suppose that the STACKHISTORY value is as follows:

005:000F:1, 003:0017:3.

You should translate the first address into 002:000F:1, and then look at the compiler listing of the subprogram to determine which statement had that code address. The second address does not begin with 5, 7, or 8, so you don't need to translate it. You can find the procedure invocation referred to by the second address at 003:0017:3 in the compiler listing for the host program. If the host program and the subprogram were compiled with the LINEINFO compiler option set, and the Binder was run with the LINEINFO Binder option set, then the bound object code file contains sequence numbers that appear in the STACKHISTORY value. The Binder does not change the sequence numbers of the host program or the subprogram. When interpreting the sequence number, beware of the possibility that the same sequence number occurred in both the host program and the subprogram file. For example, suppose that the following is the STACKHISTORY value:

005:000F:1 (00000750), 003:0017:3 (00001350).

The subprogram and the host program both might contain lines with the sequence number 750, and they also both might contain lines with the sequence number 1350. However, the last address in the STACKHISTORY value always refers to a statement in the host program. At line 1350 in the host program listing is a procedure invocation statement. If this statement invokes the bound-in procedure, then line 750 is found in the subprogram listing. Otherwise, line 750 is found in the host program listing.

Another task attribute that provides information related to process history is the STOPPOINT task attribute. This real-valued attribute has fields defined that store the fault reason and the code address. The fault reason is the same as the value returned by the HISTORYREASON task attribute, and the code address is the same as the first code address of the STACKHISTORY value.