Coding Conventions:
Error Checking Code
Table of Contents
back to master Table of Contents
Why Error Checking Code?
GEOS-SC comes in two flavors: an error-checking and a non-error-checking
(production) version. The EC version is used for development, the non-EC
version is used for performance testing, some Quality Assurance testing
and actual production.
EC code has two main purposes:
-
To uncover bugs.
-
To make it easier to find their cause.
EC code contributes to these goals by:
-
Explicitly documenting and verifying assumptions made by the code.
-
Verifying program state more frequently, often greatly reducing the number
of possible places a bug could reside.
-
Providing very aggressive verifying functions one can temporarily insert
into code (possibly every other line) in order to find particularly problematic
bugs. GEOS-SC's "CheckHeap()" function falls into this category.
-
Emitting warnings when the system is in a suspicious but not necessarily
incorrect state (e.g. the allocator has run out of memory, attempt to modify
without write permissions, etc.).
EC code checks for all programming errors that can be reasonably
checked for. Non-EC code checks for errors that can happen during execution
assuming that the code itself is not flawed. In general, these are errors
returned due to non-availibility of a resource or user error.
Below are some example errors that each version, EC and non-EC, would
check for.
non-EC code checks for...
-
malloc returns NULL (memory full)
-
serial port already in use
-
persistent object store full
-
PCMCIA card removed
-
all potential user errors
EC code checks for...
-
freed memory
-
validations of code invariants
-
data structure validation
-
parameter validation
-
return values that can only happen due to programmer error
General Guidelines
In order for EC code to be useful, it should adhere to these guidelines:
-
The program flow should be the same as final production code as much as
possible. This reduces the number of bugs that happen in non-EC only.
-
If non-EC code checks and gracefully handles an error condition, do not
cause an EC failure at that point. Emitting an EC warning is more
appropriate. Exceptions include cases where the condition can "never
possibly happen, but I want to catch it in non-EC if it does."
-
EC code should be fast. The (possibly unachievable) goal is to have
all EC code running all the time. This ensures that it is kept up
to date with changes in the code. We've found that a system that
is 2-3 times slower in EC than non-EC is generally usable. Anything
more than that and some EC should be taken out of the default set.
-
The EC code should be well documented.
-
EC code should not depend on debug aid code.
-
Be smart about using VERIFY (see below for its description).
It is provided because there are cases where its use is justified, but
you should be able to justify why the non-EC version doesn't have to check
the same condition.
-
If your EC code runs fast, don't bother adding a new EC flag for it.
Explicit Guidelines
-
Every parameter passed to every external/public function should either
be checked with EC code or checked with non-EC code, but not both.
-
Every parameter passed to every internal function should be checked with
EC code only.
ErrorChecking Flags
Although most compilation during development will actually be with error
checking compiled in, the default is to compile GEOS-SC without any error
checking code. Error checking code is compiled in by passing one or more
flags to the compiler (gcc is used here for example only, this is the same
with all compilers). Normally this work is done automatically by the build
system:
gcc -DERROR_CHECK -c foo.cc
ERROR_CHECK is the flag that turns on standard error checking. EC_<special-type>
turns on all erorr checking.
Two special types of error checking are instance data validation and
parameter validation, which are turned on with EC_INSTANCE and EC_PARAM.
These types of error checking are described later.
Note that all constants and macros used for error checking are all upper
case. This is to visually distinguish error checking code.
ErrorChecking Macros
The header file system.h defines, among other things, several
macros for error checking. They are listed in the following sub-sections.
In order for error checking to work, every GEOS-SC source file must
include system.h.
ASSERT
ASSERT is just like the standard C assert macro and is
used to assert that a condition is true, terminating execution otherwise.
ASSERT(ptr != NULL);
ASSERTS
ASSERTS is similar to ASSERT but takes an additional
string arguent that describes the error condition. This string can provide
useful information when testing is being done without a debugger.
ASSERTS(ptr != NULL, "DrawString passed a NULL string");
EC_FAIL
EC_FAIL("message") is a compact way of writing ASSERTS(FALSE,
"message").
INSTANCE_ASSERT, INSTANCE_ASSERTS
INSTANCE_ASSERT and INSTANCE_ASSERTS are just like ASSERT
and ASSERTS but are only compiled in if EC_INSTANCE is
true.
INSTANCE_ASSERT(count <= MAX_COUNT);
INSTANCE_ASSERTS(count <= MAX_COUNT, "Button: illegal count");
PARAM_ASSERT, PARAM_ASSERTS
PARAM_ASSERT and PARAM_ASSERTS are just like ASSERT and
ASSERTS but are only compiled in if EC_PARAM is true.
PARAM_ASSERT(count <= MAX_COUNT);
PARAM_ASSERT(count <= MAX_COUNT, "Buttone: illegal count");
ASSERT_WARN, ASSERTS_WARN
These macros are similar to ASSERT and ASSERTS except
that if the assertion fails, they do not terminate execution. Rather, they
do whatever ASSERT and ASSERTS would do without aborting.
EC_WARN
EC_WARN("message") is a compact way of writing ASSERTS_WARN(FALSE,
"message").
_EC, _EC_INSTANCE, _EC_PARAM
These macros are used as a shorthand for including code if the respective
EC flag is set.
_EC(ecTag == TRUE; ) // same as #ifdef EC ... #endif
_EC_INSTANCE(void VALID(); ) // same as #ifdef EC_INSTANCE... #endif
VERIFY, VERIFY_WARN
These are identical to ASSERT and ASSERT_WARN, but they still evaluate
their expression in non-EC. These are provided because their use can be
justified in certain cases, but convince yourself why the error condition
doesn't need to be checked in production code before doing so.
VERIFY(myWin->SetVisible() == SUCCESS);