mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
669 lines
28 KiB
XML
669 lines
28 KiB
XML
<?xml version="1.0"?> <!-- -*- sgml -*- -->
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
|
[ <!ENTITY % vg-entities SYSTEM "vg-entities.xml"> %vg-entities; ]>
|
|
|
|
|
|
<chapter id="manual-core-adv" xreflabel="Valgrind's core: advanced topics">
|
|
<title>Using and understanding the Valgrind core: Advanced Topics</title>
|
|
|
|
<para>This chapter describes advanced aspects of the Valgrind core
|
|
services, which are mostly of interest to power users who wish to
|
|
customise and modify Valgrind's default behaviours in certain useful
|
|
ways. The subjects covered are:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem><para>The "Client Request" mechanism</para></listitem>
|
|
<listitem><para>Function Wrapping</para></listitem>
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<sect1 id="manual-core-adv.clientreq"
|
|
xreflabel="The Client Request mechanism">
|
|
<title>The Client Request mechanism</title>
|
|
|
|
<para>Valgrind has a trapdoor mechanism via which the client
|
|
program can pass all manner of requests and queries to Valgrind
|
|
and the current tool. Internally, this is used extensively
|
|
to make various things work, although that's not visible from the
|
|
outside.</para>
|
|
|
|
<para>For your convenience, a subset of these so-called client
|
|
requests is provided to allow you to tell Valgrind facts about
|
|
the behaviour of your program, and also to make queries.
|
|
In particular, your program can tell Valgrind about things that it
|
|
otherwise would not know, leading to better results.
|
|
</para>
|
|
|
|
<para>Clients need to include a header file to make this work.
|
|
Which header file depends on which client requests you use. Some
|
|
client requests are handled by the core, and are defined in the
|
|
header file <filename>valgrind/valgrind.h</filename>. Tool-specific
|
|
header files are named after the tool, e.g.
|
|
<filename>valgrind/memcheck.h</filename>. Each tool-specific header file
|
|
includes <filename>valgrind/valgrind.h</filename> so you don't need to
|
|
include it in your client if you include a tool-specific header. All header
|
|
files can be found in the <literal>include/valgrind</literal> directory of
|
|
wherever Valgrind was installed.</para>
|
|
|
|
<para>The macros in these header files have the magical property
|
|
that they generate code in-line which Valgrind can spot.
|
|
However, the code does nothing when not run on Valgrind, so you
|
|
are not forced to run your program under Valgrind just because you
|
|
use the macros in this file. Also, you are not required to link your
|
|
program with any extra supporting libraries.</para>
|
|
|
|
<para>The code added to your binary has negligible performance impact:
|
|
on x86, amd64, ppc32, ppc64 and ARM, the overhead is 6 simple integer
|
|
instructions and is probably undetectable except in tight loops.
|
|
However, if you really wish to compile out the client requests, you
|
|
can compile with <option>-DNVALGRIND</option> (analogous to
|
|
<option>-DNDEBUG</option>'s effect on
|
|
<function>assert</function>).
|
|
</para>
|
|
|
|
<para>You are encouraged to copy the <filename>valgrind/*.h</filename> headers
|
|
into your project's include directory, so your program doesn't have a
|
|
compile-time dependency on Valgrind being installed. The Valgrind headers,
|
|
unlike most of the rest of the code, are under a BSD-style license so you may
|
|
include them without worrying about license incompatibility.</para>
|
|
|
|
<para>Here is a brief description of the macros available in
|
|
<filename>valgrind.h</filename>, which work with more than one
|
|
tool (see the tool-specific documentation for explanations of the
|
|
tool-specific macros).</para>
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>RUNNING_ON_VALGRIND</computeroutput></command>:</term>
|
|
<listitem>
|
|
<para>Returns 1 if running on Valgrind, 0 if running on the
|
|
real CPU. If you are running Valgrind on itself, returns the
|
|
number of layers of Valgrind emulation you're running on.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_DISCARD_TRANSLATIONS</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Discards translations of code in the specified address
|
|
range. Useful if you are debugging a JIT compiler or some other
|
|
dynamic code generation system. After this call, attempts to
|
|
execute code in the invalidated address range will cause
|
|
Valgrind to make new translations of that code, which is
|
|
probably the semantics you want. Note that code invalidations
|
|
are expensive because finding all the relevant translations
|
|
quickly is very difficult, so try not to call it often.
|
|
Note that you can be clever about
|
|
this: you only need to call it when an area which previously
|
|
contained code is overwritten with new code. You can choose
|
|
to write code into fresh memory, and just call this
|
|
occasionally to discard large chunks of old code all at
|
|
once.</para>
|
|
<para>
|
|
Alternatively, for transparent self-modifying-code support,
|
|
use<option>--smc-check=all</option>, or run
|
|
on ppc32/Linux, ppc64/Linux or ARM/Linux.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_COUNT_ERRORS</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Returns the number of errors found so far by Valgrind. Can be
|
|
useful in test harness code when combined with the
|
|
<option>--log-fd=-1</option> option; this runs Valgrind silently,
|
|
but the client program can detect when errors occur. Only useful
|
|
for tools that report errors, e.g. it's useful for Memcheck, but for
|
|
Cachegrind it will always return zero because Cachegrind doesn't
|
|
report errors.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_MALLOCLIKE_BLOCK</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>If your program manages its own memory instead of using
|
|
the standard <function>malloc</function> /
|
|
<function>new</function> /
|
|
<function>new[]</function>, tools that track
|
|
information about heap blocks will not do nearly as good a
|
|
job. For example, Memcheck won't detect nearly as many
|
|
errors, and the error messages won't be as informative. To
|
|
improve this situation, use this macro just after your custom
|
|
allocator allocates some new memory. See the comments in
|
|
<filename>valgrind.h</filename> for information on how to use
|
|
it.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_FREELIKE_BLOCK</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>This should be used in conjunction with
|
|
<computeroutput>VALGRIND_MALLOCLIKE_BLOCK</computeroutput>.
|
|
Again, see <filename>valgrind.h</filename> for
|
|
information on how to use it.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_RESIZEINPLACE_BLOCK</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Informs a Valgrind tool that the size of an allocated block has been
|
|
modified but not its address. See <filename>valgrind.h</filename> for
|
|
more information on how to use it.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>
|
|
<command><computeroutput>VALGRIND_CREATE_MEMPOOL</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_DESTROY_MEMPOOL</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_MEMPOOL_ALLOC</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_MEMPOOL_FREE</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_MOVE_MEMPOOL</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_MEMPOOL_CHANGE</computeroutput></command>,
|
|
<command><computeroutput>VALGRIND_MEMPOOL_EXISTS</computeroutput></command>:
|
|
</term>
|
|
<listitem>
|
|
<para>These are similar to
|
|
<computeroutput>VALGRIND_MALLOCLIKE_BLOCK</computeroutput> and
|
|
<computeroutput>VALGRIND_FREELIKE_BLOCK</computeroutput>
|
|
but are tailored towards code that uses memory pools. See
|
|
<xref linkend="mc-manual.mempools"/> for a detailed description.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_NON_SIMD_CALL[0123]</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Executes a function in the client program on the
|
|
<emphasis>real</emphasis> CPU, not the virtual CPU that Valgrind
|
|
normally runs code on. The function must take an integer (holding a
|
|
thread ID) as the first argument and then 0, 1, 2 or 3 more arguments
|
|
(depending on which client request is used). These are used in various
|
|
ways internally to Valgrind. They might be useful to client
|
|
programs.</para>
|
|
|
|
<para><command>Warning:</command> Only use these if you
|
|
<emphasis>really</emphasis> know what you are doing. They aren't
|
|
entirely reliable, and can cause Valgrind to crash. See
|
|
<filename>valgrind.h</filename> for more details.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_PRINTF(format, ...)</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Print a printf-style message to the Valgrind log file. The
|
|
message is prefixed with the PID between a pair of
|
|
<computeroutput>**</computeroutput> markers. (Like all client requests,
|
|
nothing is output if the client program is not running under Valgrind.)
|
|
Output is not produced until a newline is encountered, or subsequent
|
|
Valgrind output is printed; this allows you to build up a single line of
|
|
output over multiple calls. Returns the number of characters output,
|
|
excluding the PID prefix.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_PRINTF_BACKTRACE(format, ...)</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Like <computeroutput>VALGRIND_PRINTF</computeroutput> (in
|
|
particular, the return value is identical), but prints a stack backtrace
|
|
immediately afterwards.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_STACK_REGISTER(start, end)</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Registers a new stack. Informs Valgrind that the memory range
|
|
between start and end is a unique stack. Returns a stack identifier
|
|
that can be used with other
|
|
<computeroutput>VALGRIND_STACK_*</computeroutput> calls.</para>
|
|
<para>Valgrind will use this information to determine if a change to
|
|
the stack pointer is an item pushed onto the stack or a change over
|
|
to a new stack. Use this if you're using a user-level thread package
|
|
and are noticing spurious errors from Valgrind about uninitialized
|
|
memory reads.</para>
|
|
|
|
<para><command>Warning:</command> Unfortunately, this client request is
|
|
unreliable and best avoided.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_STACK_DEREGISTER(id)</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Deregisters a previously registered stack. Informs
|
|
Valgrind that previously registered memory range with stack id
|
|
<computeroutput>id</computeroutput> is no longer a stack.</para>
|
|
|
|
<para><command>Warning:</command> Unfortunately, this client request is
|
|
unreliable and best avoided.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><command><computeroutput>VALGRIND_STACK_CHANGE(id, start, end)</computeroutput>:</command></term>
|
|
<listitem>
|
|
<para>Changes a previously registered stack. Informs
|
|
Valgrind that the previously registered stack with stack id
|
|
<computeroutput>id</computeroutput> has changed its start and end
|
|
values. Use this if your user-level thread package implements
|
|
stack growth.</para>
|
|
|
|
<para><command>Warning:</command> Unfortunately, this client request is
|
|
unreliable and best avoided.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
|
|
<sect1 id="manual-core-adv.wrapping" xreflabel="Function Wrapping">
|
|
<title>Function wrapping</title>
|
|
|
|
<para>
|
|
Valgrind allows calls to some specified functions to be intercepted and
|
|
rerouted to a different, user-supplied function. This can do whatever it
|
|
likes, typically examining the arguments, calling onwards to the original,
|
|
and possibly examining the result. Any number of functions may be
|
|
wrapped.</para>
|
|
|
|
<para>
|
|
Function wrapping is useful for instrumenting an API in some way. For
|
|
example, Helgrind wraps functions in the POSIX pthreads API so it can know
|
|
about thread status changes, and the core is able to wrap
|
|
functions in the MPI (message-passing) API so it can know
|
|
of memory status changes associated with message arrival/departure.
|
|
Such information is usually passed to Valgrind by using client
|
|
requests in the wrapper functions, although the exact mechanism may vary.
|
|
</para>
|
|
|
|
<sect2 id="manual-core-adv.wrapping.example" xreflabel="A Simple Example">
|
|
<title>A Simple Example</title>
|
|
|
|
<para>Supposing we want to wrap some function</para>
|
|
|
|
<programlisting><![CDATA[
|
|
int foo ( int x, int y ) { return x + y; }]]></programlisting>
|
|
|
|
<para>A wrapper is a function of identical type, but with a special name
|
|
which identifies it as the wrapper for <computeroutput>foo</computeroutput>.
|
|
Wrappers need to include
|
|
supporting macros from <filename>valgrind.h</filename>.
|
|
Here is a simple wrapper which prints the arguments and return value:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
#include <stdio.h>
|
|
#include "valgrind.h"
|
|
int I_WRAP_SONAME_FNNAME_ZU(NONE,foo)( int x, int y )
|
|
{
|
|
int result;
|
|
OrigFn fn;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
printf("foo's wrapper: args %d %d\n", x, y);
|
|
CALL_FN_W_WW(result, fn, x,y);
|
|
printf("foo's wrapper: result %d\n", result);
|
|
return result;
|
|
}
|
|
]]></programlisting>
|
|
|
|
<para>To become active, the wrapper merely needs to be present in a text
|
|
section somewhere in the same process' address space as the function
|
|
it wraps, and for its ELF symbol name to be visible to Valgrind. In
|
|
practice, this means either compiling to a
|
|
<computeroutput>.o</computeroutput> and linking it in, or
|
|
compiling to a <computeroutput>.so</computeroutput> and
|
|
<computeroutput>LD_PRELOAD</computeroutput>ing it in. The latter is more
|
|
convenient in that it doesn't require relinking.</para>
|
|
|
|
<para>All wrappers have approximately the above form. There are three
|
|
crucial macros:</para>
|
|
|
|
<para><computeroutput>I_WRAP_SONAME_FNNAME_ZU</computeroutput>:
|
|
this generates the real name of the wrapper.
|
|
This is an encoded name which Valgrind notices when reading symbol
|
|
table information. What it says is: I am the wrapper for any function
|
|
named <computeroutput>foo</computeroutput> which is found in
|
|
an ELF shared object with an empty
|
|
("<computeroutput>NONE</computeroutput>") soname field. The specification
|
|
mechanism is powerful in
|
|
that wildcards are allowed for both sonames and function names.
|
|
The details are discussed below.</para>
|
|
|
|
<para><computeroutput>VALGRIND_GET_ORIG_FN</computeroutput>:
|
|
once in the the wrapper, the first priority is
|
|
to get hold of the address of the original (and any other supporting
|
|
information needed). This is stored in a value of opaque
|
|
type <computeroutput>OrigFn</computeroutput>.
|
|
The information is acquired using
|
|
<computeroutput>VALGRIND_GET_ORIG_FN</computeroutput>. It is crucial
|
|
to make this macro call before calling any other wrapped function
|
|
in the same thread.</para>
|
|
|
|
<para><computeroutput>CALL_FN_W_WW</computeroutput>: eventually we will
|
|
want to call the function being
|
|
wrapped. Calling it directly does not work, since that just gets us
|
|
back to the wrapper and leads to an infinite loop. Instead, the result
|
|
lvalue,
|
|
<computeroutput>OrigFn</computeroutput> and arguments are
|
|
handed to one of a family of macros of the form
|
|
<computeroutput>CALL_FN_*</computeroutput>. These
|
|
cause Valgrind to call the original and avoid recursion back to the
|
|
wrapper.</para>
|
|
</sect2>
|
|
|
|
<sect2 id="manual-core-adv.wrapping.specs" xreflabel="Wrapping Specifications">
|
|
<title>Wrapping Specifications</title>
|
|
|
|
<para>This scheme has the advantage of being self-contained. A library of
|
|
wrappers can be compiled to object code in the normal way, and does
|
|
not rely on an external script telling Valgrind which wrappers pertain
|
|
to which originals.</para>
|
|
|
|
<para>Each wrapper has a name which, in the most general case says: I am the
|
|
wrapper for any function whose name matches FNPATT and whose ELF
|
|
"soname" matches SOPATT. Both FNPATT and SOPATT may contain wildcards
|
|
(asterisks) and other characters (spaces, dots, @, etc) which are not
|
|
generally regarded as valid C identifier names.</para>
|
|
|
|
<para>This flexibility is needed to write robust wrappers for POSIX pthread
|
|
functions, where typically we are not completely sure of either the
|
|
function name or the soname, or alternatively we want to wrap a whole
|
|
set of functions at once.</para>
|
|
|
|
<para>For example, <computeroutput>pthread_create</computeroutput>
|
|
in GNU libpthread is usually a
|
|
versioned symbol - one whose name ends in, eg,
|
|
<computeroutput>@GLIBC_2.3</computeroutput>. Hence we
|
|
are not sure what its real name is. We also want to cover any soname
|
|
of the form <computeroutput>libpthread.so*</computeroutput>.
|
|
So the header of the wrapper will be</para>
|
|
|
|
<programlisting><![CDATA[
|
|
int I_WRAP_SONAME_FNNAME_ZZ(libpthreadZdsoZd0,pthreadZucreateZAZa)
|
|
( ... formals ... )
|
|
{ ... body ... }
|
|
]]></programlisting>
|
|
|
|
<para>In order to write unusual characters as valid C function names, a
|
|
Z-encoding scheme is used. Names are written literally, except that
|
|
a capital Z acts as an escape character, with the following encoding:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
Za encodes *
|
|
Zp +
|
|
Zc :
|
|
Zd .
|
|
Zu _
|
|
Zh -
|
|
Zs (space)
|
|
ZA @
|
|
ZZ Z
|
|
ZL ( # only in valgrind 3.3.0 and later
|
|
ZR ) # only in valgrind 3.3.0 and later
|
|
]]></programlisting>
|
|
|
|
<para>Hence <computeroutput>libpthreadZdsoZd0</computeroutput> is an
|
|
encoding of the soname <computeroutput>libpthread.so.0</computeroutput>
|
|
and <computeroutput>pthreadZucreateZAZa</computeroutput> is an encoding
|
|
of the function name <computeroutput>pthread_create@*</computeroutput>.
|
|
</para>
|
|
|
|
<para>The macro <computeroutput>I_WRAP_SONAME_FNNAME_ZZ</computeroutput>
|
|
constructs a wrapper name in which
|
|
both the soname (first component) and function name (second component)
|
|
are Z-encoded. Encoding the function name can be tiresome and is
|
|
often unnecessary, so a second macro,
|
|
<computeroutput>I_WRAP_SONAME_FNNAME_ZU</computeroutput>, can be
|
|
used instead. The <computeroutput>_ZU</computeroutput> variant is
|
|
also useful for writing wrappers for
|
|
C++ functions, in which the function name is usually already mangled
|
|
using some other convention in which Z plays an important role. Having
|
|
to encode a second time quickly becomes confusing.</para>
|
|
|
|
<para>Since the function name field may contain wildcards, it can be
|
|
anything, including just <computeroutput>*</computeroutput>.
|
|
The same is true for the soname.
|
|
However, some ELF objects - specifically, main executables - do not
|
|
have sonames. Any object lacking a soname is treated as if its soname
|
|
was <computeroutput>NONE</computeroutput>, which is why the original
|
|
example above had a name
|
|
<computeroutput>I_WRAP_SONAME_FNNAME_ZU(NONE,foo)</computeroutput>.</para>
|
|
|
|
<para>Note that the soname of an ELF object is not the same as its
|
|
file name, although it is often similar. You can find the soname of
|
|
an object <computeroutput>libfoo.so</computeroutput> using the command
|
|
<computeroutput>readelf -a libfoo.so | grep soname</computeroutput>.</para>
|
|
</sect2>
|
|
|
|
<sect2 id="manual-core-adv.wrapping.semantics" xreflabel="Wrapping Semantics">
|
|
<title>Wrapping Semantics</title>
|
|
|
|
<para>The ability for a wrapper to replace an infinite family of functions
|
|
is powerful but brings complications in situations where ELF objects
|
|
appear and disappear (are dlopen'd and dlclose'd) on the fly.
|
|
Valgrind tries to maintain sensible behaviour in such situations.</para>
|
|
|
|
<para>For example, suppose a process has dlopened (an ELF object with
|
|
soname) <filename>object1.so</filename>, which contains
|
|
<computeroutput>function1</computeroutput>. It starts to use
|
|
<computeroutput>function1</computeroutput> immediately.</para>
|
|
|
|
<para>After a while it dlopens <filename>wrappers.so</filename>,
|
|
which contains a wrapper
|
|
for <computeroutput>function1</computeroutput> in (soname)
|
|
<filename>object1.so</filename>. All subsequent calls to
|
|
<computeroutput>function1</computeroutput> are rerouted to the wrapper.</para>
|
|
|
|
<para>If <filename>wrappers.so</filename> is
|
|
later dlclose'd, calls to <computeroutput>function1</computeroutput> are
|
|
naturally routed back to the original.</para>
|
|
|
|
<para>Alternatively, if <filename>object1.so</filename>
|
|
is dlclose'd but <filename>wrappers.so</filename> remains,
|
|
then the wrapper exported by <filename>wrappers.so</filename>
|
|
becomes inactive, since there
|
|
is no way to get to it - there is no original to call any more. However,
|
|
Valgrind remembers that the wrapper is still present. If
|
|
<filename>object1.so</filename> is
|
|
eventually dlopen'd again, the wrapper will become active again.</para>
|
|
|
|
<para>In short, valgrind inspects all code loading/unloading events to
|
|
ensure that the set of currently active wrappers remains consistent.</para>
|
|
|
|
<para>A second possible problem is that of conflicting wrappers. It is
|
|
easily possible to load two or more wrappers, both of which claim
|
|
to be wrappers for some third function. In such cases Valgrind will
|
|
complain about conflicting wrappers when the second one appears, and
|
|
will honour only the first one.</para>
|
|
</sect2>
|
|
|
|
<sect2 id="manual-core-adv.wrapping.debugging" xreflabel="Debugging">
|
|
<title>Debugging</title>
|
|
|
|
<para>Figuring out what's going on given the dynamic nature of wrapping
|
|
can be difficult. The
|
|
<option>--trace-redir=yes</option> option makes
|
|
this possible
|
|
by showing the complete state of the redirection subsystem after
|
|
every
|
|
<function>mmap</function>/<function>munmap</function>
|
|
event affecting code (text).</para>
|
|
|
|
<para>There are two central concepts:</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem><para>A "redirection specification" is a binding of
|
|
a (soname pattern, fnname pattern) pair to a code address.
|
|
These bindings are created by writing functions with names
|
|
made with the
|
|
<computeroutput>I_WRAP_SONAME_FNNAME_{ZZ,_ZU}</computeroutput>
|
|
macros.</para></listitem>
|
|
|
|
<listitem><para>An "active redirection" is a code-address to
|
|
code-address binding currently in effect.</para></listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
<para>The state of the wrapping-and-redirection subsystem comprises a set of
|
|
specifications and a set of active bindings. The specifications are
|
|
acquired/discarded by watching all
|
|
<function>mmap</function>/<function>munmap</function>
|
|
events on code (text)
|
|
sections. The active binding set is (conceptually) recomputed from
|
|
the specifications, and all known symbol names, following any change
|
|
to the specification set.</para>
|
|
|
|
<para><option>--trace-redir=yes</option> shows the contents
|
|
of both sets following any such event.</para>
|
|
|
|
<para><option>-v</option> prints a line of text each
|
|
time an active specification is used for the first time.</para>
|
|
|
|
<para>Hence for maximum debugging effectiveness you will need to use both
|
|
options.</para>
|
|
|
|
<para>One final comment. The function-wrapping facility is closely
|
|
tied to Valgrind's ability to replace (redirect) specified
|
|
functions, for example to redirect calls to
|
|
<function>malloc</function> to its
|
|
own implementation. Indeed, a replacement function can be
|
|
regarded as a wrapper function which does not call the original.
|
|
However, to make the implementation more robust, the two kinds
|
|
of interception (wrapping vs replacement) are treated differently.
|
|
</para>
|
|
|
|
<para><option>--trace-redir=yes</option> shows
|
|
specifications and bindings for both
|
|
replacement and wrapper functions. To differentiate the
|
|
two, replacement bindings are printed using
|
|
<computeroutput>R-></computeroutput> whereas
|
|
wraps are printed using <computeroutput>W-></computeroutput>.
|
|
</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="manual-core-adv.wrapping.limitations-cf"
|
|
xreflabel="Limitations - control flow">
|
|
<title>Limitations - control flow</title>
|
|
|
|
<para>For the most part, the function wrapping implementation is robust.
|
|
The only important caveat is: in a wrapper, get hold of
|
|
the <computeroutput>OrigFn</computeroutput> information using
|
|
<computeroutput>VALGRIND_GET_ORIG_FN</computeroutput> before calling any
|
|
other wrapped function. Once you have the
|
|
<computeroutput>OrigFn</computeroutput>, arbitrary
|
|
calls between, recursion between, and longjumps out of wrappers
|
|
should work correctly. There is never any interaction between wrapped
|
|
functions and merely replaced functions
|
|
(eg <function>malloc</function>), so you can call
|
|
<function>malloc</function> etc safely from within wrappers.
|
|
</para>
|
|
|
|
<para>The above comments are true for {x86,amd64,ppc32,arm}-linux. On
|
|
ppc64-linux function wrapping is more fragile due to the (arguably
|
|
poorly designed) ppc64-linux ABI. This mandates the use of a shadow
|
|
stack which tracks entries/exits of both wrapper and replacement
|
|
functions. This gives two limitations: firstly, longjumping out of
|
|
wrappers will rapidly lead to disaster, since the shadow stack will
|
|
not get correctly cleared. Secondly, since the shadow stack has
|
|
finite size, recursion between wrapper/replacement functions is only
|
|
possible to a limited depth, beyond which Valgrind has to abort the
|
|
run. This depth is currently 16 calls.</para>
|
|
|
|
<para>For all platforms ({x86,amd64,ppc32,ppc64,arm}-linux) all the above
|
|
comments apply on a per-thread basis. In other words, wrapping is
|
|
thread-safe: each thread must individually observe the above
|
|
restrictions, but there is no need for any kind of inter-thread
|
|
cooperation.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="manual-core-adv.wrapping.limitations-sigs"
|
|
xreflabel="Limitations - original function signatures">
|
|
<title>Limitations - original function signatures</title>
|
|
|
|
<para>As shown in the above example, to call the original you must use a
|
|
macro of the form <computeroutput>CALL_FN_*</computeroutput>.
|
|
For technical reasons it is impossible
|
|
to create a single macro to deal with all argument types and numbers,
|
|
so a family of macros covering the most common cases is supplied. In
|
|
what follows, 'W' denotes a machine-word-typed value (a pointer or a
|
|
C <computeroutput>long</computeroutput>),
|
|
and 'v' denotes C's <computeroutput>void</computeroutput> type.
|
|
The currently available macros are:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
CALL_FN_v_v -- call an original of type void fn ( void )
|
|
CALL_FN_W_v -- call an original of type long fn ( void )
|
|
|
|
CALL_FN_v_W -- call an original of type void fn ( long )
|
|
CALL_FN_W_W -- call an original of type long fn ( long )
|
|
|
|
CALL_FN_v_WW -- call an original of type void fn ( long, long )
|
|
CALL_FN_W_WW -- call an original of type long fn ( long, long )
|
|
|
|
CALL_FN_v_WWW -- call an original of type void fn ( long, long, long )
|
|
CALL_FN_W_WWW -- call an original of type long fn ( long, long, long )
|
|
|
|
CALL_FN_W_WWWW -- call an original of type long fn ( long, long, long, long )
|
|
CALL_FN_W_5W -- call an original of type long fn ( long, long, long, long, long )
|
|
CALL_FN_W_6W -- call an original of type long fn ( long, long, long, long, long, long )
|
|
and so on, up to
|
|
CALL_FN_W_12W
|
|
]]></programlisting>
|
|
|
|
<para>The set of supported types can be expanded as needed. It is
|
|
regrettable that this limitation exists. Function wrapping has proven
|
|
difficult to implement, with a certain apparently unavoidable level of
|
|
ickiness. After several implementation attempts, the present
|
|
arrangement appears to be the least-worst tradeoff. At least it works
|
|
reliably in the presence of dynamic linking and dynamic code
|
|
loading/unloading.</para>
|
|
|
|
<para>You should not attempt to wrap a function of one type signature with a
|
|
wrapper of a different type signature. Such trickery will surely lead
|
|
to crashes or strange behaviour. This is not a limitation
|
|
of the function wrapping implementation, merely a reflection of the
|
|
fact that it gives you sweeping powers to shoot yourself in the foot
|
|
if you are not careful. Imagine the instant havoc you could wreak by
|
|
writing a wrapper which matched any function name in any soname - in
|
|
effect, one which claimed to be a wrapper for all functions in the
|
|
process.</para>
|
|
</sect2>
|
|
|
|
<sect2 id="manual-core-adv.wrapping.examples" xreflabel="Examples">
|
|
<title>Examples</title>
|
|
|
|
<para>In the source tree,
|
|
<filename>memcheck/tests/wrap[1-8].c</filename> provide a series of
|
|
examples, ranging from very simple to quite advanced.</para>
|
|
|
|
<para><filename>mpi/libmpiwrap.c</filename> is an example
|
|
of wrapping a big, complex API (the MPI-2 interface). This file defines
|
|
almost 300 different wrappers.</para>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
</chapter>
|