neatens other things up.
Also, it adds the --gen-suppressions option for automatically generating
suppressions for each error.
Note that it changes the core/skin interface:
SK_(dup_extra_and_update)() is replaced by SK_(update_extra)(), and
SK_(get_error_name)() and SK_(print_extra_suppression_info)() are added.
-----------------------------------------------------------------------------
details
-----------------------------------------------------------------------------
Removed ac_common.c -- it just #included another .c file; moved the
#include into ac_main.c.
Introduced "mac_" prefixes for files shared between Addrcheck and Memcheck,
to make it clearer which code is shared. Also using a "MAC_" prefix for
functions and variables and types that are shared. Addrcheck doesn't see
the "MC_" prefix at all.
Factored out almost-identical mc_describe_addr() and describe_addr()
(AddrCheck's version) into MAC_(describe_addr)().
Got rid of the "pp_ExeContext" closure passed to SK_(pp_SkinError)(), it
wasn't really necessary.
Introduced MAC_(pp_shared_SkinError)() for the error printing code shared by
Addrcheck and Memcheck. Fixed some bogus stuff in Addrcheck error messages
about "uninitialised bytes" (there because of an imperfect conversion from
Memcheck).
Moved the leak checker out of core (vg_memory.c), into mac_leakcheck.c.
- This meant the hacky way of recording Leak errors, which was different to
normal errors, could be changed to something better: introduced a
function VG_(unique_error)(), which unlike VG_(maybe_record_error)() just
prints the error (unless suppressed) but doesn't record it. Used for
leaks; a much better solution all round as it allowed me to remove a lot
of almost-identical code from leak handling (is_suppressible_leak(),
leaksupp_matches_callers()).
- As part of this, changed the horrible SK_(dup_extra_and_update) into the
slightly less horrible SK_(update_extra), which returns the size of the
`extra' part for the core to duplicate.
- Also renamed it from VG_(generic_detect_memory_leaks)() to
MAC_(do_detect_memory_leaks). In making the code nicer w.r.t suppressions
and error reporting, I tied it a bit more closely to Memcheck/Addrcheck,
and got rid of some of the args. It's not really "generic" any more, but
then it never really was. (This could be undone, but there doesn't seem
to be much point.)
STREQ and STREQN were #defined in several places, and in two different ways.
Made global macros VG_STREQ, VG_CLO_STREQ and VG_CLO_STREQN in vg_skin.h.
Added the --gen-suppressions code. This required adding the functions
SK_(get_error_name)() and SK_(print_extra_suppression_info)() for skins that
use the error handling need.
Added documentation for --gen-suppressions, and fixed some other minor document
problems.
Various other minor related changes too.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1517
-AUTOMAKE_OPTIONS = 1.5
+AUTOMAKE_OPTIONS = 1.4
SUBDIRS = coregrind . docs tests include auxprogs \
addrcheck \
vgskin_addrcheck_so_SOURCES = ac_main.c
vgskin_addrcheck_so_LDFLAGS = -shared
-##vgskin_addrcheck.so$(EXEEXT): $(vgskin_addrcheck_so_OBJECTS)
-## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_addrcheck.so \
-## $(vgskin_addrcheck_so_OBJECTS)
The GNU General Public License is contained in the file COPYING.
*/
-#include "mc_common.h"
+#include "mac_shared.h"
#include "memcheck.h"
//#include "vg_profile.c"
-#include "mc_common.c"
+#include "mac_leakcheck.c"
+#include "mac_needs.c"
/*--- Comparing and printing errors ---*/
/*------------------------------------------------------------*/
-void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) )
+void SK_(pp_SkinError) ( Error* err )
{
- MemCheckError* err_extra = VG_(get_error_extra)(err);
+ MAC_Error* err_extra = VG_(get_error_extra)(err);
switch (VG_(get_error_kind)(err)) {
case CoreMemErr:
- if (err_extra->isWrite) {
- VG_(message)(Vg_UserMsg,
- "%s contains unaddressable byte(s)",
- VG_(get_error_string)(err));
- } else {
- VG_(message)(Vg_UserMsg,
- "%s contains unaddressable byte(s)",
- VG_(get_error_string)(err));
- }
- pp_ExeContext();
+ VG_(message)(Vg_UserMsg, "%s contains unaddressable byte(s)",
+ VG_(get_error_string)(err));
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
case AddrErr:
default:
VG_(skin_panic)("SK_(pp_SkinError)(axskind)");
}
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
- break;
-
- case FreeErr:
- VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
- /* fall through */
- case FreeMismatchErr:
- if (VG_(get_error_kind)(err) == FreeMismatchErr)
- VG_(message)(Vg_UserMsg,
- "Mismatched free() / delete / delete []");
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
case ParamErr:
- if (err_extra->isWrite) {
- VG_(message)(Vg_UserMsg,
- "Syscall param %s contains unaddressable byte(s)",
- VG_(get_error_string)(err) );
- } else {
- VG_(message)(Vg_UserMsg,
- "Syscall param %s contains uninitialised or "
- "unaddressable byte(s)",
- VG_(get_error_string)(err));
- }
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(message)(Vg_UserMsg,
+ "Syscall param %s contains unaddressable byte(s)",
+ VG_(get_error_string)(err) );
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
case UserErr:
- if (err_extra->isWrite) {
- VG_(message)(Vg_UserMsg,
- "Unaddressable byte(s) found during client check request");
- } else {
- VG_(message)(Vg_UserMsg,
- "Uninitialised or "
- "unaddressable byte(s) found during client check request");
- }
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(message)(Vg_UserMsg,
+ "Unaddressable byte(s) found during client check request");
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
default:
- VG_(printf)("Error:\n unknown AddrCheck error code %d\n",
- VG_(get_error_kind)(err));
- VG_(skin_panic)("unknown error code in SK_(pp_SkinError)");
- }
-}
-
-/*------------------------------------------------------------*/
-/*--- Recording errors ---*/
-/*------------------------------------------------------------*/
-
-/* Describe an address as best you can, for error messages,
- putting the result in ai. */
-
-static void describe_addr ( Addr a, AddrInfo* ai )
-{
- ShadowChunk* sc;
- ThreadId tid;
-
- /* Nested functions, yeah. Need the lexical scoping of 'a'. */
-
- /* Closure for searching thread stacks */
- Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
- {
- return (stack_min <= a && a <= stack_max);
- }
- /* Closure for searching malloc'd and free'd lists */
- Bool addr_is_in_block(ShadowChunk *sh_ch)
- {
- return VG_(addr_is_in_block) ( a, VG_(get_sc_data)(sh_ch),
- VG_(get_sc_size)(sh_ch) );
- }
- /* Perhaps it's on a thread's stack? */
- tid = VG_(any_matching_thread_stack)(addr_is_in_bounds);
- if (tid != VG_INVALID_THREADID) {
- ai->akind = Stack;
- ai->stack_tid = tid;
- return;
- }
- /* Search for a recently freed block which might bracket it. */
- sc = MC_(any_matching_freed_ShadowChunks)(addr_is_in_block);
- if (NULL != sc) {
- ai->akind = Freed;
- ai->blksize = VG_(get_sc_size)(sc);
- ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc));
- ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) );
- return;
+ MAC_(pp_shared_SkinError)(err);
+ break;
}
- /* Search for a currently malloc'd block which might bracket it. */
- sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block);
- if (NULL != sc) {
- ai->akind = Mallocd;
- ai->blksize = VG_(get_sc_size)(sc);
- ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc));
- ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) );
- return;
- }
- /* Clueless ... */
- ai->akind = Unknown;
- return;
-}
-
-
-/* Creates a copy of the `extra' part, updates the copy with address info if
- necessary, and returns the copy. */
-void* SK_(dup_extra_and_update)(Error* err)
-{
- MemCheckError* extra;
- MemCheckError* new_extra = NULL;
-
- extra = ((MemCheckError*)VG_(get_error_extra)(err));
- if (extra != NULL) {
- new_extra = VG_(malloc)(sizeof(MemCheckError));
- *new_extra = *extra;
- if (new_extra->addrinfo.akind == Undescribed)
- describe_addr ( VG_(get_error_address)(err), &(new_extra->addrinfo) );
- }
-
-
-
- return new_extra;
}
/*------------------------------------------------------------*/
/*--- Suppressions ---*/
/*------------------------------------------------------------*/
-#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
- && VG_(strcmp)((s1),(s2))==0)
-
Bool SK_(recognised_suppression) ( Char* name, Supp* su )
{
- SuppKind skind;
-
- if (STREQ(name, "Param")) skind = ParamSupp;
- else if (STREQ(name, "CoreMem")) skind = CoreMemSupp;
- else if (STREQ(name, "Addr1")) skind = Addr1Supp;
- else if (STREQ(name, "Addr2")) skind = Addr2Supp;
- else if (STREQ(name, "Addr4")) skind = Addr4Supp;
- else if (STREQ(name, "Addr8")) skind = Addr8Supp;
- else if (STREQ(name, "Free")) skind = FreeSupp;
- else if (STREQ(name, "Leak")) skind = LeakSupp;
- else
- return False;
-
- VG_(set_supp_kind)(su, skind);
- return True;
+ return MAC_(shared_recognised_suppression)(name, su);
}
-# undef STREQ
-
-
#define DEBUG(fmt, args...) //VG_(printf)(fmt, ## args)
/*------------------------------------------------------------*/
if (!ok) {
switch (part) {
case Vg_CoreSysCall:
- MC_(record_param_error) ( tst, bad_addr, isWrite, s );
+ MAC_(record_param_error) ( tst, bad_addr, isWrite, s );
break;
case Vg_CoreSignal:
sk_assert(isWrite); /* Should only happen with isWrite case */
/* fall through */
case Vg_CorePThread:
- MC_(record_core_mem_error)( tst, isWrite, s );
+ MAC_(record_core_mem_error)( tst, isWrite, s );
break;
/* If we're being asked to jump to a silly address, record an error
message before potentially crashing the entire system. */
case Vg_CoreTranslate:
sk_assert(!isWrite); /* Should only happen with !isWrite case */
- MC_(record_jump_error)( tst, bad_addr );
+ MAC_(record_jump_error)( tst, bad_addr );
break;
default:
sk_assert(part == Vg_CoreSysCall);
ok = ac_check_readable_asciiz ( (Addr)str, &bad_addr );
if (!ok) {
- MC_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
+ MAC_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
}
VGP_POPCC(VgpCheckMem);
- emit addressing error
*/
/* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */
- if (!MC_(clo_partial_loads_ok)
+ if (!MAC_(clo_partial_loads_ok)
|| ((a & 3) != 0)
|| (!a0ok && !a1ok && !a2ok && !a3ok)) {
- MC_(record_address_error)( a, 4, False );
+ MAC_(record_address_error)( a, 4, False );
return;
}
/* Case 3: the address is partially valid.
- no addressing error
- Case 3 is only allowed if MC_(clo_partial_loads_ok) is True
+ Case 3 is only allowed if MAC_(clo_partial_loads_ok) is True
(which is the default), and the address is 4-aligned.
If not, Case 2 will have applied.
*/
- sk_assert(MC_(clo_partial_loads_ok));
+ sk_assert(MAC_(clo_partial_loads_ok));
{
return;
}
/* If an address error has happened, report it. */
if (aerr) {
- MC_(record_address_error)( a, 2, False );
+ MAC_(record_address_error)( a, 2, False );
}
}
/* If an address error has happened, report it. */
if (aerr) {
- MC_(record_address_error)( a, 1, False );
+ MAC_(record_address_error)( a, 1, False );
}
}
}
if (aerr) {
- MC_(record_address_error)( addr, size, False );
+ MAC_(record_address_error)( addr, size, False );
}
}
skin. */
static void ac_detect_memory_leaks ( void )
{
- VG_(generic_detect_memory_leaks) (
- ac_is_valid_64k_chunk,
- ac_is_valid_address,
- MC_(get_where),
- MC_(clo_leak_resolution),
- MC_(clo_show_reachable),
- (UInt)LeakSupp
- );
+ MAC_(do_detect_memory_leaks) ( ac_is_valid_64k_chunk, ac_is_valid_address );
}
Bool SK_(process_cmd_line_option)(Char* arg)
{
- return MC_(process_common_cmd_line_option)(arg);
+ return MAC_(process_common_cmd_line_option)(arg);
}
Char* SK_(usage)(void)
VG_(track_new_mem_brk) ( & ac_make_accessible );
VG_(track_new_mem_mmap) ( & ac_set_perms );
- VG_(track_new_mem_stack_4) ( & MC_(new_mem_stack_4) );
- VG_(track_new_mem_stack_8) ( & MC_(new_mem_stack_8) );
- VG_(track_new_mem_stack_12) ( & MC_(new_mem_stack_12) );
- VG_(track_new_mem_stack_16) ( & MC_(new_mem_stack_16) );
- VG_(track_new_mem_stack_32) ( & MC_(new_mem_stack_32) );
- VG_(track_new_mem_stack) ( & MC_(new_mem_stack) );
+ VG_(track_new_mem_stack_4) ( & MAC_(new_mem_stack_4) );
+ VG_(track_new_mem_stack_8) ( & MAC_(new_mem_stack_8) );
+ VG_(track_new_mem_stack_12) ( & MAC_(new_mem_stack_12) );
+ VG_(track_new_mem_stack_16) ( & MAC_(new_mem_stack_16) );
+ VG_(track_new_mem_stack_32) ( & MAC_(new_mem_stack_32) );
+ VG_(track_new_mem_stack) ( & MAC_(new_mem_stack) );
VG_(track_copy_mem_heap) ( & ac_copy_address_range_state );
VG_(track_copy_mem_remap) ( & ac_copy_address_range_state );
VG_(track_die_mem_brk) ( & ac_make_noaccess );
VG_(track_die_mem_munmap) ( & ac_make_noaccess );
- VG_(track_die_mem_stack_4) ( & MC_(die_mem_stack_4) );
- VG_(track_die_mem_stack_8) ( & MC_(die_mem_stack_8) );
- VG_(track_die_mem_stack_12) ( & MC_(die_mem_stack_12) );
- VG_(track_die_mem_stack_16) ( & MC_(die_mem_stack_16) );
- VG_(track_die_mem_stack_32) ( & MC_(die_mem_stack_32) );
- VG_(track_die_mem_stack) ( & MC_(die_mem_stack) );
+ VG_(track_die_mem_stack_4) ( & MAC_(die_mem_stack_4) );
+ VG_(track_die_mem_stack_8) ( & MAC_(die_mem_stack_8) );
+ VG_(track_die_mem_stack_12) ( & MAC_(die_mem_stack_12) );
+ VG_(track_die_mem_stack_16) ( & MAC_(die_mem_stack_16) );
+ VG_(track_die_mem_stack_32) ( & MAC_(die_mem_stack_32) );
+ VG_(track_die_mem_stack) ( & MAC_(die_mem_stack) );
- VG_(track_bad_free) ( & MC_(record_free_error) );
- VG_(track_mismatched_free) ( & MC_(record_freemismatch_error) );
+ VG_(track_bad_free) ( & MAC_(record_free_error) );
+ VG_(track_mismatched_free) ( & MAC_(record_freemismatch_error) );
VG_(track_pre_mem_read) ( & ac_check_is_readable );
VG_(track_pre_mem_read_asciiz) ( & ac_check_is_readable_asciiz );
VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" );
init_shadow_memory();
- MC_(init_prof_mem)();
+ MAC_(init_prof_mem)();
}
void SK_(post_clo_init) ( void )
VG_(print_malloc_stats)();
if (VG_(clo_verbosity) == 1) {
- if (!MC_(clo_leak_check))
+ if (!MAC_(clo_leak_check))
VG_(message)(Vg_UserMsg,
"For a detailed leak analysis, rerun with: --leak-check=yes");
VG_(message)(Vg_UserMsg,
"For counts of detected errors, rerun with: -v");
}
- if (MC_(clo_leak_check)) ac_detect_memory_leaks();
+ if (MAC_(clo_leak_check)) ac_detect_memory_leaks();
- MC_(done_prof_mem)();
+ MAC_(done_prof_mem)();
}
/*--------------------------------------------------------------------*/
recording them in a suppressions file which is read when Valgrind
starts up. The build mechanism attempts to select suppressions which
give reasonable behaviour for the libc and XFree86 versions detected
-on your machine.
+on your machine. To make it easier to write suppressions, you can use
+the <code>--gen-suppressions=yes</code> option which tells Valgrind to
+print out a suppression for each error that appears, which you can
+then copy into a suppressions file.
<p>
Different skins report different kinds of errors. The suppression
for technical reasons, valgrind's core itself can't use the GNU C
library, and this makes it difficult to do hostname-to-IP lookups.
<p>
- Writing to a network socket it pretty useless if you don't have
+ Writing to a network socket is pretty useless if you don't have
something listening at the other end. We provide a simple
listener program, <code>valgrind-listener</code>, which accepts
connections on the specified port and copies whatever it is sent
socket, I guess this option doesn't make any sense. Caveat emptor.
</li><br><p>
+ <li><code>--gen-suppressions=no</code> [the default]<br>
+ <code>--gen-suppressions=yes</code>
+ <p>When enabled, Valgrind will pause after every error shown,
+ and print the line
+ <br>
+ <code>---- Print suppression ? --- [Return/N/n/Y/y/C/c] ----</code>
+ <p>
+ The prompt's behaviour is the same as for the <code>--gdb-attach</code>
+ option.
+ <p>
+ If you choose to, Valgrind will print out a suppression for this error.
+ You can then cut and paste it into a suppression file if you don't want
+ to hear about the error in the future.
+ <p>
+ This option is particularly useful with C++ programs, as it prints out
+ the suppressions with mangled names, as required.
+ <p>
+ Note that the suppressions printed are as specific as possible. You
+ may want to common up similar ones, eg. by adding wildcards to function
+ names. Also, sometimes two different errors are suppressed by the same
+ suppression, in which case Valgrind will output the suppression more than
+ once, but you only need to have one copy in your suppression file (but
+ having more than one won't cause problems). Also, the suppression
+ name is given as <code><insert a suppression name here></code>;
+ the name doesn't really matter, it's only used with the
+ <code>-v</code> option which prints out all used suppression records.
+ </li><br><p>
+
<li><code>--alignment=<number></code> [default: 4]<br> <p>By
default valgrind's <code>malloc</code>, <code>realloc</code>,
etc, return 4-byte aligned addresses. These are suitable for
unexpectedly in the <code>write()</code> system call, you
may find the <code>--trace-syscalls=yes
--trace-sched=yes</code> flags useful.
+ <p>
+ <li><code>lax-ioctls</code> Be very lax about ioctl handling; the only
+ assumption is that the size is correct. Doesn't require the full
+ buffer to be initialized when writing. Without this, using some
+ device drivers with a large number of strange ioctl commands becomes
+ very tiresome.
</ul>
</li><br><p>
</ul>
</li><br>
<p>
+ <li><code>--trace-codegen=XXXXX</code> [default: 00000]
+ <p>Enable/disable tracing of code generation. Code can be printed
+ at five different stages of translation; each <code>X</code> element
+ must be 0 or 1.
+ </li><br>
+ <p>
+
<li><code>--stop-after=<number></code>
[default: infinity, more or less]
<p>After <number> basic blocks have been executed, shut down
(NOTE 20021117: this subsection is illogical here now; it jumbles up
core and skin issues. To be fixed.).
+(NOTE 20030318: the most important correction is that
+<code>valgrind.h</code> should not be included in your program, but
+instead <code>memcheck.h</code> (for the Memcheck and Addrcheck skins)
+or <code>helgrind.h</code> (for Helgrind).)
+
<p>
Valgrind has a trapdoor mechanism via which the client program can
pass all manner of requests and queries to Valgrind. Internally, this
<h3>2.11 If you have problems</h3>
Mail me (<a href="mailto:jseward@acm.org">jseward@acm.org</a>).
-<p>See <a href="#limits">Section 4</a> for the known limitations of
+<p>See <a href="#limits">this section</a> for the known limitations of
Valgrind, and for a list of programs which are known not to work on
it.
VG_(arena_free) ( VG_AR_CORE, sc );
}
+static
+void sort_malloc_shadows ( ShadowChunk** shadows, UInt n_shadows )
+{
+ Int incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720,
+ 797161, 2391484 };
+ Int lo = 0;
+ Int hi = n_shadows-1;
+ Int i, j, h, bigN, hp;
+ ShadowChunk* v;
+
+ bigN = hi - lo + 1; if (bigN < 2) return;
+ hp = 0; while (hp < 14 && incs[hp] < bigN) hp++; hp--;
+ vg_assert(0 <= hp && hp < 14);
+
+ for (; hp >= 0; hp--) {
+ h = incs[hp];
+ i = lo + h;
+ while (1) {
+ if (i > hi) break;
+ v = shadows[i];
+ j = i;
+ while (shadows[j-h]->data > v->data) {
+ shadows[j] = shadows[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ shadows[j] = v;
+ i++;
+ }
+ }
+}
/* Allocate a suitably-sized array, copy all the malloc-d block
shadows into it, and return both the array and the size of it.
}
}
vg_assert(i == *n_shadows);
+
+ sort_malloc_shadows(arr, *n_shadows);
+
+ /* Sanity check; assert that the blocks are now in order and that
+ they don't overlap. */
+ for (i = 0; i < *n_shadows-1; i++) {
+ sk_assert( arr[i]->data < arr[i+1]->data );
+ sk_assert( arr[i]->data + arr[i]->size < arr[i+1]->data );
+ }
+
return arr;
}
}
/* Return the first shadow chunk satisfying the predicate p. */
-ShadowChunk* VG_(any_matching_mallocd_ShadowChunks)
+ShadowChunk* VG_(first_matching_mallocd_ShadowChunk)
( Bool (*p) ( ShadowChunk* ))
{
UInt ml_no;
}
__attribute__ ((weak))
-void SK_(pp_SkinError)(Error* err, void (*pp_ExeContext)(void))
+void SK_(pp_SkinError)(Error* err)
{
non_fund_panic("SK_(pp_SkinError)");
}
__attribute__ ((weak))
-void* SK_(dup_extra_and_update)(Error* err)
+UInt SK_(update_extra)(Error* err)
{
- non_fund_panic("SK_(dup_extra_and_update)");
+ non_fund_panic("SK_(update_extra)");
}
__attribute__ ((weak))
non_fund_panic("SK_(error_matches_suppression)");
}
+__attribute__ ((weak))
+Char* SK_(get_error_name)(Error* err)
+{
+ non_fund_panic("SK_(get_error_name)");
+}
+
+__attribute__ ((weak))
+void SK_(print_extra_suppression_info)(Error* err)
+{
+ non_fund_panic("SK_(print_extra_suppression_info)");
+}
+
/* ---------------------------------------------------------------------
For throwing out basic block level info when code is invalidated
static void pp_Error ( Error* err, Bool printCount )
{
- /* Closure for printing where the error occurred. Abstracts details
- about the `where' field away from the skin. */
- void pp_ExeContextClosure(void)
- {
- VG_(pp_ExeContext) ( err->where );
- }
-
if (printCount)
VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
if (err->tid > 1)
break;
default:
if (VG_(needs).skin_errors)
- SK_(pp_SkinError)( err, &pp_ExeContextClosure );
+ SK_(pp_SkinError)( err );
else {
VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n"
"probably needs to be set?\n",
/* Figure out if we want to attach for GDB for this error, possibly
by asking the user. */
-static
-Bool vg_is_GDB_attach_requested ( void )
+Bool VG_(is_action_requested) ( Char* action, Bool* clo )
{
Char ch, ch2;
Int res;
- if (VG_(clo_GDB_attach) == False)
+ if (*clo == False)
return False;
VG_(message)(Vg_UserMsg, "");
again:
VG_(printf)(
"==%d== "
- "---- Attach to GDB ? --- [Return/N/n/Y/y/C/c] ---- ",
- VG_(getpid)()
+ "---- %s ? --- [Return/N/n/Y/y/C/c] ---- ",
+ VG_(getpid)(), action
);
res = VG_(read)(0 /*stdin*/, &ch, 1);
if (res != 1) goto ioerror;
if (ch2 != '\n') goto again;
- /* No, don't want to attach. */
+ /* No, don't want to do action. */
if (ch == 'n' || ch == 'N') return False;
- /* Yes, want to attach. */
+ /* Yes, want to do action. */
if (ch == 'y' || ch == 'Y') return True;
- /* No, don't want to attach, and don't ask again either. */
+ /* No, don't want to do action, and don't ask again either. */
vg_assert(ch == 'c' || ch == 'C');
ioerror:
- VG_(clo_GDB_attach) = False;
+ *clo = False;
return False;
}
stored thread state, not from VG_(baseBlock).
*/
static __inline__
-void construct_error ( Error* err, ThreadState* tst,
- ErrorKind ekind, Addr a, Char* s, void* extra )
+void construct_error ( Error* err, ThreadState* tst, ErrorKind ekind, Addr a,
+ Char* s, void* extra, ExeContext* where )
{
/* Core-only parts */
err->next = NULL;
err->supp = NULL;
err->count = 1;
- err->where = VG_(get_ExeContext)( tst );
+ if (NULL == where)
+ err->where = VG_(get_ExeContext)( tst );
+ else
+ err->where = where;
if (NULL == tst) {
err->tid = VG_(get_current_tid)();
vg_assert(err->tid >= 0 && err->tid < VG_N_THREADS);
}
+void VG_(gen_suppression)(Error* err)
+{
+ UInt i;
+ UChar buf[M_VG_ERRTXT];
+ ExeContext* ec = VG_(get_error_where)(err);
+ Int stop_at = VG_(clo_backtrace_size);
+ Char* name = SK_(get_error_name)(err);
+
+ if (NULL == name) {
+ VG_(message)(Vg_UserMsg, "(skin does not allow error to be suppressed)");
+ return;
+ }
+
+ if (stop_at > 3) stop_at = 3; /* At most three names */
+ vg_assert(stop_at > 0);
+
+ VG_(printf)("{\n");
+ VG_(printf)(" <insert a suppression name here>\n");
+ VG_(printf)(" %s:%s\n", VG_(details).name, name);
+ SK_(print_extra_suppression_info)(err);
+
+ /* This loop condensed from VG_(mini_stack_dump)() */
+ i = 0;
+ do {
+ Addr eip = ec->eips[i];
+ if (i > 0)
+ eip--; /* point to calling line */
+
+ if ( VG_(get_fnname_nodemangle) (eip, buf, M_VG_ERRTXT) ) {
+ VG_(printf)(" fun:%s\n", buf);
+ } else if ( VG_(get_objname)(eip, buf, M_VG_ERRTXT) ) {
+ VG_(printf)(" obj:%s\n", buf);
+ } else {
+ VG_(printf)(" ???:??? "
+ "# unknown, suppression will not work, sorry)\n");
+ }
+ i++;
+ } while (i < stop_at && ec->eips[i] != 0);
+
+ VG_(printf)("}\n");
+}
+
+void do_actions_on_error(Error* err)
+{
+ /* Perhaps we want a GDB attach at this point? */
+ if (VG_(is_action_requested)( "Attach to GDB", & VG_(clo_GDB_attach) )) {
+ VG_(swizzle_esp_then_start_GDB)(
+ err->m_eip, err->m_esp, err->m_ebp);
+ }
+ /* Or maybe we want to generate the error's suppression? */
+ if (VG_(is_action_requested)( "Print suppression",
+ & VG_(clo_gen_suppressions) )) {
+ VG_(gen_suppression)(err);
+ }
+}
+
+/* Shared between VG_(maybe_record_error)() and VG_(unique_error)(),
+ just for pretty printing purposes. */
+static Bool is_first_shown_context = True;
+
/* Top-level entry point to the error management subsystem.
All detected errors are notified here; this routine decides if/when the
user should see the error. */
Error err;
Error* p;
Error* p_prev;
+ UInt extra_size;
VgRes exe_res = Vg_MedRes;
- static Bool is_first_shown_context = True;
static Bool stopping_message = False;
static Bool slowdown_message = False;
static Int vg_n_errs_shown = 0;
}
/* Build ourselves the error */
- construct_error ( &err, tst, ekind, a, s, extra );
+ construct_error ( &err, tst, ekind, a, s, extra, NULL );
/* First, see if we've got an error record matching this one. */
p = vg_errors;
/* Didn't see it. Copy and add. */
- /* OK, we're really going to collect it. First make a copy,
- because the error context is on the stack and will disappear shortly.
- We can duplicate the main part ourselves, but use
- SK_(dup_extra_and_update) to duplicate the `extra' part.
+ /* OK, we're really going to collect it. The context is on the stack and
+ will disappear shortly, so we must copy it. First do the main
+ (non-`extra') part.
- SK_(dup_extra_and_update) can also update the `extra' part. This is
- for when there are more details to fill in which take time to work out
- but don't affect our earlier decision to include the error -- by
+ Then SK_(update_extra) can update the `extra' part. This is for when
+ there are more details to fill in which take time to work out but
+ don't affect our earlier decision to include the error -- by
postponing those details until now, we avoid the extra work in the
case where we ignore the error. Ugly.
- */
+
+ Then, if there is an `extra' part, copy it too, using the size that
+ SK_(update_extra) returned.
+ */
+
+ /* copy main part */
p = VG_(arena_malloc)(VG_AR_ERRORS, sizeof(Error));
*p = err;
- p->extra = SK_(dup_extra_and_update)(p);
+
+ /* update `extra' */
+ extra_size = SK_(update_extra)(p);
+
+ /* copy `extra' if there is one */
+ if (NULL != p->extra) {
+ void* new_extra = VG_(malloc)(extra_size);
+ VG_(memcpy)(new_extra, p->extra, extra_size);
+ p->extra = new_extra;
+ }
+
p->next = vg_errors;
p->supp = is_suppressible_error(&err);
vg_errors = p;
vg_n_errs_found++;
if (!is_first_shown_context)
VG_(message)(Vg_UserMsg, "");
- pp_Error(p, False);
+ pp_Error(p, False);
is_first_shown_context = False;
vg_n_errs_shown++;
- /* Perhaps we want a GDB attach at this point? */
- if (vg_is_GDB_attach_requested()) {
- VG_(swizzle_esp_then_start_GDB)(
- err.m_eip, err.m_esp, err.m_ebp);
- }
+ do_actions_on_error(p);
} else {
vg_n_errs_suppressed++;
p->supp->count++;
}
}
+/* Second top-level entry point to the error management subsystem, for
+ errors that the skin want to report immediately, eg. because they're
+ guaranteed to only happen once. This avoids all the recording and
+ comparing stuff. But they can be suppressed; returns True if it is
+ suppressed. Bool `print_error' dictates whether to print the error. */
+Bool VG_(unique_error) ( ThreadState* tst, ErrorKind ekind, Addr a, Char* s,
+ void* extra, ExeContext* where, Bool print_error )
+{
+ Error err;
+
+ /* Build ourselves the error */
+ construct_error ( &err, tst, ekind, a, s, extra, where );
+
+ /* Unless it's suppressed, we're going to show it. Don't need to make
+ a copy, because it's only temporary anyway.
+
+ Then update the `extra' part with SK_(update_extra), because that can
+ have an affect on whether it's suppressed. Ignore the size return
+ value of SK_(update_extra), because we're not copying `extra'. */
+ (void)SK_(update_extra)(&err);
+
+ if (NULL == is_suppressible_error(&err)) {
+ vg_n_errs_found++;
+
+ if (print_error) {
+ if (!is_first_shown_context)
+ VG_(message)(Vg_UserMsg, "");
+ pp_Error(&err, False);
+ is_first_shown_context = False;
+ }
+ do_actions_on_error(&err);
+
+ return False;
+
+ } else {
+ vg_n_errs_suppressed++;
+ return True;
+ }
+}
+
/*------------------------------------------------------------*/
/*--- Exported fns ---*/
return found;
}
-#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
- && VG_(strcmp)((s1),(s2))==0)
-
/* Read suppressions from the file specified in vg_clo_suppressions
and place them in the suppressions list. If there's any difficulty
doing this, just give up -- there's no point in trying to recover.
eof = VG_(get_line) ( fd, buf, N_BUF );
if (eof) break;
- if (!STREQ(buf, "{")) goto syntax_error;
+ if (!VG_STREQ(buf, "{")) goto syntax_error;
eof = VG_(get_line) ( fd, buf, N_BUF );
- if (eof || STREQ(buf, "}")) goto syntax_error;
+ if (eof || VG_STREQ(buf, "}")) goto syntax_error;
supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf);
eof = VG_(get_line) ( fd, buf, N_BUF );
/* Is it a core suppression? */
if (VG_(needs).core_errors && skin_name_present("core", skin_names))
{
- if (STREQ(supp_name, "PThread"))
+ if (VG_STREQ(supp_name, "PThread"))
supp->skind = PThreadSupp;
else
goto syntax_error;
while (True) {
eof = VG_(get_line) ( fd, buf, N_BUF );
if (eof) goto syntax_error;
- if (STREQ(buf, "}"))
+ if (VG_STREQ(buf, "}"))
break;
}
continue;
for (i = 0; i < VG_N_SUPP_CALLERS; i++) {
eof = VG_(get_line) ( fd, buf, N_BUF );
if (eof) goto syntax_error;
- if (i > 0 && STREQ(buf, "}"))
+ if (i > 0 && VG_STREQ(buf, "}"))
break;
supp->caller[i] = VG_(arena_strdup)(VG_AR_CORE, buf);
if (!setLocationTy(&(supp->caller[i]), &(supp->caller_ty[i])))
Doesn't demangle the fn name, because we want to refer to
mangled names in the suppressions file.
*/
-void VG_(get_objname_fnname) ( Addr a,
- Char* obj_buf, Int n_obj_buf,
- Char* fun_buf, Int n_fun_buf )
+static void get_objname_fnname ( Addr a, Char* obj_buf, Int n_obj_buf,
+ Char* fun_buf, Int n_fun_buf )
{
(void)VG_(get_objname) ( a, obj_buf, n_obj_buf );
(void)VG_(get_fnname_nodemangle)( a, fun_buf, n_fun_buf );
case FunName: if (VG_(string_match)(su->caller[i],
caller_fun[i])) break;
return False;
- default: VG_(skin_panic)("is_suppressible_error");
+ default: VG_(skin_panic)("supp_matches_callers");
}
}
Supp* su;
/* get_objname_fnname() writes the function name and object name if
- it finds them in the debug info. so the strings in the suppression
+ it finds them in the debug info. So the strings in the suppression
file should match these.
*/
caller_obj[i][0] = caller_fun[i][0] = 0;
for (i = 0; i < VG_N_SUPP_CALLERS && i < VG_(clo_backtrace_size); i++) {
- VG_(get_objname_fnname) ( err->where->eips[i],
- caller_obj[i], M_VG_ERRTXT,
- caller_fun[i], M_VG_ERRTXT );
+ get_objname_fnname ( err->where->eips[i], caller_obj[i], M_VG_ERRTXT,
+ caller_fun[i], M_VG_ERRTXT );
}
/* See if the error context matches any suppression. */
return NULL; /* no matches */
}
-#undef STREQ
-
/*--------------------------------------------------------------------*/
/*--- end vg_errcontext.c ---*/
/*--------------------------------------------------------------------*/
extern Bool VG_(clo_error_limit);
/* Enquire about whether to attach to GDB at errors? default: NO */
extern Bool VG_(clo_GDB_attach);
+/* Enquire about generating a suppression for each error? default: NO */
+extern Bool VG_(clo_gen_suppressions);
/* Sanity-check level: 0 = none, 1 (default), > 1 = expensive. */
extern Int VG_(sanity_level);
/* Automatically attempt to demangle C++ names? default: YES */
extern void VG_(show_all_errors) ( void );
-extern void VG_(get_objname_fnname) ( Addr a,
- Char* obj_buf, Int n_obj_buf,
- Char* fun_buf, Int n_fun_buf );
-
/* Get hold of the suppression list ... just so we don't have to
make it global. */
extern Supp* VG_(get_suppressions) ( void );
+extern Bool VG_(is_action_requested) ( Char* action, Bool* clo );
+
+extern void VG_(gen_suppression) ( Error* err );
/* ---------------------------------------------------------------------
Exports of vg_procselfmaps.c
/* Define, and set defaults. */
Bool VG_(clo_error_limit) = True;
Bool VG_(clo_GDB_attach) = False;
+Bool VG_(clo_gen_suppressions) = False;
Int VG_(sanity_level) = 1;
Int VG_(clo_verbosity) = 1;
Bool VG_(clo_demangle) = True;
" -q --quiet run silently; only print error msgs\n"
" -v --verbose be more verbose, incl counts of errors\n"
" --gdb-attach=no|yes start GDB when errors detected? [no]\n"
+" --gen-suppressions=no|yes print suppressions for errors detected [no]\n"
" --demangle=no|yes automatically demangle C++ names? [yes]\n"
" --num-callers=<number> show <num> callers in stack traces [4]\n"
" --error-limit=no|yes stop showing new errors if too many? [yes]\n"
Int i, eventually_logfile_fd, ctr;
# define ISSPACE(cc) ((cc) == ' ' || (cc) == '\t' || (cc) == '\n')
-# define STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2)))
-# define STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
eventually_logfile_fd = VG_(clo_logfile_fd);
for (i = 0; i < argc; i++) {
- if (STREQ(argv[i], "-v") || STREQ(argv[i], "--verbose"))
+ if (VG_CLO_STREQ(argv[i], "-v") ||
+ VG_CLO_STREQ(argv[i], "--verbose"))
VG_(clo_verbosity)++;
- else if (STREQ(argv[i], "-q") || STREQ(argv[i], "--quiet"))
+ else if (VG_CLO_STREQ(argv[i], "-q") ||
+ VG_CLO_STREQ(argv[i], "--quiet"))
VG_(clo_verbosity)--;
- else if (STREQ(argv[i], "--error-limit=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--error-limit=yes"))
VG_(clo_error_limit) = True;
- else if (STREQ(argv[i], "--error-limit=no"))
+ else if (VG_CLO_STREQ(argv[i], "--error-limit=no"))
VG_(clo_error_limit) = False;
- else if (STREQ(argv[i], "--gdb-attach=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--gdb-attach=yes"))
VG_(clo_GDB_attach) = True;
- else if (STREQ(argv[i], "--gdb-attach=no"))
+ else if (VG_CLO_STREQ(argv[i], "--gdb-attach=no"))
VG_(clo_GDB_attach) = False;
- else if (STREQ(argv[i], "--demangle=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--gen-suppressions=yes"))
+ VG_(clo_gen_suppressions) = True;
+ else if (VG_CLO_STREQ(argv[i], "--gen-suppressions=no"))
+ VG_(clo_gen_suppressions) = False;
+
+ else if (VG_CLO_STREQ(argv[i], "--demangle=yes"))
VG_(clo_demangle) = True;
- else if (STREQ(argv[i], "--demangle=no"))
+ else if (VG_CLO_STREQ(argv[i], "--demangle=no"))
VG_(clo_demangle) = False;
- else if (STREQ(argv[i], "--sloppy-malloc=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--sloppy-malloc=yes"))
VG_(clo_sloppy_malloc) = True;
- else if (STREQ(argv[i], "--sloppy-malloc=no"))
+ else if (VG_CLO_STREQ(argv[i], "--sloppy-malloc=no"))
VG_(clo_sloppy_malloc) = False;
- else if (STREQN(12, argv[i], "--alignment="))
+ else if (VG_CLO_STREQN(12, argv[i], "--alignment="))
VG_(clo_alignment) = (Int)VG_(atoll)(&argv[i][12]);
- else if (STREQ(argv[i], "--trace-children=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-children=yes"))
VG_(clo_trace_children) = True;
- else if (STREQ(argv[i], "--trace-children=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-children=no"))
VG_(clo_trace_children) = False;
- else if (STREQ(argv[i], "--run-libc-freeres=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--run-libc-freeres=yes"))
VG_(clo_run_libc_freeres) = True;
- else if (STREQ(argv[i], "--run-libc-freeres=no"))
+ else if (VG_CLO_STREQ(argv[i], "--run-libc-freeres=no"))
VG_(clo_run_libc_freeres) = False;
- else if (STREQN(15, argv[i], "--sanity-level="))
+ else if (VG_CLO_STREQN(15, argv[i], "--sanity-level="))
VG_(sanity_level) = (Int)VG_(atoll)(&argv[i][15]);
- else if (STREQN(13, argv[i], "--logfile-fd=")) {
+ else if (VG_CLO_STREQN(13, argv[i], "--logfile-fd=")) {
VG_(clo_log_to) = VgLogTo_Fd;
VG_(clo_logfile_name) = NULL;
eventually_logfile_fd = (Int)VG_(atoll)(&argv[i][13]);
}
- else if (STREQN(10, argv[i], "--logfile=")) {
+ else if (VG_CLO_STREQN(10, argv[i], "--logfile=")) {
VG_(clo_log_to) = VgLogTo_File;
VG_(clo_logfile_name) = &argv[i][10];
}
- else if (STREQN(12, argv[i], "--logsocket=")) {
+ else if (VG_CLO_STREQN(12, argv[i], "--logsocket=")) {
VG_(clo_log_to) = VgLogTo_Socket;
VG_(clo_logfile_name) = &argv[i][12];
}
- else if (STREQN(15, argv[i], "--suppressions=")) {
+ else if (VG_CLO_STREQN(15, argv[i], "--suppressions=")) {
if (VG_(clo_n_suppressions) >= VG_CLO_MAX_SFILES) {
VG_(message)(Vg_UserMsg, "Too many suppression files specified.");
VG_(message)(Vg_UserMsg,
VG_(clo_suppressions)[VG_(clo_n_suppressions)] = &argv[i][15];
VG_(clo_n_suppressions)++;
}
- else if (STREQ(argv[i], "--profile=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--profile=yes"))
VG_(clo_profile) = True;
- else if (STREQ(argv[i], "--profile=no"))
+ else if (VG_CLO_STREQ(argv[i], "--profile=no"))
VG_(clo_profile) = False;
- else if (STREQ(argv[i], "--chain-bb=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--chain-bb=yes"))
VG_(clo_chain_bb) = True;
- else if (STREQ(argv[i], "--chain-bb=no"))
+ else if (VG_CLO_STREQ(argv[i], "--chain-bb=no"))
VG_(clo_chain_bb) = False;
- else if (STREQ(argv[i], "--single-step=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--single-step=yes"))
VG_(clo_single_step) = True;
- else if (STREQ(argv[i], "--single-step=no"))
+ else if (VG_CLO_STREQ(argv[i], "--single-step=no"))
VG_(clo_single_step) = False;
- else if (STREQ(argv[i], "--optimise=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--optimise=yes"))
VG_(clo_optimise) = True;
- else if (STREQ(argv[i], "--optimise=no"))
+ else if (VG_CLO_STREQ(argv[i], "--optimise=no"))
VG_(clo_optimise) = False;
/* "vwxyz" --> 000zyxwv (binary) */
- else if (STREQN(16, argv[i], "--trace-codegen=")) {
+ else if (VG_CLO_STREQN(16, argv[i], "--trace-codegen=")) {
Int j;
char* opt = & argv[i][16];
}
}
- else if (STREQ(argv[i], "--trace-syscalls=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-syscalls=yes"))
VG_(clo_trace_syscalls) = True;
- else if (STREQ(argv[i], "--trace-syscalls=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-syscalls=no"))
VG_(clo_trace_syscalls) = False;
- else if (STREQ(argv[i], "--trace-signals=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-signals=yes"))
VG_(clo_trace_signals) = True;
- else if (STREQ(argv[i], "--trace-signals=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-signals=no"))
VG_(clo_trace_signals) = False;
- else if (STREQ(argv[i], "--trace-symtab=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-symtab=yes"))
VG_(clo_trace_symtab) = True;
- else if (STREQ(argv[i], "--trace-symtab=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-symtab=no"))
VG_(clo_trace_symtab) = False;
- else if (STREQ(argv[i], "--trace-malloc=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-malloc=yes"))
VG_(clo_trace_malloc) = True;
- else if (STREQ(argv[i], "--trace-malloc=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-malloc=no"))
VG_(clo_trace_malloc) = False;
- else if (STREQ(argv[i], "--trace-sched=yes"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-sched=yes"))
VG_(clo_trace_sched) = True;
- else if (STREQ(argv[i], "--trace-sched=no"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-sched=no"))
VG_(clo_trace_sched) = False;
- else if (STREQ(argv[i], "--trace-pthread=none"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-pthread=none"))
VG_(clo_trace_pthread_level) = 0;
- else if (STREQ(argv[i], "--trace-pthread=some"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-pthread=some"))
VG_(clo_trace_pthread_level) = 1;
- else if (STREQ(argv[i], "--trace-pthread=all"))
+ else if (VG_CLO_STREQ(argv[i], "--trace-pthread=all"))
VG_(clo_trace_pthread_level) = 2;
- else if (STREQN(14, argv[i], "--weird-hacks="))
+ else if (VG_CLO_STREQN(14, argv[i], "--weird-hacks="))
VG_(clo_weird_hacks) = &argv[i][14];
- else if (STREQN(13, argv[i], "--stop-after="))
+ else if (VG_CLO_STREQN(13, argv[i], "--stop-after="))
VG_(clo_stop_after) = VG_(atoll)(&argv[i][13]);
- else if (STREQN(13, argv[i], "--dump-error="))
+ else if (VG_CLO_STREQN(13, argv[i], "--dump-error="))
VG_(clo_dump_error) = (Int)VG_(atoll)(&argv[i][13]);
- else if (STREQN(14, argv[i], "--num-callers=")) {
+ else if (VG_CLO_STREQN(14, argv[i], "--num-callers=")) {
/* Make sure it's sane. */
VG_(clo_backtrace_size) = (Int)VG_(atoll)(&argv[i][14]);
if (VG_(clo_backtrace_size) < 2)
}
# undef ISSPACE
-# undef STREQ
-# undef STREQN
if (VG_(clo_verbosity < 0))
VG_(clo_verbosity) = 0;
}
}
-/*--------------------------------------------------------------------*/
-/*--- Support for memory leak detectors ---*/
-/*--------------------------------------------------------------------*/
-
-/*------------------------------------------------------------*/
-/*--- Low-level address-space scanning, for the leak ---*/
-/*--- detector. ---*/
-/*------------------------------------------------------------*/
-
-static
-jmp_buf memscan_jmpbuf;
-
-
-static
-void vg_scan_all_valid_memory_sighandler ( Int sigNo )
-{
- __builtin_longjmp(memscan_jmpbuf, 1);
-}
-
-
-/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address
- space and pass the addresses and values of all addressible,
- defined, aligned words to notify_word. This is the basis for the
- leak detector. Returns the number of calls made to notify_word.
-
- Addresses are validated 3 ways. First we enquire whether (addr >>
- 16) denotes a 64k chunk in use, by asking is_valid_64k_chunk(). If
- so, we decide for ourselves whether each x86-level (4 K) page in
- the chunk is safe to inspect. If yes, we enquire with
- is_valid_address() whether or not each of the 1024 word-locations
- on the page is valid. Only if so are that address and its contents
- passed to notify_word.
-
- This is all to avoid duplication of this machinery between the
- memcheck and addrcheck skins.
-*/
-static
-UInt vg_scan_all_valid_memory ( Bool is_valid_64k_chunk ( UInt ),
- Bool is_valid_address ( Addr ),
- void (*notify_word)( Addr, UInt ) )
-{
- /* All volatile, because some gccs seem paranoid about longjmp(). */
- volatile Bool anyValid;
- volatile Addr pageBase, addr;
- volatile UInt res, numPages, page, primaryMapNo;
- volatile UInt page_first_word, nWordsNotified;
-
- vki_ksigaction sigbus_saved;
- vki_ksigaction sigbus_new;
- vki_ksigaction sigsegv_saved;
- vki_ksigaction sigsegv_new;
- vki_ksigset_t blockmask_saved;
- vki_ksigset_t unblockmask_new;
-
- /* Temporarily install a new sigsegv and sigbus handler, and make
- sure SIGBUS, SIGSEGV and SIGTERM are unblocked. (Perhaps the
- first two can never be blocked anyway?) */
-
- sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
- sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
- sigbus_new.ksa_restorer = NULL;
- res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
- sk_assert(res == 0);
-
- sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
- sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
- sigsegv_new.ksa_restorer = NULL;
- res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
- sk_assert(res == 0+0);
-
- res = VG_(ksigemptyset)( &unblockmask_new );
- res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
- res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
- res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
- sk_assert(res == 0+0+0);
-
- res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
- sk_assert(res == 0+0+0+0);
-
- res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
- sk_assert(res == 0+0+0+0+0);
-
- res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
- sk_assert(res == 0+0+0+0+0+0);
-
- /* The signal handlers are installed. Actually do the memory scan. */
- numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS);
- sk_assert(numPages == 1048576);
- sk_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS));
-
- nWordsNotified = 0;
-
- for (page = 0; page < numPages; page++) {
-
- /* Base address of this 4k page. */
- pageBase = page << VKI_BYTES_PER_PAGE_BITS;
-
- /* Skip if this page is in an unused 64k chunk. */
- primaryMapNo = pageBase >> 16;
- if (!is_valid_64k_chunk(primaryMapNo))
- continue;
-
- /* Next, establish whether or not we want to consider any
- locations on this page. We need to do so before actually
- prodding it, because prodding it when in fact it is not
- needed can cause a page fault which under some rare
- circumstances can cause the kernel to extend the stack
- segment all the way down to here, which is seriously bad.
- Hence: */
- anyValid = False;
- for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
- if (is_valid_address(addr)) {
- anyValid = True;
- break;
- }
- }
-
- if (!anyValid)
- continue; /* nothing interesting here .. move to the next page */
-
- /* Ok, we have to prod cautiously at the page and see if it
- explodes or not. */
- if (__builtin_setjmp(memscan_jmpbuf) == 0) {
- /* try this ... */
- page_first_word = * (volatile UInt*)pageBase;
- /* we get here if we didn't get a fault */
- /* Scan the page */
- for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
- if (is_valid_address(addr)) {
- nWordsNotified++;
- notify_word ( addr, *(UInt*)addr );
- }
- }
- } else {
- /* We get here if reading the first word of the page caused a
- fault, which in turn caused the signal handler to longjmp.
- Ignore this page. */
- if (0)
- VG_(printf)(
- "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n",
- (void*)pageBase
- );
- }
- }
-
- /* Restore signal state to whatever it was before. */
- res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
- sk_assert(res == 0 +0);
-
- res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
- sk_assert(res == 0 +0 +0);
-
- res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
- sk_assert(res == 0 +0 +0 +0);
-
- return nWordsNotified;
-}
-
-
-/*------------------------------------------------------------*/
-/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/
-/*------------------------------------------------------------*/
-
-/* A block is either
- -- Proper-ly reached; a pointer to its start has been found
- -- Interior-ly reached; only an interior pointer to it has been found
- -- Unreached; so far, no pointers to any part of it have been found.
-*/
-typedef
- enum { Unreached, Interior, Proper }
- Reachedness;
-
-/* A block record, used for generating err msgs. */
-typedef
- struct _LossRecord {
- struct _LossRecord* next;
- /* Where these lost blocks were allocated. */
- ExeContext* allocated_at;
- /* Their reachability. */
- Reachedness loss_mode;
- /* Number of blocks and total # bytes involved. */
- UInt total_bytes;
- UInt num_blocks;
- }
- LossRecord;
-
-
-/* Find the i such that ptr points at or inside the block described by
- shadows[i]. Return -1 if none found. This assumes that shadows[]
- has been sorted on the ->data field. */
-
-#ifdef VG_DEBUG_LEAKCHECK
-/* Used to sanity-check the fast binary-search mechanism. */
-static
-Int find_shadow_for_OLD ( Addr ptr,
- ShadowChunk** shadows,
- Int n_shadows )
-
-{
- Int i;
- Addr a_lo, a_hi;
- PROF_EVENT(70);
- for (i = 0; i < n_shadows; i++) {
- PROF_EVENT(71);
- a_lo = shadows[i]->data;
- a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1;
- if (a_lo <= ptr && ptr <= a_hi)
- return i;
- }
- return -1;
-}
-#endif
-
-
-static
-Int find_shadow_for ( Addr ptr,
- ShadowChunk** shadows,
- Int n_shadows )
-{
- Addr a_mid_lo, a_mid_hi;
- Int lo, mid, hi, retVal;
- /* VG_(printf)("find shadow for %p = ", ptr); */
- retVal = -1;
- lo = 0;
- hi = n_shadows-1;
- while (True) {
- /* invariant: current unsearched space is from lo to hi,
- inclusive. */
- if (lo > hi) break; /* not found */
-
- mid = (lo + hi) / 2;
- a_mid_lo = shadows[mid]->data;
- a_mid_hi = ((Addr)shadows[mid]->data) + shadows[mid]->size - 1;
-
- if (ptr < a_mid_lo) {
- hi = mid-1;
- continue;
- }
- if (ptr > a_mid_hi) {
- lo = mid+1;
- continue;
- }
- sk_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
- retVal = mid;
- break;
- }
-
-# ifdef VG_DEBUG_LEAKCHECK
- vg_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows ));
-# endif
- /* VG_(printf)("%d\n", retVal); */
- return retVal;
-}
-
-
-
-static
-void sort_malloc_shadows ( ShadowChunk** shadows, UInt n_shadows )
-{
- Int incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
- 9841, 29524, 88573, 265720,
- 797161, 2391484 };
- Int lo = 0;
- Int hi = n_shadows-1;
- Int i, j, h, bigN, hp;
- ShadowChunk* v;
-
- bigN = hi - lo + 1; if (bigN < 2) return;
- hp = 0; while (hp < 14 && incs[hp] < bigN) hp++; hp--;
- vg_assert(0 <= hp && hp < 14);
-
- for (; hp >= 0; hp--) {
- h = incs[hp];
- i = lo + h;
- while (1) {
- if (i > hi) break;
- v = shadows[i];
- j = i;
- while (shadows[j-h]->data > v->data) {
- shadows[j] = shadows[j-h];
- j = j - h;
- if (j <= (lo + h - 1)) break;
- }
- shadows[j] = v;
- i++;
- }
- }
-}
-
-
-/* Globals, for the callback used by VG_(detect_memory_leaks). */
-
-static ShadowChunk** vglc_shadows;
-static Int vglc_n_shadows;
-static Reachedness* vglc_reachedness;
-static Addr vglc_min_mallocd_addr;
-static Addr vglc_max_mallocd_addr;
-
-static
-void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a )
-{
- Int sh_no;
- Addr ptr;
-
- /* Rule out some known causes of bogus pointers. Mostly these do
- not cause much trouble because only a few false pointers can
- ever lurk in these places. This mainly stops it reporting that
- blocks are still reachable in stupid test programs like this
-
- int main (void) { char* a = malloc(100); return 0; }
-
- which people seem inordinately fond of writing, for some reason.
-
- Note that this is a complete kludge. It would be better to
- ignore any addresses corresponding to valgrind.so's .bss and
- .data segments, but I cannot think of a reliable way to identify
- where the .bss segment has been put. If you can, drop me a
- line.
- */
- if (VG_(within_stack)(a)) return;
- if (VG_(within_m_state_static)(a)) return;
- if (a == (Addr)(&vglc_min_mallocd_addr)) return;
- if (a == (Addr)(&vglc_max_mallocd_addr)) return;
-
- /* OK, let's get on and do something Useful for a change. */
-
- ptr = (Addr)word_at_a;
- if (ptr >= vglc_min_mallocd_addr && ptr <= vglc_max_mallocd_addr) {
- /* Might be legitimate; we'll have to investigate further. */
- sh_no = find_shadow_for ( ptr, vglc_shadows, vglc_n_shadows );
- if (sh_no != -1) {
- /* Found a block at/into which ptr points. */
- sk_assert(sh_no >= 0 && sh_no < vglc_n_shadows);
- sk_assert(ptr < vglc_shadows[sh_no]->data
- + vglc_shadows[sh_no]->size);
- /* Decide whether Proper-ly or Interior-ly reached. */
- if (ptr == vglc_shadows[sh_no]->data) {
- if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a );
- vglc_reachedness[sh_no] = Proper;
- } else {
- if (vglc_reachedness[sh_no] == Unreached)
- vglc_reachedness[sh_no] = Interior;
- }
- }
- }
-}
-
-
-/* Stuff for figuring out if a leak report should be suppressed. */
-static
-Bool leaksupp_matches_callers(Supp* su, Char caller_obj[][M_VG_ERRTXT],
- Char caller_fun[][M_VG_ERRTXT])
-{
- Int i;
-
- for (i = 0; su->caller[i] != NULL; i++) {
- switch (su->caller_ty[i]) {
- case ObjName: if (VG_(string_match)(su->caller[i],
- caller_obj[i])) break;
- return False;
- case FunName: if (VG_(string_match)(su->caller[i],
- caller_fun[i])) break;
- return False;
- default: VG_(skin_panic)("leaksupp_matches_callers");
- }
- }
-
- /* If we reach here, it's a match */
- return True;
-}
-
-
-/* Does a leak record match a suppression? ie is this a suppressible
- leak? Tries to minimise the number of symbol searches since they
- are expensive. Copy n paste (more or less) of
- is_suppressible_error. We have to pass in the actual value of
- LeakSupp for comparison since this is the core and LeakSupp is a
- skin-specific value. */
-static
-Bool is_suppressible_leak ( ExeContext* allocated_at,
- UInt /*CoreErrorKind*/ leakSupp )
-{
- Int i;
-
- Char caller_obj[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
- Char caller_fun[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
-
- Supp* su;
-
- /* get_objname_fnname() writes the function name and object name if
- it finds them in the debug info. so the strings in the suppression
- file should match these.
- */
-
- /* Initialise these strs so they are always safe to compare, even
- if get_objname_fnname doesn't write anything to them. */
- for (i = 0; i < VG_N_SUPP_CALLERS; i++)
- caller_obj[i][0] = caller_fun[i][0] = 0;
-
- for (i = 0; i < VG_N_SUPP_CALLERS && i < VG_(clo_backtrace_size); i++) {
- VG_(get_objname_fnname) ( allocated_at->eips[i],
- caller_obj[i], M_VG_ERRTXT,
- caller_fun[i], M_VG_ERRTXT );
- }
-
- /* See if the leak record any suppression. */
- for (su = VG_(get_suppressions)(); su != NULL; su = su->next) {
- if (VG_(get_supp_kind)(su) == (CoreErrorKind)leakSupp
- && leaksupp_matches_callers(su, caller_obj, caller_fun)) {
- return True;
- }
- }
- return False; /* no matches */
-}
-
-/* Top level entry point to leak detector. Call here, passing in
- suitable address-validating functions (see comment at top of
- vg_scan_all_valid_memory above). All this is to avoid duplication
- of the leak-detection code for the Memcheck and Addrcheck skins.
- Also pass in a skin-specific function to extract the .where field
- for allocated blocks, an indication of the resolution wanted for
- distinguishing different allocation points, and whether or not
- reachable blocks should be shown.
-*/
-void VG_(generic_detect_memory_leaks) (
- Bool is_valid_64k_chunk ( UInt ),
- Bool is_valid_address ( Addr ),
- ExeContext* get_where ( ShadowChunk* ),
- VgRes leak_resolution,
- Bool show_reachable,
- UInt /*CoreErrorKind*/ leakSupp
-)
-{
- Int i;
- Int blocks_leaked, bytes_leaked;
- Int blocks_dubious, bytes_dubious;
- Int blocks_reachable, bytes_reachable;
- Int blocks_suppressed, bytes_suppressed;
- Int n_lossrecords;
- UInt bytes_notified;
-
- LossRecord* errlist;
- LossRecord* p;
-
- /* VG_(get_malloc_shadows) allocates storage for shadows */
- vglc_shadows = VG_(get_malloc_shadows)( &vglc_n_shadows );
- if (vglc_n_shadows == 0) {
- sk_assert(vglc_shadows == NULL);
- VG_(message)(Vg_UserMsg,
- "No malloc'd blocks -- no leaks are possible.");
- return;
- }
-
- VG_(message)(Vg_UserMsg,
- "searching for pointers to %d not-freed blocks.",
- vglc_n_shadows );
- sort_malloc_shadows ( vglc_shadows, vglc_n_shadows );
-
- /* Sanity check; assert that the blocks are now in order and that
- they don't overlap. */
- for (i = 0; i < vglc_n_shadows-1; i++) {
- sk_assert( ((Addr)vglc_shadows[i]->data)
- < ((Addr)vglc_shadows[i+1]->data) );
- sk_assert( ((Addr)vglc_shadows[i]->data) + vglc_shadows[i]->size
- < ((Addr)vglc_shadows[i+1]->data) );
- }
-
- vglc_min_mallocd_addr = ((Addr)vglc_shadows[0]->data);
- vglc_max_mallocd_addr = ((Addr)vglc_shadows[vglc_n_shadows-1]->data)
- + vglc_shadows[vglc_n_shadows-1]->size - 1;
-
- vglc_reachedness
- = VG_(malloc)( vglc_n_shadows * sizeof(Reachedness) );
- for (i = 0; i < vglc_n_shadows; i++)
- vglc_reachedness[i] = Unreached;
-
- /* Do the scan of memory. */
- bytes_notified
- = VKI_BYTES_PER_WORD
- * vg_scan_all_valid_memory (
- is_valid_64k_chunk,
- is_valid_address,
- &vg_detect_memory_leaks_notify_addr
- );
-
- VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified);
-
- /* Common up the lost blocks so we can print sensible error
- messages. */
-
- n_lossrecords = 0;
- errlist = NULL;
- for (i = 0; i < vglc_n_shadows; i++) {
-
- /* 'where' stored in 'skin_extra' field; extract using function
- supplied by the calling skin. */
- ExeContext* where = get_where ( vglc_shadows[i] );
-
- for (p = errlist; p != NULL; p = p->next) {
- if (p->loss_mode == vglc_reachedness[i]
- && VG_(eq_ExeContext) ( leak_resolution,
- p->allocated_at,
- where) ) {
- break;
- }
- }
- if (p != NULL) {
- p->num_blocks ++;
- p->total_bytes += vglc_shadows[i]->size;
- } else {
- n_lossrecords ++;
- p = VG_(malloc)(sizeof(LossRecord));
- p->loss_mode = vglc_reachedness[i];
- p->allocated_at = where;
- p->total_bytes = vglc_shadows[i]->size;
- p->num_blocks = 1;
- p->next = errlist;
- errlist = p;
- }
- }
-
- /* Print out the commoned-up blocks and collect summary stats. */
-
- blocks_leaked = bytes_leaked = 0;
- blocks_dubious = bytes_dubious = 0;
- blocks_reachable = bytes_reachable = 0;
- blocks_suppressed = bytes_suppressed = 0;
-
- for (i = 0; i < n_lossrecords; i++) {
- LossRecord* p_min = NULL;
- UInt n_min = 0xFFFFFFFF;
- for (p = errlist; p != NULL; p = p->next) {
- if (p->num_blocks > 0 && p->total_bytes < n_min) {
- n_min = p->total_bytes;
- p_min = p;
- }
- }
- sk_assert(p_min != NULL);
-
- if (is_suppressible_leak(p_min->allocated_at, leakSupp)) {
- blocks_suppressed += p_min->num_blocks;
- bytes_suppressed += p_min->total_bytes;
- p_min->num_blocks = 0;
- continue;
- } else {
- switch (p_min->loss_mode) {
- case Unreached:
- blocks_leaked += p_min->num_blocks;
- bytes_leaked += p_min->total_bytes;
- break;
- case Interior:
- blocks_dubious += p_min->num_blocks;
- bytes_dubious += p_min->total_bytes;
- break;
- case Proper:
- blocks_reachable += p_min->num_blocks;
- bytes_reachable += p_min->total_bytes;
- break;
- default:
- VG_(core_panic)("generic_detect_memory_leaks: "
- "unknown loss mode");
- }
- }
-
- if ( (!show_reachable) && (p_min->loss_mode == Proper)) {
- p_min->num_blocks = 0;
- continue;
- }
-
- VG_(message)(Vg_UserMsg, "");
- VG_(message)(
- Vg_UserMsg,
- "%d bytes in %d blocks are %s in loss record %d of %d",
- p_min->total_bytes, p_min->num_blocks,
- p_min->loss_mode==Unreached ? "definitely lost" :
- (p_min->loss_mode==Interior ? "possibly lost"
- : "still reachable"),
- i+1, n_lossrecords
- );
- VG_(pp_ExeContext)(p_min->allocated_at);
- p_min->num_blocks = 0;
- }
-
- VG_(message)(Vg_UserMsg, "");
- VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
- VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.",
- bytes_leaked, blocks_leaked );
- VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.",
- bytes_dubious, blocks_dubious );
- VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.",
- bytes_reachable, blocks_reachable );
- VG_(message)(Vg_UserMsg, " suppressed: %d bytes in %d blocks.",
- bytes_suppressed, blocks_suppressed );
- if (!show_reachable) {
- VG_(message)(Vg_UserMsg,
- "Reachable blocks (those to which a pointer was found) are not shown.");
- VG_(message)(Vg_UserMsg,
- "To see them, rerun with: --show-reachable=yes");
- }
- VG_(message)(Vg_UserMsg, "");
-
- VG_(free) ( vglc_shadows );
- VG_(free) ( vglc_reachedness );
-}
-
-
/*--------------------------------------------------------------------*/
/*--- end vg_memory.c ---*/
/*--------------------------------------------------------------------*/
if none do. A small complication is dealing with any currently
VG_(baseBlock)-resident thread.
*/
-ThreadId VG_(any_matching_thread_stack)
+ThreadId VG_(first_matching_thread_stack)
( Bool (*p) ( Addr stack_min, Addr stack_max ))
{
ThreadId tid, tid_to_skip;
n = 0;
if (i > 0)
eip--; /* point to calling line */
- know_fnname = get_fnname (True, eip, buf_fn, M_VG_ERRTXT, True, False);
+ know_fnname = VG_(get_fnname) (eip, buf_fn, M_VG_ERRTXT);
know_objname = VG_(get_objname)(eip, buf_obj, M_VG_ERRTXT);
- know_srcloc = VG_(get_filename_linenum)(eip,
- buf_srcloc, M_VG_ERRTXT,
+ know_srcloc = VG_(get_filename_linenum)(eip, buf_srcloc, M_VG_ERRTXT,
&lineno);
if (i == 0) APPEND(" at ") else APPEND(" by ");
}
/* Search for a currently malloc'd block which might bracket it. */
- sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block);
+ sc = VG_(first_matching_mallocd_ShadowChunk)(addr_is_in_block);
if (NULL != sc) {
ai->akind = Mallocd;
ai->blksize = VG_(get_sc_size)(sc);
return buf;
}
-void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) )
+void SK_(pp_SkinError) ( Error* err )
{
HelgrindError *extra = (HelgrindError *)VG_(get_error_extra)(err);
Char buf[100];
VG_(message)(Vg_UserMsg, "Possible data race %s variable at %p %(y",
VG_(get_error_string)(err), err_addr, err_addr);
- pp_ExeContext();
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
pp_AddrInfo(err_addr, &extra->addrinfo);
switch(extra->prevstate.state) {
VG_(get_error_address)(err),
VG_(get_error_address)(err),
VG_(get_error_string)(err));
- pp_ExeContext();
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
if (extra->lasttouched.uu_ec_eip.ec != NULL) {
VG_(message)(Vg_UserMsg, " last touched by thread %d", extra->lasttid);
VG_(pp_ExeContext)(extra->lasttouched.uu_ec_eip.ec);
VG_(message)(Vg_UserMsg, "Mutex %p%(y locked in inconsistent order",
err_addr, err_addr);
- pp_ExeContext();
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
VG_(message)(Vg_UserMsg, " while holding locks %s", msg);
for(i = 0; i < heldset->setsize; i++) {
return True;
}
+extern Char* SK_(get_error_name) ( Error* err )
+{
+ if (EraserErr == VG_(get_error_kind)(err)) {
+ return "Eraser";
+ } else {
+ return NULL; /* Other errors types can't be suppressed */
+ }
+}
+
+extern void SK_(print_extra_suppression_info) ( Error* err )
+{
+ /* Do nothing */
+}
static void eraser_pre_mutex_lock(ThreadId tid, void* void_mutex)
{
/*=== Command-line options ===*/
/*====================================================================*/
+/* Use this for normal null-termination-style string comparison */
+#define VG_STREQ(s1,s2) (s1 != NULL && s2 != NULL \
+ && VG_(strcmp)((s1),(s2))==0)
+
+/* Use these for recognising skin command line options -- stops comparing
+ once whitespace is reached. */
+# define VG_CLO_STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2)))
+# define VG_CLO_STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
+
/* Verbosity level: 0 = silent, 1 (default), > 1 = more verbose. */
extern Int VG_(clo_verbosity);
If the error occurs in generated code, 'tst' should be NULL. If the
error occurs in non-generated code, 'tst' should be non-NULL. The
- `extra' field can be stack-allocated; it will be copied (using
- SKN_(dup_extra_and_update)()) if needed. But it won't be copied
- if it's NULL.
+ `extra' field can be stack-allocated; it will be copied by the core
+ if needed. But it won't be copied if it's NULL.
If no 'a', 's' or 'extra' of interest needs to be recorded, just use
NULL for them.
extern void VG_(maybe_record_error) ( ThreadState* tst, ErrorKind ekind,
Addr a, Char* s, void* extra );
+/* Similar to VG_(maybe_record_error)(), except this one doesn't record the
+ error -- useful for errors that can only happen once. The errors can be
+ suppressed, though. Return value is True if it was suppressed.
+ `print_error' dictates whether to print the error, which is a bit of a
+ hack that's useful sometimes if you just want to know if the error would
+ be suppressed without possibly printing it. */
+extern Bool VG_(unique_error) ( ThreadState* tst, ErrorKind ekind,
+ Addr a, Char* s, void* extra,
+ ExeContext* where, Bool print_error );
+
/* Gets a non-blank, non-comment line of at most nBuf chars from fd.
Skips leading spaces on the line. Returns True if EOF was hit instead.
Useful for reading in extra skin-specific suppression lines.
/* Searches through currently malloc'd blocks until a matching one is found.
Returns NULL if none match. Extra arguments can be implicitly passed to
p using nested functions; see memcheck/mc_errcontext.c for an example. */
-extern ShadowChunk* VG_(any_matching_mallocd_ShadowChunks)
+extern ShadowChunk* VG_(first_matching_mallocd_ShadowChunk)
( Bool (*p) ( ShadowChunk* ));
/* Searches through all thread's stacks to see if any match. Returns
* VG_INVALID_THREADID if none match. */
-extern ThreadId VG_(any_matching_thread_stack)
+extern ThreadId VG_(first_matching_thread_stack)
( Bool (*p) ( Addr stack_min, Addr stack_max ));
/* Do memory leak detection. */
*/
extern Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 );
-/* Print error context. The passed function pp_ExeContext() can be (and
- probably should be) used to print the location of the error. */
-extern void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) );
+/* Print error context. */
+extern void SK_(pp_SkinError) ( Error* err );
-/* Should copy the `extra' part which the core uses to override the old
- version. This is necessary to move from a temporary stack copy to a
- permanent heap one.
-
- Then should fill in any details that could be postponed until after the
+/* Should fill in any details that could be postponed until after the
decision whether to ignore the error (ie. details not affecting the
result of SK_(eq_SkinError)()). This saves time when errors are ignored.
-
Yuk.
+
+ Return value: must be the size of the `extra' part in bytes -- used by
+ the core to make a copy.
*/
-extern void* SK_(dup_extra_and_update) ( Error* err );
+extern UInt SK_(update_extra) ( Error* err );
/* Return value indicates recognition. If recognised, must set skind using
VG_(set_supp_kind)(). */
/* This should just check the kinds match and maybe some stuff in the
`string' and `extra' field if appropriate (using VG_(get_supp_*)() to
get the relevant suppression parts). */
-extern Bool SK_(error_matches_suppression)(Error* err, Supp* su);
+extern Bool SK_(error_matches_suppression) ( Error* err, Supp* su );
+
+/* This should return the suppression name, for --gen-suppressions, or NULL
+ if that error type cannot be suppressed. This is the inverse of
+ SK_(recognised_suppression)(). */
+extern Char* SK_(get_error_name) ( Error* err );
+
+/* This should print any extra info for the error, for --gen-suppressions,
+ including the newline. This is the inverse of
+ SK_(read_extra_suppression_info)(). */
+extern void SK_(print_extra_suppression_info) ( Error* err );
/* ------------------------------------------------------------------ */
val_PROGRAMS = vgskin_memcheck.so
vgskin_memcheck_so_SOURCES = \
+ mac_leakcheck.c \
+ mac_needs.c \
mc_main.c \
mc_clientreqs.c \
- mc_common.c \
mc_errcontext.c \
mc_from_ucode.c \
mc_translate.c \
memcheck.h
noinst_HEADERS = \
- mc_common.h \
+ mac_shared.h \
mc_constants.h \
mc_include.h
-##vgskin_memcheck.so$(EXEEXT): $(vgskin_memcheck_so_OBJECTS)
-## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_memcheck.so \
-## $(vgskin_memcheck_so_OBJECTS)
<a name="errormsgs"></a>
-<h3>3.3 Explaination of error messages from Memcheck</h3>
+<h3>3.3 Explanation of error messages from Memcheck</h3>
Despite considerable sophistication under the hood, Memcheck can only
really detect two kinds of errors, use of illegal addresses, and use
discover all sorts of memory-management nasties in your code. This
section presents a quick summary of what error messages mean. The
precise behaviour of the error-checking machinery is described in
-<a href="#machine">Section 4</a>.
+<a href="#machine">this section</a>.
<h4>3.3.1 Illegal read / Illegal write errors</h4>
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- The leak checker, shared between Memcheck and Addrcheck. ---*/
+/*--- mac_leakcheck.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of MemCheck, a heavyweight Valgrind skin for
+ detecting memory errors, and AddrCheck, a lightweight Valgrind skin
+ for detecting memory errors.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "mac_shared.h"
+
+/* Define to debug the memory-leak-detector. */
+/* #define VG_DEBUG_LEAKCHECK */
+
+/*------------------------------------------------------------*/
+/*--- Low-level address-space scanning, for the leak ---*/
+/*--- detector. ---*/
+/*------------------------------------------------------------*/
+
+static
+jmp_buf memscan_jmpbuf;
+
+
+static
+void vg_scan_all_valid_memory_sighandler ( Int sigNo )
+{
+ __builtin_longjmp(memscan_jmpbuf, 1);
+}
+
+
+/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address
+ space and pass the addresses and values of all addressible,
+ defined, aligned words to notify_word. This is the basis for the
+ leak detector. Returns the number of calls made to notify_word.
+
+ Addresses are validated 3 ways. First we enquire whether (addr >>
+ 16) denotes a 64k chunk in use, by asking is_valid_64k_chunk(). If
+ so, we decide for ourselves whether each x86-level (4 K) page in
+ the chunk is safe to inspect. If yes, we enquire with
+ is_valid_address() whether or not each of the 1024 word-locations
+ on the page is valid. Only if so are that address and its contents
+ passed to notify_word.
+
+ This is all to avoid duplication of this machinery between the
+ memcheck and addrcheck skins.
+*/
+static
+UInt vg_scan_all_valid_memory ( Bool is_valid_64k_chunk ( UInt ),
+ Bool is_valid_address ( Addr ),
+ void (*notify_word)( Addr, UInt ) )
+{
+ /* All volatile, because some gccs seem paranoid about longjmp(). */
+ volatile Bool anyValid;
+ volatile Addr pageBase, addr;
+ volatile UInt res, numPages, page, primaryMapNo;
+ volatile UInt page_first_word, nWordsNotified;
+
+ vki_ksigaction sigbus_saved;
+ vki_ksigaction sigbus_new;
+ vki_ksigaction sigsegv_saved;
+ vki_ksigaction sigsegv_new;
+ vki_ksigset_t blockmask_saved;
+ vki_ksigset_t unblockmask_new;
+
+ /* Temporarily install a new sigsegv and sigbus handler, and make
+ sure SIGBUS, SIGSEGV and SIGTERM are unblocked. (Perhaps the
+ first two can never be blocked anyway?) */
+
+ sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+ sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+ sigbus_new.ksa_restorer = NULL;
+ res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
+ sk_assert(res == 0);
+
+ sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+ sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+ sigsegv_new.ksa_restorer = NULL;
+ res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
+ sk_assert(res == 0+0);
+
+ res = VG_(ksigemptyset)( &unblockmask_new );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
+ sk_assert(res == 0+0+0);
+
+ res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
+ sk_assert(res == 0+0+0+0);
+
+ res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
+ sk_assert(res == 0+0+0+0+0);
+
+ res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
+ sk_assert(res == 0+0+0+0+0+0);
+
+ /* The signal handlers are installed. Actually do the memory scan. */
+ numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS);
+ sk_assert(numPages == 1048576);
+ sk_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS));
+
+ nWordsNotified = 0;
+
+ for (page = 0; page < numPages; page++) {
+
+ /* Base address of this 4k page. */
+ pageBase = page << VKI_BYTES_PER_PAGE_BITS;
+
+ /* Skip if this page is in an unused 64k chunk. */
+ primaryMapNo = pageBase >> 16;
+ if (!is_valid_64k_chunk(primaryMapNo))
+ continue;
+
+ /* Next, establish whether or not we want to consider any
+ locations on this page. We need to do so before actually
+ prodding it, because prodding it when in fact it is not
+ needed can cause a page fault which under some rare
+ circumstances can cause the kernel to extend the stack
+ segment all the way down to here, which is seriously bad.
+ Hence: */
+ anyValid = False;
+ for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
+ if (is_valid_address(addr)) {
+ anyValid = True;
+ break;
+ }
+ }
+
+ if (!anyValid)
+ continue; /* nothing interesting here .. move to the next page */
+
+ /* Ok, we have to prod cautiously at the page and see if it
+ explodes or not. */
+ if (__builtin_setjmp(memscan_jmpbuf) == 0) {
+ /* try this ... */
+ page_first_word = * (volatile UInt*)pageBase;
+ /* we get here if we didn't get a fault */
+ /* Scan the page */
+ for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
+ if (is_valid_address(addr)) {
+ nWordsNotified++;
+ notify_word ( addr, *(UInt*)addr );
+ }
+ }
+ } else {
+ /* We get here if reading the first word of the page caused a
+ fault, which in turn caused the signal handler to longjmp.
+ Ignore this page. */
+ if (0)
+ VG_(printf)(
+ "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n",
+ (void*)pageBase
+ );
+ }
+ }
+
+ /* Restore signal state to whatever it was before. */
+ res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
+ sk_assert(res == 0 +0);
+
+ res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
+ sk_assert(res == 0 +0 +0);
+
+ res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
+ sk_assert(res == 0 +0 +0 +0);
+
+ return nWordsNotified;
+}
+
+/*------------------------------------------------------------*/
+/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/
+/*------------------------------------------------------------*/
+
+/* A block is either
+ -- Proper-ly reached; a pointer to its start has been found
+ -- Interior-ly reached; only an interior pointer to it has been found
+ -- Unreached; so far, no pointers to any part of it have been found.
+*/
+typedef
+ enum { Unreached, Interior, Proper }
+ Reachedness;
+
+/* A block record, used for generating err msgs. */
+typedef
+ struct _LossRecord {
+ struct _LossRecord* next;
+ /* Where these lost blocks were allocated. */
+ ExeContext* allocated_at;
+ /* Their reachability. */
+ Reachedness loss_mode;
+ /* Number of blocks and total # bytes involved. */
+ UInt total_bytes;
+ UInt num_blocks;
+ }
+ LossRecord;
+
+
+/* Find the i such that ptr points at or inside the block described by
+ shadows[i]. Return -1 if none found. This assumes that shadows[]
+ has been sorted on the ->data field. */
+
+#ifdef VG_DEBUG_LEAKCHECK
+/* Used to sanity-check the fast binary-search mechanism. */
+static
+Int find_shadow_for_OLD ( Addr ptr,
+ ShadowChunk** shadows,
+ Int n_shadows )
+
+{
+ Int i;
+ Addr a_lo, a_hi;
+ PROF_EVENT(70);
+ for (i = 0; i < n_shadows; i++) {
+ PROF_EVENT(71);
+ a_lo = shadows[i]->data;
+ a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1;
+ if (a_lo <= ptr && ptr <= a_hi)
+ return i;
+ }
+ return -1;
+}
+#endif
+
+
+static
+Int find_shadow_for ( Addr ptr,
+ ShadowChunk** shadows,
+ Int n_shadows )
+{
+ Addr a_mid_lo, a_mid_hi;
+ Int lo, mid, hi, retVal;
+ /* VG_(printf)("find shadow for %p = ", ptr); */
+ retVal = -1;
+ lo = 0;
+ hi = n_shadows-1;
+ while (True) {
+ /* invariant: current unsearched space is from lo to hi,
+ inclusive. */
+ if (lo > hi) break; /* not found */
+
+ mid = (lo + hi) / 2;
+ a_mid_lo = VG_(get_sc_data)(shadows[mid]);
+ a_mid_hi = VG_(get_sc_data)(shadows[mid]) +
+ VG_(get_sc_size)(shadows[mid]) - 1;
+
+ if (ptr < a_mid_lo) {
+ hi = mid-1;
+ continue;
+ }
+ if (ptr > a_mid_hi) {
+ lo = mid+1;
+ continue;
+ }
+ sk_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
+ retVal = mid;
+ break;
+ }
+
+# ifdef VG_DEBUG_LEAKCHECK
+ sk_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows ));
+# endif
+ /* VG_(printf)("%d\n", retVal); */
+ return retVal;
+}
+
+/* Globals, for the following callback used by VG_(detect_memory_leaks). */
+static ShadowChunk** vglc_shadows;
+static Int vglc_n_shadows;
+static Reachedness* vglc_reachedness;
+static Addr vglc_min_mallocd_addr;
+static Addr vglc_max_mallocd_addr;
+
+static
+void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a )
+{
+ Int sh_no;
+ Addr ptr;
+
+ /* Rule out some known causes of bogus pointers. Mostly these do
+ not cause much trouble because only a few false pointers can
+ ever lurk in these places. This mainly stops it reporting that
+ blocks are still reachable in stupid test programs like this
+
+ int main (void) { char* a = malloc(100); return 0; }
+
+ which people seem inordinately fond of writing, for some reason.
+
+ Note that this is a complete kludge. It would be better to
+ ignore any addresses corresponding to valgrind.so's .bss and
+ .data segments, but I cannot think of a reliable way to identify
+ where the .bss segment has been put. If you can, drop me a
+ line.
+ */
+ if (VG_(within_stack)(a)) return;
+ if (VG_(within_m_state_static)(a)) return;
+ if (a == (Addr)(&vglc_min_mallocd_addr)) return;
+ if (a == (Addr)(&vglc_max_mallocd_addr)) return;
+
+ /* OK, let's get on and do something Useful for a change. */
+
+ ptr = (Addr)word_at_a;
+ if (ptr >= vglc_min_mallocd_addr && ptr <= vglc_max_mallocd_addr) {
+ /* Might be legitimate; we'll have to investigate further. */
+ sh_no = find_shadow_for ( ptr, vglc_shadows, vglc_n_shadows );
+ if (sh_no != -1) {
+ /* Found a block at/into which ptr points. */
+ sk_assert(sh_no >= 0 && sh_no < vglc_n_shadows);
+ sk_assert(ptr < VG_(get_sc_data)(vglc_shadows[sh_no])
+ + VG_(get_sc_size)(vglc_shadows[sh_no]));
+ /* Decide whether Proper-ly or Interior-ly reached. */
+ if (ptr == VG_(get_sc_data)(vglc_shadows[sh_no])) {
+ if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a );
+ vglc_reachedness[sh_no] = Proper;
+ } else {
+ if (vglc_reachedness[sh_no] == Unreached)
+ vglc_reachedness[sh_no] = Interior;
+ }
+ }
+ }
+}
+
+/* Used for printing leak errors, avoids exposing the LossRecord type (which
+ comes in as void*, requiring a cast. */
+void MAC_(pp_LeakError)(void* vl, UInt n_this_record, UInt n_total_records)
+{
+ LossRecord* l = (LossRecord*)vl;
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg,
+ "%d bytes in %d blocks are %s in loss record %d of %d",
+ l->total_bytes, l->num_blocks,
+ l->loss_mode==Unreached ? "definitely lost"
+ : (l->loss_mode==Interior ? "possibly lost"
+ : "still reachable"),
+ n_this_record, n_total_records
+ );
+ VG_(pp_ExeContext)(l->allocated_at);
+}
+
+/* Top level entry point to leak detector. Call here, passing in
+ suitable address-validating functions (see comment at top of
+ vg_scan_all_valid_memory above). All this is to avoid duplication
+ of the leak-detection code for the Memcheck and Addrcheck skins.
+ Also pass in a skin-specific function to extract the .where field
+ for allocated blocks, an indication of the resolution wanted for
+ distinguishing different allocation points, and whether or not
+ reachable blocks should be shown.
+*/
+void MAC_(do_detect_memory_leaks) (
+ Bool is_valid_64k_chunk ( UInt ),
+ Bool is_valid_address ( Addr )
+)
+{
+ Int i;
+ Int blocks_leaked, bytes_leaked;
+ Int blocks_dubious, bytes_dubious;
+ Int blocks_reachable, bytes_reachable;
+ Int blocks_suppressed, bytes_suppressed;
+ Int n_lossrecords;
+ UInt bytes_notified;
+ Bool is_suppressed;
+
+ LossRecord* errlist;
+ LossRecord* p;
+
+ /* VG_(get_malloc_shadows) allocates storage for shadows */
+ vglc_shadows = VG_(get_malloc_shadows)( &vglc_n_shadows );
+ if (vglc_n_shadows == 0) {
+ sk_assert(vglc_shadows == NULL);
+ VG_(message)(Vg_UserMsg,
+ "No malloc'd blocks -- no leaks are possible.");
+ return;
+ }
+
+ VG_(message)(Vg_UserMsg, "searching for pointers to %d not-freed blocks.",
+ vglc_n_shadows );
+
+ vglc_min_mallocd_addr = VG_(get_sc_data)(vglc_shadows[0]);
+ vglc_max_mallocd_addr = VG_(get_sc_data)(vglc_shadows[vglc_n_shadows-1])
+ + VG_(get_sc_size)(vglc_shadows[vglc_n_shadows-1]) - 1;
+
+ vglc_reachedness = VG_(malloc)( vglc_n_shadows * sizeof(Reachedness) );
+ for (i = 0; i < vglc_n_shadows; i++)
+ vglc_reachedness[i] = Unreached;
+
+ /* Do the scan of memory. */
+ bytes_notified
+ = VKI_BYTES_PER_WORD
+ * vg_scan_all_valid_memory (
+ is_valid_64k_chunk,
+ is_valid_address,
+ &vg_detect_memory_leaks_notify_addr
+ );
+
+ VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified);
+
+ /* Common up the lost blocks so we can print sensible error messages. */
+ n_lossrecords = 0;
+ errlist = NULL;
+ for (i = 0; i < vglc_n_shadows; i++) {
+
+ ExeContext* where = MAC_(get_where) ( vglc_shadows[i] );
+
+ for (p = errlist; p != NULL; p = p->next) {
+ if (p->loss_mode == vglc_reachedness[i]
+ && VG_(eq_ExeContext) ( MAC_(clo_leak_resolution),
+ p->allocated_at,
+ where) ) {
+ break;
+ }
+ }
+ if (p != NULL) {
+ p->num_blocks ++;
+ p->total_bytes += VG_(get_sc_size)(vglc_shadows[i]);
+ } else {
+ n_lossrecords ++;
+ p = VG_(malloc)(sizeof(LossRecord));
+ p->loss_mode = vglc_reachedness[i];
+ p->allocated_at = where;
+ p->total_bytes = VG_(get_sc_size)(vglc_shadows[i]);
+ p->num_blocks = 1;
+ p->next = errlist;
+ errlist = p;
+ }
+ }
+
+ /* Print out the commoned-up blocks and collect summary stats. */
+ blocks_leaked = bytes_leaked = 0;
+ blocks_dubious = bytes_dubious = 0;
+ blocks_reachable = bytes_reachable = 0;
+ blocks_suppressed = bytes_suppressed = 0;
+
+ for (i = 0; i < n_lossrecords; i++) {
+ Bool print_record;
+ LossRecord* p_min = NULL;
+ UInt n_min = 0xFFFFFFFF;
+ for (p = errlist; p != NULL; p = p->next) {
+ if (p->num_blocks > 0 && p->total_bytes < n_min) {
+ n_min = p->total_bytes;
+ p_min = p;
+ }
+ }
+ sk_assert(p_min != NULL);
+
+ /* Ok to have tst==NULL; it's only used if --gdb-attach=yes, and
+ we disallow that when --leak-check=yes.
+
+ Prints the error if not suppressed, unless it's reachable (Proper)
+ and --show-reachable=no */
+
+ print_record = ( MAC_(clo_show_reachable) || Proper != p_min->loss_mode );
+ is_suppressed =
+ VG_(unique_error) ( /*tst*/NULL, LeakErr, (UInt)i+1,
+ (Char*)n_lossrecords, (void*) p_min,
+ p_min->allocated_at, print_record );
+
+ if (is_suppressed) {
+ blocks_suppressed += p_min->num_blocks;
+ bytes_suppressed += p_min->total_bytes;
+
+ } else if (Unreached == p_min->loss_mode) {
+ blocks_leaked += p_min->num_blocks;
+ bytes_leaked += p_min->total_bytes;
+
+ } else if (Interior == p_min->loss_mode) {
+ blocks_dubious += p_min->num_blocks;
+ bytes_dubious += p_min->total_bytes;
+
+ } else if (Proper == p_min->loss_mode) {
+ blocks_reachable += p_min->num_blocks;
+ bytes_reachable += p_min->total_bytes;
+
+ } else {
+ VG_(skin_panic)("generic_detect_memory_leaks: unknown loss mode");
+ }
+ p_min->num_blocks = 0;
+ }
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
+ VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.",
+ bytes_leaked, blocks_leaked );
+ VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.",
+ bytes_dubious, blocks_dubious );
+ VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.",
+ bytes_reachable, blocks_reachable );
+ VG_(message)(Vg_UserMsg, " suppressed: %d bytes in %d blocks.",
+ bytes_suppressed, blocks_suppressed );
+ if (!MAC_(clo_show_reachable)) {
+ VG_(message)(Vg_UserMsg,
+ "Reachable blocks (those to which a pointer was found) are not shown.");
+ VG_(message)(Vg_UserMsg,
+ "To see them, rerun with: --show-reachable=yes");
+ }
+ VG_(message)(Vg_UserMsg, "");
+
+ VG_(free) ( vglc_shadows );
+ VG_(free) ( vglc_reachedness );
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end mac_leakcheck.c ---*/
+/*--------------------------------------------------------------------*/
+
/*--------------------------------------------------------------------*/
/*--- Code that is shared between MemCheck and AddrCheck. ---*/
-/*--- mc_common.c ---*/
+/*--- mac_needs.c ---*/
/*--------------------------------------------------------------------*/
/*
*/
-#include "mc_common.h"
+#include "mac_shared.h"
/*------------------------------------------------------------*/
/*--- Defns ---*/
/*--- Command line options ---*/
/*------------------------------------------------------------*/
-Bool MC_(clo_partial_loads_ok) = True;
-Int MC_(clo_freelist_vol) = 1000000;
-Bool MC_(clo_leak_check) = False;
-VgRes MC_(clo_leak_resolution) = Vg_LowRes;
-Bool MC_(clo_show_reachable) = False;
-Bool MC_(clo_workaround_gcc296_bugs) = False;
-Bool MC_(clo_cleanup) = True;
-Bool MC_(clo_avoid_strlen_errors) = True;
+Bool MAC_(clo_partial_loads_ok) = True;
+Int MAC_(clo_freelist_vol) = 1000000;
+Bool MAC_(clo_leak_check) = False;
+VgRes MAC_(clo_leak_resolution) = Vg_LowRes;
+Bool MAC_(clo_show_reachable) = False;
+Bool MAC_(clo_workaround_gcc296_bugs) = False;
-Bool MC_(process_common_cmd_line_option)(Char* arg)
+Bool MAC_(process_common_cmd_line_option)(Char* arg)
{
-# define STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2)))
-# define STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
-
- if (STREQ(arg, "--partial-loads-ok=yes"))
- MC_(clo_partial_loads_ok) = True;
- else if (STREQ(arg, "--partial-loads-ok=no"))
- MC_(clo_partial_loads_ok) = False;
-
- else if (STREQN(15, arg, "--freelist-vol=")) {
- MC_(clo_freelist_vol) = (Int)VG_(atoll)(&arg[15]);
- if (MC_(clo_freelist_vol) < 0) MC_(clo_freelist_vol) = 0;
+ if (VG_CLO_STREQ(arg, "--partial-loads-ok=yes"))
+ MAC_(clo_partial_loads_ok) = True;
+ else if (VG_CLO_STREQ(arg, "--partial-loads-ok=no"))
+ MAC_(clo_partial_loads_ok) = False;
+
+ else if (VG_CLO_STREQN(15, arg, "--freelist-vol=")) {
+ MAC_(clo_freelist_vol) = (Int)VG_(atoll)(&arg[15]);
+ if (MAC_(clo_freelist_vol) < 0) MAC_(clo_freelist_vol) = 0;
}
- else if (STREQ(arg, "--leak-check=yes"))
- MC_(clo_leak_check) = True;
- else if (STREQ(arg, "--leak-check=no"))
- MC_(clo_leak_check) = False;
-
- else if (STREQ(arg, "--leak-resolution=low"))
- MC_(clo_leak_resolution) = Vg_LowRes;
- else if (STREQ(arg, "--leak-resolution=med"))
- MC_(clo_leak_resolution) = Vg_MedRes;
- else if (STREQ(arg, "--leak-resolution=high"))
- MC_(clo_leak_resolution) = Vg_HighRes;
+ else if (VG_CLO_STREQ(arg, "--leak-check=yes"))
+ MAC_(clo_leak_check) = True;
+ else if (VG_CLO_STREQ(arg, "--leak-check=no"))
+ MAC_(clo_leak_check) = False;
+
+ else if (VG_CLO_STREQ(arg, "--leak-resolution=low"))
+ MAC_(clo_leak_resolution) = Vg_LowRes;
+ else if (VG_CLO_STREQ(arg, "--leak-resolution=med"))
+ MAC_(clo_leak_resolution) = Vg_MedRes;
+ else if (VG_CLO_STREQ(arg, "--leak-resolution=high"))
+ MAC_(clo_leak_resolution) = Vg_HighRes;
- else if (STREQ(arg, "--show-reachable=yes"))
- MC_(clo_show_reachable) = True;
- else if (STREQ(arg, "--show-reachable=no"))
- MC_(clo_show_reachable) = False;
-
- else if (STREQ(arg, "--workaround-gcc296-bugs=yes"))
- MC_(clo_workaround_gcc296_bugs) = True;
- else if (STREQ(arg, "--workaround-gcc296-bugs=no"))
- MC_(clo_workaround_gcc296_bugs) = False;
+ else if (VG_CLO_STREQ(arg, "--show-reachable=yes"))
+ MAC_(clo_show_reachable) = True;
+ else if (VG_CLO_STREQ(arg, "--show-reachable=no"))
+ MAC_(clo_show_reachable) = False;
- else if (STREQ(arg, "--cleanup=yes"))
- MC_(clo_cleanup) = True;
- else if (STREQ(arg, "--cleanup=no"))
- MC_(clo_cleanup) = False;
+ else if (VG_CLO_STREQ(arg, "--workaround-gcc296-bugs=yes"))
+ MAC_(clo_workaround_gcc296_bugs) = True;
+ else if (VG_CLO_STREQ(arg, "--workaround-gcc296-bugs=no"))
+ MAC_(clo_workaround_gcc296_bugs) = False;
else
return False;
return True;
+}
+
+/*------------------------------------------------------------*/
+/*--- Shadow chunks info ---*/
+/*------------------------------------------------------------*/
+
+void MAC_(set_where)( ShadowChunk* sc, ExeContext* ec )
+{
+ VG_(set_sc_extra)( sc, 0, (UInt)ec );
+}
-#undef STREQ
-#undef STREQN
+ExeContext *MAC_(get_where)( ShadowChunk* sc )
+{
+ return (ExeContext*)VG_(get_sc_extra)(sc, 0);
+}
+
+void SK_(complete_shadow_chunk) ( ShadowChunk* sc, ThreadState* tst )
+{
+ VG_(set_sc_extra) ( sc, 0, (UInt)VG_(get_ExeContext)(tst) );
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Postponing free()ing ---*/
+/*------------------------------------------------------------*/
+
+/* Holds blocks after freeing. */
+static ShadowChunk* freed_list_start = NULL;
+static ShadowChunk* freed_list_end = NULL;
+static Int freed_list_volume = 0;
+
+__attribute__ ((unused))
+Int MAC_(count_freelist) ( void )
+{
+ ShadowChunk* sc;
+ Int n = 0;
+ for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+ n++;
+ return n;
+}
+
+__attribute__ ((unused))
+void MAC_(freelist_sanity) ( void )
+{
+ ShadowChunk* sc;
+ Int n = 0;
+ /* VG_(printf)("freelist sanity\n"); */
+ for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+ n += VG_(get_sc_size)(sc);
+ sk_assert(n == freed_list_volume);
+}
+
+/* Put a shadow chunk on the freed blocks queue, possibly freeing up
+ some of the oldest blocks in the queue at the same time. */
+static void add_to_freed_queue ( ShadowChunk* sc )
+{
+ ShadowChunk* sc1;
+
+ /* Put it at the end of the freed list */
+ if (freed_list_end == NULL) {
+ sk_assert(freed_list_start == NULL);
+ freed_list_end = freed_list_start = sc;
+ freed_list_volume = VG_(get_sc_size)(sc);
+ } else {
+ sk_assert(VG_(get_sc_next)(freed_list_end) == NULL);
+ VG_(set_sc_next)(freed_list_end, sc);
+ freed_list_end = sc;
+ freed_list_volume += VG_(get_sc_size)(sc);
+ }
+ VG_(set_sc_next)(sc, NULL);
+
+ /* Release enough of the oldest blocks to bring the free queue
+ volume below vg_clo_freelist_vol. */
+
+ while (freed_list_volume > MAC_(clo_freelist_vol)) {
+ /* freelist_sanity(); */
+ sk_assert(freed_list_start != NULL);
+ sk_assert(freed_list_end != NULL);
+
+ sc1 = freed_list_start;
+ freed_list_volume -= VG_(get_sc_size)(sc1);
+ /* VG_(printf)("volume now %d\n", freed_list_volume); */
+ sk_assert(freed_list_volume >= 0);
+
+ if (freed_list_start == freed_list_end) {
+ freed_list_start = freed_list_end = NULL;
+ } else {
+ freed_list_start = VG_(get_sc_next)(sc1);
+ }
+ VG_(set_sc_next)(sc1, NULL); /* just paranoia */
+ VG_(free_ShadowChunk) ( sc1 );
+ }
+}
+
+void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst )
+{
+ /* Record where freed */
+ MAC_(set_where)( sc, VG_(get_ExeContext) ( tst ) );
+
+ /* Put it out of harm's way for a while. */
+ add_to_freed_queue ( sc );
}
/*------------------------------------------------------------*/
ai->maybe_gcc = False;
}
-void MC_(clear_MemCheckError) ( MemCheckError* err_extra )
+void MAC_(clear_MAC_Error) ( MAC_Error* err_extra )
{
err_extra->axskind = ReadAxs;
err_extra->size = 0;
Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 )
{
- MemCheckError* e1_extra = VG_(get_error_extra)(e1);
- MemCheckError* e2_extra = VG_(get_error_extra)(e2);
+ MAC_Error* e1_extra = VG_(get_error_extra)(e1);
+ MAC_Error* e2_extra = VG_(get_error_extra)(e2);
/* Guaranteed by calling function */
sk_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
if (e1_extra->size != e2_extra->size) return False;
return True;
+ case LeakErr:
+ VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\n"
+ "since it's handled with VG_(unique_error)()!");
+
default:
VG_(printf)("Error:\n unknown error code %d\n",
VG_(get_error_kind)(e1));
}
}
-void MC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
+void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
{
switch (ai->akind) {
case Stack:
break;
}
default:
- VG_(skin_panic)("MC_(pp_AddrInfo)");
+ VG_(skin_panic)("MAC_(pp_AddrInfo)");
+ }
+}
+
+/* This prints out the message for the error types where Memcheck and
+ Addrcheck have identical messages */
+void MAC_(pp_shared_SkinError) ( Error* err )
+{
+ MAC_Error* err_extra = VG_(get_error_extra)(err);
+
+ switch (VG_(get_error_kind)(err)) {
+ case FreeErr:
+ VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
+ /* fall through */
+ case FreeMismatchErr:
+ if (VG_(get_error_kind)(err) == FreeMismatchErr)
+ VG_(message)(Vg_UserMsg,
+ "Mismatched free() / delete / delete []");
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ break;
+
+ case LeakErr: {
+ /* Totally abusing the types of these spare fields... oh well. */
+ UInt n_this_record = (UInt)VG_(get_error_address)(err);
+ UInt n_total_records = (UInt)VG_(get_error_string) (err);
+
+ MAC_(pp_LeakError)(err_extra, n_this_record, n_total_records);
+ break;
+ }
+
+ default:
+ VG_(printf)("Error:\n unknown Memcheck/Addrcheck error code %d\n",
+ VG_(get_error_kind)(err));
+ VG_(skin_panic)("unknown error code in MAC_(pp_shared_SkinError)");
}
}
/*--- Recording errors ---*/
/*------------------------------------------------------------*/
+/* Additional description function for describe_addr(); used by
+ MemCheck for user blocks, which Addrcheck doesn't support. */
+Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai ) = NULL;
+
+/* Return the first shadow chunk satisfying the predicate p. */
+static ShadowChunk* first_matching_freed_ShadowChunk ( Bool (*p)(ShadowChunk*) )
+{
+ ShadowChunk* sc;
+
+ /* No point looking through freed blocks if we're not keeping
+ them around for a while... */
+ for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+ if (p(sc))
+ return sc;
+
+ return NULL;
+}
+
+/* Describe an address as best you can, for error messages,
+ putting the result in ai. */
+static void describe_addr ( Addr a, AddrInfo* ai )
+{
+ ShadowChunk* sc;
+ ThreadId tid;
+
+ /* Nested functions, yeah. Need the lexical scoping of 'a'. */
+
+ /* Closure for searching thread stacks */
+ Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
+ {
+ return (stack_min <= a && a <= stack_max);
+ }
+ /* Closure for searching malloc'd and free'd lists */
+ Bool addr_is_in_block(ShadowChunk *sh_ch)
+ {
+ return VG_(addr_is_in_block) ( a, VG_(get_sc_data)(sh_ch),
+ VG_(get_sc_size)(sh_ch) );
+ }
+
+ /* Perhaps it's a user-def'd block ? (only check if requested, though) */
+ if (NULL != MAC_(describe_addr_supp)) {
+ if (MAC_(describe_addr_supp)( a, ai ))
+ return;
+ }
+ /* Perhaps it's on a thread's stack? */
+ tid = VG_(first_matching_thread_stack)(addr_is_in_bounds);
+ if (tid != VG_INVALID_THREADID) {
+ ai->akind = Stack;
+ ai->stack_tid = tid;
+ return;
+ }
+ /* Search for a recently freed block which might bracket it. */
+ sc = first_matching_freed_ShadowChunk(addr_is_in_block);
+ if (NULL != sc) {
+ ai->akind = Freed;
+ ai->blksize = VG_(get_sc_size)(sc);
+ ai->rwoffset = (Int)a - (Int)VG_(get_sc_data)(sc);
+ ai->lastchange = MAC_(get_where)(sc);
+ return;
+ }
+ /* Search for a currently malloc'd block which might bracket it. */
+ sc = VG_(first_matching_mallocd_ShadowChunk)(addr_is_in_block);
+ if (NULL != sc) {
+ ai->akind = Mallocd;
+ ai->blksize = VG_(get_sc_size)(sc);
+ ai->rwoffset = (Int)a - (Int)VG_(get_sc_data)(sc);
+ ai->lastchange = MAC_(get_where)(sc);
+ return;
+ }
+ /* Clueless ... */
+ ai->akind = Unknown;
+ return;
+}
+
+
/* Is this address within some small distance below %ESP? Used only
for the --workaround-gcc296-bugs kludge. */
static Bool is_just_below_ESP( Addr esp, Addr aa )
/* This one called from generated code. */
-void MC_(record_address_error) ( Addr a, Int size, Bool isWrite )
+void MAC_(record_address_error) ( Addr a, Int size, Bool isWrite )
{
- MemCheckError err_extra;
- Bool just_below_esp;
+ MAC_Error err_extra;
+ Bool just_below_esp;
just_below_esp = is_just_below_ESP( VG_(get_stack_pointer)(), a );
/* If this is caused by an access immediately below %ESP, and the
user asks nicely, we just ignore it. */
- if (MC_(clo_workaround_gcc296_bugs) && just_below_esp)
+ if (MAC_(clo_workaround_gcc296_bugs) && just_below_esp)
return;
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
err_extra.size = size;
err_extra.addrinfo.akind = Undescribed;
/* This is for memory errors in pthread functions, as opposed to pthread API
errors which are found by the core. */
-void MC_(record_core_mem_error) ( ThreadState* tst, Bool isWrite, Char* msg )
+void MAC_(record_core_mem_error) ( ThreadState* tst, Bool isWrite, Char* msg )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.isWrite = isWrite;
VG_(maybe_record_error)( tst, CoreMemErr, /*addr*/0, msg, &err_extra );
}
-void MC_(record_param_error) ( ThreadState* tst, Addr a, Bool isWrite,
+void MAC_(record_param_error) ( ThreadState* tst, Addr a, Bool isWrite,
Char* msg )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
sk_assert(NULL != tst);
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.addrinfo.akind = Undescribed;
err_extra.isWrite = isWrite;
VG_(maybe_record_error)( tst, ParamErr, a, msg, &err_extra );
}
-void MC_(record_jump_error) ( ThreadState* tst, Addr a )
+void MAC_(record_jump_error) ( ThreadState* tst, Addr a )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
sk_assert(NULL != tst);
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.axskind = ExecAxs;
err_extra.addrinfo.akind = Undescribed;
VG_(maybe_record_error)( tst, AddrErr, a, /*s*/NULL, &err_extra );
}
-void MC_(record_free_error) ( ThreadState* tst, Addr a )
+void MAC_(record_free_error) ( ThreadState* tst, Addr a )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
sk_assert(NULL != tst);
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.addrinfo.akind = Undescribed;
VG_(maybe_record_error)( tst, FreeErr, a, /*s*/NULL, &err_extra );
}
-void MC_(record_freemismatch_error) ( ThreadState* tst, Addr a )
+void MAC_(record_freemismatch_error) ( ThreadState* tst, Addr a )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
sk_assert(NULL != tst);
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.addrinfo.akind = Undescribed;
VG_(maybe_record_error)( tst, FreeMismatchErr, a, /*s*/NULL, &err_extra );
}
+/* Updates the copy with address info if necessary (but not for LeakErrs). */
+UInt SK_(update_extra)( Error* err )
+{
+ MAC_Error* extra;
+
+ /* Don't need to return the correct size -- LeakErrs are always shown with
+ VG_(unique_error)() so they're not copied anyway. */
+ if (LeakErr == VG_(get_error_kind)(err))
+ return 0;
+
+ extra = (MAC_Error*)VG_(get_error_extra)(err);
+
+ if (extra != NULL && Undescribed == extra->addrinfo.akind) {
+ describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
+ }
+
+ return sizeof(MAC_Error);
+}
+
+
/*------------------------------------------------------------*/
/*--- Suppressions ---*/
/*------------------------------------------------------------*/
+Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su )
+{
+ SuppKind skind;
+
+ if (VG_STREQ(name, "Param")) skind = ParamSupp;
+ else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp;
+ else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp;
+ else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp;
+ else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp;
+ else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp;
+ else if (VG_STREQ(name, "Free")) skind = FreeSupp;
+ else if (VG_STREQ(name, "Leak")) skind = LeakSupp;
+ else
+ return False;
+
+ VG_(set_supp_kind)(su, skind);
+ return True;
+}
+
Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su )
{
Bool eof;
return True;
}
-#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
- && VG_(strcmp)((s1),(s2))==0)
-
Bool SK_(error_matches_suppression)(Error* err, Supp* su)
{
- UInt su_size;
- MemCheckError* err_extra = VG_(get_error_extra)(err);
- ErrorKind ekind = VG_(get_error_kind )(err);
+ UInt su_size;
+ MAC_Error* err_extra = VG_(get_error_extra)(err);
+ ErrorKind ekind = VG_(get_error_kind )(err);
switch (VG_(get_supp_kind)(su)) {
case ParamSupp:
return (ekind == ParamErr
- && STREQ(VG_(get_error_string)(err), VG_(get_supp_string)(su)));
+ && VG_STREQ(VG_(get_error_string)(err),
+ VG_(get_supp_string)(su)));
case CoreMemSupp:
return (ekind == CoreMemErr
- && STREQ(VG_(get_error_string)(err), VG_(get_supp_string)(su)));
+ && VG_STREQ(VG_(get_error_string)(err),
+ VG_(get_supp_string)(su)));
case Value0Supp: su_size = 0; goto value_case;
case Value1Supp: su_size = 1; goto value_case;
return (ekind == FreeErr || ekind == FreeMismatchErr);
case LeakSupp:
- return False; /* Doesn't match any normal error */
+ return (ekind == LeakErr);
default:
VG_(printf)("Error:\n"
}
}
-# undef STREQ
+Char* SK_(get_error_name) ( Error* err )
+{
+ Char* s;
+ switch (VG_(get_error_kind)(err)) {
+ case ParamErr: return "Param";
+ case UserErr: return NULL; /* Can't suppress User errors */
+ case FreeMismatchErr: return "Free";
+ case FreeErr: return "Free";
+ case AddrErr:
+ switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
+ case 1: return "Addr1";
+ case 2: return "Addr2";
+ case 4: return "Addr4";
+ case 8: return "Addr8";
+ default: VG_(skin_panic)("unexpected size for Addr");
+ }
+
+ case ValueErr:
+ switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
+ case 0: return "Cond";
+ case 1: return "Value1";
+ case 2: return "Value2";
+ case 4: return "Value4";
+ case 8: return "Value8";
+ default: VG_(skin_panic)("unexpected size for Value");
+ }
+ case CoreMemErr: return "CoreMem";
+ case LeakErr: return "Leak";
+ default: VG_(skin_panic)("get_error_name: unexpected type");
+ }
+ VG_(printf)(s);
+}
+
+void SK_(print_extra_suppression_info) ( Error* err )
+{
+ if (ParamErr == VG_(get_error_kind)(err)) {
+ VG_(printf)(" %s\n", VG_(get_error_string)(err));
+ }
+}
/*------------------------------------------------------------*/
/*--- Crude profiling machinery. ---*/
125 die_mem_stack
*/
-#ifdef VG_PROFILE_MEMORY
+#ifdef MAC_PROFILE_MEMORY
-UInt MC_(event_ctr)[N_PROF_EVENTS];
+UInt MAC_(event_ctr)[N_PROF_EVENTS];
-void MC_(init_prof_mem) ( void )
+void MAC_(init_prof_mem) ( void )
{
Int i;
for (i = 0; i < N_PROF_EVENTS; i++)
- MC_(event_ctr)[i] = 0;
+ MAC_(event_ctr)[i] = 0;
}
-void MC_(done_prof_mem) ( void )
+void MAC_(done_prof_mem) ( void )
{
Int i;
for (i = 0; i < N_PROF_EVENTS; i++) {
if ((i % 10) == 0)
VG_(printf)("\n");
- if (MC_(event_ctr)[i] > 0)
- VG_(printf)( "prof mem event %2d: %d\n", i, MC_(event_ctr)[i] );
+ if (MAC_(event_ctr)[i] > 0)
+ VG_(printf)( "prof mem event %2d: %d\n", i, MAC_(event_ctr)[i] );
}
VG_(printf)("\n");
}
#else
-void MC_(init_prof_mem) ( void ) { }
-void MC_(done_prof_mem) ( void ) { }
+void MAC_(init_prof_mem) ( void ) { }
+void MAC_(done_prof_mem) ( void ) { }
#endif
-/*------------------------------------------------------------*/
-/*--- Shadow chunks info ---*/
-/*------------------------------------------------------------*/
-
-void MC_(set_where)( ShadowChunk* sc, ExeContext* ec )
-{
- VG_(set_sc_extra)( sc, 0, (UInt)ec );
-}
-
-ExeContext *MC_(get_where)( ShadowChunk* sc )
-{
- return (ExeContext*)VG_(get_sc_extra)(sc, 0);
-}
-
-void SK_(complete_shadow_chunk) ( ShadowChunk* sc, ThreadState* tst )
-{
- VG_(set_sc_extra) ( sc, 0, (UInt)VG_(get_ExeContext)(tst) );
-}
-
-
-/*------------------------------------------------------------*/
-/*--- Postponing free()ing ---*/
-/*------------------------------------------------------------*/
-
-/* Holds blocks after freeing. */
-static ShadowChunk* freed_list_start = NULL;
-static ShadowChunk* freed_list_end = NULL;
-static Int freed_list_volume = 0;
-
-__attribute__ ((unused))
-Int MC_(count_freelist) ( void )
-{
- ShadowChunk* sc;
- Int n = 0;
- for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
- n++;
- return n;
-}
-
-__attribute__ ((unused))
-void MC_(freelist_sanity) ( void )
-{
- ShadowChunk* sc;
- Int n = 0;
- /* VG_(printf)("freelist sanity\n"); */
- for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
- n += VG_(get_sc_size)(sc);
- sk_assert(n == freed_list_volume);
-}
-
-/* Put a shadow chunk on the freed blocks queue, possibly freeing up
- some of the oldest blocks in the queue at the same time. */
-static void add_to_freed_queue ( ShadowChunk* sc )
-{
- ShadowChunk* sc1;
-
- /* Put it at the end of the freed list */
- if (freed_list_end == NULL) {
- sk_assert(freed_list_start == NULL);
- freed_list_end = freed_list_start = sc;
- freed_list_volume = VG_(get_sc_size)(sc);
- } else {
- sk_assert(VG_(get_sc_next)(freed_list_end) == NULL);
- VG_(set_sc_next)(freed_list_end, sc);
- freed_list_end = sc;
- freed_list_volume += VG_(get_sc_size)(sc);
- }
- VG_(set_sc_next)(sc, NULL);
-
- /* Release enough of the oldest blocks to bring the free queue
- volume below vg_clo_freelist_vol. */
-
- while (freed_list_volume > MC_(clo_freelist_vol)) {
- /* freelist_sanity(); */
- sk_assert(freed_list_start != NULL);
- sk_assert(freed_list_end != NULL);
-
- sc1 = freed_list_start;
- freed_list_volume -= VG_(get_sc_size)(sc1);
- /* VG_(printf)("volume now %d\n", freed_list_volume); */
- sk_assert(freed_list_volume >= 0);
-
- if (freed_list_start == freed_list_end) {
- freed_list_start = freed_list_end = NULL;
- } else {
- freed_list_start = VG_(get_sc_next)(sc1);
- }
- VG_(set_sc_next)(sc1, NULL); /* just paranoia */
- VG_(free_ShadowChunk) ( sc1 );
- }
-}
-
-/* Return the first shadow chunk satisfying the predicate p. */
-ShadowChunk* MC_(any_matching_freed_ShadowChunks) ( Bool (*p)(ShadowChunk*) )
-{
- ShadowChunk* sc;
-
- /* No point looking through freed blocks if we're not keeping
- them around for a while... */
- for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
- if (p(sc))
- return sc;
-
- return NULL;
-}
-
-void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst )
-{
- /* Record where freed */
- MC_(set_where)( sc, VG_(get_ExeContext) ( tst ) );
-
- /* Put it out of harm's way for a while. */
- add_to_freed_queue ( sc );
-}
-
/*------------------------------------------------------------*/
/*--- Syscall wrappers ---*/
/*------------------------------------------------------------*/
}
/*--------------------------------------------------------------------*/
-/*--- end mc_common.c ---*/
+/*--- end mac_needs.c ---*/
/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------*/
-/*--- Code that is shared between MemCheck and AddrCheck. ---*/
-/*--- mc_common.h ---*/
+/*--- Declarations shared between MemCheck and AddrCheck. ---*/
+/*--- mac_shared.h ---*/
/*--------------------------------------------------------------------*/
/*
The GNU General Public License is contained in the file COPYING.
*/
-#ifndef __MC_COMMON_H
-#define __MC_COMMON_H
+/* Note: This header contains the declarations shared between
+ Addrcheck and Memcheck, and is #included by both. */
+
+#ifndef __MAC_SHARED_H
+#define __MAC_SHARED_H
#include "vg_skin.h"
-#include "mc_constants.h"
+
+#define MAC_(str) VGAPPEND(vgMAC_,str)
/*------------------------------------------------------------*/
/*--- Errors and suppressions ---*/
/* The classification of a faulting address. */
typedef
- enum { Undescribed, /* as-yet unclassified */
- Stack,
- Unknown, /* classification yielded nothing useful */
- Freed, Mallocd,
- UserG /* in a user-defined block; Addrcheck & Memcheck only */
+ enum {
+ Undescribed, /* as-yet unclassified */
+ Stack,
+ Unknown, /* classification yielded nothing useful */
+ Freed, Mallocd,
+ UserG /* in a user-defined block; Addrcheck & Memcheck only */
}
AddrKind;
/* Something to be suppressed in a leak check. */
LeakSupp
}
- MemCheckSuppKind;
+ MAC_SuppKind;
/* What kind of error it is. */
typedef
CoreMemErr,
AddrErr,
ParamErr, UserErr, /* behaves like an anonymous ParamErr */
- FreeErr, FreeMismatchErr
+ FreeErr, FreeMismatchErr,
+ LeakErr
}
- MemCheckErrorKind;
+ MAC_ErrorKind;
/* What kind of memory access is involved in the error? */
typedef
/* ParamErr, UserErr, CoreMemErr */
Bool isWrite;
}
- MemCheckError;
+ MAC_Error;
/*------------------------------------------------------------*/
/*--- Profiling of skins and memory events ---*/
VgpSkinCC;
/* Define to collect detailed performance info. */
-/* #define VG_PROFILE_MEMORY */
+/* #define MAC_PROFILE_MEMORY */
-#ifdef VG_PROFILE_MEMORY
+#ifdef MAC_PROFILE_MEMORY
# define N_PROF_EVENTS 150
-extern UInt MC_(event_ctr)[N_PROF_EVENTS];
+extern UInt MAC_(event_ctr)[N_PROF_EVENTS];
-# define PROF_EVENT(ev) \
- do { sk_assert((ev) >= 0 && (ev) < N_PROF_EVENTS); \
- MC_(event_ctr)[ev]++; \
+# define PROF_EVENT(ev) \
+ do { sk_assert((ev) >= 0 && (ev) < N_PROF_EVENTS); \
+ MAC_(event_ctr)[ev]++; \
} while (False);
#else
# define PROF_EVENT(ev) /* */
-#endif /* VG_PROFILE_MEMORY */
+#endif /* MAC_PROFILE_MEMORY */
/*------------------------------------------------------------*/
/*--- V and A bits ---*/
#define IS_DISTINGUISHED_SM(smap) \
((smap) == &distinguished_secondary_map)
-#define ENSURE_MAPPABLE(addr,caller) \
- do { \
+#define ENSURE_MAPPABLE(addr,caller) \
+ do { \
if (IS_DISTINGUISHED_SM(primary_map[(addr) >> 16])) { \
primary_map[(addr) >> 16] = alloc_secondary_map(caller); \
- /* VG_(printf)("new 2map because of %p\n", addr); */ \
- } \
+ /* VG_(printf)("new 2map because of %p\n", addr); */ \
+ } \
} while(0)
#define BITARR_SET(aaa_p,iii_p) \
/*--- Command line options + defaults ---*/
/*------------------------------------------------------------*/
-/* Most are shared between MemCheck and AddrCheck, the last two are
- MemCheck only (but here anyway for simplicity) */
+/* Memcheck defines a couple more. */
/* Allow loads from partially-valid addresses? default: YES */
-extern Bool MC_(clo_partial_loads_ok);
+extern Bool MAC_(clo_partial_loads_ok);
/* Max volume of the freed blocks queue. */
-extern Int MC_(clo_freelist_vol);
+extern Int MAC_(clo_freelist_vol);
/* Do leak check at exit? default: NO */
-extern Bool MC_(clo_leak_check);
+extern Bool MAC_(clo_leak_check);
/* How closely should we compare ExeContexts in leak records? default: 2 */
-extern VgRes MC_(clo_leak_resolution);
+extern VgRes MAC_(clo_leak_resolution);
/* In leak check, show reachable-but-not-freed blocks? default: NO */
-extern Bool MC_(clo_show_reachable);
+extern Bool MAC_(clo_show_reachable);
/* Assume accesses immediately below %esp are due to gcc-2.96 bugs.
* default: NO*/
-extern Bool MC_(clo_workaround_gcc296_bugs);
-
-/* DEBUG: clean up instrumented code? default: YES */
-extern Bool MC_(clo_cleanup);
-
-/* When instrumenting, omit some checks if tell-tale literals for
- inlined strlen() are visible in the basic block. default: YES */
-extern Bool MC_(clo_avoid_strlen_errors);
+extern Bool MAC_(clo_workaround_gcc296_bugs);
-extern Bool MC_(process_common_cmd_line_option)(Char* arg);
+extern Bool MAC_(process_common_cmd_line_option)(Char* arg);
/*------------------------------------------------------------*/
/*--- Functions ---*/
/*------------------------------------------------------------*/
-extern void MC_(set_where) ( ShadowChunk* sc, ExeContext* ec );
-extern ExeContext *MC_(get_where) ( ShadowChunk* sc );
+extern void MAC_(set_where) ( ShadowChunk* sc, ExeContext* ec );
+extern ExeContext *MAC_(get_where) ( ShadowChunk* sc );
+
+extern void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai );
+
+extern void MAC_(clear_MAC_Error) ( MAC_Error* err_extra );
+
+extern Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai );
+
+extern Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su );
-extern void MC_(pp_AddrInfo) ( Addr a, AddrInfo* ai );
+extern void MAC_(record_address_error) ( Addr a, Int size, Bool isWrite );
+extern void MAC_(record_core_mem_error) ( ThreadState* tst, Bool isWrite,
+ Char* s );
+extern void MAC_(record_param_error) ( ThreadState* tst, Addr a,
+ Bool isWriteLack, Char* msg );
+extern void MAC_(record_jump_error) ( ThreadState* tst, Addr a );
+extern void MAC_(record_free_error) ( ThreadState* tst, Addr a );
+extern void MAC_(record_freemismatch_error)( ThreadState* tst, Addr a );
-extern void MC_(clear_MemCheckError) ( MemCheckError* err_extra );
+extern void MAC_(pp_shared_SkinError) ( Error* err);
-extern void MC_(record_address_error) ( Addr a, Int size, Bool isWrite );
-extern void MC_(record_core_mem_error) ( ThreadState* tst, Bool isWrite,
- Char* s );
-extern void MC_(record_param_error) ( ThreadState* tst, Addr a,
- Bool isWriteLack, Char* msg );
-extern void MC_(record_jump_error) ( ThreadState* tst, Addr a );
-extern void MC_(record_free_error) ( ThreadState* tst, Addr a );
-extern void MC_(record_freemismatch_error)( ThreadState* tst, Addr a );
+extern void MAC_(init_prof_mem) ( void );
+extern void MAC_(done_prof_mem) ( void );
-extern void MC_(init_prof_mem) ( void );
-extern void MC_(done_prof_mem) ( void );
+extern Int MAC_(count_freelist) ( void ) __attribute__ ((unused));
+extern void MAC_(freelist_sanity) ( void ) __attribute__ ((unused));
+extern ShadowChunk* MAC_(any_matching_freed_ShadowChunks)
+ ( Bool (*p)(ShadowChunk*) );
-extern Int MC_(count_freelist) ( void ) __attribute__ ((unused));
-extern void MC_(freelist_sanity) ( void ) __attribute__ ((unused));
-extern ShadowChunk* MC_(any_matching_freed_ShadowChunks)
- ( Bool (*p)(ShadowChunk*) );
+/* For leak checking */
+extern void MAC_(pp_LeakError)(void* vl, UInt n_this_record,
+ UInt n_total_records);
+
+extern void MAC_(do_detect_memory_leaks) (
+ Bool is_valid_64k_chunk ( UInt ),
+ Bool is_valid_address ( Addr )
+ );
-extern __attribute__((regparm(1))) void MC_(new_mem_stack_4) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(die_mem_stack_4) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(new_mem_stack_8) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(die_mem_stack_8) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(new_mem_stack_12) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(die_mem_stack_12) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(new_mem_stack_16) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(die_mem_stack_16) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(new_mem_stack_32) ( Addr old_ESP );
-extern __attribute__((regparm(1))) void MC_(die_mem_stack_32) ( Addr old_ESP );
-extern void MC_(die_mem_stack) ( Addr a, UInt len );
-extern void MC_(new_mem_stack) ( Addr a, UInt len );
+extern __attribute__((regparm(1))) void MAC_(new_mem_stack_4) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(die_mem_stack_4) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(new_mem_stack_8) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(die_mem_stack_8) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(new_mem_stack_12) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(die_mem_stack_12) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(new_mem_stack_16) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(die_mem_stack_16) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(new_mem_stack_32) ( Addr old_ESP );
+extern __attribute__((regparm(1))) void MAC_(die_mem_stack_32) ( Addr old_ESP );
+extern void MAC_(die_mem_stack) ( Addr a, UInt len );
+extern void MAC_(new_mem_stack) ( Addr a, UInt len );
/*------------------------------------------------------------*/
ALIGNED8_NEW, ALIGNED8_DIE, \
UNALIGNED_NEW, UNALIGNED_DIE) \
\
-void __attribute__((regparm(1))) MC_(new_mem_stack_4)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(new_mem_stack_4)(Addr new_ESP) \
{ \
PROF_EVENT(110); \
if (IS_ALIGNED4_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(die_mem_stack_4)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(die_mem_stack_4)(Addr new_ESP) \
{ \
PROF_EVENT(120); \
if (IS_ALIGNED4_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(new_mem_stack_8)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(new_mem_stack_8)(Addr new_ESP) \
{ \
PROF_EVENT(111); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(die_mem_stack_8)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(die_mem_stack_8)(Addr new_ESP) \
{ \
PROF_EVENT(121); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(new_mem_stack_12)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(new_mem_stack_12)(Addr new_ESP) \
{ \
PROF_EVENT(112); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(die_mem_stack_12)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(die_mem_stack_12)(Addr new_ESP) \
{ \
PROF_EVENT(122); \
/* Note the -12 in the test */ \
} \
} \
\
-void __attribute__((regparm(1))) MC_(new_mem_stack_16)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(new_mem_stack_16)(Addr new_ESP) \
{ \
PROF_EVENT(113); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(die_mem_stack_16)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(die_mem_stack_16)(Addr new_ESP) \
{ \
PROF_EVENT(123); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(new_mem_stack_32)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(new_mem_stack_32)(Addr new_ESP) \
{ \
PROF_EVENT(114); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void __attribute__((regparm(1))) MC_(die_mem_stack_32)(Addr new_ESP) \
+void __attribute__((regparm(1))) MAC_(die_mem_stack_32)(Addr new_ESP) \
{ \
PROF_EVENT(124); \
if (IS_ALIGNED8_ADDR(new_ESP)) { \
} \
} \
\
-void MC_(new_mem_stack) ( Addr a, UInt len ) \
+void MAC_(new_mem_stack) ( Addr a, UInt len ) \
{ \
PROF_EVENT(115); \
UNALIGNED_NEW ( a, len ); \
} \
\
-void MC_(die_mem_stack) ( Addr a, UInt len ) \
+void MAC_(die_mem_stack) ( Addr a, UInt len ) \
{ \
PROF_EVENT(125); \
UNALIGNED_DIE ( a, len ); \
}
-#endif /* __MC_COMMON_H */
+#endif /* __MAC_SHARED_H */
/*--------------------------------------------------------------------*/
-/*--- end mc_common.h ---*/
+/*--- end mac_shared.h ---*/
/*--------------------------------------------------------------------*/
#include "mc_include.h"
/*------------------------------------------------------------*/
-/*--- Comparing and printing errors ---*/
+/*--- Printing errors ---*/
/*------------------------------------------------------------*/
-void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) )
+void SK_(pp_SkinError) ( Error* err )
{
- MemCheckError* err_extra = VG_(get_error_extra)(err);
+ MAC_Error* err_extra = VG_(get_error_extra)(err);
switch (VG_(get_error_kind)(err)) {
case CoreMemErr:
"%s contains uninitialised or unaddressable byte(s)",
VG_(get_error_string)(err));
}
- pp_ExeContext();
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
case ValueErr:
"Use of uninitialised value of size %d",
err_extra->size);
}
- pp_ExeContext();
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
case AddrErr:
default:
VG_(skin_panic)("SK_(pp_SkinError)(axskind)");
}
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
- break;
-
- case FreeErr:
- VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
- /* fall through */
- case FreeMismatchErr:
- if (VG_(get_error_kind)(err) == FreeMismatchErr)
- VG_(message)(Vg_UserMsg,
- "Mismatched free() / delete / delete []");
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
case ParamErr:
"unaddressable byte(s)",
VG_(get_error_string)(err));
}
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
case UserErr:
"Uninitialised or "
"unaddressable byte(s) found during client check request");
}
- pp_ExeContext();
- MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
default:
- VG_(printf)("Error:\n unknown MemCheck error code %d\n",
- VG_(get_error_kind)(err));
- VG_(skin_panic)("unknown error code in SK_(pp_SkinError)");
+ MAC_(pp_shared_SkinError)(err);
+ break;
}
}
/*--- Recording errors ---*/
/*------------------------------------------------------------*/
-/* Describe an address as best you can, for error messages,
- putting the result in ai. */
-
-static void mc_describe_addr ( Addr a, AddrInfo* ai )
-{
- ShadowChunk* sc;
- Bool ok;
- ThreadId tid;
-
- /* Nested functions, yeah. Need the lexical scoping of 'a'. */
-
- /* Closure for searching thread stacks */
- Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
- {
- return (stack_min <= a && a <= stack_max);
- }
- /* Closure for searching malloc'd and free'd lists */
- Bool addr_is_in_block(ShadowChunk *sh_ch)
- {
- return VG_(addr_is_in_block) ( a, VG_(get_sc_data)(sh_ch),
- VG_(get_sc_size)(sh_ch) );
- }
-
- /* Perhaps it's a user-def'd block ? */
- ok = MC_(client_perm_maybe_describe)( a, ai );
- if (ok)
- return;
- /* Perhaps it's on a thread's stack? */
- tid = VG_(any_matching_thread_stack)(addr_is_in_bounds);
- if (tid != VG_INVALID_THREADID) {
- ai->akind = Stack;
- ai->stack_tid = tid;
- return;
- }
- /* Search for a recently freed block which might bracket it. */
- sc = MC_(any_matching_freed_ShadowChunks)(addr_is_in_block);
- if (NULL != sc) {
- ai->akind = Freed;
- ai->blksize = VG_(get_sc_size)(sc);
- ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc));
- ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) );
- return;
- }
- /* Search for a currently malloc'd block which might bracket it. */
- sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block);
- if (NULL != sc) {
- ai->akind = Mallocd;
- ai->blksize = VG_(get_sc_size)(sc);
- ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc));
- ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) );
- return;
- }
- /* Clueless ... */
- ai->akind = Unknown;
- return;
-}
-
/* Creates a copy of the `extra' part, updates the copy with address info if
necessary, and returns the copy. */
-void* SK_(dup_extra_and_update)( Error* err )
-{
- MemCheckError* extra;
- MemCheckError* new_extra = NULL;
-
- extra = ((MemCheckError*)VG_(get_error_extra)(err));
- if (extra != NULL) {
- new_extra = VG_(malloc)(sizeof(MemCheckError));
- *new_extra = *extra;
- if (new_extra->addrinfo.akind == Undescribed)
- mc_describe_addr ( VG_(get_error_address)(err),
- &(new_extra->addrinfo) );
- }
-
- return new_extra;
-}
-
-
/* This one called from generated code. */
void MC_(record_value_error) ( Int size )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.size = size;
VG_(maybe_record_error)( NULL, ValueErr, /*addr*/0, /*s*/NULL, &err_extra );
}
/* This one called from non-generated code */
void MC_(record_user_error) ( ThreadState* tst, Addr a, Bool isWrite )
{
- MemCheckError err_extra;
+ MAC_Error err_extra;
sk_assert(NULL != tst);
- MC_(clear_MemCheckError)( &err_extra );
+ MAC_(clear_MAC_Error)( &err_extra );
err_extra.addrinfo.akind = Undescribed;
err_extra.isWrite = isWrite;
VG_(maybe_record_error)( tst, UserErr, a, /*s*/NULL, &err_extra );
/*--- Suppressions ---*/
/*------------------------------------------------------------*/
-#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
- && VG_(strcmp)((s1),(s2))==0)
-
Bool SK_(recognised_suppression) ( Char* name, Supp* su )
{
SuppKind skind;
- if (STREQ(name, "Param")) skind = ParamSupp;
- else if (STREQ(name, "CoreMem")) skind = CoreMemSupp;
- else if (STREQ(name, "Value0")) skind = Value0Supp; /* backwards compat */
- else if (STREQ(name, "Cond")) skind = Value0Supp;
- else if (STREQ(name, "Value1")) skind = Value1Supp;
- else if (STREQ(name, "Value2")) skind = Value2Supp;
- else if (STREQ(name, "Value4")) skind = Value4Supp;
- else if (STREQ(name, "Value8")) skind = Value8Supp;
- else if (STREQ(name, "Addr1")) skind = Addr1Supp;
- else if (STREQ(name, "Addr2")) skind = Addr2Supp;
- else if (STREQ(name, "Addr4")) skind = Addr4Supp;
- else if (STREQ(name, "Addr8")) skind = Addr8Supp;
- else if (STREQ(name, "Free")) skind = FreeSupp;
- else if (STREQ(name, "Leak")) skind = LeakSupp;
+ if (MAC_(shared_recognised_suppression)(name, su))
+ return True;
+
+ /* Extra suppressions not used by Addrcheck */
+ else if (VG_STREQ(name, "Cond")) skind = Value0Supp;
+ else if (VG_STREQ(name, "Value0")) skind = Value0Supp; /* backwards compat */
+ else if (VG_STREQ(name, "Value1")) skind = Value1Supp;
+ else if (VG_STREQ(name, "Value2")) skind = Value2Supp;
+ else if (VG_STREQ(name, "Value4")) skind = Value4Supp;
+ else if (VG_STREQ(name, "Value8")) skind = Value8Supp;
else
return False;
return True;
}
-# undef STREQ
-
/*--------------------------------------------------------------------*/
/*--- end mc_errcontext.c ---*/
/*--------------------------------------------------------------------*/
The GNU General Public License is contained in the file COPYING.
*/
+/* Note: this header should contain declarations that are for use by
+ Memcheck only -- declarations shared with Addrcheck go in mac_shared.h.
+*/
+
#ifndef __MC_INCLUDE_H
#define __MC_INCLUDE_H
-#include "mc_common.h"
+#include "mac_shared.h"
+#include "mc_constants.h"
+
+/*------------------------------------------------------------*/
+/*--- Types ---*/
+/*------------------------------------------------------------*/
/* UCode extension for efficient memory checking operations */
typedef
TagOp;
+/*------------------------------------------------------------*/
+/*--- Command line options ---*/
+/*------------------------------------------------------------*/
+
+/* DEBUG: clean up instrumented code? default: YES */
+extern Bool MC_(clo_cleanup);
+
+/* When instrumenting, omit some checks if tell-tale literals for
+ inlined strlen() are visible in the basic block. default: YES */
+extern Bool MC_(clo_avoid_strlen_errors);
+
+
/*------------------------------------------------------------*/
/*--- Functions ---*/
/*------------------------------------------------------------*/
if (!ok) {
switch (part) {
case Vg_CoreSysCall:
- MC_(record_param_error) ( tst, bad_addr, /*isWrite =*/True, s );
+ MAC_(record_param_error) ( tst, bad_addr, /*isWrite =*/True, s );
break;
case Vg_CorePThread:
case Vg_CoreSignal:
- MC_(record_core_mem_error)( tst, /*isWrite=*/True, s );
+ MAC_(record_core_mem_error)( tst, /*isWrite=*/True, s );
break;
default:
if (!ok) {
switch (part) {
case Vg_CoreSysCall:
- MC_(record_param_error) ( tst, bad_addr, /*isWrite =*/False, s );
+ MAC_(record_param_error) ( tst, bad_addr, /*isWrite =*/False, s );
break;
case Vg_CorePThread:
- MC_(record_core_mem_error)( tst, /*isWrite=*/False, s );
+ MAC_(record_core_mem_error)( tst, /*isWrite=*/False, s );
break;
/* If we're being asked to jump to a silly address, record an error
message before potentially crashing the entire system. */
case Vg_CoreTranslate:
- MC_(record_jump_error)( tst, bad_addr );
+ MAC_(record_jump_error)( tst, bad_addr );
break;
default:
sk_assert(part == Vg_CoreSysCall);
ok = mc_check_readable_asciiz ( (Addr)str, &bad_addr );
if (!ok) {
- MC_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
+ MAC_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
}
VGP_POPCC(VgpCheckMem);
error arose in the first place from an invalid address.
*/
/* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */
- if (!MC_(clo_partial_loads_ok)
+ if (!MAC_(clo_partial_loads_ok)
|| ((a & 3) != 0)
|| (!a0ok && !a1ok && !a2ok && !a3ok)) {
- MC_(record_address_error)( a, 4, False );
+ MAC_(record_address_error)( a, 4, False );
return (VGM_BYTE_VALID << 24) | (VGM_BYTE_VALID << 16)
| (VGM_BYTE_VALID << 8) | VGM_BYTE_VALID;
}
(which is the default), and the address is 4-aligned.
If not, Case 2 will have applied.
*/
- sk_assert(MC_(clo_partial_loads_ok));
+ sk_assert(MAC_(clo_partial_loads_ok));
{
UInt vw = VGM_WORD_INVALID;
vw <<= 8; vw |= (a3ok ? vb3 : VGM_BYTE_INVALID);
/* If an address error has happened, report it. */
if (aerr)
- MC_(record_address_error)( a, 4, True );
+ MAC_(record_address_error)( a, 4, True );
}
static UInt mc_rd_V2_SLOWLY ( Addr a )
/* If an address error has happened, report it. */
if (aerr) {
- MC_(record_address_error)( a, 2, False );
+ MAC_(record_address_error)( a, 2, False );
vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16)
| (VGM_BYTE_VALID << 8) | (VGM_BYTE_VALID);
}
/* If an address error has happened, report it. */
if (aerr)
- MC_(record_address_error)( a, 2, True );
+ MAC_(record_address_error)( a, 2, True );
}
static UInt mc_rd_V1_SLOWLY ( Addr a )
/* If an address error has happened, report it. */
if (aerr) {
- MC_(record_address_error)( a, 1, False );
+ MAC_(record_address_error)( a, 1, False );
vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16)
| (VGM_BYTE_INVALID << 8) | (VGM_BYTE_VALID);
}
/* If an address error has happened, report it. */
if (aerr)
- MC_(record_address_error)( a, 1, True );
+ MAC_(record_address_error)( a, 1, True );
}
}
if (aerr) {
- MC_(record_address_error)( addr, size, False );
+ MAC_(record_address_error)( addr, size, False );
} else {
if (verr)
MC_(record_value_error)( size );
}
}
if (aerr) {
- MC_(record_address_error)( addr, size, True );
+ MAC_(record_address_error)( addr, size, True );
}
}
skin. */
void MC_(detect_memory_leaks) ( void )
{
- VG_(generic_detect_memory_leaks) (
- mc_is_valid_64k_chunk,
- mc_is_valid_address,
- MC_(get_where),
- MC_(clo_leak_resolution),
- MC_(clo_show_reachable),
- (UInt)LeakSupp
- );
+ MAC_(do_detect_memory_leaks) ( mc_is_valid_64k_chunk, mc_is_valid_address );
}
*eflags_value = VGM_EFLAGS_VALID;
}
+Bool MC_(clo_avoid_strlen_errors) = True;
+Bool MC_(clo_cleanup) = True;
+
Bool SK_(process_cmd_line_option)(Char* arg)
{
-# define STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2)))
-# define STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
-
- if (STREQ(arg, "--avoid-strlen-errors=yes"))
+ if (VG_CLO_STREQ(arg, "--avoid-strlen-errors=yes"))
MC_(clo_avoid_strlen_errors) = True;
- else if (STREQ(arg, "--avoid-strlen-errors=no"))
+ else if (VG_CLO_STREQ(arg, "--avoid-strlen-errors=no"))
MC_(clo_avoid_strlen_errors) = False;
+ else if (VG_CLO_STREQ(arg, "--cleanup=yes"))
+ MC_(clo_cleanup) = True;
+ else if (VG_CLO_STREQ(arg, "--cleanup=no"))
+ MC_(clo_cleanup) = False;
+
else
- return MC_(process_common_cmd_line_option)(arg);
+ return MAC_(process_common_cmd_line_option)(arg);
return True;
-
-#undef STREQ
-#undef STREQN
}
Char* SK_(usage)(void)
VG_(track_new_mem_brk) ( & MC_(make_writable) );
VG_(track_new_mem_mmap) ( & mc_set_perms );
- VG_(track_new_mem_stack_4) ( & MC_(new_mem_stack_4) );
- VG_(track_new_mem_stack_8) ( & MC_(new_mem_stack_8) );
- VG_(track_new_mem_stack_12) ( & MC_(new_mem_stack_12) );
- VG_(track_new_mem_stack_16) ( & MC_(new_mem_stack_16) );
- VG_(track_new_mem_stack_32) ( & MC_(new_mem_stack_32) );
- VG_(track_new_mem_stack) ( & MC_(new_mem_stack) );
+ VG_(track_new_mem_stack_4) ( & MAC_(new_mem_stack_4) );
+ VG_(track_new_mem_stack_8) ( & MAC_(new_mem_stack_8) );
+ VG_(track_new_mem_stack_12) ( & MAC_(new_mem_stack_12) );
+ VG_(track_new_mem_stack_16) ( & MAC_(new_mem_stack_16) );
+ VG_(track_new_mem_stack_32) ( & MAC_(new_mem_stack_32) );
+ VG_(track_new_mem_stack) ( & MAC_(new_mem_stack) );
VG_(track_copy_mem_heap) ( & mc_copy_address_range_state );
VG_(track_copy_mem_remap) ( & mc_copy_address_range_state );
VG_(track_die_mem_brk) ( & MC_(make_noaccess) );
VG_(track_die_mem_munmap) ( & MC_(make_noaccess) );
- VG_(track_die_mem_stack_4) ( & MC_(die_mem_stack_4) );
- VG_(track_die_mem_stack_8) ( & MC_(die_mem_stack_8) );
- VG_(track_die_mem_stack_12) ( & MC_(die_mem_stack_12) );
- VG_(track_die_mem_stack_16) ( & MC_(die_mem_stack_16) );
- VG_(track_die_mem_stack_32) ( & MC_(die_mem_stack_32) );
- VG_(track_die_mem_stack) ( & MC_(die_mem_stack) );
+ VG_(track_die_mem_stack_4) ( & MAC_(die_mem_stack_4) );
+ VG_(track_die_mem_stack_8) ( & MAC_(die_mem_stack_8) );
+ VG_(track_die_mem_stack_12) ( & MAC_(die_mem_stack_12) );
+ VG_(track_die_mem_stack_16) ( & MAC_(die_mem_stack_16) );
+ VG_(track_die_mem_stack_32) ( & MAC_(die_mem_stack_32) );
+ VG_(track_die_mem_stack) ( & MAC_(die_mem_stack) );
- VG_(track_bad_free) ( & MC_(record_free_error) );
- VG_(track_mismatched_free) ( & MC_(record_freemismatch_error) );
+ VG_(track_bad_free) ( & MAC_(record_free_error) );
+ VG_(track_mismatched_free) ( & MAC_(record_freemismatch_error) );
VG_(track_pre_mem_read) ( & mc_check_is_readable );
VG_(track_pre_mem_read_asciiz) ( & mc_check_is_readable_asciiz );
VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" );
VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" );
+ /* Additional block description for VG_(describe_addr)() */
+ MAC_(describe_addr_supp) = MC_(client_perm_maybe_describe);
+
init_shadow_memory();
- MC_(init_prof_mem)();
+ MAC_(init_prof_mem)();
}
void SK_(post_clo_init) ( void )
VG_(print_malloc_stats)();
if (VG_(clo_verbosity) == 1) {
- if (!MC_(clo_leak_check))
+ if (!MAC_(clo_leak_check))
VG_(message)(Vg_UserMsg,
"For a detailed leak analysis, rerun with: --leak-check=yes");
VG_(message)(Vg_UserMsg,
"For counts of detected errors, rerun with: -v");
}
- if (MC_(clo_leak_check)) MC_(detect_memory_leaks)();
+ if (MAC_(clo_leak_check)) MC_(detect_memory_leaks)();
- MC_(done_prof_mem)();
+ MAC_(done_prof_mem)();
if (0) {
VG_(message)(Vg_DebugMsg,