Allows to run regression tests in an outer/inner setup.

A '3 lines how to':
   perl tests/vg_regtest --outer-valgrind=../trunk_untouched/install/bin/valgrind --all
           (the outer results for a test xxx is in xxx.outer.log)
   To run with another tool (e.g. drd), add the argument --outer-tool=drd


Still to do/things to improve:

* Most (inner) tests are successful when running under an outer
  memcheck. Need to analyse the reasons of remaining failures.

* The memcheck annotations in m_mallocfree.c can be improved:
  - A superblock is marked 'undefined', it should rather be marked
    'no access'.
  - When a free block is splitted, the remaining free block is
    not made 'no access'. Instead, it is made 'undefined'.
      => this decreases the chance to find bugs.
      => this is not very efficient (e.g. the rest of a superblock
         is often marked undefined repetitively).
    Similarly, the free block created by VG_(arena_memalign)
    is marked 'undefined'. 'No access' would be preferrable.
  - mkInuseBlock marks the new block as undefined. This is probably
    not needed, as VALGRIND_MALLOCLIKE_BLOCK will do it already.
  - VG_(arena_malloc) should give the requested size to
    VALGRIND_MALLOCLIKE_BLOCK, not the malloc usable size,
    as this decreases the chance to find buffer overrun bugs.
    But giving the requested size is tricky (see comments in
    the code).

* need to do memcheck annotations in m_poolalloc.c
   so as to allow leak checking for pool allocated elements.

* vg_regtest.in
  - should analyse the results of the outer and should
    produce a separate result for the tests for which
    the outer detects an error or a memory leak or ...


Changes done:
   README_DEVELOPERS: document the new outer/inner features.
   manual-core.xml: document the new sim-hint no-inner-prefix
   tests/outer_inner.supp: new file, containing the suppressions for inner.
   vg_regtest.in: implement new args --outer-valgrind, --outer-tool, --outer-args.
   m_mallocfree.c: annotations for memcheck.
   m_libcprint.c: handle the new sim-hint no-inner-prefix
   m_main.c: do an (early) parse of --sim-hints




git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12441
This commit is contained in:
Philippe Waroquiers 2012-03-11 22:24:03 +00:00
parent 12633f0117
commit aa50a7e4df
8 changed files with 236 additions and 26 deletions

2
NEWS
View File

@ -36,6 +36,8 @@ Release 3.8.0 (????)
and scheduling of multithreaded applications (in particular
on multiprocessor/multicore systems).
* Support to run Valgrind on Valgrind has been improved.
* ==================== FIXED BUGS ====================
The following bugs have been fixed or resolved. Note that "n-i-bz"

View File

@ -141,8 +141,7 @@ To run Valgrind under Valgrind:
(1) Check out 2 trees, "Inner" and "Outer". Inner runs the app
directly. Outer runs Inner.
(2) Configure inner with --enable-inner and build/install as
usual.
(2) Configure inner with --enable-inner and build/install as usual.
(3) Configure Outer normally and build/install as usual.
@ -169,15 +168,48 @@ Debugging the whole thing might imply to use up to 3 GDB:
The whole thing is fragile, confusing and slow, but it does work well enough
for you to get some useful performance data. Inner has most of
its output (ie. those lines beginning with "==<pid>==") prefixed with a '>',
which helps a lot.
which helps a lot. However, when running regression tests in an Outer/Inner
setup, this prefix causes the reg test diff to fail. Give
--sim-hints=no-inner-prefix to the Inner to disable the production
of the prefix in the stdout/stderr output of Inner.
At the time of writing the allocator is not annotated with client requests
so Memcheck is not as useful as it could be. It also has not been tested
much, so don't be surprised if you hit problems.
The allocator (coregrind/m_mallocfree.c) is annotated with client requests
so Memcheck can be used to find leaks and use after free in an Inner
Valgrind.
The Valgrind "big lock" is annotated with helgrind client requests
so helgrind and drd can be used to find race conditions in an Inner
Valgrind.
All this has not been tested much, so don't be surprised if you hit problems.
When using self-hosting with an outer Callgrind tool, use '--pop-on-jump'
(on the outer). Otherwise, Callgrind has much higher memory requirements.
Regression tests in an outer/inner setup:
To run all the regression tests with an outer memcheck, do :
perl test/vg_regtest --outer-valgrind=../outer/.../bin/valgrind \
--all
To run a specific regression tests with an outer memcheck, do:
perl test/vg_regtest --outer-valgrind=../outer/.../bin/valgrind \
none/tests/args.vgtest
To run regression tests with another outer tool:
perl tests/vg_regtest --outer-valgrind=../outer/.../bin/valgrind \
--outer-tool=helgrind " --all
--outer-args allows to give specific arguments to the outer tool,
replacing the default one provided by vg_regtest.
When an outer valgrind runs an inner valgrind, a regression test
produces one additional file <testname>.outer.log which contains the
errors detected by the outer valgrind. E.g. for an outer memcheck, it
contains the leaks found in the inner, for an outer helgrind or drd,
it contains the detected race conditions.
The file tests/outer_inner.supp contains suppressions for
the irrelevant or benign errors found in the inner.
Printing out problematic blocks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -396,11 +396,16 @@ static void add_to__vmessage_buf ( HChar c, void *p )
// Print one '>' in front of the messages for each level of
// self-hosting being performed.
// Do not print such '>' if sim hint "no-inner-prefix" given
// (useful to run regression tests in an outer/inner setup
// and avoid the diff failing due to these unexpected '>').
depth = RUNNING_ON_VALGRIND;
if (depth > 10)
depth = 10; // ?!?!
for (i = 0; i < depth; i++) {
b->buf[b->buf_used++] = '>';
if (depth > 0 && !VG_(strstr)(VG_(clo_sim_hints), "no-inner-prefix")) {
if (depth > 10)
depth = 10; // ?!?!
for (i = 0; i < depth; i++) {
b->buf[b->buf_used++] = '>';
}
}
if (Vg_FailMsg == b->kind) {

View File

@ -294,6 +294,7 @@ static void usage_NORETURN ( Bool debug_help )
- get the toolname (--tool=)
- set VG_(clo_max_stackframe) (--max-stackframe=)
- set VG_(clo_main_stacksize) (--main-stacksize=)
- set VG_(clo_sim_hints) (--sim-hints=)
That's all it does. The main command line processing is done below
by main_process_cmd_line_options. Note that
@ -334,6 +335,11 @@ static void early_process_cmd_line_options ( /*OUT*/Int* need_help,
// before main_process_cmd_line_options().
else if VG_INT_CLO(str, "--max-stackframe", VG_(clo_max_stackframe)) {}
else if VG_INT_CLO(str, "--main-stacksize", VG_(clo_main_stacksize)) {}
// Set up VG_(clo_sim_hints). This is needed a.o. for an inner
// running in an outer, to have "no-inner-prefix" enabled
// as early as possible.
else if VG_STR_CLO (str, "--sim-hints", VG_(clo_sim_hints)) {}
}
}
@ -451,6 +457,7 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
else if VG_STREQ( arg, "-d") {}
else if VG_STREQN(16, arg, "--max-stackframe") {}
else if VG_STREQN(16, arg, "--main-stacksize") {}
else if VG_STREQN(11, arg, "--sim-hints") {}
else if VG_STREQN(14, arg, "--profile-heap") {}
// These options are new.
@ -514,7 +521,6 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
else if VG_BOOL_CLO(arg, "--trace-syscalls", VG_(clo_trace_syscalls)) {}
else if VG_BOOL_CLO(arg, "--wait-for-gdb", VG_(clo_wait_for_gdb)) {}
else if VG_STR_CLO (arg, "--db-command", VG_(clo_db_command)) {}
else if VG_STR_CLO (arg, "--sim-hints", VG_(clo_sim_hints)) {}
else if VG_BOOL_CLO(arg, "--sym-offsets", VG_(clo_sym_offsets)) {}
else if VG_BOOL_CLO(arg, "--read-var-info", VG_(clo_read_var_info)) {}

View File

@ -42,9 +42,11 @@
#include "pub_core_threadstate.h" // For VG_INVALID_THREADID
#include "pub_core_transtab.h"
#include "pub_core_tooliface.h"
#include "valgrind.h"
//zz#include "memcheck/memcheck.h"
#include "pub_tool_inner.h"
#if defined(ENABLE_INNER_CLIENT_REQUEST)
#include "memcheck/memcheck.h"
#endif
// #define DEBUG_MALLOC // turn on heavyweight debugging machinery
// #define VERBOSE_MALLOC // make verbose, esp. in debugging machinery
@ -776,7 +778,7 @@ Superblock* newSuperblock ( Arena* a, SizeT cszB )
}
}
vg_assert(NULL != sb);
//zzVALGRIND_MAKE_MEM_UNDEFINED(sb, cszB);
INNER_REQUEST(VALGRIND_MAKE_MEM_UNDEFINED(sb, cszB));
vg_assert(0 == (Addr)sb % VG_MIN_MALLOC_SZB);
sb->n_payload_bytes = cszB - sizeof(Superblock);
sb->unsplittable = (unsplittable ? sb : NULL);
@ -1042,6 +1044,12 @@ Bool blockSane ( Arena* a, Block* b )
// The lo and hi size fields will be checked (indirectly) by the call
// to get_rz_hi_byte().
if (!a->clientmem && is_inuse_block(b)) {
// In the inner, for memcheck sake, temporarily mark redzone accessible.
INNER_REQUEST(VALGRIND_MAKE_MEM_DEFINED
(b + hp_overhead_szB() + sizeof(SizeT), a->rz_szB));
INNER_REQUEST(VALGRIND_MAKE_MEM_DEFINED
(b + get_bszB(b)
- sizeof(SizeT) - a->rz_szB, a->rz_szB));
for (i = 0; i < a->rz_szB; i++) {
if (get_rz_lo_byte(b, i) !=
(UByte)(((Addr)b&0xff) ^ REDZONE_LO_MASK))
@ -1050,6 +1058,11 @@ Bool blockSane ( Arena* a, Block* b )
(UByte)(((Addr)b&0xff) ^ REDZONE_HI_MASK))
{BLEAT("redzone-hi");return False;}
}
INNER_REQUEST(VALGRIND_MAKE_MEM_NOACCESS
(b + hp_overhead_szB() + sizeof(SizeT), a->rz_szB));
INNER_REQUEST(VALGRIND_MAKE_MEM_NOACCESS
(b + get_bszB(b)
- sizeof(SizeT) - a->rz_szB, a->rz_szB));
}
return True;
# undef BLEAT
@ -1338,7 +1351,7 @@ void mkFreeBlock ( Arena* a, Block* b, SizeT bszB, UInt b_lno )
{
SizeT pszB = bszB_to_pszB(a, bszB);
vg_assert(b_lno == pszB_to_listNo(pszB));
//zzVALGRIND_MAKE_MEM_UNDEFINED(b, bszB);
INNER_REQUEST(VALGRIND_MAKE_MEM_UNDEFINED(b, bszB));
// Set the size fields and indicate not-in-use.
set_bszB(b, mk_free_bszB(bszB));
@ -1367,7 +1380,7 @@ void mkInuseBlock ( Arena* a, Block* b, SizeT bszB )
{
UInt i;
vg_assert(bszB >= min_useful_bszB(a));
//zzVALGRIND_MAKE_MEM_UNDEFINED(b, bszB);
INNER_REQUEST(VALGRIND_MAKE_MEM_UNDEFINED(b, bszB));
set_bszB(b, mk_inuse_bszB(bszB));
set_prev_b(b, NULL); // Take off freelist
set_next_b(b, NULL); // ditto
@ -1594,7 +1607,26 @@ void* VG_(arena_malloc) ( ArenaId aid, HChar* cc, SizeT req_pszB )
v = get_block_payload(a, b);
vg_assert( (((Addr)v) & (VG_MIN_MALLOC_SZB-1)) == 0 );
/* VALGRIND_MALLOCLIKE_BLOCK(v, req_pszB, 0, False); */
// Which size should we pass to VALGRIND_MALLOCLIKE_BLOCK ?
// We have 2 possible options:
// 1. The final resulting usable size.
// 2. The initial (non-aligned) req_pszB.
// Memcheck implements option 2 easily, as the initial requested size
// is maintained in the mc_chunk data structure.
// This is not as easy in the core, as there is no such structure.
// (note: using the aligned req_pszB is not simpler than 2, as
// requesting an aligned req_pszB might still be satisfied by returning
// a (slightly) bigger block than requested if the remaining part of
// of a free block is not big enough to make a free block by itself).
// Implement Sol 2 can be done the following way:
// After having called VALGRIND_MALLOCLIKE_BLOCK, the non accessible
// redzone just after the block can be used to determine the
// initial requested size.
// Currently, not implemented => we use Option 1.
INNER_REQUEST
(VALGRIND_MALLOCLIKE_BLOCK(v,
VG_(arena_malloc_usable_size)(aid, v),
a->rz_szB, False));
/* For debugging/testing purposes, fill the newly allocated area
with a definite value in an attempt to shake out any
@ -1788,6 +1820,31 @@ void VG_(arena_free) ( ArenaId aid, void* ptr )
deferred_reclaimSuperblock (a, sb);
}
// Inform that ptr has been released. We give redzone size
// 0 instead of a->rz_szB as proper accessibility is done just after.
INNER_REQUEST(VALGRIND_FREELIKE_BLOCK(ptr, 0));
// We need to (re-)establish the minimum accessibility needed
// for free list management. E.g. if block ptr has been put in a free
// list and a neighbour block is released afterwards, the
// "lo" and "hi" portions of the block ptr will be accessed to
// glue the 2 blocks together.
// We could mark the whole block as not accessible, and each time
// transiently mark accessible the needed lo/hi parts. Not done as this
// is quite complex, for very little expected additional bug detection.
// fully unaccessible. Note that the below marks the (possibly) merged
// block, not the block corresponding to the ptr argument.
// First mark the whole block unaccessible.
INNER_REQUEST(VALGRIND_MAKE_MEM_NOACCESS(b, b_bszB));
// Then mark the relevant administrative headers as defined.
// No need to mark the heap profile portion as defined, this is not
// used for free blocks.
INNER_REQUEST(VALGRIND_MAKE_MEM_DEFINED(b + hp_overhead_szB(),
sizeof(SizeT) + sizeof(void*)));
INNER_REQUEST(VALGRIND_MAKE_MEM_DEFINED(b + b_bszB
- sizeof(SizeT) - sizeof(void*),
sizeof(SizeT) + sizeof(void*)));
} else {
// b must be first block (i.e. no unused bytes at the beginning)
vg_assert((Block*)sb_start == b);
@ -1796,6 +1853,12 @@ void VG_(arena_free) ( ArenaId aid, void* ptr )
other_b = b + b_bszB;
vg_assert(other_b-1 == (Block*)sb_end);
// Inform that ptr has been released. Redzone size value
// is not relevant (so we give 0 instead of a->rz_szB)
// as it is expected that the aspacemgr munmap will be used by
// outer to mark the whole superblock as unaccessible.
INNER_REQUEST(VALGRIND_FREELIKE_BLOCK(ptr, 0));
// Reclaim immediately the unsplittable superblock sb.
reclaimSuperblock (a, sb);
}
@ -1804,7 +1867,6 @@ void VG_(arena_free) ( ArenaId aid, void* ptr )
sanity_check_malloc_arena(aid);
# endif
//zzVALGRIND_FREELIKE_BLOCK(ptr, 0);
}
@ -1897,6 +1959,11 @@ void* VG_(arena_memalign) ( ArenaId aid, HChar* cc,
/* Give up if we couldn't allocate enough space */
if (base_p == 0)
return 0;
/* base_p was marked as allocated by VALGRIND_MALLOCLIKE_BLOCK
inside VG_(arena_malloc). We need to indicate it is free, then
we need to mark it undefined to allow the below code to access is. */
INNER_REQUEST(VALGRIND_FREELIKE_BLOCK(base_p, a->rz_szB));
INNER_REQUEST(VALGRIND_MAKE_MEM_UNDEFINED(base_p, base_pszB_req));
/* Block ptr for the block we are going to split. */
base_b = get_payload_block ( a, base_p );
@ -1949,7 +2016,8 @@ void* VG_(arena_memalign) ( ArenaId aid, HChar* cc,
vg_assert( (((Addr)align_p) % req_alignB) == 0 );
//zzVALGRIND_MALLOCLIKE_BLOCK(align_p, req_pszB, 0, False);
INNER_REQUEST(VALGRIND_MALLOCLIKE_BLOCK(align_p,
req_pszB, a->rz_szB, False));
return align_p;
}
@ -2041,8 +2109,6 @@ void* VG_(arena_calloc) ( ArenaId aid, HChar* cc,
VG_(memset)(p, 0, size);
//zzVALGRIND_MALLOCLIKE_BLOCK(p, size, 0, True);
return p;
}

View File

@ -1646,6 +1646,15 @@ need to use these.</para>
magic needed when the program being run is itself
Valgrind.</para>
</listitem>
<listitem>
<para><option>no-inner-prefix: </option> Disable printing
a prefix <option>&gt;</option> in front of each stdout or
stderr output line in an inner Valgrind being run by an
outer Valgrind. This is useful when running Valgrind
regression tests in an outer/inner setup. Note that the
prefix <option>&gt;</option> will always be printed in
front of the inner debug logging lines.</para>
</listitem>
<listitem>
<para><option>fuse-compatible: </option> Enable special
handling for certain system calls that may block in a FUSE

46
tests/outer_inner.supp Normal file
View File

@ -0,0 +1,46 @@
# errors to suppress when an outer runs an inner.
# ==31040== 16 bytes in 1 blocks are definitely lost in loss record 10 of 106
# ==31040== at 0x2803A367: vgPlain_arena_malloc (m_mallocfree.c:1599)
# ==31040== by 0x2803ADE3: vgPlain_strdup (m_mallocfree.c:2140)
# ==31040== by 0x2803208A: valgrind_main (m_main.c:437)
# ==31040== by 0x28035F3C: _start_in_C_linux (m_main.c:2799)
# ==31040== by 0x28030B4B: ??? (in install/lib/valgrind/memcheck-x86-linux)
{
"keep duplicated args forever as tools can make copies"
Memcheck:Leak
fun:vgPlain_arena_malloc
fun:vgPlain_strdup
fun:valgrind_main
}
# ==31040== 392 bytes in 1 blocks are definitely lost in loss record 58 of 106
# ==31040== at 0x2803A367: vgPlain_arena_malloc (m_mallocfree.c:1599)
# ==31040== by 0x2803A9CF: vgPlain_malloc (m_mallocfree.c:2156)
# ==31040== by 0x280738F0: vgPlain_ii_create_image (initimg-linux.c:202)
# ==31040== by 0x280327CF: valgrind_main (m_main.c:1718)
# ==31040== by 0x28035F3C: _start_in_C_linux (m_main.c:2799)
# ==31040== by 0x28030B4B: ??? (in install/lib/valgrind/memcheck-x86-linux)
{
"LD_PRELOAD_STRING inserted in env, difficult to free"
Memcheck:Leak
fun:vgPlain_arena_malloc
fun:vgPlain_malloc
fun:vgPlain_ii_create_image
fun:valgrind_main
}
# ==32749== 400 bytes in 1 blocks are definitely lost in loss record 47 of 96
# ==32749== at 0x2803D0D7: vgPlain_arena_malloc (m_mallocfree.c:1599)
# ==32749== by 0x28072DAB: vgPlain_ii_create_image (initimg-linux.c:202)
# ==32749== by 0x28036264: valgrind_main (m_main.c:1718)
# ==32749== by 0x280392FC: _start_in_C_linux (m_main.c:2799)
# ==32749== by 0x28034920: ??? (in install/lib/valgrind/memcheck-amd64-linux)
{
"LD_PRELOAD_STRING inserted in env, difficult to free"
Memcheck:Leak
fun:vgPlain_arena_malloc
fun:vgPlain_ii_create_image
fun:valgrind_main
}

View File

@ -40,6 +40,11 @@
# --keep-unfiltered: keep a copy of the unfiltered output/error output
# of each test by adding an extension .unfiltered.out
#
# --outer-valgrind: run this valgrind under the given outer valgrind.
# This valgrind must be configured with --enable-inner.
# --outer-tool: tool to use by the outer valgrind (default memcheck).
# --outer-args: use this as outer tool args.
#
# The easiest way is to run all tests in valgrind/ with (assuming you installed
# in $PREFIX):
#
@ -120,7 +125,8 @@ use strict;
#----------------------------------------------------------------------------
my $usage="\n"
. "Usage:\n"
. " vg_regtest [--all, --valgrind, --valgrind-lib, --keep-unfiltered]\n"
. " vg_regtest [--all, --valgrind, --valgrind-lib, --keep-unfiltered\n"
. " --outer-valgrind, --outer-tool, --outer-args]\n"
. " Use EXTRA_REGTEST_OPTS to supply extra args for all tests\n"
. "\n";
@ -157,6 +163,13 @@ my $valgrind = "./coregrind/valgrind";
chomp(my $tests_dir = `pwd`);
# Outer valgrind to use, and args to use for it.
# If this is set, --valgrind should be set to the installed inner valgrind,
# and --valgrind-lib will be ignore
my $outer_valgrind;
my $outer_tool = "memcheck";
my $outer_args;
my $valgrind_lib = "$tests_dir/.in_place";
my $keepunfiltered = 0;
@ -205,6 +218,12 @@ sub process_command_line()
$alldirs = 1;
} elsif ($arg =~ /^--valgrind=(.*)$/) {
$valgrind = $1;
} elsif ($arg =~ /^--outer-valgrind=(.*)$/) {
$outer_valgrind = $1;
} elsif ($arg =~ /^--outer-tool=(.*)$/) {
$outer_tool = $1;
} elsif ($arg =~ /^--outer-args=(.*)$/) {
$outer_args = $1;
} elsif ($arg =~ /^--valgrind-lib=(.*)$/) {
$valgrind_lib = $1;
} elsif ($arg =~ /^--keep-unfiltered$/) {
@ -217,6 +236,20 @@ sub process_command_line()
}
}
$valgrind = validate_program($tests_dir, $valgrind, 1, 0);
if (defined $outer_valgrind) {
$outer_valgrind = validate_program($tests_dir, $outer_valgrind, 1, 0);
if (not defined $outer_args) {
$outer_args =
" --command-line-only=yes"
. " --run-libc-freeres=no --sim-hints=enable-outer"
. " --vgdb=no --trace-children=yes --read-var-info=no"
. " --suppressions="
. validate_program($tests_dir,"./tests/outer_inner.supp",1,0)
. " --memcheck:leak-check=full --memcheck:show-reachable=no"
. " ";
}
}
if ($alldirs) {
@fs = ();
@ -428,10 +461,21 @@ sub do_one_test($$)
# VALGRIND_LIB_INNER in case this Valgrind was configured with
# --enable-inner.
my $tool=determine_tool();
mysystem("VALGRIND_LIB=$valgrind_lib VALGRIND_LIB_INNER=$valgrind_lib "
. "$valgrind --command-line-only=yes --memcheck:leak-check=no "
. "--tool=$tool $extraopts $vgopts "
. "$prog $args > $name.stdout.out 2> $name.stderr.out");
if (defined $outer_valgrind ) {
mysystem("$outer_valgrind "
. "--tool=" . $outer_tool . " "
. "$outer_args "
. "--log-file=" . "$name.outer.log "
. "$valgrind --command-line-only=yes --memcheck:leak-check=no "
. "--sim-hints=no-inner-prefix "
. "--tool=$tool $extraopts $vgopts "
. "$prog $args > $name.stdout.out 2> $name.stderr.out");
} else {
mysystem("VALGRIND_LIB=$valgrind_lib VALGRIND_LIB_INNER=$valgrind_lib "
. "$valgrind --command-line-only=yes --memcheck:leak-check=no "
. "--tool=$tool $extraopts $vgopts "
. "$prog $args > $name.stdout.out 2> $name.stderr.out");
}
# Filter stdout
if (defined $stdout_filter) {