[ 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
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
For the output to be human readable, compile with debugging symbols (-g).
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.
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
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
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; }
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[94] but the Mac Developer Tools include a graphical tool, called MallocDebug.app, that can replace a number of valgrind's functions.
[94] 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. |