From: Nicholas Nethercote Date: Tue, 8 Apr 2003 00:08:52 +0000 (+0000) Subject: This commit moves some skin-specific stuff out of core, and generally X-Git-Tag: svn/VALGRIND_2_0_0~242 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=594c7fc446d348853c0c42d652e0ce026ffefcc8;p=thirdparty%2Fvalgrind.git This commit moves some skin-specific stuff out of core, and generally 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 --- diff --git a/Makefile.am b/Makefile.am index 59981ddf0e..15e552ef59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -AUTOMAKE_OPTIONS = 1.5 +AUTOMAKE_OPTIONS = 1.4 SUBDIRS = coregrind . docs tests include auxprogs \ addrcheck \ diff --git a/addrcheck/Makefile.am b/addrcheck/Makefile.am index af1d54fa36..1fd6b3853d 100644 --- a/addrcheck/Makefile.am +++ b/addrcheck/Makefile.am @@ -14,6 +14,3 @@ val_PROGRAMS = vgskin_addrcheck.so 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) diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c index 5b3cc6bd61..fc13efc733 100644 --- a/addrcheck/ac_main.c +++ b/addrcheck/ac_main.c @@ -30,11 +30,12 @@ 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" @@ -44,22 +45,15 @@ VG_DETERMINE_INTERFACE_VERSION /*--- 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: @@ -77,161 +71,40 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) 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) /*------------------------------------------------------------*/ @@ -685,21 +558,21 @@ void ac_check_is_accessible ( CorePart part, ThreadState* tst, 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: @@ -737,7 +610,7 @@ void ac_check_is_readable_asciiz ( CorePart part, ThreadState* tst, 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); @@ -890,20 +763,20 @@ static void ac_ACCESS4_SLOWLY ( Addr a ) - 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; } @@ -920,7 +793,7 @@ static void ac_ACCESS2_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 ); } } @@ -934,7 +807,7 @@ static void ac_ACCESS1_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 ); } } @@ -1039,7 +912,7 @@ void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size ) } if (aerr) { - MC_(record_address_error)( addr, size, False ); + MAC_(record_address_error)( addr, size, False ); } } @@ -1168,14 +1041,7 @@ Bool ac_is_valid_address ( Addr a ) 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 ); } @@ -1285,7 +1151,7 @@ Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block, UInt *ret ) 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) @@ -1333,12 +1199,12 @@ void SK_(pre_clo_init)(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 ); @@ -1352,15 +1218,15 @@ void SK_(pre_clo_init)(void) 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 ); @@ -1377,7 +1243,7 @@ void SK_(pre_clo_init)(void) VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" ); init_shadow_memory(); - MC_(init_prof_mem)(); + MAC_(init_prof_mem)(); } void SK_(post_clo_init) ( void ) @@ -1389,16 +1255,16 @@ void SK_(fini) ( 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)(); } /*--------------------------------------------------------------------*/ diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html index 16b7c1bf8b..14a235e471 100644 --- a/coregrind/docs/coregrind_core.html +++ b/coregrind/docs/coregrind_core.html @@ -72,7 +72,10 @@ interested, Valgrind allows you to selectively suppress errors, by 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 --gen-suppressions=yes option which tells Valgrind to +print out a suppression for each error that appears, which you can +then copy into a suppressions file.

Different skins report different kinds of errors. The suppression @@ -189,7 +192,7 @@ does about where that stream is sent to. You have three options: 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.

- 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, valgrind-listener, which accepts connections on the specified port and copies whatever it is sent @@ -453,6 +456,34 @@ follows: socket, I guess this option doesn't make any sense. Caveat emptor.

+

  • --gen-suppressions=no [the default]
    + --gen-suppressions=yes +

    When enabled, Valgrind will pause after every error shown, + and print the line +
    + ---- Print suppression ? --- [Return/N/n/Y/y/C/c] ---- +

    + The prompt's behaviour is the same as for the --gdb-attach + option. +

    + 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. +

    + This option is particularly useful with C++ programs, as it prints out + the suppressions with mangled names, as required. +

    + 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 <insert a suppression name here>; + the name doesn't really matter, it's only used with the + -v option which prints out all used suppression records. +


  • +

  • --alignment=<number> [default: 4]

    By default valgrind's malloc, realloc, etc, return 4-byte aligned addresses. These are suitable for @@ -620,6 +651,12 @@ follows: unexpectedly in the write() system call, you may find the --trace-syscalls=yes --trace-sched=yes flags useful. +

    +

  • lax-ioctls 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.

  • @@ -685,6 +722,13 @@ shouldn't need to use them in the normal run of things. Nevertheless:

    +

  • --trace-codegen=XXXXX [default: 00000] +

    Enable/disable tracing of code generation. Code can be printed + at five different stages of translation; each X element + must be 0 or 1. +


  • +

    +

  • --stop-after=<number> [default: infinity, more or less]

    After <number> basic blocks have been executed, shut down @@ -709,6 +753,11 @@ shouldn't need to use them in the normal run of things. Nevertheless: (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 +valgrind.h should not be included in your program, but +instead memcheck.h (for the Memcheck and Addrcheck skins) +or helgrind.h (for Helgrind).) +

    Valgrind has a trapdoor mechanism via which the client program can pass all manner of requests and queries to Valgrind. Internally, this @@ -937,7 +986,7 @@ build problems.

    2.11  If you have problems

    Mail me (jseward@acm.org). -

    See Section 4 for the known limitations of +

    See this section for the known limitations of Valgrind, and for a list of programs which are known not to work on it. diff --git a/coregrind/vg_clientmalloc.c b/coregrind/vg_clientmalloc.c index 653dc06edb..025de27ac4 100644 --- a/coregrind/vg_clientmalloc.c +++ b/coregrind/vg_clientmalloc.c @@ -153,6 +153,38 @@ void VG_(free_ShadowChunk) ( ShadowChunk* sc ) 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. @@ -180,6 +212,16 @@ ShadowChunk** VG_(get_malloc_shadows) ( /*OUT*/ UInt* n_shadows ) } } 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; } @@ -190,7 +232,7 @@ Bool VG_(addr_is_in_block)( Addr a, Addr start, UInt size ) } /* 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; diff --git a/coregrind/vg_default.c b/coregrind/vg_default.c index 8778b84685..1bd36ff87b 100644 --- a/coregrind/vg_default.c +++ b/coregrind/vg_default.c @@ -105,15 +105,15 @@ Bool SK_(eq_SkinError)(VgRes res, Error* e1, Error* e2) } __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)) @@ -134,6 +134,18 @@ Bool SK_(error_matches_suppression)(Error* err, Supp* su) 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 diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c index 6ba7343a91..3040fb2f3a 100644 --- a/coregrind/vg_errcontext.c +++ b/coregrind/vg_errcontext.c @@ -91,13 +91,6 @@ static Bool eq_Error ( VgRes res, Error* e1, Error* e2 ) 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) @@ -111,7 +104,7 @@ static void pp_Error ( Error* err, Bool printCount ) 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", @@ -123,13 +116,12 @@ static void pp_Error ( Error* err, Bool printCount ) /* 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, ""); @@ -137,8 +129,8 @@ Bool vg_is_GDB_attach_requested ( void ) 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); @@ -152,15 +144,15 @@ Bool vg_is_GDB_attach_requested ( void ) 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; } @@ -178,14 +170,17 @@ Bool vg_is_GDB_attach_requested ( void ) 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)(); @@ -209,6 +204,66 @@ void construct_error ( Error* err, ThreadState* tst, 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)(" \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. */ @@ -218,8 +273,8 @@ void VG_(maybe_record_error) ( ThreadState* tst, 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; @@ -279,7 +334,7 @@ void VG_(maybe_record_error) ( ThreadState* tst, } /* 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; @@ -312,20 +367,34 @@ void VG_(maybe_record_error) ( ThreadState* tst, /* 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; @@ -333,20 +402,56 @@ void VG_(maybe_record_error) ( ThreadState* tst, 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 ---*/ @@ -529,9 +634,6 @@ Bool skin_name_present(Char *name, Char *names) 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. @@ -563,10 +665,10 @@ static void load_one_suppressions_file ( Char* filename ) 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 ); @@ -588,7 +690,7 @@ static void load_one_suppressions_file ( Char* filename ) /* 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; @@ -610,7 +712,7 @@ static void load_one_suppressions_file ( Char* filename ) while (True) { eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) goto syntax_error; - if (STREQ(buf, "}")) + if (VG_STREQ(buf, "}")) break; } continue; @@ -624,7 +726,7 @@ static void load_one_suppressions_file ( Char* filename ) 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]))) @@ -673,9 +775,8 @@ void VG_(load_suppressions) ( void ) 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 ); @@ -714,7 +815,7 @@ Bool supp_matches_callers(Supp* su, Char caller_obj[][M_VG_ERRTXT], 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"); } } @@ -736,7 +837,7 @@ static Supp* is_suppressible_error ( Error* err ) 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. */ @@ -746,9 +847,8 @@ static Supp* is_suppressible_error ( Error* err ) 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. */ @@ -761,8 +861,6 @@ static Supp* is_suppressible_error ( Error* err ) return NULL; /* no matches */ } -#undef STREQ - /*--------------------------------------------------------------------*/ /*--- end vg_errcontext.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index a420d456f4..85ba492b16 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -171,6 +171,8 @@ typedef 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 */ @@ -1192,14 +1194,13 @@ extern void VG_(record_pthread_error) ( ThreadId tid, Char* msg ); 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 diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c index 351aa50f3e..aca07cc7f7 100644 --- a/coregrind/vg_main.c +++ b/coregrind/vg_main.c @@ -487,6 +487,7 @@ UInt VG_(num_scheduling_events_MAJOR) = 0; /* 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; @@ -588,6 +589,7 @@ static void usage ( void ) " -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= show callers in stack traces [4]\n" " --error-limit=no|yes stop showing new errors if too many? [yes]\n" @@ -685,8 +687,6 @@ static void process_cmd_line_options ( void ) 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); @@ -863,64 +863,71 @@ static void process_cmd_line_options ( void ) 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, @@ -930,28 +937,28 @@ static void process_cmd_line_options ( void ) 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]; @@ -971,48 +978,48 @@ static void process_cmd_line_options ( void ) } } - 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) @@ -1031,8 +1038,6 @@ static void process_cmd_line_options ( void ) } # undef ISSPACE -# undef STREQ -# undef STREQN if (VG_(clo_verbosity < 0)) VG_(clo_verbosity) = 0; diff --git a/coregrind/vg_memory.c b/coregrind/vg_memory.c index 64200ed7ac..6ade64268b 100644 --- a/coregrind/vg_memory.c +++ b/coregrind/vg_memory.c @@ -292,612 +292,6 @@ void VG_(unknown_esp_update)(Addr new_ESP) } } -/*--------------------------------------------------------------------*/ -/*--- 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 ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index 90b2ac65b2..72f33d6a26 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -197,7 +197,7 @@ Bool VG_(is_valid_or_empty_tid) ( ThreadId tid ) 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; diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c index c36d32b258..5f64156301 100644 --- a/coregrind/vg_symtab2.c +++ b/coregrind/vg_symtab2.c @@ -2272,10 +2272,9 @@ void VG_(mini_stack_dump) ( ExeContext* ec ) 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 "); diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index d56f681d32..21bfe7431d 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -2247,7 +2247,7 @@ static void describe_addr ( Addr a, AddrInfo* ai ) } /* 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); @@ -2462,7 +2462,7 @@ static Char *lockset_str(const Char *prefix, const LockSet *lockset) 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]; @@ -2477,7 +2477,7 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) 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) { @@ -2550,7 +2550,7 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) 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); @@ -2567,7 +2567,7 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) 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++) { @@ -2620,6 +2620,19 @@ Bool SK_(error_matches_suppression)(Error* err, Supp* su) 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) { diff --git a/include/vg_skin.h b/include/vg_skin.h index 986b8f321c..723555f4e4 100644 --- a/include/vg_skin.h +++ b/include/vg_skin.h @@ -132,6 +132,15 @@ const Int VG_(skin_interface_minor_version) = VG_CORE_INTERFACE_MINOR_VERSION; /*=== 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); @@ -1175,9 +1184,8 @@ void* VG_(get_error_extra) ( Error* err ); 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. @@ -1185,6 +1193,16 @@ void* VG_(get_error_extra) ( Error* err ); 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. @@ -1296,12 +1314,12 @@ extern Bool VG_(addr_is_in_block) ( Addr a, Addr start, UInt size ); /* 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. */ @@ -1585,21 +1603,18 @@ extern void SK_(fini) ( void ); */ 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)(). */ @@ -1615,7 +1630,17 @@ extern Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, /* 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 ); /* ------------------------------------------------------------------ */ diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am index af140b925a..9c32164c04 100644 --- a/memcheck/Makefile.am +++ b/memcheck/Makefile.am @@ -14,9 +14,10 @@ valdir = $(libdir)/valgrind 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 \ @@ -29,10 +30,7 @@ mcinclude_HEADERS = \ 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) diff --git a/memcheck/docs/mc_main.html b/memcheck/docs/mc_main.html index 41e9c57f70..55e56bc892 100644 --- a/memcheck/docs/mc_main.html +++ b/memcheck/docs/mc_main.html @@ -138,7 +138,7 @@ Memcheck is Valgrind-1.0.X checking mechanism bundled up into a skin. -

    3.3  Explaination of error messages from Memcheck

    +

    3.3  Explanation of error messages from Memcheck

    Despite considerable sophistication under the hood, Memcheck can only really detect two kinds of errors, use of illegal addresses, and use @@ -146,7 +146,7 @@ of undefined values. Nevertheless, this is enough to help you 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 -Section 4. +this section.

    3.3.1  Illegal read / Illegal write errors

    diff --git a/memcheck/mac_leakcheck.c b/memcheck/mac_leakcheck.c new file mode 100644 index 0000000000..83d3ada445 --- /dev/null +++ b/memcheck/mac_leakcheck.c @@ -0,0 +1,526 @@ + +/*--------------------------------------------------------------------*/ +/*--- 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 ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/memcheck/mc_common.c b/memcheck/mac_needs.c similarity index 62% rename from memcheck/mc_common.c rename to memcheck/mac_needs.c index deedf8e494..ad6b2f6652 100644 --- a/memcheck/mc_common.c +++ b/memcheck/mac_needs.c @@ -1,7 +1,7 @@ /*--------------------------------------------------------------------*/ /*--- Code that is shared between MemCheck and AddrCheck. ---*/ -/*--- mc_common.c ---*/ +/*--- mac_needs.c ---*/ /*--------------------------------------------------------------------*/ /* @@ -31,7 +31,7 @@ */ -#include "mc_common.h" +#include "mac_shared.h" /*------------------------------------------------------------*/ /*--- Defns ---*/ @@ -45,64 +45,152 @@ /*--- 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 ); } /*------------------------------------------------------------*/ @@ -120,7 +208,7 @@ void clear_AddrInfo ( AddrInfo* ai ) 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; @@ -150,8 +238,8 @@ static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 ) 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)); @@ -200,6 +288,10 @@ Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* 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)); @@ -207,7 +299,7 @@ Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 ) } } -void MC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) +void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) { switch (ai->akind) { case Stack: @@ -251,7 +343,41 @@ void MC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) 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)"); } } @@ -259,6 +385,81 @@ void MC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) /*--- 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 ) @@ -272,19 +473,19 @@ 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; @@ -296,65 +497,104 @@ void MC_(record_address_error) ( Addr a, Int size, Bool isWrite ) /* 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; @@ -367,23 +607,22 @@ Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su ) 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; @@ -404,7 +643,7 @@ Bool SK_(error_matches_suppression)(Error* err, Supp* su) return (ekind == FreeErr || ekind == FreeMismatchErr); case LeakSupp: - return False; /* Doesn't match any normal error */ + return (ekind == LeakErr); default: VG_(printf)("Error:\n" @@ -415,7 +654,45 @@ Bool SK_(error_matches_suppression)(Error* err, Supp* su) } } -# 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. ---*/ @@ -517,151 +794,36 @@ M 89 fpu_write 10/28/108 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 ---*/ /*------------------------------------------------------------*/ @@ -688,5 +850,5 @@ void SK_(post_syscall) ( ThreadId tid, UInt syscallno, } /*--------------------------------------------------------------------*/ -/*--- end mc_common.c ---*/ +/*--- end mac_needs.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_common.h b/memcheck/mac_shared.h similarity index 78% rename from memcheck/mc_common.h rename to memcheck/mac_shared.h index 7a8d14b4bb..fc3d86bdf7 100644 --- a/memcheck/mc_common.h +++ b/memcheck/mac_shared.h @@ -1,7 +1,7 @@ /*--------------------------------------------------------------------*/ -/*--- Code that is shared between MemCheck and AddrCheck. ---*/ -/*--- mc_common.h ---*/ +/*--- Declarations shared between MemCheck and AddrCheck. ---*/ +/*--- mac_shared.h ---*/ /*--------------------------------------------------------------------*/ /* @@ -30,11 +30,15 @@ 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 ---*/ @@ -42,11 +46,12 @@ /* 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; @@ -83,7 +88,7 @@ typedef /* Something to be suppressed in a leak check. */ LeakSupp } - MemCheckSuppKind; + MAC_SuppKind; /* What kind of error it is. */ typedef @@ -91,9 +96,10 @@ 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 @@ -112,7 +118,7 @@ typedef /* ParamErr, UserErr, CoreMemErr */ Bool isWrite; } - MemCheckError; + MAC_Error; /*------------------------------------------------------------*/ /*--- Profiling of skins and memory events ---*/ @@ -127,23 +133,23 @@ typedef 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 ---*/ @@ -152,12 +158,12 @@ extern UInt MC_(event_ctr)[N_PROF_EVENTS]; #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) \ @@ -198,78 +204,85 @@ extern UInt MC_(event_ctr)[N_PROF_EVENTS]; /*--- 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 ); /*------------------------------------------------------------*/ @@ -288,7 +301,7 @@ extern void MC_(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)) { \ @@ -298,7 +311,7 @@ void __attribute__((regparm(1))) MC_(new_mem_stack_4)(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)) { \ @@ -308,7 +321,7 @@ void __attribute__((regparm(1))) MC_(die_mem_stack_4)(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)) { \ @@ -321,7 +334,7 @@ void __attribute__((regparm(1))) MC_(new_mem_stack_8)(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)) { \ @@ -334,7 +347,7 @@ void __attribute__((regparm(1))) MC_(die_mem_stack_8)(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)) { \ @@ -348,7 +361,7 @@ void __attribute__((regparm(1))) MC_(new_mem_stack_12)(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 */ \ @@ -363,7 +376,7 @@ void __attribute__((regparm(1))) MC_(die_mem_stack_12)(Addr new_ESP) \ } \ } \ \ -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)) { \ @@ -378,7 +391,7 @@ void __attribute__((regparm(1))) MC_(new_mem_stack_16)(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)) { \ @@ -393,7 +406,7 @@ void __attribute__((regparm(1))) MC_(die_mem_stack_16)(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)) { \ @@ -412,7 +425,7 @@ void __attribute__((regparm(1))) MC_(new_mem_stack_32)(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)) { \ @@ -431,20 +444,20 @@ void __attribute__((regparm(1))) MC_(die_mem_stack_32)(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 ---*/ /*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_errcontext.c b/memcheck/mc_errcontext.c index cb757c7c81..eda5ca8d5c 100644 --- a/memcheck/mc_errcontext.c +++ b/memcheck/mc_errcontext.c @@ -32,12 +32,12 @@ #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: @@ -49,7 +49,7 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) "%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: @@ -62,7 +62,7 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) "Use of uninitialised value of size %d", err_extra->size); } - pp_ExeContext(); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); break; case AddrErr: @@ -82,19 +82,8 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) 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: @@ -108,8 +97,8 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) "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: @@ -121,14 +110,13 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) "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; } } @@ -136,89 +124,14 @@ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) /*--- 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 ); } @@ -226,11 +139,11 @@ void MC_(record_value_error) ( Int size ) /* 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 ); @@ -240,27 +153,20 @@ void MC_(record_user_error) ( ThreadState* tst, Addr a, Bool isWrite ) /*--- 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; @@ -268,8 +174,6 @@ Bool SK_(recognised_suppression) ( Char* name, Supp* su ) return True; } -# undef STREQ - /*--------------------------------------------------------------------*/ /*--- end mc_errcontext.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index af3e2ed362..7d568e5b36 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -29,10 +29,19 @@ 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 @@ -88,6 +97,18 @@ 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 ---*/ /*------------------------------------------------------------*/ diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index c5d9ef056c..46ae522cd2 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -586,12 +586,12 @@ void mc_check_is_writable ( CorePart part, ThreadState* tst, 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: @@ -617,17 +617,17 @@ void mc_check_is_readable ( CorePart part, ThreadState* tst, 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: @@ -650,7 +650,7 @@ void mc_check_is_readable_asciiz ( CorePart part, ThreadState* tst, 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); @@ -897,10 +897,10 @@ static UInt mc_rd_V4_SLOWLY ( Addr a ) 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; } @@ -913,7 +913,7 @@ static UInt mc_rd_V4_SLOWLY ( Addr a ) (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); @@ -943,7 +943,7 @@ static void mc_wr_V4_SLOWLY ( Addr a, UInt vbytes ) /* 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 ) @@ -962,7 +962,7 @@ 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); } @@ -984,7 +984,7 @@ static void mc_wr_V2_SLOWLY ( Addr a, UInt vbytes ) /* 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 ) @@ -1001,7 +1001,7 @@ 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); } @@ -1020,7 +1020,7 @@ static void mc_wr_V1_SLOWLY ( Addr a, UInt vbytes ) /* If an address error has happened, report it. */ if (aerr) - MC_(record_address_error)( a, 1, True ); + MAC_(record_address_error)( a, 1, True ); } @@ -1250,7 +1250,7 @@ void mc_fpu_read_check_SLOWLY ( Addr addr, Int size ) } if (aerr) { - MC_(record_address_error)( addr, size, False ); + MAC_(record_address_error)( addr, size, False ); } else { if (verr) MC_(record_value_error)( size ); @@ -1280,7 +1280,7 @@ void mc_fpu_write_check_SLOWLY ( Addr addr, Int size ) } } if (aerr) { - MC_(record_address_error)( addr, size, True ); + MAC_(record_address_error)( addr, size, True ); } } @@ -1329,14 +1329,7 @@ Bool mc_is_valid_address ( Addr a ) 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 ); } @@ -1489,23 +1482,25 @@ void SK_(written_shadow_regs_values)( UInt* gen_reg_value, UInt* eflags_value ) *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) @@ -1556,12 +1551,12 @@ void SK_(pre_clo_init)(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 ); @@ -1575,15 +1570,15 @@ void SK_(pre_clo_init)(void) 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 ); @@ -1610,8 +1605,11 @@ void SK_(pre_clo_init)(void) 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 ) @@ -1623,16 +1621,16 @@ void SK_(fini) ( 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,