[ fromfile: findingmemoryerrors.xml id: findingmemoryerrors ]
Memory errors are difficult to track down without the aid of a runtime analysis tool.
A program that analyzes the running performance of a program is called a profiler.
valgrind
is an open source profiling tool for Linux that tracks the memory and CPU usage of your code and detects a variety of runtime errors. These include
Memory leaks, memory that is no longer accessible but which has not been deleted
Invalid pointer use for heap memory, such as
Out of bounds index
Mismatches between allocation and deallocation syntax (e.g., allocating with new[]
but deallocating with delete
)
Use of uninitialized memory
Any of these errors can cause catastrophic results in a piece of software. Profilers can also be used for performance-tuning, and determining which code is responsible for slowing down a program (i.e., finding bottlenecks).
Example C.5 is a short program that contains a deliberate memory usage error.
Example C.5. src/debugging/wrongdelete.cpp
debugging/wrongdelete> g++ -g -Wall wrongdelete.cpp debugging/wrongdelete> ./a.out debugging/wrongdelete>
The compiler didn't complain, and even after running the program, no error behavior appears. However, memory is corrupted by this program.
Here is a (slightly abbreviated) look at valgrind's analysis of Example C.5. We have removed the process id of the valgrind job from the beginning of each line. The process id is, of course, different each time you run valgrind.
src/debugging> valgrind a.out --3332-- DWARF2 CFI reader: unhandled CFI instruction 0:50 --3332-- DWARF2 CFI reader: unhandled CFI instruction 0:50 Mismatched free() / delete / delete [] at 0x401C1CB: operator delete(void*) (vg_replace_malloc.c:246) by 0x80484BD: badpointer1(int*, int) (wrongdelete.cpp:3) by 0x80484F4: main (wrongdelete.cpp:9) Address 0x4277028 is 0 bytes inside a block of size 16 alloc'd at 0x401BBF4: operator new[](unsigned) (vg_replace_malloc.c:197) by 0x80484AC: badpointer1(int*, int) (wrongdelete.cpp:2) by 0x80484F4: main (wrongdelete.cpp:9)
valgrind
found the errors and, with debugging symbols, could point you to the location of the problem code.
Example C.6 is a little more interesting because it contains memory leaks and array index errors.
Example C.6. src/debugging/valgrind-test.cpp
#include <iostream> int badpointer2(int k) { int* ip = new int[3]; ip[0] = k; return ip[3]; } int main() { using namespace std; int* iptr; int num(4), k; cout << iptr[num-1] << endl; cout << badpointer2(k) << endl; }
<include src="src/debugging/valgrind-test.cpp" href="src/debugging/valgrind-test.cpp" id="valgrindtestcpp" mode="cpp"/>
Running Example C.6 through valgrind shows you the exact locations of some errors:
For more details, rerun with: -v --2164-- DWARF2 CFI reader: unhandled CFI instruction 0:50 --2164-- DWARF2 CFI reader: unhandled CFI instruction 0:50 Use of uninitialised value of size 4 at 0x80486AF: main (valgrind-test.cpp:17) 68500558 Invalid read of size 4 at 0x804867C: badpointer2(int) (valgrind-test.cpp:8) by 0x80486DD: main (valgrind-test.cpp:18) Address 0x4277034 is 0 bytes after a block of size 12 alloc'd at 0x401BBF4: operator new[](unsigned) (vg_replace_malloc.c:197) by 0x8048667: badpointer2(int) (valgrind-test.cpp:6) by 0x80486DD: main (valgrind-test.cpp:18) 0 ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 19 from 1) malloc/free: in use at exit: 12 bytes in 1 blocks. malloc/free: 1 allocs, 0 frees, 12 bytes allocated. For counts of detected errors, rerun with: -v searching for pointers to 1 not-freed blocks. checked 120,048 bytes. LEAK SUMMARY: definitely lost: 12 bytes in 1 blocks. possibly lost: 0 bytes in 0 blocks. still reachable: 0 bytes in 0 blocks. suppressed: 0 bytes in 0 blocks. Use --leak-check=full to see details of leaked memory.
If this is not enough information to find where the memory leak is, you can rerun valgrind with --leak-check=full
.
We repaired some of the errors in Example C.7
Example C.7. src/debugging/valgrind-test2.cpp
#include <iostream> int notSoBadPointer(int k) { int* ip = new int[3]; ip[0] = k; delete[] ip; return k; } int main() { using namespace std; int* iptr; int num(4), k(4); cout << iptr[num-1] << endl; cout << notSoBadPointer(k) << endl; }
<include src="src/debugging/valgrind-test2.cpp" href="src/debugging/valgrind-test2.cpp" id="valgrindtest2cpp" mode="cpp"/>
Compiling and running the slightly repaired test without valgrind produces no warnings or errors and one nonsense output value.
src/debugging> g++ -g -Wall valgrind-test2.cpp src/debugging> ./a.out -1078391036 4
Running it with valgrind produces fewer complaints than before.
src/debugging> valgrind ./a.out For more details, rerun with: -v Use of uninitialised value of size 4 at 0x8048794: main (valgrind-test2.cpp:18) -1096641724 4 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 18 from 1) malloc/free: in use at exit: 0 bytes in 0 blocks. malloc/free: 1 allocs, 1 frees, 12 bytes allocated. For counts of detected errors, rerun with: -v All heap blocks were freed -- no leaks are possible. src/debugging>
Finally, in Example C.8, we eliminate the last error.
Example C.8. src/debugging/valgrind-test3.cpp
#include <iostream> int notSoBadPointer(int k) { int* ip = new int[3]; ip[0] = k; delete[] ip; return k; } int main() { using namespace std; int num(4), k(4); int* iptr = new int[num] ; for (int i = 0; i < num; ++i) iptr[i] = i; cout << iptr[num-1] << endl; cout << notSoBadPointer(k) << endl; delete[] iptr; }
<include src="src/debugging/valgrind-test3.cpp" href="src/debugging/valgrind-test3.cpp" id="valgrindtest3cpp" mode="cpp"/>
We compile, run, and then run with valgrind.
src/debugging> g++ -g -Wall valgrind-test3.cpp src/debugging> ./a.out 3 4 src/debugging> valgrind ./a.out 3 4 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 18 from 1) malloc/free: in use at exit: 0 bytes in 0 blocks. malloc/free: 2 allocs, 2 frees, 28 bytes allocated. For counts of detected errors, rerun with: -v All heap blocks were freed -- no leaks are possible. src/debugging>
valgrind is not readily available for MacOSX[131] but the Mac Developer Tools include a graphical tool, called MallocDebug.app, that can replace a number of valgrind's functions.
[131] It can be installed from source code, which you can find at http://valgrind.org/downloads/repository.html.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |