Your program is then run on a synthetic CPU provided by the Valgrind core. As new code is executed for the first time, the core hands the code to the selected tool. The tool adds its own instrumentation code to this and hands the result back to the core, which coordinates the continued execution of this instrumented code.
The amount of instrumentation code added varies widely between tools. At one end of the scale, Memcheck adds code to check every memory access and every value computed, making it run 10-50 times slower than natively. At the other end of the spectrum, the minimal tool, called Nulgrind, adds no instrumentation at all and causes in total "only" about a 4 times slowdown.
Valgrind simulates every single instruction your program executes. Because of this, the active tool checks, or profiles, not only the code in your application but also in all supporting dynamically-linked libraries, including the C library, graphical libraries, and so on.
First off, consider
whether it might be beneficial to recompile your application and supporting libraries with debugging info enabled (the -g
option).
Without debugging info, the best Valgrind tools will be able to do is guess which function a particular piece of code belongs to, which makes both error messages and profiling output nearly useless. With -g
,
you'll get messages which point directly to the relevant source code lines.
If you are planning to use Memcheck: On rare occasions, compiler optimisations (at -O2
and
above, and sometimes -O1
) have been observed to generate code
which fools Memcheck into wrongly reporting uninitialised value errors, or missing uninitialised value errors. We have looked in detail into fixing this, and unfortunately the result is that doing so would give a further significant slowdown in what is already
a slow tool. So the best solution is to turn off optimisation altogether. Since this often makes things unmanageably slow, a reasonable compromise is to use -O
.
This gets you the majority of the benefits of higher optimisation levels whilst keeping relatively small the chances of false positives or false negatives from Memcheck.
Compile your program with -g
to include debugging information so that Memcheck's error messages include exact line numbers. Using -O0
is
also a good idea, if you can tolerate the slowdown. With -O1
line numbers in error messages can be inaccurate, although generally speaking running Memcheck on code compiled at -O1
works
fairly well, and the speed improvement compared to running -O0
is quite significant. Use of -O2
and above is not recommended as Memcheck occasionally
reports uninitialised-value errors which don't really exist.
Memcheck also reports uses of uninitialised values, most commonly with the message "Conditional jump or move depends on uninitialised value(s)".
It can be difficult to determine the root cause of these errors. Try using the --track-origins=yes
to
get extra information. This makes Memcheck run slower, but the extra information you get often saves a lot of time figuring out where the uninitialised values are coming from.
--redzone-size=<number> [default: depends on the tool]
Valgrind's malloc, realloc,
etc,
add padding blocks before and after each heap block allocated by the program being run. Such padding blocks are called
redzones. The default value for the redzone size depends on the tool. For example, Memcheck adds and protects a minimum of 16 bytes before and after each block allocated by the client.
This allows it to detect block underruns or overruns of up to 16 bytes.
Increasing the redzone size makes it possible to detect overruns of larger distances, but increases the amount of memory used by Valgrind. Decreasing the redzone size will reduce the memory needed by Valgrind but also reduces the chances of detecting over/underruns, so is not recommended.
2.6.7. Setting Default Options
Note that Valgrind also reads options from three places:
-
The file
~/.valgrindrc
-
The environment variable
$VALGRIND_OPTS
-
The file
./.valgrindrc
These are processed in the given order, before the command-line options. Options processed later override those processed earlier; for example, options in ./.valgrindrc
will take precedence over
those in ~/.valgrindrc
.
Please note that the ./.valgrindrc
file is ignored if it is marked as world writeable or not owned by the current user. This is because the ./.valgrindrc
can
contain options that are potentially harmful or can be used by a local attacker to execute code under your user account.
Any tool-specific options put in $VALGRIND_OPTS
or the .valgrindrc
files should be prefixed with the tool name and a colon. For
example, if you want Memcheck to always do leak checking, you can put the following entry in ~/.valgrindrc
:
--memcheck:leak-check=yes
This will be ignored if any tool other than Memcheck is run. Without the memcheck:
part, this will cause problems if you select other tools that don't understand --leak-check=yes
.
2.7. Support for Threads
Threaded programs are fully supported.
The main thing to point out with respect to threaded programs is that your program will use the native threading library, but Valgrind serialises execution so that only one (kernel) thread is running at a time. This approach avoids the horrible implementation problems of implementing a truly multithreaded version of Valgrind, but it does mean that threaded apps never use more than one CPU simultaneously, even if you have a multiprocessor or multicore machine.
Valgrind doesn't schedule the threads itself. It merely ensures that only one thread runs at once, using a simple locking scheme. The actual thread scheduling remains under control of the OS kernel. What this does mean, though, is that your program will see very different scheduling when run on Valgrind than it does when running normally. This is both because Valgrind is serialising the threads, and because the code runs so much slower than normal.
This difference in scheduling may cause your program to behave differently, if you have some kind of concurrency, critical race, locking, or similar, bugs. In that case you might consider using the tools Helgrind and/or DRD to track them down.
On Linux, Valgrind also supports direct use of the clone
system call, futex
and so on. clone
is
supported where either everything is shared (a thread) or nothing is shared (fork-like); partial sharing will fail.