Introducción a Patrones de Diseño en C++ con Qt4/C.3.3 Encontrando Errores de Memoria

Versión para imprimir esta pagina

C.3.3. Finding Memory Errors

[ fromfile: findingmemoryerrors.xml id: findingmemoryerrors ]

Memory errors are very 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 run time 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.4 is a short program that contains a deliberate memory usage error.

Example C.4. src/debugging/wrongdelete.cpp

void badpointer1(int* ip, int n) {

 ip = new int[n];
 delete ip;   1


int main() {

 int* iptr;
 int num(4);
 badpointer1(iptr, num); 


1 wrong delete syntax

(link) For the output to be human readable, we compile with debugging symbols (-g).

debugging/wrongdelete> g++ -g -pedantic -Wall wrongdelete.cpp debugging/wrongdelete> ./a.out debugging/wrongdelete>

The compiler didn't complain, and even after running the program, no error behavior is exhibited. However, memory is corrupted by this program.

Here is a (slightly abbreviated) look at valgrind's analysis of Example C.4. We have removed the process id of the valgrind job from the beginning of each line. The process id will, of course, be 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 us out to the location of the problem code. Example C.5 is a little more interesting because it contains memory leaks and array index errors.

Example C.5. src/debugging/valgrind-test.cpp

  1. include <iostream>

int badpointer2(int k) {

 int* ip = new int[3];
 ip[0] = k;
 return ip[3];   1

} 2

int main() {

 using namespace std;
 int* iptr;
 int num(4), k;  3
 /* what is the state of iptr? */
 cout << iptr[num-1] << endl; 
 cout << badpointer2(k) << endl;


1 out of bounds index 2 memory leak 3 k is uninitialized


Running Example C.5 through valgrind shows us 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)


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)


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.
   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, we can rerun valgrind with --leak-check=full.

Versión para imprimir esta pagina