Valgrind memcheck usage

Valgrind memcheck usage

Valgrind is a collection of debugging and profiling tools. Memcheck is one of the most widely used. It checks for memory-related problems, including memory access violations, memory leaks, and so on.

1. Use steps

1.1 Compile the program

  • -g: Use this option to generate symbol information. In this way, the code location displayed in the report generated by Valgrind is the location of the source code, otherwise Valgrind can only guess the function name.
  • -fno-inline: Without this option, the call stack reported by Valgrind may be confusing. If the debug version is compiled, this option is the default
  • -O1. Use this option to strike a balance between speed and accuracy. If the speed is tolerable, it is best to use -O0.
  • -Wall. When using this option, gcc can solve some problems that memcheck cannot detect.

1.2 Run the program with Valgrind

valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full \
--show-leak-kinds=all ./your_app arg1 arg2
  • --log-file Report file name. If not specified, output to stderr.
  • --tool=memcheck Specifies the tool used by Valgrind. Valgrind is a tool set, including Memcheck, Cachegrind, Callgrind and many other tools. memcheck is the default item.
  • --leak-check specifies how to report memory leaks (memcheck can check a variety of memory usage errors, and memory leaks are a common one). The optional values ​​are:
    • no do not report
    • summary displays brief information, how many memory leaks there are. summary is the default value.
    • yes and full show where each leaked memory is allocated.
  • show-leak-kinds specifies the combination of the types of memory leaks to be displayed. Types include definite, indirect, possible, reachable. You can also specify all or none. The default value is definite, possible.

2. Error Type

2.1 illegal read/ illegal write

The following code allocates a buffer buf with a length of 1024, and then writes an 8-byte uint64_t at the location of buf+1020 and prints it. This uint64_t has exceeded the limit of buf.

 10     char* buf = (char*) malloc (1024);
 11     uint64_t* bigNum = (uint64_t*)(buf + 1020);
 12     *bigNum = 0x12345678AABBCCDD;
 13     printf ("bigNum is %llu\n", *bigNum);
 14     free(buf);

The report points out the illegally written location main.cpp:12 and the illegally read location main.cpp:13.

==17387== Invalid write of size 8
==17387==    at 0x400849: main (main.cpp:12)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)
==17387== 
==17387== Invalid read of size 8
==17387==    at 0x400850: main (main.cpp:13)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)

2.2 Use of uninitialised values

The following code prints an unused value unused.

10     int unused; 
11     printf ("unused=%d", unused);

The report points out where unused is used, main.cpp:11.

==17418== Use of uninitialised value of size 8
==17418==    at 0x4E7A39B: _itoa_word (_itoa.c:195)
==17418==    by 0x4E7C3E7: vfprintf (vfprintf.c:1596)
==17418==    by 0x4E85198: printf (printf.c:35)
==17418==    by 0x4007A2: main (main.cpp:11)

2.3 Use of uninitialised or unaddressable values ​​in system calls

In the following code, arr is not initialized, and write() is called to output.

10     char* arr = (char*)malloc(10);
11     write (1, arr, 10);

The report gives the location of the system call write main.cpp:11.

==17450== Syscall param write(buf) points to uninitialised byte(s)
==17450==    at 0x4F1AF10: __write_nocancel (syscall-template.S:82)
==17450==    by 0x400802: main (main.cpp:11)
==17450==  Address 0x51f3040 is 0 bytes inside a block of size 10 alloc'd
==17450==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17450==    by 0x4007E8: main (main.cpp:10)

2.4 illegal fress

In the following code, the released address arr+2 is not the address arr obtained during allocation.

10     char* arr = (char*)malloc(10);
11     free (arr+2);

The report pointed out the location of main.cpp:11 when it was released, and also pointed out the location of main.cpp:10 when it was allocated.

==3429== Invalid free() / delete / delete[] / realloc()
==3429==    at 0x4C2A82E: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007FC: main (main.cpp:11)
==3429==  Address 0x51f3042 is 2 bytes inside a block of size 10 alloc'd
==3429==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007E8: main (main.cpp:10)

2.5 When a heap block is freed with an inappropriate deallocation function

In the following code, an int array d[] is allocated, but when it is released, delete is used instead of delete[].

10     int* d = new int[5];
11     delete d;

The report pointed out the location of the array release main.cpp:11, and also pointed out the location of the allocation main.cpp:10.

==3498== Mismatched free() / delete / delete []
==3498==    at 0x4C2A4BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400828: main (main.cpp:11)
==3498==  Address 0x5a2ac80 is 0 bytes inside a block of size 20 alloc'd
==3498==    at 0x4C2AC27: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400818: main (main.cpp:10)

2.6 Overlapping source and destination blocks When moving/copying cached data, the source and destination overlap

The source and destination of memcpy copy in the following code overlap.

10     char buf[256] = "";
11     memcpy (buf+10, buf, 100);

The report indicates the location of the copy main.cpp:11.

==3530== Source and destination overlap in memcpy(0x7feffffea, 0x7feffffe0, 100)
==3530==    at 0x4C2CFA0: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3530==    by 0x40087E: main (main.cpp:11)

2.7 Fishy argument values ​​function call parameters may be illegal

In the following code, malloc is called with a negative value of -10.

10     int sz = -10;
11     int* arr2 = (int*)malloc (sz);
12     free(arr2);

The report only warns that the parameter of malloc() is illegal, but does not indicate the location. (Even if you don't use Valgrind to run the program, this error should be found immediately, right?)

==3583== Warning: silly arg (-10) to malloc()

2.8 Memory leak detection

The following code allocates 100 bytes without releasing it.

10     int* arr = (int*)malloc (100);

The report lists the location of each leaked memory allocation in turn (here is main.cpp:10), and finally gives a statistical summary of all leaks (LEAK_SUMMARY section).

==3869== HEAP SUMMARY:
==3869==     in use at exit: 100 bytes in 1 blocks
==3869==   total heap usage: 1 allocs, 0 frees, 100 bytes allocated
==3869== 
==3869== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3869==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3869==    by 0x400798: main (main.cpp:10)
==3869== 
==3869== LEAK SUMMARY:
==3869==    definitely lost: 100 bytes in 1 blocks
==3869==    indirectly lost: 0 bytes in 0 blocks
==3869==    possibly lost: 0 bytes in 0 blocks
==3869==    still reachable: 0 bytes in 0 blocks
==3869==    suppressed: 0 bytes in 0 blocks

3. About memory leaks

  • memcheck tracks all the heap blocks allocated by malloc()/new(), so it can know which blocks have not been released when the program exits.
  • The set of pointers that the program can access is called root-set. Based on whether a block can be reached from the root-set, memcheck determines whether the block is leaked (reachable or lost).
    • There are two ways to reach a block: one is start-pointer, which points to the start address of the block; the other is interior-pointer, which points to a position in the middle of the block.
    • Caches referenced by start-pointer are considered reachable.
    • The cache referenced by interior-pointer is not necessarily.
      • It may be the result of the pointer moved backward deliberately after the allocation. At this time, the program uses the first few bytes of the buffer to store special information, and the real data is somewhere later. For example, pointers to C++ std::string internal arrays, pointers to C++ object arrays allocated by new[], multiple inheritance, and so on. At this time the cache is reachable.
      • But it may also point to the cache just by accident. At this time, the cache is lost.
      • So the cache referenced by interior-pointer is possible lost.

According to whether there is a need to jump from root-set to the cache, memcheck divides the state of the allocated cache into several types:

  • directly reachable: the pointer in the root-set points to the cache, no need to jump
  • Indirectly reachable: The pointer in the root-set does not directly point to the cache, it needs to be jumped
  • directly lost: No pointer points to the cache
  • indirectly lost: There is a pointer to the cache, but this pointer cannot be reached from the root-set

The following figure lists the various situations of the cache. Where RRR is the node in the root-set, and AAA and BBB are caches.

     Pointer chain            AAA Leak Case   BBB Leak Case
     -------------            -------------   -------------
(1)  RRR ------------> BBB                    DR
(2)  RRR ---> AAA ---> BBB    DR              IR
(3)  RRR               BBB                    DL
(4)  RRR      AAA ---> BBB    DL              IL
(5)  RRR ------?-----> BBB                    (y)DR, (n)DL
(6)  RRR ---> AAA -?-> BBB    DR              (y)IR, (n)DL
(7)  RRR -?-> AAA ---> BBB    (y)DR, (n)DL    (y)IR, (n)IL
(8)  RRR -?-> AAA -?-> BBB    (y)DR, (n)DL    (y,y)IR, (n,y)IL, (_,n)DL
(9)  RRR      AAA -?-> BBB    DL              (y)IL, (n)DL

Pointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointer

Leak Case legend:
- DR: Directly reachable
- IR: Indirectly reachable
- DL: Directly lost
- IL: Indirectly lost
- (y)XY: it's XY if the interior-pointer is a real pointer
- (n)XY: it's XY if the interior-pointer is not a real pointer
- (_)XY: it's XY in either case

The following are the items in the LEAK SUMMARY section of memcheck:

  • Still reachable: In the case of (1) and (2) in the above figure, it is directly reached or jumped to the cache from the root-set through the start-pointer. This item is determined not to be lost, and the user does not need to be related.
  • Definitely lost: The situation of (3). There is no pointer to the cache. This item is determined to be lost, and the user needs to solve it.
  • Indirectly lost: The situation of BBB in (4) and (9). A start-pointer or interior-pointer points to the cache, but these pointers cannot be reached from the root-set. This item can be postponed to solve, because indirectly lost must correspond to definitely lost. When definitely lost is solved, indirectly lost becomes reachable or possible lost.
  • Possibly lost: There are interior-pointers pointing to the cache, and these pointers can be reached from the root-set. Because memcheck cannot determine whether the interior-pointer is lost, it needs to be excluded by the user.
    So in fact, just care about definitely lost and possible lost. This is also the default value of memcheck:
--show-leak-kinds=definite,possible
  • suppressed: The user specifies the items not to be reported in the suppressed file, regardless of the previous situation of the cache.

4. Implementation details of memcheck

4.1 V (Valid-value) bit

It can be simply considered that memcheck implements a CPU equivalent to the real CPU. This CPU has an extra bit associated with this bit for each bit processed and stored on the real CPU. The latter bit is called V bit, which indicates whether the previous bit contains a legal value.

The entire address space of the process corresponds to a bitmap. If the CPU loads a word size of data, it loads the corresponding 32 V bits from the bitmap at the same time. If U writes all or part of the word data to a different address, it will write the 32 V bits corresponding to that address back to the bitmap at the same time.

Even the bit inside the CPU has a corresponding V bit. All CPU registers, no matter what type, have their own V bit. Memcheck uses a lot of compression technology in bitmap in order to express the V bit in it compactly.

4.2 A (Valid-address) bit

Each byte in the memory has a valid-address(A) bit corresponding to it. Unlike the V bit, the byte in the CPU does not have an A bit. A bit indicates whether the program can legally read and write the corresponding position. A bit does not care about data, but V bit only cares.

When the program reads and writes the memory, memcheck checks the A bit corresponding to the position, and if the A bit is invalid, an error is reported.

The timing of A bit setting is as follows:

  • When the program is started, the A bit setting of all global data is valid
  • When calling malloc/new, allocate the size of the area, the A bit setting is valid, and the setting is invalid immediately after release
  • When the stack pointer SP moves, the A bit is set accordingly. Because the stack grows downwards, SP moves downwards, A bit setting is valid, SP moves upwards, A bit setting is invalid.
  • When some system calls, the A bit is set accordingly. For example, mmap() maps the file to the memory space, so the A bit setting is effective.

Related Links

GDB common usage
GDB debugging Coredump problems in
embedded development GDB debugging Coredump problems in
embedded development GDB serial port remote debugging
with backtrace() debugging coredump problems
Valgrind memcheck usage
Address Sanitizer usage

Reference

Memcheck: a memory error detector
http://valgrind.org/docs/manual/mc-manual.html#mc-manual.badfrees

valgrind tool introduction and simple use
https://www.cnblogs.com/AndyStudy/p/6409287.html

About gcc 2.96
https://www.jianshu.com/p/a317e8208f8e

Guess you like

Origin blog.csdn.net/hyl999/article/details/108243096