Valgrind is licensed under the GNU General Public License,
version 2
An open-source tool for finding memory-management problems in
Linux-x86 executables.
Valgrind is closely tied to details of the CPU, operating system and to a less extent, compiler and basic C libraries. This makes it difficult to make it portable, so I have chosen at the outset to concentrate on what I believe to be a widely used platform: Red Hat Linux 7.2, on x86s. I believe that it will work without significant difficulty on other x86 GNU/Linux systems which use the 2.4 kernel and GNU libc 2.2.X, for example SuSE 7.1 and Mandrake 8.0. Red Hat 6.2 is also supported. It has worked in the past, and probably still does, on RedHat 7.1 and 6.2. Note that I haven't compiled it on RedHat 7.1 and 6.2 for a while, so they may no longer work now.
(Early Feb 02: after feedback from the KDE people it also works better on other Linuxes).
At some point in the past, Valgrind has also worked on Red Hat 6.2 (x86), thanks to the efforts of Rob Noble.
Valgrind is licensed under the GNU General Public License, version
2. Read the file LICENSE in the source distribution for details.
Valgrind takes control of your program before it starts. Debugging
information is read from the executable and associated libraries, so
that error messages can be phrased in terms of source code
locations. Your program is then run on a synthetic x86 CPU which
checks every memory access. All detected errors are written to a
log. When the program finishes, Valgrind searches for and reports on
leaked memory.
You can run pretty much any dynamically linked ELF x86 executable using
Valgrind. Programs run 25 to 50 times slower, and take a lot more
memory, than they usually would. It works well enough to run large
programs. For example, the Konqueror web browser from the KDE Desktop
Environment, version 2.1.1, runs slowly but usably on Valgrind.
Valgrind simulates every single instruction your program executes.
Because of this, it finds errors not only in your application but also
in all supporting dynamically-linked (.so-format) libraries, including
the GNU C library, the X client libraries, Qt, if you work with KDE, and
so on. That often includes libraries, for example the GNU C library,
which contain memory access violations, but which you cannot or do not
want to fix.
Rather than swamping you with errors in which you are not
interested, Valgrind allows you to selectively suppress errors, by
recording them in a suppressions file which is read when Valgrind
starts up. As supplied, Valgrind comes with a suppressions file
designed to give reasonable behaviour on Red Hat 7.2 (also 7.1 and
6.2) when running text-only and simple X applications.
Section 6 shows an example of use.
1.2 What it does with your program
Valgrind is designed to be as non-intrusive as possible. It works
directly with existing executables. You don't need to recompile,
relink, or otherwise modify, the program to be checked. Simply place
the word valgrind
at the start of the command line
normally used to run the program. So, for example, if you want to run
the command ls -l
on Valgrind, simply issue the
command: valgrind ls -l
.
-g
flag). You don't have to
do this, but doing so helps Valgrind produce more accurate and less
confusing error reports. Chances are you're set up like this already,
if you intended to debug your program with GNU gdb, or some other
debugger.
Then just run your application, but place the word
valgrind
in front of your usual command-line invokation.
Note that you should run the real (machine-code) executable here. If
your application is started by, for example, a shell or perl script,
you'll need to modify it to invoke Valgrind on the real executables.
Running such scripts directly under Valgrind will result in you
getting error reports pertaining to /bin/sh
,
/usr/bin/perl
, or whatever interpreter you're using.
This almost certainly isn't what you want and can be hugely confusing.
All lines in the commentary are of the following form:
==12345== some-message-from-Valgrind
The 12345
is the process ID. This scheme makes it easy
to distinguish program output from Valgrind commentary, and also easy
to differentiate commentaries from different processes which have
become merged together, for whatever reason.
By default, Valgrind writes only essential messages to the commentary,
so as to avoid flooding you with information of secondary importance.
If you want more information about what is happening, re-run, passing
the -v
flag to Valgrind.
==25832== Invalid read of size 4 ==25832== at 0x8048724: BandMatrix::ReSize(int, int, int) (bogon.cpp:45) ==25832== by 0x80487AF: main (bogon.cpp:66) ==25832== by 0x40371E5E: __libc_start_main (libc-start.c:129) ==25832== by 0x80485D1: (within /home/sewardj/newmat10/bogon) ==25832== Address 0xBFFFF74C is not stack'd, malloc'd or free'd
This message says that the program did an illegal 4-byte read of
address 0xBFFFF74C, which, as far as it can tell, is not a valid stack
address, nor corresponds to any currently malloc'd or free'd blocks.
The read is happening at line 45 of bogon.cpp
, called
from line 66 of the same file, etc. For errors associated with an
identified malloc'd/free'd block, for example reading free'd memory,
Valgrind reports not only the location where the error happened, but
also where the associated block was malloc'd/free'd.
Valgrind remembers all error reports. When an error is detected, it is compared against old reports, to see if it is a duplicate. If so, the error is noted, but no further commentary is emitted. This avoids you being swamped with bazillions of duplicate error reports.
If you want to know how many times each error occurred, run with
the -v
option. When execution finishes, all the reports
are printed out, along with, and sorted by, their occurrence counts.
This makes it easy to see which errors have occurred most frequently.
Errors are reported before the associated operation actually happens. For example, if you program decides to read from address zero, Valgrind will emit a message to this effect, and the program will then duly die with a segmentation fault.
In general, you should try and fix errors in the order that they are reported. Not doing so can be confusing. For example, a program which copies uninitialised values to several memory locations, and later uses them, will generate several error messages. The first such error message may well give the most direct clue to the root cause of the problem.
redhat72.supp
, located in the Valgrind
installation directory.
You can modify and add to the suppressions file at your leisure, or write your own. Multiple suppression files are allowed. This is useful if part of your project contains errors you can't or don't want to fix, yet you don't want to continuously be reminded of them.
Each error to be suppressed is described very specifically, to minimise the possibility that a suppression-directive inadvertantly suppresses a bunch of similar errors which you did want to see. The suppression mechanism is designed to allow precise yet flexible specification of errors to suppress.
If you use the -v
flag, at the end of execution, Valgrind
prints out one line for each used suppression, giving its name and the
number of times it got used. Here's the suppressions used by a run of
ls -l
:
--27579-- supp: 1 socketcall.connect(serv_addr)/__libc_connect/__nscd_getgrgid_r --27579-- supp: 1 socketcall.connect(serv_addr)/__libc_connect/__nscd_getpwuid_r --27579-- supp: 6 strrchr/_dl_map_object_from_fd/_dl_map_object
valgrind [options-for-Valgrind] your-prog [options for your-prog]
Valgrind's default settings succeed in giving reasonable behaviour in most cases. Available options, in no particular order, are as follows:
--help
--version
The usual deal.
-v
Be more verbose. Gives extra information on various aspects of your program, such as: the shared objects loaded, the suppressions used, the progress of the instrumentation engine, and warnings about unusual behaviour.
--demangle=no
--demangle=yes
[the default]
Disable/enable automatic demangling (decoding) of C++ names. Enabled by default. When enabled, Valgrind will attempt to translate encoded C++ procedure names back to something approaching the original. The demangler is very incomplete and only handles the most common cases. If it fails, the mangled name is output verbatim. Note also that different name mangling schemes exist, and this demangler only works right for names created by gcc/g++ prior to version 3.0. Versions 3.0 and later use a new mangling scheme which is not yet handled by Valgrind.
Another important fact about demangling is that function names mentioned in suppressions files should be in their mangled form. Valgrind does not demangle function names when searching for applicable suppressions, because to do otherwise would make suppressions file contents dependent on the state of Valgrind's demangling machinery, and would also be slow and pointless.
--num-callers=<number>
[default=4]By default, Valgrind shows four levels of function call names to help you identify program locations. You can change that number with this option. This can help in determining the program's location in deeply-nested call chains. Note that errors are commoned up using only the top three function locations (the place in the current function, and that of its two immediate callers). So this doesn't affect the total number of errors reported.
The maximum value for this is 50. Note that higher settings will make Valgrind run a bit more slowly and take a bit more memory, but can be useful when working with programs with deeply-nested call chains.
--gdb-attach=no
[the default]--gdb-attach=yes
When enabled, Valgrind will pause after every error shown,
and print the line
---- Attach to GDB ? --- [Return/N/n/Y/y/C/c] ----
Pressing Ret
, or N
Ret
or n
Ret
, causes Valgrind not to
start GDB for this error.
Y
Ret
or y
Ret
causes Valgrind to
start GDB, for the program at this point. When you have
finished with GDB, quit from it, and the program will continue.
Trying to continue from inside GDB doesn't work.
C
Ret
or c
Ret
causes Valgrind not to
start GDB, and not to ask again.
--gdb-attach=yes
conflicts with
--trace-children=yes
. You can't use them
together. Valgrind refuses to start up in this situation.
--partial-loads-ok=yes
[the default]--partial-loads-ok=no
Controls how Valgrind handles word (4-byte) loads from
addresses for which some bytes are addressible and others
are not. When yes
(the default), such loads
do not elicit an address error. Instead, the loaded V bytes
corresponding to the illegal addresses indicate undefined, and
those corresponding to legal addresses are loaded from shadow
memory, as usual.
When no
, loads from partially
invalid addresses are treated the same as loads from completely
invalid addresses: an illegal-address error is issued,
and the resulting V bytes indicate valid data.
--sloppy-malloc=no
[the default]--sloppy-malloc=yes
When enabled, all requests for malloc/calloc are rounded up to a whole number of machine words -- in other words, made divisible by 4. For example, a request for 17 bytes of space would result in a 20-byte area being made available. This works around bugs in sloppy libraries which assume that they can safely rely on malloc/calloc requests being rounded up in this fashion. Without the workaround, these libraries tend to generate large numbers of errors when they access the ends of these areas. Valgrind snapshots dated 17 Feb 2002 and later are cleverer about this problem, and you should no longer need to use this flag.
--trace-children=no
[the default]
--trace-children=yes
When enabled, Valgrind will trace into child processes. This is confusing and usually not what you want, so is disabled by default.
--freelist-vol=<number>
[default: 1000000]
When the client program releases memory using free (in C) or delete (C++), that memory is not immediately made available for re-allocation. Instead it is marked inaccessible and placed in a queue of freed blocks. The purpose is to delay the point at which freed-up memory comes back into circulation. This increases the chance that Valgrind will be able to detect invalid accesses to blocks for some significant period of time after they have been freed.
This flag specifies the maximum total size, in bytes, of the blocks in the queue. The default value is one million bytes. Increasing this increases the total amount of memory used by Valgrind but may detect invalid uses of freed blocks which would otherwise go undetected.
--logfile-fd=<number>
[default: 2, stderr]
Specifies the file descriptor on which Valgrind communicates
all of its messages. The default, 2, is the standard error
channel. This may interfere with the client's own use of
stderr. To dump Valgrind's commentary in a file without using
stderr, something like the following works well (sh/bash
syntax):
valgrind --logfile-fd=9 my_prog 9> logfile
That is: tell Valgrind to send all output to file descriptor 9,
and ask the shell to route file descriptor 9 to "logfile".
--suppressions=<filename>
[default:
/installation/directory/redhat72.supp] Specifies an extra file from which to read descriptions of errors to suppress. You may use as many extra suppressions files as you like.
--leak-check=no
[default]--leak-check=yes
When enabled, search for memory leaks when the client program finishes. A memory leak means a malloc'd block, which has not yet been free'd, but to which no pointer can be found. Such a block can never be free'd by the program, since no pointer to it exists. Leak checking is disabled by default because it tends to generate dozens of error messages.
--show-reachable=no
[default]--show-reachable=yes
When disabled, the memory leak detector only shows blocks for which it cannot find a pointer to at all, or it can only find a pointer to the middle of. These blocks are prime candidates for memory leaks. When enabled, the leak detector also reports on blocks which it could find a pointer to. Your program could, at least in principle, have freed such blocks before exit. Contrast this to blocks for which no pointer, or only an interior pointer could be found: they are more likely to indicate memory leaks, because you do not actually have a pointer to the start of the block which you can hand to free(), even if you wanted to.
--leak-resolution=low
[default]--leak-resolution=med
--leak-resolution=high
When doing leak checking, determines how willing Valgrind is
to consider different backtraces the same. When set to
low
, the default, only the first two entries need
match. When med
, four entries have to match. When
high
, all entries need to match.
For hardcore leak debugging, you probably want to use
--leak-resolution=high
together with
--num-callers=40
or some such large number. Note
however that this can give an overwhelming amount of
information, which is why the defaults are 4 callers and
low-resolution matching.
Note that the --leak-resolution=
setting does not
affect Valgrind's ability to find leaks. It only changes how
the results are presented to you.
--workaround-gcc296-bugs=no
[default]--workaround-gcc296-bugs=yes
When enabled,
assume that reads and writes some small distance below the stack
pointer %esp
are due to bugs in gcc 2.96, and does
not report them. The "small distance" is 256 bytes by default.
Note that gcc 2.96 is the default compiler on some popular Linux
distributions (RedHat 7.X, Mandrake) and so you may well need to
use this flag. Do not use it if you do not have to, as it can
cause real errors to be overlooked. A better option is to use a
gcc/g++ which works properly; 2.95.3 seems to be a good choice.
Unfortunately (27 Feb 02) it looks like g++ 3.0.4 is similarly buggy, so you may need to issue this flag if you use 3.0.4.
--single-step=no
[default]--single-step=yes
When enabled, each x86 insn is translated seperately into instrumented code. When disabled, translation is done on a per-basic-block basis, giving much better translations.
--optimise=no
--optimise=yes
[default]
When enabled, various improvements are applied to the intermediate code, mainly aimed at allowing the simulated CPU's registers to be cached in the real CPU's registers over several simulated instructions.
--instrument=no
--instrument=yes
[default]
When disabled, the translations don't actually contain any instrumentation.
--cleanup=no
--cleanup=yes
[default]
When enabled, various improvments are applied to the post-instrumented intermediate code, aimed at removing redundant value checks.
--trace-syscalls=no
[default]--trace-syscalls=yes
Enable/disable tracing of system call intercepts.
--trace-signals=no
[default]--trace-signals=yes
Enable/disable tracing of signal handling.
--trace-symtab=no
[default]--trace-symtab=yes
Enable/disable tracing of symbol table reading.
--trace-malloc=no
[default]--trace-malloc=yes
Enable/disable tracing of malloc/free (et al) intercepts.
--stop-after=<number>
[default: infinity, more or less]
After <number> basic blocks have been executed, shut down Valgrind and switch back to running the client on the real CPU.
--dump-error=<number>
[default: inactive]
After the program has exited, show gory details of the
translation of the basic block containing the <number>'th
error context. When used with --single-step=yes
,
can show the
exact x86 instruction causing an error.
--smc-check=none
--smc-check=some
[default]--smc-check=all
How carefully should Valgrind check for self-modifying code writes, so that translations can be discarded? When "none", no writes are checked. When "some", only writes resulting from moves from integer registers to memory are checked. When "all", all memory writes are checked, even those with which are no sane program would generate code -- for example, floating-point writes.
==30975== Invalid read of size 4 ==30975== at 0x40F6BBCC: (within /usr/lib/libpng.so.2.1.0.9) ==30975== by 0x40F6B804: (within /usr/lib/libpng.so.2.1.0.9) ==30975== by 0x40B07FF4: read_png_image__FP8QImageIO (kernel/qpngio.cpp:326) ==30975== by 0x40AC751B: QImageIO::read() (kernel/qimage.cpp:3621) ==30975== Address 0xBFFFF0E0 is not stack'd, malloc'd or free'd
This happens when your program reads or writes memory at a place which Valgrind reckons it shouldn't. In this example, the program did a 4-byte read at address 0xBFFFF0E0, somewhere within the system-supplied library libpng.so.2.1.0.9, which was called from somewhere else in the same library, called from line 326 of qpngio.cpp, and so on.
Valgrind tries to establish what the illegal address might relate to, since that's often useful. So, if it points into a block of memory which has already been freed, you'll be informed of this, and also where the block was free'd at.. Likewise, if it should turn out to be just off the end of a malloc'd block, a common result of off-by-one-errors in array subscripting, you'll be informed of this fact, and also where the block was malloc'd.
In this example, Valgrind can't identify the address. Actually the address is on the stack, but, for some reason, this is not a valid stack address -- it is below the stack pointer, %esp, and that isn't allowed.
Note that Valgrind only tells you that your program is about to access memory at an illegal address. It can't stop the access from happening. So, if your program makes an access which normally would result in a segmentation fault, you program will still suffer the same fate -- but you will get a message from Valgrind immediately prior to this. In this particular example, reading junk on the stack is non-fatal, and the program stays alive.
==19146== Use of uninitialised CPU condition code ==19146== at 0x402DFA94: _IO_vfprintf (_itoa.h:49) ==19146== by 0x402E8476: _IO_printf (printf.c:36) ==19146== by 0x8048472: main (tests/manuel1.c:8) ==19146== by 0x402A6E5E: __libc_start_main (libc-start.c:129)
An uninitialised-value use error is reported when your program uses a value which hasn't been initialised -- in other words, is undefined. Here, the undefined value is used somewhere inside the printf() machinery of the C library. This error was reported when running the following small program:
int main() { int x; printf ("x = %d\n", x); }
It is important to understand that your program can copy around junk (uninitialised) data to its heart's content. Valgrind observes this and keeps track of the data, but does not complain. A complaint is issued only when your program attempts to make use of uninitialised data. In this example, x is uninitialised. Valgrind observes the value being passed to _IO_printf and thence to _IO_vfprintf, but makes no comment. However, _IO_vfprintf has to examine the value of x so it can turn it into the corresponding ASCII string, and it is at this point that Valgrind complains.
Sources of uninitialised data tend to be:
==7593== Invalid free() ==7593== at 0x4004FFDF: free (ut_clientmalloc.c:577) ==7593== by 0x80484C7: main (tests/doublefree.c:10) ==7593== by 0x402A6E5E: __libc_start_main (libc-start.c:129) ==7593== by 0x80483B1: (within tests/doublefree) ==7593== Address 0x3807F7B4 is 0 bytes inside a block of size 177 free'd ==7593== at 0x4004FFDF: free (ut_clientmalloc.c:577) ==7593== by 0x80484C7: main (tests/doublefree.c:10) ==7593== by 0x402A6E5E: __libc_start_main (libc-start.c:129) ==7593== by 0x80483B1: (within tests/doublefree)
Valgrind keeps track of the blocks allocated by your program with malloc/new, so it can know exactly whether or not the argument to free/delete is legitimate or not. Here, this test program has freed the same block twice. As with the illegal read/write errors, Valgrind attempts to make sense of the address free'd. If, as here, the address is one which has previously been freed, you wil be told that -- making duplicate frees of the same block easy to spot.
Here's an example of a system call with an invalid parameter:
#include <stdlib.h> #include <unistd.h> int main( void ) { char* arr = malloc(10); (void) write( 1 /* stdout */, arr, 10 ); return 0; }
You get this complaint ...
==8230== Syscall param write(buf) lacks read permissions ==8230== at 0x4035E072: __libc_write ==8230== by 0x402A6E5E: __libc_start_main (libc-start.c:129) ==8230== by 0x80483B1: (within tests/badwrite) ==8230== by <bogus frame pointer> ??? ==8230== Address 0x3807E6D0 is 0 bytes inside a block of size 10 alloc'd ==8230== at 0x4004FEE6: malloc (ut_clientmalloc.c:539) ==8230== by 0x80484A0: main (tests/badwrite.c:6) ==8230== by 0x402A6E5E: __libc_start_main (libc-start.c:129) ==8230== by 0x80483B1: (within tests/badwrite)
... because the program has tried to write uninitialised junk from the malloc'd block to the standard output.
-v
):
More than 50 errors detected. Subsequent errors
will still be recorded, but in less detail than before.
More than 500 errors detected. I'm not reporting any more.
Final error counts may be inaccurate. Go fix your
program!
Warning: client exiting by calling exit(<number>).
Bye!
exit
system call, which
will immediately terminate the process. You'll get no exit-time
error summaries or leak checks. Note that this is not the same
as your program calling the ANSI C function exit()
-- that causes a normal, controlled shutdown of Valgrind.
Warning: client switching stacks?
Warning: client attempted to close Valgrind's logfile fd <number>
--logfile-fd=<number>
option to specify a different logfile file-descriptor number.
Warning: noted but unhandled ioctl <number>
ioctl
system calls, but did not modify its
memory status info (because I have not yet got round to it).
The call will still have gone through, but you may get spurious
errors after this as a result of the non-update of the memory info.
Warning: unblocking signal <number> due to
sigprocmask
longjmp
ing out of the signal handler,
and then unblocking the signal with sigprocmask
-- a standard signal-handling idiom.
Warning: bad signal number <number> in __NR_sigaction.
Warning: set address range perms: large range <number>
linux24.supp
in the directory where it is installed.
You can ask to add suppressions from another file, by specifying
--suppressions=/path/to/file.supp
.
Each suppression has the following components:
Value1
,
Value2
,
Value4
,
Value8
or
Value0
,
meaning an uninitialised-value error when
using a value of 1, 2, 4 or 8 bytes,
or the CPU's condition codes, respectively. Or:
Addr1
,
Addr2
,
Addr4
or
Addr8
, meaning an invalid address during a
memory access of 1, 2, 4 or 8 bytes respectively. Or
Param
,
meaning an invalid system call parameter error. Or
Free
, meaning an invalid or mismatching free.
free
,
__builtin_vec_delete
, etc)
Locations may be either names of shared objects or wildcards matching
function names. They begin obj:
and fun:
respectively. Function and object names to match against may use the
wildcard characters *
and ?
.
A suppression only suppresses an error when the error matches all the
details in the suppression. Here's an example:
{ __gconv_transform_ascii_internal/__mbrtowc/mbtowc Value4 fun:__gconv_transform_ascii_internal fun:__mbr*toc fun:mbtowc }
What is means is: suppress a use-of-uninitialised-value error, when
the data size is 4, when it occurs in the function
__gconv_transform_ascii_internal
, when that is called
from any function of name matching __mbr*toc
,
when that is called from
mbtowc
. It doesn't apply under any other circumstances.
The string by which this suppression is identified to the user is
__gconv_transform_ascii_internal/__mbrtowc/mbtowc.
Another example:
{ libX11.so.6.2/libX11.so.6.2/libXaw.so.7.0 Value4 obj:/usr/X11R6/lib/libX11.so.6.2 obj:/usr/X11R6/lib/libX11.so.6.2 obj:/usr/X11R6/lib/libXaw.so.7.0 }
Suppress any size 4 uninitialised-value error which occurs anywhere
in libX11.so.6.2
, when called from anywhere in the same
library, when called from anywhere in libXaw.so.7.0
. The
inexact specification of locations is regrettable, but is about all
you can hope for, given that the X11 libraries shipped with Red Hat
7.2 have had their symbol tables removed.
Note -- since the above two examples did not make it clear -- that
you can freely mix the obj:
and fun:
styles of description within a single suppression record.
The tarball is set up for a standard Red Hat 7.1 (6.2) machine. To build, just do "make". No configure script, no autoconf, no nothing.
The files needed for installation are: valgrind.so, valgring.so,
valgrind, VERSION, redhat72.supp (or redhat62.supp). You can copy
these to any directory you like. However, you then need to edit the
shell script "valgrind". On line 4, set the environment variable
VALGRIND
to point to the directory you have copied the
installation into.
See Section 4 for the known limitations of Valgrind, and for a list of programs which are known not to work on it.
The translator/instrumentor has a lot of assertions in it. They are permanently enabled, and I have no plans to disable them. If one of these breaks, please mail me!
If you get an assertion failure on the expression
chunkSane(ch)
in vg_free()
in
vg_malloc.c
, this may have happened because your program
wrote off the end of a malloc'd block, or before its beginning.
Valgrind should have emitted a proper message to that effect before
dying in this way. This is a known problem which I should fix.
Each byte in the system therefore has a 8 V bits which accompanies it wherever it goes. For example, when the CPU loads a word-size item (4 bytes) from memory, it also loads the corresponding 32 V bits from a bitmap which stores the V bits for the process' entire address space. If the CPU should later write the whole or some part of that value to memory at a different address, the relevant V bits will be stored back in the V-bit bitmap.
In short, each bit in the system has an associated V bit, which follows it around everywhere, even inside the CPU. Yes, the CPU's (integer) registers have their own V bit vectors.
Copying values around does not cause Valgrind to check for, or report on, errors. However, when a value is used in a way which might conceivably affect the outcome of your program's computation, the associated V bits are immediately checked. If any of these indicate that the value is undefined, an error is reported.
Here's an (admittedly nonsensical) example:
int i, j; int a[10], b[10]; for (i = 0; i < 10; i++) { j = a[i]; b[i] = j; }
Valgrind emits no complaints about this, since it merely copies
uninitialised values from a[]
into b[]
, and
doesn't use them in any way. However, if the loop is changed to
for (i = 0; i < 10; i++) { j += a[i]; } if (j == 77) printf("hello there\n");then Valgrind will complain, at the
if
, that the
condition depends on uninitialised values.
Most low level operations, such as adds, cause Valgrind to use the V bits for the operands to calculate the V bits for the result. Even if the result is partially or wholly undefined, it does not complain.
Checks on definedness only occur in two places: when a value is used to generate a memory address, and where control flow decision needs to be made. Also, when a system call is detected, valgrind checks definedness of parameters as required.
If a check should detect undefinedness, and error message is issued. The resulting value is subsequently regarded as well-defined. To do otherwise would give long chains of error messages. In effect, we say that undefined values are non-infectious.
This sounds overcomplicated. Why not just check all reads from memory, and complain if an undefined value is loaded into a CPU register? Well, that doesn't work well, because perfectly legitimate C programs routinely copy uninitialised values around in memory, and we don't want endless complaints about that. Here's the canonical example. Consider a struct like this:
struct S { int x; char c; }; struct S s1, s2; s1.x = 42; s1.c = 'z'; s2 = s1;
The question to ask is: how large is struct S
, in
bytes? An int is 4 bytes and a char one byte, so perhaps a struct S
occupies 5 bytes? Wrong. All (non-toy) compilers I know of will
round the size of struct S
up to a whole number of words,
in this case 8 bytes. Not doing this forces compilers to generate
truly appalling code for subscripting arrays of struct
S
's.
So s1 occupies 8 bytes, yet only 5 of them will be initialised.
For the assignment s2 = s1
, gcc generates code to copy
all 8 bytes wholesale into s2
without regard for their
meaning. If Valgrind simply checked values as they came out of
memory, it would yelp every time a structure assignment like this
happened. So the more complicated semantics described above is
necessary. This allows gcc to copy s1
into
s2
any way it likes, and a warning will only be emitted
if the uninitialised values are later used.
One final twist to this story. The above scheme allows garbage to pass through the CPU's integer registers without complaint. It does this by giving the integer registers V tags, passing these around in the expected way. This complicated and computationally expensive to do, but is necessary. Valgrind is more simplistic about floating-point loads and stores. In particular, V bits for data read as a result of floating-point loads are checked at the load instruction. So if your program uses the floating-point registers to do memory-to-memory copies, you will get complaints about uninitialised values. Fortunately, I have not yet encountered a program which (ab)uses the floating-point registers in this way.
As described above, every bit in memory or in the CPU has an associated valid-value (V) bit. In addition, all bytes in memory, but not in the CPU, have an associated valid-address (A) bit. This indicates whether or not the program can legitimately read or write that location. It does not give any indication of the validity or the data at that location -- that's the job of the V bits -- only whether or not the location may be accessed.
Every time your program reads or writes memory, Valgrind checks the A bits associated with the address. If any of them indicate an invalid address, an error is emitted. Note that the reads and writes themselves do not change the A bits, only consult them.
So how do the A bits get set/cleared? Like this:
This apparently strange choice reduces the amount of confusing information presented to the user. It avoids the unpleasant phenomenon in which memory is read from a place which is both unaddressible and contains invalid values, and, as a result, you get not only an invalid-address (read/write) error, but also a potentially large set of uninitialised-value errors, one for every time the value is used.
There is a hazy boundary case to do with multi-byte loads from
addresses which are partially valid and partially invalid. See
details of the flag --partial-loads-ok
for details.
Under the hood, dealing with signals is a real pain, and Valgrind's simulation leaves much to be desired. If your program does way-strange stuff with signals, bad things may happen. If so, let me know. I don't promise to fix it, but I'd at least like to be aware of it.
For each such block, Valgrind scans the entire address space of the process, looking for pointers to the block. One of three situations may result:
The precise area of memory in which Valgrind searches for pointers is: all naturally-aligned 4-byte words for which all A bits indicate addressibility and all V bits indicated that the stored value is actually valid.
Valgrind will run x86-GNU/Linux ELF dynamically linked binaries, on a kernel 2.4.X system, subject to the following constraints:
The dynamic linker allows each .so in the process image to have an initialisation function which is run before main(). It also allows each .so to have a finalisation function run after main() exits.
When valgrind.so's initialisation function is called by the dynamic linker, the synthetic CPU to starts up. The real CPU remains locked in valgrind.so for the entire rest of the program, but the synthetic CPU returns from the initialisation function. Startup of the program now continues as usual -- the dynamic linker calls all the other .so's initialisation routines, and eventually runs main(). This all runs on the synthetic CPU, not the real one, but the client program cannot tell the difference.
Eventually main() exits, so the synthetic CPU calls valgrind.so's finalisation function. Valgrind detects this, and uses it as its cue to exit. It prints summaries of all errors detected, possibly checks for memory leaks, and then exits the finalisation routine, but now on the real CPU. The synthetic CPU has now lost control -- permanently -- so the program exits back to the OS on the real CPU, just as it would have done anyway.
On entry, Valgrind switches stacks, so it runs on its own stack. On exit, it switches back. This means that the client program continues to run on its own stack, so we can switch back and forth between running it on the simulated and real CPUs without difficulty. This was an important design decision, because it makes it easy (well, significantly less difficult) to debug the synthetic CPU.
Valgrind can optionally check writes made by the application, to see if they are writing an address contained within code which has been translated. Such a write invalidates translations of code bracketing the written address. Valgrind will discard the relevant translations, which causes them to be re-made, if they are needed again, reflecting the new updated data stored there. In this way, self modifying code is supported. In practice I have not found any Linux applications which use self-modifying-code.
The JITter translates basic blocks -- blocks of straight-line-code -- as single entities. To minimise the considerable difficulties of dealing with the x86 instruction set, x86 instructions are first translated to a RISC-like intermediate code, similar to sparc code, but with an infinite number of virtual integer registers. Initially each insn is translated seperately, and there is no attempt at instrumentation.
The intermediate code is improved, mostly so as to try and cache the simulated machine's registers in the real machine's registers over several simulated instructions. This is often very effective. Also, we try to remove redundant updates of the simulated machines's condition-code register.
The intermediate code is then instrumented, giving more intermediate code. There are a few extra intermediate-code operations to support instrumentation; it is all refreshingly simple. After instrumentation there is a cleanup pass to remove redundant value checks.
This gives instrumented intermediate code which mentions arbitrary numbers of virtual registers. A linear-scan register allocator is used to assign real registers and possibly generate spill code. All of this is still phrased in terms of the intermediate code. This machinery is inspired by the work of Reuben Thomas (MITE).
Then, and only then, is the final x86 code emitted. The intermediate code is carefully designed so that x86 code can be generated from it without need for spare registers or other inconveniences.
The translations are managed using a traditional LRU-based caching scheme. The translation cache has a default size of about 14MB.
When such a signal arrives, Valgrind's own handler catches it, and notes the fact. At a convenient safe point in execution, Valgrind builds a signal delivery frame on the client's stack and runs its handler. If the handler longjmp()s, there is nothing more to be said. If the handler returns, Valgrind notices this, zaps the delivery frame, and carries on where it left off before delivering the signal.
The purpose of this nonsense is that setting signal handlers essentially amounts to giving callback addresses to the Linux kernel. We can't allow this to happen, because if it did, signal handlers would run on the real CPU, not the simulated one. This means the checking machinery would not operate during the handler run, and, worse, memory permissions maps would not be updated, which could cause spurious error reports once the handler had returned.
An even worse thing would happen if the signal handler longjmp'd rather than returned: Valgrind would completely lose control of the client program.
Upshot: we can't allow the client to install signal handlers directly. Instead, Valgrind must catch, on behalf of the client, any signal the client asks to catch, and must delivery it to the client on the simulated CPU, not the real one. This involves considerable gruesome fakery; see vg_signals.c for details.
sewardj@phoenix:~/newmat10$ ~/Valgrind-6/valgrind -v ./bogon ==25832== Valgrind 0.10, a memory error detector for x86 RedHat 7.1. ==25832== Copyright (C) 2000-2001, and GNU GPL'd, by Julian Seward. ==25832== Startup, with flags: ==25832== --suppressions=/home/sewardj/Valgrind/redhat71.supp ==25832== reading syms from /lib/ld-linux.so.2 ==25832== reading syms from /lib/libc.so.6 ==25832== reading syms from /mnt/pima/jrs/Inst/lib/libgcc_s.so.0 ==25832== reading syms from /lib/libm.so.6 ==25832== reading syms from /mnt/pima/jrs/Inst/lib/libstdc++.so.3 ==25832== reading syms from /home/sewardj/Valgrind/valgrind.so ==25832== reading syms from /proc/self/exe ==25832== loaded 5950 symbols, 142333 line number locations ==25832== ==25832== Invalid read of size 4 ==25832== at 0x8048724: _ZN10BandMatrix6ReSizeEiii (bogon.cpp:45) ==25832== by 0x80487AF: main (bogon.cpp:66) ==25832== by 0x40371E5E: __libc_start_main (libc-start.c:129) ==25832== by 0x80485D1: (within /home/sewardj/newmat10/bogon) ==25832== Address 0xBFFFF74C is not stack'd, malloc'd or free'd ==25832== ==25832== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) ==25832== malloc/free: in use at exit: 0 bytes in 0 blocks. ==25832== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==25832== For a detailed leak analysis, rerun with: --leak-check=yes ==25832== ==25832== exiting, did 1881 basic blocks, 0 misses. ==25832== 223 translations, 3626 bytes in, 56801 bytes out.
The GCC folks fixed this about a week before gcc-3.0 shipped.