In this section:

Introduction

This section introduces a C and a C++ example supplied with Insure++. You should copy the appropriate files to a local directory and perform the steps as they are described. The examples chosen here illustrate some of the simpler features of Insure++ and have been chosen to help you start using the system quickly. Once you have gone through this section, you should be in a position to use Insure++ on your own programs.

Using Insure++ with C Code

The C demonstration code used in this section is a very simple program that attempts to sort an array of numbers. If you wish to follow along with the example in this section, you can save some typing time by using the source code supplied with Insure++.

Make a directory for temporary use and copy the source code for this example to it, e.g.:

mkdir $HOME/insure 
cd $HOME/insure 
cp /usr/local/insure/examples/c/bubble1.c .

Step 1 - Compile and Run Without Insure++

Compile the example program bubble1.c with the following command:

cc -g -o bubble1 bubble1.c

Run the program using the normal commands for your shell, e.g.:

bubble1

The program doesn't crash or print any error messages, but it contains a simple memory-related bug.

Step 2 - Compile Bubble1 With Insure++ 

You can start looking for the error by using the insure command to recompile it with Insure++:

insure cc -g -o bubble1 bubble1.c

The insure command prepends your normal compile and link commands on the command line. The -g flag in the above commands is necessary on many platforms for Insure++ to be able to generate stack traces with file names and line numbers.

You can use the same options you normally use to compile and link your code by prepending cc with insure in either your command lines or your makefile.

You may also compile the source code with compilers, such as CC, gcc, or g++ with Insure++ instrumentation:

insure <CC, gcc, or g++>

For example, to compile bubble1 in CC mode:

insure CC -g -o bubble1 bubble1.c

To instrument bubble1 and compile it with gcc:

insure gcc -g -o bubble1 bubble1.c

Contact Parasoft technical support if Insure++ does not directly support your compiler. Using a makefile is often easier because many makefiles use the CC variable to define the name of the compiler. If this is true in your case, you can build a version of your program with Insure++ by typing the following command:

make CC=’insure cc’

If your makefile contains the following macro definition, you can use a simple command to instrument and compile:

CC = /use/local/gcc-3.1/bin/gcc

The following command will perform instrumentation and compile your source with gcc-3.1 without editing anything:

make CC=’insure /use/local/gcc-3.1/bin/gcc’

You can build the original (unchecked) version or the Insure++ version by simply changing the command you type. If your makefile uses a different variable for the link command, such as LD , you will need to use a command similar to the following:

make CC=’insure cc’ LD=’insure cc’

If your original LD definition is in the /bin/ld directory, then replace it with the following:

make CC=’insure cc’ LD=’ins_ld’

If your original LD was a compiler driver, such as cc, gcc, or g++, then prepend it with insure:

make CC=’insure gcc’ LD=’insure gcc’ 

Insure++ generally hides standard compiler warnings to prevent confusion.

Step 3 - Run Bubble1 With Insure++

Run the bubble1.c program using the normal commands for your shell, e.g.:

bubble1

This time you get more interesting responses, as shown below:

[bubble1.c:27] **READ_BAD_INDEX**
>>			 if(a[j-1] > a[j]) {
Reading array out of range: a[j - 1]

Index used 	:  -1
In block	:  0x0804b1a0 thru 0x0804b1c7 (40 bytes, 10 elements)
				vector, declared at bubble1.c, 10
Stack trace where the error occurred:
				  bubble_sort() bubble1.c, 27
					main() bubble1.c, 16

The output from Insure++ indicates that something is wrong and indicates the exact line number where the error occurs.

The error detected is indicated by the error code READ_BAD_INDEX and occurs at line 25 of bubble1.c. The line of code that causes the error is also shown along with a description of the problem. The other information shown in the display is best understood by examining the source code for the example:

/*
 * File: bubble1.c
 */

/*
 * pad1 and pad2 reduce possibility of reading
 * garbage when a[-1] is erroneously dereferenced.
 */
int pad1[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
int vector[10] = {4, 3, 6, 9, 1, 5, 8, 2, 0, 7};
int pad2[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
main()
{
	bubble_sort(vector,
	sizeof(vector)/sizeof(vector[0]));
	exit(0);
}
bubble_sort(a, n)
	int a[], n;
{
	int i, j;

	for(i=0; i<n; i++) {
		for(j=0; j<n-i; j++) {
			if(a[j-1] > a[j]) {
				int temp;
				
				temp = a[j-1];
				a[j-1] = a[j];
				a[j] = temp;
			}
		}
	}
}

We first declare an array that contains the list of values to be sorted in line 9. This is then passed from the main routine to the sorting subroutine in line 13.

The Insure++ bug report contains the following information:

  • The illegal index used in line 25 has the value -1, which implies that the j variable must have the value 0.
  • The block of memory being accessed is fully described. Its starting and ending memory locations are given along with the size and number of elements in the array.
  • The name of the array being accessed, as well as the location at which it was declared, is given. The information describes the global variable vector, even though the bubble_sort routine is accessing this variable by the name a as passed in its argument list.
  • A stack trace is given which shows the sequence of function calls leading to this error.

From this information, it is hard to miss the cause of the problem in the code. The operation in line 25 is to compare an array element with its predecessor. This is the correct operation to perform, but because we use the index values j and j-1, the loop range of j (in line 24) should start at 1, not 0.

This type of problem is very common and can easily go unnoticed in production code, because it doesn't crash the program and it may not even affect the result. In the example shown, the out-of-bounds value is quite likely zero, because it refers to another global variable.

Step 4 - Eliminate the Bug in Bubble1

The fix in this case is to change line 24 to the following:

for(j=1; j<n-i; j++) {

To see that this actually fixes the problem, either modify the source file or copy bubble2.c from the Insure++ examples directory. Compile and run it under Insure++ with the following commands:

insure gcc -g -o bubble2 bubble2.c

bubble2

This time no errors are reported.

C++ Memory Leak Example

Code that contains serious errors may appear normal until the problems start manifesting themselves in crashes, core dumps, or memory exhaustion. To illustrate how Insure++ can detect tricky, well-disguised memory leaks in C++ code, let's consider the program whose source is presented below.

/*
 * File: leak.C
 */
#include <string.h>

union S1 {
		char *cp;
		S1() { cp=new char[10]; }
		S1(char *p) {
			cp=new char[10];
			strcpy(cp,p);
		}
		S1(S1 &s) {
			cp=new char[10];
			strcpy(cp,s.cp);
		}
		void mf(char *p) { strcpy(cp,p); }
};
void foo() {
		S1 s1,s2("Hello "),s3=s2;
		s1.mf("SADF");
		s3.mf("World");
}
int main() {
	foo();
	return(0);
}

Step 1 - Link the Memory Leak with Insure++

Compile and link the leak.c example file using the following command:


g++ -g -c leak.C 
insure g++ -g -o leak leak.o

Add the following option to your .psrc file to enable the leak summary report:

insure++.summarize leaks

Execute the program:

leak

The following output will be printed:

*************INSURE SUMMARY ****************** v7.4 **
*	 Program 		: leak 			*
*	 Arguments 		: Not available *
*	 Directory 		: /usr/local/parasoft/examples/cpp*
*	 Compiled on 	: Not available *
*	 Run on 		: Mar 25, 2007 15:15:01 *
*	 Elapsed time 	: 00:00:00 		*
*	 Malloc HWM 	: 30 bytes 		*
*****************************************************
MEMORY LEAK SUMMARY
===================
3 outstanding memory references for 30 bytes.
Leaks detected at exit
----------------------
	30 bytes 	3 chunks allocated
					malloc() (interface)
			 __builtin_new()
					   foo() leak.C, 20
					  main() leak.C, 25


The output indicates that there is a leak from each of the constructors in the S1 class. In many cases this may be enough information to find and fix the bug. If it is not, however, Insure++ can give you more information, including where the leak actually occurred, not just where the leaked block was allocated.

Step 2 - Compile and Run the Memory Leak with Insure++


Compile and link the leak.C example with the following command:

insure g++ -g -o leak leak.C

Execute the program:

leak

The following output is printed:

[leak.C:23] **LEAK_SCOPE**
>> }
Memory leaked leaving scope: cp

Lost block : 0x0804bad0 thru 0x0804bad9 (10 bytes)
			cp, allocated at:
				malloc() (interface)
			** routines compiled without debug info **
				S1::S1() leak.C, 8
				   foo() leak.C, 20
				  main() leak.C, 25

Stack trace where the error occurred:
				   foo() leak.C, 23
				  main() leak.C, 25

The leak occurs because there is no destructor in S1. When s1, s2, and s3 are called in foo, they appear to be on the stack, which would not cause memory to be allocated. However, S1 calls new to allocate memory and does not have any way to deallocate it. This causes a large leak. Only the first leak is reported at runtime because by default Insure++ reports only one error per category per line. This behavior can be changed using the insure++.report_limit .psrc option.

Step 3 - Eliminate the Bug in Leak.C

You can correct the error by adding a destructor to S1. For example, adding the following line of code between lines 16 and 17 would eliminate the bug:

~S1() { delete[] cp; } //destructor

Checking STL with Insure++


The -Zstl flag enables you to keep track of containers and iterators, as well as references and pointers into container storage, and then issue errors if incorrect STL usage is detected. Currently, STL checking is only supported for g++ versions 3.0 and above.

Checking STL with Insure++ is similar to how STLport debug mode and gcc debug mode (enabled with the -D_GLIBCXX_DEBUG flag) check STL, but Insure++ messages are more detailed. Insure++ can also detect more bugs than either STLport or gcc debug mode. C

onsider vector.cpp in the examples directory. When compiled with gcc-4.1.1, it works for all cases with the exceptions of dh and i—despite serious bugs in all cases:

$ g++ -g vector.cpp
$ for i in a b c d e f g h i; do ./a.out $i && echo "$i: ok" ; done
a: ok
b: ok
c: ok
Segmentation fault
e: ok
f: ok
g: ok
Aborted
Segmentation fault

Instrument the example with Insure++:

$ insure g++ -g vector.cpp -Zstl

Observe that Insure++ flags a bug in each of the test cases:

typedef std::vector<int> VI;
int main(int argc, char *argv[])
{
	VI v;
	v.push_back(1);
	v.push_back(2);


	if (1 < argc) {
		switch (argv[1][0]) {
		case 'a': {
			VI::iterator it = v.begin();
			v.clear();
			return (*it == 1) ? 0 : 1;  // invalid iterator
		} break;
		case 'b': {
			int &x = v[0];
			v.clear();
			return (x == 1) ? 0 : 1; 	// invalid reference
		} break;
		case 'c' : {
			int *ip = &v[0];
			v.clear();
			return (*ip) == 1 ? 0 : 1; 	// invalid pointer
		} break;
		case 'd' : {
			VI::iterator it;
			return *it;					// unbound iterator
		} break;
		case 'e' : {
			VI::iterator it = v.end();
			return *it;					//iterator at end
		} break;
		case 'f' : {
			int &x = *v.end();
			return x;					//invalid reference
		} break;
		case 'g' : {
			int &x = *(v.begin());
			v.clear();
			return x == 1? 0 : 1;		// invalid reference
		} break;
		case 'h' : {
			v.erase(v.end(), v.begin()); // invalide range
		} break;
		case 'i' : {
			VI v2;
			VI::iterator i = v.begin();
			v2.insert(i, 2u, 42);		//different containers
		}
		}
	}
	return 0;
}

We encourage you to do the same with STLport or gcc debug mode, and then compare the results.

  • No labels