This implements the debugger based invariant checking code. The first two macros are the normal user interface; the remainder are used for configuring the behaviour on failure, etc. Note that these macros have no effect unless you run your program under the debugger and read in the commands generated by the ‘nana’ command. You also need to compile the program with the ‘-g’ option.
The exprn should always be true if the program is working. If it is true then nothing happens otherwise the code given by ‘DI_DEFAULT_HANDLER’ will be called which by default prints a message and dies just like ‘assert.h’.
The checking using DI can be enabled and disabled by using the DI_LEVEL and DI_DEFAULT_GUARD macros. See the definitions below for these macros for further details.
Note that exprn should have no side-effects1 since disabling the checking shouldn't change your programs behaviour.
The opposite of ‘DI’, i.e. the expression must never ever be true if the program is working properly. It is equivelant to
I(!(e))
and exists as piece of syntactic sugar which is helpful for complicated boolean expressions.
The ‘DI_LEVEL’ macro is used to globally enable and disable checking, in particular it can take on one of three values:
0
- Disable all checking. Regardless of anything else no code will be generated for
DI
,DN
, etc.1
- Enable checking only if the corresponding guard condition is true. The guard condition can be used to enable and disable checking at compile and run time.
2
- Enable all checking regardless of guard conditions, etc.
DI_LEVEL
defaults to1
.
The
DI_DEFAULT_GUARD
is used to selectively enable or disable checking at compile or run time.
DI_DEFAULT_GUARD
defaults toTRUE
, i.e. always enabled.A user would typically define
DI_DEFAULT_GUARD
to be global or local variable which is used to turn checking on or off at run–time. For example:#define DI_DEFAULT_GUARD (i_guard) extern int i_guard;
This is passed off to the
DI_DEFAULT_HANDLER
and defaults to nothing, it is just some text and is intended to pass failure codes (e.g.IEH303
) or requests (e.g.HW_DEAD
) information off to the handler.
DI_DEFAULT_PARAMS
defaults to nothing.
When an error is detected the
DI_DEFAULT_HANDLER
will be called to handle the error. The arguments are:
exprn
- A string representation of the expression that has failed, e.g.
"I(i>=0)"
.file
- The file that this error occurred in, i.e.
__FILE__
.line
- The line number for the error, i.e.
__LINE__
.param
- An optional parameter which can be passed across which defaults to
DI_DEFAULT_PARAMS
. This can be used to pass failure codes or other information from the checking code to the handler.
This macro is used to ensure that a breakpoint can be set at the location we are checking using
DI
, etc. It defaults toasm("nop")
and can be redefined by the user.
All of these macros are used to individually override the default values defined above. Normally these macros would be used in a system wide header file to define macros appropriate for the application.
These macros are used to assign values to convenience variables in the debugger. Convenience variables are dynamically typed, global in scope and initialised to 0. They start with a single
$
and can be used be used for saving the state of program or for counting events. The ‘DS’ macro executes e under the same rules asDI
. The ‘DSG’ macro executes e only if the the expression g is true.Note that ‘DS’ and ‘DSG’ can also be used for modifying C variables and calling functions.