Linux USES Valgrind for checking of to prevent memory leaks

  • 2020-04-02 02:08:26
  • OfStack

One of the most frustrating aspects of C/C++ development is memory management, and sometimes it can take days to find a memory leak or an out-of-bounds memory access.

Valgrind is a software suite that simulates the debugger and profiler of Linux and runs on x86, amd64, and ppc32 architectures. Valgrind contains a core that provides a virtual CPU to run programs, and a series of tools that perform debugging, profiling, and similar tasks. Valgrind is highly modular, so developers or users can add new tools to it without breaking existing structures.

Valgrind's official website is: (link: http://valgrind.org)
You can download the latest valgrind from its website, which is open source and free.

Introduce a,

Valgrind contains several standard tools:

1, memcheck

Problems with memory management in the memcheck probe program. It checks all reads/writes to memory and intercepts all malloc/new/free/delete calls. So memcheck can detect the following problems:

1) use uninitialized memory
2) read/write memory that has been freed
3) read/write memory out of bounds
4) improper read/write memory stack space
5) memory leak
6) malloc/new/new[] and free/delete/delete[] do not match.

2, cachegrind

Cachegrind is a cache profiler. It simulates the L1, D1, and L2 caches of the executing CPU, so it can accurately indicate that the caches in the code were not hit. If you want, it can print out the number of cache misses, the memory references, and the summary of each line of code, function, module, and program that missed. If you ask for more detailed information, it can print out the number of misses per line of machine code. On x86 and amd64, cachegrind automatically detects the machine's cache configuration through the CPUID, so in most cases it doesn't require any more configuration information.

3, helgrind

Helgrind looks for race data in multithreaded programs. Helgrind looks up memory addresses that are accessed by more than one thread, but without a consistent lock. This means that the addresses are not synchronized when accessed between multiple threads, which can cause timing problems that are difficult to find.

What does valgrind do with your program

Valgrind is designed to be non-invasive and works directly on the executable file, so you don't need to recompile, connect, and modify your program before checking. Checking a program is as simple as executing the following command

 valgrind --tool=tool_name program_name 

For example, to do memory checking on the ls -l command, simply execute the following command

valgrind --tool=memcheck ls -l 

Regardless of which tool you use, valgrind always takes control of your program before you start, reading debugging information from an executable library. The program is then run on a virtual CPU provided by the valgrind core. Valgrind processes the code according to the tool of choice, which adds inspection code to the code and returns it as final code to the valgrind core, which runs the code.

If you want to check for a memory leak, simply add the leak-check=yes command

valgrind --tool=memcheck --leak-check=yes ls -l 

The amount of code added between different tools varies greatly. At the end of each scope, memcheck adds code to check the access and value calculations of each chunk of memory, increasing the size of the code by at least 12 times and running 25 to 50 times slower than usual.

Every instruction in the valgrind emulator executes, so check tools and profiling tools work not only for your application, but also for Shared libraries, GNU C libraries, and X client libraries.

Three, start now

First, turn on debug mode (the -g option of the GCC compiler) when compiling the program. Without debugging information, even the best valgrind tool will be able to guess which function a particular code belongs to. Turn on the debug option and compile before checking with valgrind. Valgrind will give you a detailed report, such as which line of code has a memory leak.

Another option to consider when checking a C++ program is -fno-inline. It makes the chain of function calls clear, which reduces the clutter you get when browsing large C++ programs. For example, when using this option, it's easy to check openoffice with memcheck. Of course, you probably won't do this, but using this option allows valgrind to generate more accurate error reports and reduce clutter.

Some compilation optimizations, such as -o2 or higher optimizations, may cause memcheck to submit an incorrect uninitialized report, so it is best not to use optimizations at compile time in order to make valgrind's report more accurate.

If the program is started with a script, you can modify the code in the script that started the program, or you can use the trace-children=yes option to run the script.

The following is the output report of the ls -l command checked with memcheck, and the following command is executed under the terminal

The same code at the page code block index 1

The program prints out the results of the ls-l command, and the final check report of valgrind is as follows:

= = = = 4187
= = 4187 = = ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from 2)
==4187== malloc/free: in use at exit: 15,154 bytes in 105 blocks.
= = 4187 = = malloc/free: 310 allocs, 205 frees, 60093 bytes allocated.
==4187== For counts of detected errors, rerun with: -v
==4187== searching for Pointers to 105 not-freed blocks.
= = 4187 = = checked 145292 bytes.
= = = = 4187
= = = = 4187 LEAK the SUMMARY:
==4187== definitely lost: 0 bytes in 0 blocks.
==4187== noisy lost: 0 bytes in 0 blocks.
= = 4187 = = still reachable: 15154 bytes in 105 blocks.
= = = = 4187 suppressed: 0 bytes in 0 blocks.
= = 4187 = = Reachable blocks (those to which a pointer was found) are not to.
= = = = 4187 To see them, and rerun with: � show - reachable = yes

Here "4187" refers to the process ID that performs ls-l, which is useful for distinguishing reports from different processes. Memcheck reports how much memory was allocated and freed, how much memory was leaked, how much memory access was still accessible, and how many bytes of memory were checked.

Here are two examples of using valgrind for memory checking

Example 1 (test.c) :


#include <string.h> 

int main(int argc, char *argv[]) 
{ 
    char *ptr; 

    ptr = (char*) malloc(10); 
    strcpy(ptr, "01234567890"); 

    return 0; 
}

compiler

 gcc -g -o test test.c 

Execute the command with valgrind

valgrind --tool=memcheck --leak-check=yes ./test 

Report the following

==4270== Memcheck, a memory error detector.
==4270== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==4270== Using LibVEX rev 1606, a library for dynamic binary translation.
==4270== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==4270== Using valgrind-3.2.0, a dynamic binary instrumentation framework.
==4270== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==4270== For more details, rerun with: -v
= = = = 4270
==4270== Invalid write of size 1
==4270== at 0 x 4006190: strcpy (Mc_replace_strmem.c :271)
==4270== by 0x80483DB: main (test.c:8)
== Address 0 x 4023032 is 0 bytes after a block of size 10 alloc'd
==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)
==4270== by 0x80483C5: main (test.c:7)
= = = = 4270
==4270== Invalid write of size 1
==4270== at 0x400619C: strcpy (Mc_replace_strmem.c :271)
==4270== by 0x80483DB: main (test.c:8)
== Address 0 x 4023033 is 1 bytes after a block of size 10 alloc'd
==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)
==4270== by 0x80483C5: main (test.c:7)
= = = = 4270
= = 4270 = = ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 12 from 1)
==4270== malloc/free: in use at exit: 10 bytes in 1 blocks.
= = 4270 = = malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
==4270== For counts of detected errors, rerun with: -v
== searching for Pointers to 1 not-freed blocks.
= = 4270 = = checked 51496 bytes.
= = = = 4270
= = = = 4270
==4270== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)
==4270== by 0x80483C5: main (test.c:7)
= = = = 4270
= = = = 4270 LEAK the SUMMARY:
==4270== definitely lost: 10 bytes in 1 blocks.
Possibly lost: 0 bytes in 0 blocks ==4270== noisy lost: 0 bytes in 0 blocks.
= = 4270 = = still reachable: 0 bytes in 0 blocks.
= = = = 4270 suppressed: 0 bytes in 0 blocks.
= = 4270 = = Reachable blocks (those to which a pointer was found) are not to.
= = = = 4270 To see them, and rerun with: � show - reachable = yes

As you can see from this report, the process number is 4270. Line 8 of test.c oversteps the write memory. The strcpy function is responsible for the write memory overstep.
Line 7 leaks 10 bytes of memory, caused by the malloc function.

Example 2 (test2.c)


#include <stdio.h> 

int foo(int x) 
{ 
    if (x < 0) { 
        printf("%d ", x); 
    } 

    return 0; 
} 

int main(int argc, char *argv[]) 
{ 
    int x; 

    foo(x); 

    return 0; 
}

compiler

gcc -g -o test2 test2.c 

Use valgrind for memory checking

valgrind --tool=memcheck ./test2 

The output report is as follows

==4285== Memcheck, a memory error detector.
==4285== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==4285== Using LibVEX rev 1606, a library for dynamic binary translation.
==4285== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==4285== Using valgrind-3.2.0, a dynamic binary instrumentation framework.
==4285== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==4285== For more details, rerun with: -v
= = = = 4285
== Conditional jump or move depends on uninitialised value(s)
==4285== at 0 x 8048372: foo (test2.c:5)
==4285== by 0x80483B4: main (test2.c:16)
= = 4285 = = p p
= = 4285 = = ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 12 from 1)
==4285== malloc/free: in use at exit: 0 bytes in 0 blocks.
= = 4285 = = malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==4285== For counts of detected errors, rerun with: -v
==4285== All heap blocks were freed -- no leaks are possible.

From this report we can see that the PID of the process is 4285, the foo function is called on line 16 of the test2.c file, and the foo function USES an uninitialized variable on line 5 of the test2.c file.

Valgrind also has a number of options, including the man man page of valgrind and the online documentation on the valgrind official website.


Related articles: