Insure++ User's Guide - Code Insertions
Part I
Most programmers write code that makes assumptions about various things
that can happen. These assumptions can vary from the very simple, such
as "I'm never going to pass a NULL pointer to
this routine", to more subtle, such as "a and
b are going to be positive".
Whether this is done consciously or not, the problems that occur when
these assumptions are violated are often the most difficult to track
down. In many cases, the program will run to completion with no
indication of error, except that the final answer is incorrect.
The simple case just described, the NULL pointer,
will probably be tracked down pretty easily, since Insure++ will
pinpoint the error immediately. A few minutes of work should eliminate
this problem.
The second case is much harder.
One option is to add large chunks of debugging code to your application
to check for the various cases that you don't expect to show up. Of
course, you normally have an idea of where the problem is, so you start
by putting checks there. You then run the code and sort through the mass
of output, trying to see where things started to go awry. If you guessed
wrong, you insert more checks in other places of the code and repeat the
entire process.
If you are lucky, the code you insert to catch the problem won't add
bugs of its own.
Once you've found the problem, you can either remove the debugging code
(introducing the possibility of deleting the wrong things and bringing
in new bugs) or comment it out for use next time (cluttering the source
code).
A second option is to have Insure++ add the checking code to your
application automatically and invisibly.
The basic idea is that you tell Insure++ what you'd like to check
by providing an "Insure++ interface module". This can
be kept separate from your main application and added and removed at
compile time. Furthermore, Insure++ automatically inserts it in
every place that you use a particular piece of code so you only have to
go through this process once. Finally, errors that are detected are
diagnosed in the same way as all other Insure++ errors. You get a
complete report of the source file, line number, and function call stack
together with any other information that you think is useful.
Assume that you have a routine in your program called
cruncher which takes three double precision arguments
and returns one. For some reason, possibly connected with the details of
your application, you expect the following rules to be true when calls are
made to this routine
- The sum of the three parameters is less than 10.
- The first parameter is always greater than zero.
- The return value is never zero.
To enforce these rules with Insure++, you create a file
containing the following "code".
1: /*
2: * crun_iic.c
3: */
4: (double a, double b, double c)
5: {
6: double ret;
7:
8: if(a+b+c >= 10.) {
9: iic_error(USER_ERROR,
10: "Sum exceeds 10: %f+%f+%f\n",
11: a, b, c);
12: }
13: if(a <= 0) {
14: iic_error(USER_ERROR,
15: "a is negative: %f\n", a);
16: }
17: ret = cruncher(a, b, c);
18: if(ret == 0) {
19: iic_error(USER_ERROR,
20: "Return zero: %f,%f,%f => %f\n",
21: a, b, c, ret);
22: }
24: return ret;
25: }
Note that this looks just like normal C code with the rather strange
exception that the routine cruncher calls itself at line
17!
This is not normal C code - it's an Insure++ interface
description, and it behaves rather like a complicated macro insertion.
Wherever your original source code contains calls to the function
cruncher , they will be replaced by this set of error
checks, and the indicated call to the routine cruncher .
The net effect will be as though you had added all this complex error
checking and printing code manually, except that Insure++ does
it automatically for you. Another advantage is the use of the
iic_error routine rather than a
conventional call to printf or
fprintf . The iic_error routine
performs the same task - printing data and strings, but also includes in
its output information about the source file and line number at which the
call is being made and a full stack trace.
Once you've written this interface description, using it is trivial.
First, you compile it with the special Insure++ interface
compiler, iic . If you put the code
in a file called crun_iic.c , for example, you would
type the command
iic crun_iic.c
This creates a file called
crun_iic.tqs , which is an
"Insure++ interface module".
You can use this module in one of two ways.
If you plan to use this interface check on a regular basis during the
development of your project, you should insert the line
insure++.interface_library crun_iic.tqs
in the .psrc file in either your current working
or $HOME directory. All future invocations of
.psrc will then insert this interface check.
If you wish to use the interface check intermittently on some of
your compiles, you can add the name of the interface module to the
Insure command line when you compile and link your
source code. For example the command
insure -c myfile1.c
would become
insure crun_iic.tqs -c myfile1.c
Note that you can specify more than one interface in any interface file
or include multiple interface modules on the interface_library
line in your .psrc file or on the
insure command line.
This section has shown how you can add your own error checking either
to extend or replace that done automatically by Insure++ by
defining "interface modules". These are actually a very
powerful way of extending the capabilities of Insure++, and are
described more fully in "Interfaces". The current discussion, however, has
shown their simplest use.
For more information, call (888) 305-0041 or send email to:
insure@parasoft.com
Signals
Interfaces
Insure++ User's Guide TOC
|