MAINSAIL Language Manual, Chapter 18

previous   next   top   complete contents   complete index   framed top   this page unframed


18. SUPERCHECK

Beginning in MAINSAIL Version 16.29, for every MAINSAIL platform, there is a corresponding SUPERCHECK platform. On a SUPERCHECK platform, MAINSAIL automatically performs far more detailed runtime checking than is the default on non-SUPERCHECK platforms. This makes it much easier to track bugs that corrupt MAINSAIL's memory or violate assumptions made by the MAINSAIL compiler and runtime system.

If you wish to use SUPERCHECK, please contact XIDAK for information on how to obtain a MAINSAIL system for a SUPERCHECK platform.

18.1. What SUPERCHECK Checks

In modules compiled for SUPERCHECK platforms, extra checking code is generated at most forms of references to dynamic memory, to verify that the object being referred to is the kind of object that is expected. At each reference to a data field or PROCEDURE field of the form p.f or m.f, the SUPERCHECK code verifies that the fields of the record or data section pointed to by p or m match the fields of the CLASS in which f was declared, up to and including field f. At each $PROCVAR call, the SUPERCHECK code verifies that the $PROCVAR refers to a PROCEDURE whose interface matches the $PROCVAR's declared model PROCEDURE. And at each reference to an element of a dynamic ARRAY, the SUPERCHECK code verifies that the ARRAY POINTER points to an ARRAY whose element type, number of dimensions, and actual bounds match those in the ARRAY POINTER's declaration.

At the moment, SUPERCHECK does not cause checking code to be generated to verify $REFERENCE parameters or to check the upper bound of the first dimension in a reference to an element of an unsized inplace ARRAY. (The standard subscript-checking code described in Section 17.2 does not check the first dimension's subscript against an unsized inplace ARRAY's upper bound, since the upper bound is not always available, and even when it is, it must be computed based on the total number of elements in the ARRAY and the values of the other bounds.)

18.1.1. Implementation Overview: Data Field References

When the SUPERCHECK code checks a reference to a data field of the form p.f or m.f, it first makes sure that p or m points to a valid record chunk or data section, and then finds the CLASS descriptor for the CLASS describing that record or data section (the “actual” CLASS). Then it compares this descriptor to the CLASS descriptor for the expected CLASS. If the two descriptor POINTERs are the same, then the reference to f is valid; p or m points to exactly the sort of chunk it was expected to point to.

If the two CLASS descriptor POINTERs are different, the reference is valid if the expected CLASS is a prefix of the actual CLASS. However, since CLASS descriptors currently contain no information about prefix CLASSes, the SUPERCHECK code cannot do as strict a check as the compiler would be able to. Instead, the SUPERCHECK code does a primitive check for structural compatibility; if the fields of the expected CLASS and actual CLASS (up to and including the referenced field) seem to have the same declared attributes, the reference is assumed to be valid even if the two CLASSes are unrelated.

Rather than actually comparing the fields' attributes, SUPERCHECK uses a hash code based on the fields' attributes. This makes for a more efficient check, although it does mean that there is an extremely small possibility that SUPERCHECK will fail to flag an erroneous reference.

18.1.2. Implementation Overview: Intermodule Calls

SUPERCHECK code uses a similar hash encoding scheme when checking intermodule calls of the form p.f or m.f. First, the SUPERCHECK code verifies that p or m points to a valid data section, and finds the data section's module descriptor (the “actual” module descriptor). Then it compares two hash encodings derived from the PROCEDURE fields' declared attributes. One hash encoding denotes the calling module's view of the interface to the module being called. It is obtained from information in the expected CLASS's CLASS descriptor. The other hash encoding denotes the module's actual interface, and is obtained from the actual module descriptor.

18.1.3. Implementation Overview: $PROCVAR Calls

The SUPERCHECK code for $PROCVAR calls first verifies that the $PROCVAR's data section part refers to a valid data section. Then it finds the referenced PROCEDURE based on the $PROCVAR's displacement, verifying that the $PROCVAR actually refers to a PROCEDURE. Finally, it compares a hash encoding for the $PROCVAR's model PROCEDURE (computed by the compiler) to a hash encoding for the PROCEDURE referred to by the $PROCVAR. The two hash encodings denote the expected and actual PROCEDURE interface. If they are unequal, the $PROCVAR call is assumed to be invalid.

18.2. Decreased Frequency of POINTER Rigging: DELAYRIGPTRS

Normally when POINTER rigging (the MAINEX subcommand RIGPTRS; see Chapter 6 of the MAINDEBUG User's Guide) is in effect, MAINSAIL walks through memory at each call to dispose, rigging any POINTERs to the disposed chunk. This can make programs run extremely slowly, but on non-SUPERCHECK platforms is the only reliable way to catch dangling POINTER bugs. On SUPERCHECK platforms, many dangling POINTER references can be caught with SUPERCHECK, so MAINSAIL provides an option on SUPERCHECK platforms for batching up the rigging of POINTERs to disposed chunks until a sufficient number of chunks have been disposed to make rigging worthwhile, thereby reducing the number of times MAINSAIL must walk memory to rig POINTERs. On SUPERCHECK platforms, any dangling POINTER references to these chunks will be detected even though the POINTERs have not been rigged yet.

When a chunk is disposed while delayed POINTER rigging is in effect, the chunk is not really freed. Instead, the chunk is specially marked so that a reference to it will cause the SUPERCHECK code to detect an error. When the total amount of space occupied by all such marked chunks exceeds a user-specifiable threshold, MAINSAIL makes a single pass through memory, rigging all POINTERs to any of the chunks on the lists, and marking the chunks on the lists as free. This rigging also happens during calls to $disposeArea, $clearArea, $collect, and any other runtime system routines that could free chunk memory, unless there are no chunks for which rigging has been delayed.

Two CONF parameters (see Chapter 6 of the MAINSAIL Utilities User's Guide) specify the threshold governing the maximum amount of memory that can be occupied by disposed chunks for which rigging has been delayed: MAXSIZEDELAYRIGCHUNKS and MAXPERCENTDELAYRIGCHUNKS. If MAXSIZEDELAYRIGCHUNKS is nonzero, it specifies the threshold as an absolute number of bytes. If MAXPERCENTDELAYRIGCHUNKS is nonzero, it specifies the threshold as a percentage of the total memory currently allocated by MAINSAIL. Both parameters can be nonzero, in which case the smaller threshold is the one that applies. The default value of MAXSIZEDELAYRIGCHUNKS is zero and the default value of MAXPERCENTDELAYRIGCHUNKS is 5.

Two MAINEX subcommands control whether or not delayed POINTER rigging is in effect: DELAYRIGPTRS and NODELAYRIGPTRS. Delayed POINTER rigging can also be controlled from a program by calling the PROCEDUREs $setDelayPtrRigging and $clearDelayPtrRigging.

When delayed POINTER rigging is in effect, programs will use memory differently than they otherwise would, because disposed chunks will not be recycled for reuse in the same way. But delayed POINTER rigging will still catch any dangling POINTERs that are dereferenced when SUPERCHECK is in effect, while reducing on the number of times MAINSAIL walks memory to rig POINTERs.

On SUPERCHECK platforms, delayed rigging is in effect by default. That is, MAINSAIL acts as if the MAINEX subcommands RIGPTRS, RIGSTRS, and DELAYRIGPTRS had all been given; if you do not want to use rigging on a SUPERCHECK platform, you must turn it off explicitly. On non-SUPERCHECK platforms, all these subcommands are off by default.

18.3. Effect of SUPERCHECK on Speed

SUPERCHECK code makes different programs slower by different amounts, depending on how many times a program uses a POINTER to refer to data and how often it does intermodule calls and $PROCVAR calls. Also, the SUPERCHECK code for POINTER-based data references where the expected CLASS is the same as the actual CLASS of the referenced record are much faster than data references where the expected CLASS and actual CLASS are different.

In XIDAK's experiments, SUPERCHECK programs take about 3 to 5 times longer than the same programs on non-SUPERCHECK platforms.

Because SUPERCHECK makes programs run substantially slower, XIDAK recommends that you use SUPERCHECK only while testing your code, and not use it in code that will be put into production.

18.4. Effect of SUPERCHECK on Size

SUPERCHECK object modules take up more space than standard object modules. Although the increase in size depends on exactly what the modules do, in XIDAK's benchmarks, SUPERCHECK code is about twice as large as non-SUPERCHECK code.
previous   next   top   complete contents   complete index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 18