Index: exp-dmdv.supp =================================================================== --- exp-dmdv.supp (revision 0) +++ exp-dmdv.supp (revision 0) @@ -0,0 +1,17 @@ +##----------------------------------------------------------------------## +# For DMDV and Firefox. SQLite has its own memory reporting infrastructure +# which we can't tie into mallocSizeOf. So just suppress those ones. +{ + SQLite-malloc + DMDV:Unreported + fun:malloc + fun:sqlite3MemMalloc +} + +{ + SQLite-realloc + DMDV:Unreported + fun:realloc + fun:sqlite3MemRealloc +} + Index: exp-dmdv/dmdv_main.c =================================================================== --- exp-dmdv/dmdv_main.c (revision 0) +++ exp-dmdv/dmdv_main.c (revision 0) @@ -0,0 +1,1099 @@ + +/*--------------------------------------------------------------------*/ +/*--- DMDV: A dark matter detector. dmdv_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of DMDV, a dark matter detector. + + Copyright (C) 2011-2011 Nicholas Nethercote + njn@valgrind.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 "pub_tool_basics.h" +#include "pub_tool_aspacemgr.h" +#include "pub_tool_execontext.h" +#include "pub_tool_hashtable.h" +#include "pub_tool_libcbase.h" +#include "pub_tool_libcassert.h" +#include "pub_tool_libcprint.h" +#include "pub_tool_mallocfree.h" +#include "pub_tool_options.h" +#include "pub_tool_oset.h" +#include "pub_tool_replacemalloc.h" +#include "pub_tool_stacktrace.h" // XXX: temp? just for printing stacks +#include "pub_tool_threadstate.h" // XXX: temp? just for printing stacks +#include "pub_tool_tooliface.h" +#include "pub_tool_vki.h" + +#include "dmdv.h" /* for client requests */ + +// Todo: +// +// - Use a Massif-style allocation tree for unreported blocks -- makes it more +// obvious if lots of small almost-the-same records add up to significant +// amounts, and saves the hassle of trying different --num-callers values. +// Compute it from the stack traces only at CHECK_REPORTING time. Should be +// able to avoid looking up the debug info until the very end? +// +// - Would be good to track mmap'd blocks in such a way that any breakage +// (e.g. regexp-code wasn't working for a while) is detected. Probably +// would need suppressions for the common cases like dl_load and pthread +// stacks. +// +// - Insert a sanity-check that REPORT isn't called outside of a +// CHECK_REPORTING context (need to mark the end of such a context). [Would +// need to distinguish between traverse-REPORT and counter-REPORT]. +// +// - Probably should split DMDV_REPORT into two, one for each kind of reporter, +// and store maintain-a-counter counts in a separate table. +// + +// A note about reporter styles: +// - Traverse-based reporters are easy to handle. They just call DMDV_REPORT +// on each heap block. If we only had that style of reporter then all R +// bits would be clear before about:memory is loaded, and we could clear all +// R bits after CHECK_REPORTING is finished. +// +// - Counter-based reporters are more difficult. We could suppress +// them, but that's inaccurate -- e.g. we could be missing some slop bytes +// and not know. It's better to call DMDV_REPORT and DMDV_UNREPORT on the +// relevant heap blocks as they are counted/uncounted. But that means that +// some hb->isReported flags are set even before about:memory is loaded; +// and those bits should still be set afterwards. Could introduce a second +// flag to handle this, but for now we just restrict CHECK_REPORTING to only +// be called once. + +// A note about terminology: +// - A block that isn't fully reported is "under-reported". (But all heap +// blocks are now either fully-reported or not reported at all.) +// - Bytes within that block that aren't reported are "unreported". +// - So "under-reported" refers to blocks, "unreported" refers to bytes. + +//------------------------------------------------------------// +//--- Command line args ---// +//------------------------------------------------------------// + +//static Bool cloMmap = False; + +static Bool dmdv_process_cmd_line_option(Char* arg) +{ + VG_(clo_backtrace_size) = 8; // re-define for DMDV + + // njn: disabled for now + //if VG_BOOL_CLO(arg, "--mmap", cloMmap) {} + //else + // return False; + + return True; +} + +static void dmdv_print_usage(void) +{ + VG_(printf)( +" --mmap=no|yes track mmap blocks [no]\n" + ); +} + +static void dmdv_print_debug_usage(void) +{ + VG_(printf)( +" (none)\n" + ); +} + +//------------------------------------------------------------// +//--- Heap management ---// +//------------------------------------------------------------// + +// This is used by both HeapBlocks and Records. +typedef + struct { + ExeContext* allocPt; // Where allocated + ExeContext* reportPt; // Where reported; NULL if unreported + const Char* reporter; // Reporter name; NULL if unreported + } + HeapKey; + +// Nb: first two fields must match core's VgHashNode. +typedef + struct _HeapBlock { + struct _HeapBlock* next; + Addr data; // Ptr to actual block + SizeT reqSzB; // Size requested + SizeT usableSzB; // Size allocated + HeapKey key; + } + HeapBlock; + +static VgHashTable heapBlocks = NULL; + +// Compute the size of the block that would be allocated by jemalloc. +static +SizeT getJemallocSize(SizeT n) +{ + // Small/Tiny: 2, 4, 8 (bytes) + if (n <= 2) n = 2; + else if (n <= 4) n = 4; + else if (n <= 8) n = 8; + + // Small/Quantum-spaced: 16, 32, 48, ..., 480, 496, 512 (bytes) + else if (n <= 512) n = VG_ROUNDUP(n, 16); + + // Small/Sub-page: 1, 2 (KiB) + else if (n <= 2*1024) n = VG_ROUNDUP(n, 1024); + + // Large: 4, 8, 12, ..., 1012, 1016, 1020 (KiB) + // Huge: 1 MiB + 4 KiB, 1 MiB + 8 KiB, ... + else n = VG_ROUNDUP(n, 4*1024); + + return n; +} + +static __inline__ +void* allocAndRecordBlock(ThreadId tid, SizeT reqSzB, SizeT reqAlignB, + Bool isZeroed) +{ + SizeT usableSzB; + void* p; + + if ((SSizeT)reqSzB < 0) + return NULL; + + // Round up the request size so that we allocate the amount of memory that + // jemalloc would allocate. And determine the amount of slop jemalloc + // would produce. In some cases Valgrind's allocator produces extra slop + // on top; we don't count that. + usableSzB = getJemallocSize(reqSzB); + p = VG_(cli_malloc)(reqAlignB, usableSzB); + if (!p) { + return NULL; + } + + // Zero if necessary. + if (isZeroed) { + VG_(memset)(p, 0, usableSzB); + } + + // Record block. + HeapBlock* hb = VG_(malloc)("dmdv.main.rb.1", sizeof(HeapBlock)); + hb->reqSzB = reqSzB; + hb->usableSzB = usableSzB; + hb->data = (Addr)p; + hb->key.allocPt = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/); + hb->key.reportPt = NULL; + hb->key.reporter = NULL; + VG_(HT_add_node)(heapBlocks, hb); + + return p; +} + +static __inline__ +void unrecordAndFreeBlock(void* p) +{ + HeapBlock* hb = VG_(HT_remove)(heapBlocks, (UWord)p); + if (NULL == hb) { + return; // must have been a bogus free() + } + + // Free the block and its metadata. + VG_(free)(hb); + VG_(cli_free)(p); +} + +static __inline__ +void* reallocAndRecordBlock(ThreadId tid, void* pOld, SizeT newReqSzB) +{ + HeapBlock* hb; + void* pNew; + SizeT oldUsableSzB, newUsableSzB; + + // Remove the old block + hb = VG_(HT_remove)(heapBlocks, (UWord)pOld); + if (hb == NULL) { + return NULL; // must have been a bogus realloc() + } + + oldUsableSzB = hb->usableSzB; + + if (newReqSzB <= oldUsableSzB) { + // New size is smaller or same; block not moved. + pNew = pOld; + newUsableSzB = oldUsableSzB; + + } else { + newUsableSzB = getJemallocSize(newReqSzB); + + // New size is bigger; make new block, copy shared contents, free old. + pNew = VG_(cli_malloc)(VG_(clo_alignment), newUsableSzB); + if (!pNew) { + // Nb: if realloc fails, NULL is returned but the old block is not + // touched. What an awful function. + return NULL; + } + VG_(memcpy)(pNew, pOld, oldUsableSzB); + VG_(cli_free)(pOld); + } + + if (pNew) { + // Update HeapBlock. + hb->data = (Addr)pNew; + hb->reqSzB = newReqSzB; + hb->usableSzB = newUsableSzB; + hb->key.allocPt = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/); + } + + // Now insert the new hb (with a possibly new 'data' field) into + // heapBlocks. If this realloc() did not increase the memory size, we + // will have removed and then re-added hb unnecessarily. But that's ok + // because shrinking a block with realloc() is (presumably) much rarer + // than growing it, and this way simplifies the growing case. + VG_(HT_add_node)(heapBlocks, hb); + + return pNew; +} + +//------------------------------------------------------------// +//--- Mapping management ---// +//------------------------------------------------------------// + +#if 0 +static __inline__ +void recordMapBlock(ThreadId tid, Addr a, SizeT szB) +{ + // Record block. + HeapBlock* hb = VG_(malloc)("dmdv.main.rmb.1", sizeof(HeapBlock)); + hb->data = a; + hb->reqSzB = szB; + tl_assert(VG_IS_PAGE_ALIGNED(szB)); + hb->slopSzB = 0; + hb->where = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/); + hb->isHeap = False; + VG_(HT_add_node)(blocks, hb); +} + +static __inline__ +void unrecordMapBlock(Addr a, SizeT szB) +{ + HeapBlock* hb = VG_(HT_remove)(blocks, a); + if (NULL == hb) { + tl_assert(0); // njn + VG_(printf)("WARNING: removing unknown mapBlock: 0x%lx, %lu\n", a, szB); + return; + } + + // Clear R bits; if the block has been reported and is recycled after it's + // freed, we don't want it to be bogusly considered reported. + Addr limit = a + szB; + while (a < limit) { + clearRbit(a); + a++; + } + + // Free the mapping's metadata. + VG_(free)(hb); hb = NULL; +} +#endif + +//------------------------------------------------------------// +//--- malloc() et al replacement wrappers ---// +//------------------------------------------------------------// + +static void* dmdv_malloc(ThreadId tid, SizeT szB) +{ + return allocAndRecordBlock(tid, szB, VG_(clo_alignment), /*isZeroed*/False); +} + +static void* dmdv___builtin_new(ThreadId tid, SizeT szB) +{ + return allocAndRecordBlock(tid, szB, VG_(clo_alignment), /*isZeroed*/False); +} + +static void* dmdv___builtin_vec_new(ThreadId tid, SizeT szB) +{ + return allocAndRecordBlock(tid, szB, VG_(clo_alignment), /*isZeroed*/False); +} + +static void* dmdv_calloc(ThreadId tid, SizeT m, SizeT szB) +{ + return allocAndRecordBlock(tid, m*szB, VG_(clo_alignment), /*isZeroed*/True); +} + +static void *dmdv_memalign(ThreadId tid, SizeT alignB, SizeT szB) +{ + return allocAndRecordBlock(tid, szB, alignB, /*isZeroed*/False); +} + +static void dmdv_free(ThreadId tid __attribute__((unused)), void* p) +{ + unrecordAndFreeBlock(p); +} + +static void dmdv___builtin_delete(ThreadId tid, void* p) +{ + unrecordAndFreeBlock(p); +} + +static void dmdv___builtin_vec_delete(ThreadId tid, void* p) +{ + unrecordAndFreeBlock(p); +} + +static void* dmdv_realloc(ThreadId tid, void* pOld, SizeT new_szB) +{ + return reallocAndRecordBlock(tid, pOld, new_szB); +} + +static SizeT dmdv_malloc_usable_size(ThreadId tid, void* p) +{ + HeapBlock* hb = VG_(HT_lookup)(heapBlocks, (UWord)p); + return hb ? hb->usableSzB : 0; +} + +//------------------------------------------------------------// +//--- mmap() et al tracers ---// +//------------------------------------------------------------// + +#if 0 +static +void dmdv_new_mem_startup(Addr a, SizeT len, + Bool rr, Bool ww, Bool xx, ULong di_handle) +{ + // Ignore. +} + +static +void dmdv_new_mem_mmap(Addr a, SizeT len, + Bool rr, Bool ww, Bool xx, ULong di_handle) +{ + if (cloMmap) { + tl_assert(VG_IS_PAGE_ALIGNED(len)); + recordMapBlock(VG_(get_running_tid)(), a, len); + //VG_(printf)("foo: mmap: 0x%lx, %lu\n", a, len); + //VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), 8); + } +} + +static +void dmdv_die_mem_munmap(Addr a, SizeT len) +{ + if (cloMmap) { + tl_assert(VG_IS_PAGE_ALIGNED(len)); + unrecordMapBlock(a, len); + //VG_(printf)("foo: munmap: 0x%lx, %lu\n", a, len); + //VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), 8); + } +} + +static +void dmdv_copy_mem_remap(Addr from, Addr to, SizeT len) +{ + if (cloMmap) { + tl_assert(0); // njn + tl_assert(VG_IS_PAGE_ALIGNED(len)); + //dmdv_unrecord_page_mem(from, len); + //dmdv_record_page_mem(to, len); + VG_(printf)("foo: mremap: 0x%lx, 0x%lx, %lu\n", from, to, len); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), 8); + } +} + +static +void dmdv_new_mem_brk ( Addr a, SizeT len, ThreadId tid ) +{ + if (cloMmap) { + tl_assert(0); // njn + tl_assert(VG_IS_PAGE_ALIGNED(len)); + //dmdv_record_page_mem(a, len); + VG_(printf)("foo: brk: 0x%lx, %lu\n", a, len); + VG_(get_and_pp_StackTrace)(tid, 8); + } +} + +static +void dmdv_die_mem_brk( Addr a, SizeT len ) +{ + if (cloMmap) { + tl_assert(0); // njn + tl_assert(VG_IS_PAGE_ALIGNED(len)); + //dmdv_unrecord_page_mem(a, len); + VG_(printf)("foo: unbrk: 0x%lx, %lu\n", a, len); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), 8); + } +} +#endif + +//------------------------------------------------------------// +//--- Errors ---// +//------------------------------------------------------------// + +// An record about one or more heap blocks with a common HeapKey, used for +// generating err msgs. All reported blocks with a common HeapKey get +// aggregated into one Record, and all unreported blocks with the same HeapKey +// trace get aggregated into a different record. +typedef + struct _Record { + HeapKey key; + SizeT nBlocks; // Num. of blocks of this kind with this ExeContext + SizeT reqSzB; // Requested bytes that are (un)reported + SizeT usableSzB; // Usable (requested+slop) bytes that are (un)reported + } + Record; + +typedef + enum { + Err_DoubleReported, + Err_Unreported, + Err_Reported + } + DMDV_ErrorTag; + +typedef struct _DMDV_Error DMDV_Error; + +struct _DMDV_Error { + // Nb: we don't need the tag here, as it's stored in the Error type! Yuk. + //ErrorTag tag; + + union { + // Double-reported block. + struct { + HeapBlock* hb; + const Char* newReporter; + } DoubleReported; + + // Unreported heap block. Storing aSumB in every block is inefficient, + // but it hardly matters. + struct { + Record* r; + Int nThisRecord; + Int nTotalRecords; + SizeT cSumB; + SizeT aSumB; + } Unreported; + + // Reported heap block. Storing aSumB in every block is inefficient, + // but it hardly matters. + // njn: same as Unreported + struct { + Record* r; + Int nThisRecord; + Int nTotalRecords; + SizeT cSumB; + SizeT aSumB; + } Reported; + + } Err; +}; + +static void dmdv_record_doubleReported_error(ThreadId tid, Addr a, HeapBlock* hb, + const Char* newReporter) +{ + DMDV_Error extra; + extra.Err.DoubleReported.hb = hb; + extra.Err.DoubleReported.newReporter = newReporter; + VG_(maybe_record_error)(tid, Err_DoubleReported, a, /*s*/NULL, &extra); +} + +static Bool dmdv_record_unreported_error(ThreadId tid, Int n, Int nRecords, + Record* r, SizeT cSumB, SizeT aSumB) +{ + DMDV_Error extra; + extra.Err.Unreported.r = r; + extra.Err.Unreported.nThisRecord = n; + extra.Err.Unreported.nTotalRecords = nRecords; + extra.Err.Unreported.cSumB = cSumB; + extra.Err.Unreported.aSumB = aSumB; + return + VG_(unique_error)(tid, Err_Unreported, /*Addr*/0, /*s*/NULL, &extra, + r->key.allocPt, /*print_record*/True, + /*allow_GDB_attach*/False, /*count_error*/True); +} + +static Bool dmdv_record_reported_error(ThreadId tid, Int n, Int nRecords, + Record* r, SizeT cSumB, SizeT aSumB) +{ + DMDV_Error extra; + extra.Err.Reported.r = r; + extra.Err.Reported.nThisRecord = n; + extra.Err.Reported.nTotalRecords = nRecords; + extra.Err.Reported.cSumB = cSumB; + extra.Err.Reported.aSumB = aSumB; + return + VG_(unique_error)(tid, Err_Reported, /*Addr*/0, /*s*/NULL, &extra, + r->key.allocPt, /*print_record*/True, + /*allow_GDB_attach*/False, /*count_error*/True); +} + +static Bool dmdv_eq_Error(VgRes res, Error* e1, Error* e2) +{ + /* Guaranteed by calling function */ + tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); + + switch (VG_(get_error_kind)(e1)) { + case Err_DoubleReported: { + DMDV_Error* extra1 = (DMDV_Error*)VG_(get_error_extra)(e1); + DMDV_Error* extra2 = (DMDV_Error*)VG_(get_error_extra)(e2); + // We could check if the hb->key.allocPt fields match, but it doesn't + // seem like it would matter much. + const Char* oldReporter1 = extra1->Err.DoubleReported.hb->key.reporter; + const Char* oldReporter2 = extra2->Err.DoubleReported.hb->key.reporter; + const Char* newReporter1 = extra1->Err.DoubleReported.newReporter; + const Char* newReporter2 = extra2->Err.DoubleReported.newReporter; + + // njn: oldReporter1 here can be 0xDDDDDDDDDDDDDDDD, which indicates + // it's in memory that's been freed. That's odd, because AFAICT + // oldReporter1 will always comes from the 3rd arg to DMDV_REPORT, + // which should always be a static string in Firefox. Hmm. + if (VG_STREQ(oldReporter1, oldReporter2) && + VG_STREQ(newReporter1, newReporter2)) + { + return True; + } + return False; + } + + case Err_Unreported: + case Err_Reported: + tl_assert(0); // should never be called for unique errors + return True; + + default: + VG_(printf)("Error:\n unknown error code %d\n", + VG_(get_error_kind)(e1)); + VG_(tool_panic)("unknown error code in mc_eq_Error"); + } +} + +static void dmdv_before_pp_Error(Error* err) { /* Do nothing. */ } + +static void dmdv_pp_Error(Error* err) +{ + DMDV_Error* extra = VG_(get_error_extra)(err); + #define BUFLEN 32 + char buf1[BUFLEN]; + char buf2[BUFLEN]; + + switch (VG_(get_error_kind)(err)) { + case Err_DoubleReported: + VG_(umsg)("Double report of heap block %p:\n", + (void*)VG_(get_error_address)(err)); + + VG_(umsg)(" Allocated\n"); + VG_(pp_ExeContext)(extra->Err.DoubleReported.hb->key.allocPt); + + VG_(umsg)(" Previously reported by '%s'\n", + extra->Err.DoubleReported.hb->key.reporter); + VG_(pp_ExeContext)(extra->Err.DoubleReported.hb->key.reportPt); + + VG_(umsg)(" Now reported by '%s'\n", + extra->Err.DoubleReported.newReporter); + VG_(pp_ExeContext)(VG_(get_error_where)(err)); + break; + + case Err_Unreported: { + Record* r = extra->Err.Unreported.r; + SizeT aSumB = extra->Err.Unreported.aSumB; + SizeT cSumB = extra->Err.Unreported.cSumB + r->usableSzB; + VG_(umsg)("Unreported: %'lu block(s) in record %d of %d\n", + r->nBlocks, + extra->Err.Unreported.nThisRecord, + extra->Err.Unreported.nTotalRecords); + VG_(umsg)(" %'lu bytes (%'lu requested / %'lu slop)\n", + r->usableSzB, r->reqSzB, r->usableSzB - r->reqSzB); + VG_(percentify)(r->usableSzB, aSumB, 2, 0, buf1); + VG_(percentify)(cSumB, aSumB, 2, 0, buf2); + VG_(umsg)(" %s of the heap (%s cumulative unreported)\n", buf1, buf2); + VG_(pp_ExeContext)(VG_(get_error_where)(err)); + break; + } + + case Err_Reported: { + Record* r = extra->Err.Reported.r; + SizeT aSumB = extra->Err.Reported.aSumB; + SizeT cSumB = extra->Err.Reported.cSumB + r->usableSzB; + VG_(umsg)("Reported(%s): %'lu block(s) in record %d of %d\n", + r->key.reporter, r->nBlocks, + extra->Err.Reported.nThisRecord, + extra->Err.Reported.nTotalRecords); + VG_(umsg)(" %'lu bytes (%'lu requested / %'lu slop)\n", + r->usableSzB, r->reqSzB, r->usableSzB - r->reqSzB); + VG_(percentify)(r->usableSzB, aSumB, 2, 0, buf1); + VG_(percentify)(cSumB, aSumB, 2, 0, buf2); + VG_(umsg)(" %s of the heap (%s cumulative reported)\n", buf1, buf2); + + VG_(umsg)(" Allocated\n"); + VG_(pp_ExeContext)(VG_(get_error_where)(err)); // the allocPt + + VG_(umsg)(" Reported\n"); + VG_(pp_ExeContext)(r->key.reportPt); + break; + } + + default: + VG_(printf)("Error:\n unknown DMDV error code %d\n", + VG_(get_error_kind)(err)); + VG_(tool_panic)("unknown error code in dmdv_pp_Error)"); + } +} + +static UInt dmdv_update_extra(Error* err) +{ + return sizeof(DMDV_Error); +} + +static Char* dmdv_get_error_name(Error* err) +{ + switch (VG_(get_error_kind)(err)) { + case Err_DoubleReported: return "DoubleReported"; + case Err_Unreported: return "Unreported"; + case Err_Reported: return "Reported"; + default: VG_(tool_panic)("dmdv_get_error_name: unexpected type"); + } +} + +/*------------------------------------------------------------*/ +/*--- Suppressions ---*/ +/*------------------------------------------------------------*/ + +typedef + enum { + DoubleReportedSupp, + UnreportedSupp, + ReportedSupp, + } + DMDV_SuppKind; + +static Bool dmdv_is_recognised_suppression(Char* name, Supp* su) +{ + SuppKind skind; + + if (VG_STREQ(name, "DoubleReported")) skind = DoubleReportedSupp; + else if (VG_STREQ(name, "Unreported")) skind = UnreportedSupp; + else if (VG_STREQ(name, "Reported")) skind = ReportedSupp; + else + return False; + + VG_(set_supp_kind)(su, skind); + return True; +} + +static Bool dmdv_read_extra_suppression_info(Int fd, Char** bufpp, + SizeT* nBufp, Supp *su ) +{ + return True; +} + +static Bool dmdv_error_matches_suppression(Error* err, Supp* su) +{ + ErrorKind ekind = VG_(get_error_kind )(err); + + switch (VG_(get_supp_kind)(su)) { + case DoubleReportedSupp: + return (ekind == Err_DoubleReported); + + case UnreportedSupp: + return (ekind == Err_Unreported); + + case ReportedSupp: + return (ekind == Err_Reported); + + default: + VG_(printf)("Error:\n unknown suppression type %d\n", + VG_(get_supp_kind)(su)); + VG_(tool_panic)("unknown suppression type in " + "dmdv_error_matches_suppression"); + } +} + +static Bool dmdv_get_extra_suppression_info(Error* err, /*OUT*/Char* buf, + Int nBuf) +{ + return False; +} + +/*------------------------------------------------------------*/ +/*--- Checking ---*/ +/*------------------------------------------------------------*/ + +static Word cmpRecordByHeapKey(const void* key, const void* elem) +{ + HeapKey* a = (HeapKey*)key; + HeapKey* b = &((Record*)elem)->key; + + // Always use Vg_HighRes -- the lower values don't make that much sense, + // because then the lower entries shown for the record aren't + // representative. Better to control this via --num-callers. + VgRes res = Vg_HighRes; + + // Check the allocation point. + if (!VG_(eq_ExeContext)(res, a->allocPt, b->allocPt)) { + if (a->allocPt < b->allocPt) return -1; + if (a->allocPt > b->allocPt) return 1; + VG_(tool_panic)("bad Record comparison"); + } + + // Same allocation point, now check the reporter. + if (!a->reporter && !b->reporter) return 0; + if ( a->reporter && !b->reporter) return -1; + if (!a->reporter && b->reporter) return 1; + Int cmp = VG_(strcmp)(a->reporter, b->reporter); + if (cmp != 0) return cmp; + + // Same reporter, now check the report point. + tl_assert(a->reportPt && b->reportPt); + if (!VG_(eq_ExeContext)(res, a->reportPt, b->reportPt)) { + if (a->reportPt < b->reportPt) return -1; + if (a->reportPt > b->reportPt) return 1; + VG_(tool_panic)("bad HeapKey comparison"); + } + + // They're equal. + return 0; +} + +static Int cmpRecordBySize(void* va, void* vb) +{ + Record* a = *(Record**)va; + Record* b = *(Record**)vb; + + // Compare by sizes. + if (a->usableSzB < b->usableSzB) return 1; + if (a->usableSzB > b->usableSzB) return -1; + return 0; +} + +static void reportHeapBlock(ThreadId tid, Addr a, SizeT len, const Char* name) +{ + if (!a && !len) + return; + + // Get HeapBlock, check it matches the report, and mark it as reported. + HeapBlock* hb = VG_(HT_lookup)(heapBlocks, a); + if (!hb) { + VG_(umsg)("REPORT WARNING(%s): no such heap block %p (length %'lu):\n", + name, (void*)a, len); + VG_(get_and_pp_StackTrace)(tid, VG_(clo_backtrace_size)); + VG_(umsg)("\n"); + return; + } + if (hb->usableSzB != len) { + VG_(umsg)("REPORT WARNING(%s): size mismatch: reported=%'lu, actual=%'lu (using actual)\n", + name, len, hb->usableSzB); + } + if (hb->key.reporter) { + tl_assert(hb->key.reportPt); + dmdv_record_doubleReported_error(tid, a, hb, name); + } else { + tl_assert(!hb->key.reportPt); + hb->key.reportPt = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/); + hb->key.reporter = name; + } +} + +static void unreportHeapBlock(ThreadId tid, Addr a) +{ + if (!a) + return; + + // Get HeapBlock, check it matches the report, and mark it as unreported. + HeapBlock* hb = VG_(HT_lookup)(heapBlocks, a); + if (!hb) { + VG_(umsg)("UNREPORT WARNING: no such heap block\n"); + return; + } + + if (!hb->key.reporter) { + VG_(umsg)("UNREPORT WARNING: block is not already reported\n"); + return; + } + + hb->key.reporter = NULL; + hb->key.reportPt = NULL; +} + +static Record** createSortedRecordsArray(OSet *records, Int *nRecords) +{ + Record* r; + Record** recordsArray; + Int i = 0; + + *nRecords = VG_(OSetGen_Size)(records); + + // Create an array of pointers to the records. + recordsArray = VG_(malloc)("mc.cSRA", *nRecords * sizeof(Record*)); + VG_(OSetGen_ResetIter)(records); + while ( (r = VG_(OSetGen_Next)(records)) ) { + recordsArray[i++] = r; + } + tl_assert(i == *nRecords); + + // Sort the array by record size. + VG_(ssort)(recordsArray, *nRecords, sizeof(Record*), cmpRecordBySize); + + return recordsArray; +} + +static void checkReporting(ThreadId tid) +{ + Int i; + HeapBlock *hb; + OSet *uRecords = + VG_(OSetGen_Create)(offsetof(Record, key), + cmpRecordByHeapKey, + VG_(malloc), "dmdv.cr.1", + VG_(free)); + OSet *rRecords = + VG_(OSetGen_Create)(offsetof(Record, key), + cmpRecordByHeapKey, + VG_(malloc), "dmdv.cr.2", + VG_(free)); + Int nRecords; + Record** recordsArray; + Record* r; + SizeT aSumB = 0; // all == rSumB + uSumB + sSumB + SizeT rSumB = 0; // reported + SizeT uSumB = 0; // unreported + SizeT sSumB = 0; // suppressed + SizeT cSumB = 0; // cumulative + #define BUFLEN 32 + char buf[BUFLEN]; + + static Bool hasChecked = False; + if (hasChecked) { + VG_(umsg)("Sorry, CHECK_REPORTING will only work once.\n"); + VG_(umsg)("DMDV will ignore this request.\n\n"); + return; + } + hasChecked = True; + + // Create Records from heapBlocks. + VG_(HT_ResetIter)(heapBlocks); + while ( (hb = VG_(HT_Next)(heapBlocks)) ) { + OSet *records; + + if (hb->key.reporter) { + records = rRecords; + } else { + records = uRecords; + } + r = VG_(OSetGen_Lookup)(records, &hb->key); + if (!r) { + // No existing record matches this chunk. Create a new record, + // zero it, and insert it into records. + r = VG_(OSetGen_AllocNode)(records, sizeof(Record)); + r->key = hb->key; + r->nBlocks = 0; + r->reqSzB = 0; + r->usableSzB = 0; + VG_(OSetGen_Insert)(records, r); + } else { + // If we matched an existing Record, ensure the names match. + tl_assert((!r->key.reporter && !hb->key.reporter) || + VG_STREQ(r->key.reporter, hb->key.reporter)); + } + // Update the Record's details in-situ. This is safe because we + // don't change the elements used as the OSet key. + r->nBlocks++; + r->reqSzB += hb->reqSzB; + r->usableSzB += hb->usableSzB; + + aSumB += hb->usableSzB; + } + + // Print the unreported records in size order and gather stats. + VG_(umsg)("UNREPORTED BLOCKS:\n\n"); + recordsArray = createSortedRecordsArray(uRecords, &nRecords); + if (nRecords > 0) { + for (i = 0; i < nRecords; i++) { + Bool isSuppressed; + r = recordsArray[i]; + isSuppressed = dmdv_record_unreported_error(tid, i+1, nRecords, r, cSumB, + aSumB); + if (isSuppressed) { + sSumB += r->usableSzB; + } else { + uSumB += r->usableSzB; + cSumB += r->usableSzB; + } + } + } else { + VG_(umsg)("(none)\n"); + } + + // Print the reported records in size order and gather stats. + VG_(umsg)("REPORTED BLOCKS:\n\n"); + cSumB = 0; + recordsArray = createSortedRecordsArray(rRecords, &nRecords); + if (nRecords > 0) { + for (i = 0; i < nRecords; i++) { + Bool isSuppressed; + r = recordsArray[i]; + isSuppressed = dmdv_record_reported_error(tid, i+1, nRecords, r, cSumB, + aSumB); + tl_assert(!isSuppressed); + rSumB += r->usableSzB; + cSumB += r->usableSzB; + } + } else { + VG_(umsg)("(none)\n\n"); + } + + VG_(umsg)("SUMMARY:\n"); + tl_assert(aSumB == rSumB + uSumB + sSumB); + VG_(umsg)(" Total: %'13lu bytes\n", aSumB); + + VG_(percentify)(rSumB, aSumB, 2, 7, buf); + VG_(umsg)(" Reported: %'13lu bytes (%s)\n", rSumB, buf); + + VG_(percentify)(uSumB, aSumB, 2, 7, buf); + VG_(umsg)(" Unreported: %'13lu bytes (%s)\n", uSumB, buf); + + VG_(percentify)(sSumB, aSumB, 2, 7, buf); + VG_(umsg)(" Suppressed: %'13lu bytes (%s)\n", sSumB, buf); +} + +/*------------------------------------------------------------*/ +/*--- Client requests ---*/ +/*------------------------------------------------------------*/ + +static Bool dmdv_handle_client_request(ThreadId tid, UWord* arg, UWord* ret) +{ + if (!VG_IS_TOOL_USERREQ('D','M',arg[0]) + && VG_USERREQ__DMDV_REPORT != arg[0] + && VG_USERREQ__DMDV_UNREPORT != arg[0] + && VG_USERREQ__DMDV_CHECK_REPORTING != arg[0]) + return False; + + switch (arg[0]) { + case VG_USERREQ__DMDV_REPORT: { + reportHeapBlock(tid, arg[1], arg[2], (const char*)arg[3]); + *ret = 0; + break; + } + + case VG_USERREQ__DMDV_UNREPORT: { + unreportHeapBlock(tid, arg[1]); + *ret = 0; + break; + } + + case VG_USERREQ__DMDV_CHECK_REPORTING: { + checkReporting(tid); + *ret = 0; + break; + } + + default: + VG_(message)( + Vg_UserMsg, + "Warning: unknown DMDV client request code %llx\n", + (ULong)arg[0] + ); + return False; + } + return True; +} + +//------------------------------------------------------------// +//--- Basic functions ---// +//------------------------------------------------------------// + +static void dmdv_post_clo_init(void) +{ +} + +static +IRSB* dmdv_instrument(VgCallbackClosure* closure, + IRSB* sb, + VexGuestLayout* layout, + VexGuestExtents* vge, + VexArchInfo* archinfo_host, + IRType gWordTy, + IRType hWordTy) +{ + return sb; +} + +static void dmdv_fini(Int exitcode) +{ +} + +static void dmdv_pre_clo_init(void) +{ + VG_(details_name) ("DMDV"); + VG_(details_version) (NULL); + VG_(details_description) ("a dark matter detector"); + VG_(details_copyright_author)( + "Copyright (C) 2011-2011, and GNU GPL'd, by Nicholas Nethercote."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + + VG_(details_avg_translation_sizeB)(275); + + VG_(basic_tool_funcs) (dmdv_post_clo_init, + dmdv_instrument, + dmdv_fini); + + // Needs. + VG_(needs_command_line_options)(dmdv_process_cmd_line_option, + dmdv_print_usage, + dmdv_print_debug_usage); + VG_(needs_tool_errors) (dmdv_eq_Error, + dmdv_before_pp_Error, + dmdv_pp_Error, + True,/*show TIDs for errors*/ + dmdv_update_extra, + dmdv_is_recognised_suppression, + dmdv_read_extra_suppression_info, + dmdv_error_matches_suppression, + dmdv_get_error_name, + dmdv_get_extra_suppression_info); + VG_(needs_client_requests) (dmdv_handle_client_request); + VG_(needs_malloc_replacement)(dmdv_malloc, + dmdv___builtin_new, + dmdv___builtin_vec_new, + dmdv_memalign, + dmdv_calloc, + dmdv_free, + dmdv___builtin_delete, + dmdv___builtin_vec_delete, + dmdv_realloc, + dmdv_malloc_usable_size, + 0); + +#if 0 + // Tracked events. + VG_(track_new_mem_startup) ( dmdv_new_mem_startup ); + VG_(track_new_mem_brk) ( dmdv_new_mem_brk ); + VG_(track_new_mem_mmap) ( dmdv_new_mem_mmap ); + + VG_(track_copy_mem_remap) ( dmdv_copy_mem_remap ); + + VG_(track_die_mem_brk) ( dmdv_die_mem_brk ); + VG_(track_die_mem_munmap) ( dmdv_die_mem_munmap ); +#endif + + heapBlocks = VG_(HT_construct)("DMDV's heapBlocks"); +} + +VG_DETERMINE_INTERFACE_VERSION(dmdv_pre_clo_init) + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ Index: exp-dmdv/dmdv.h =================================================================== --- exp-dmdv/dmdv.h (revision 0) +++ exp-dmdv/dmdv.h (revision 0) @@ -0,0 +1,83 @@ +/* + ---------------------------------------------------------------- + The following BSD-style license applies to this one file (dmdv.h) only. + ---------------------------------------------------------------- + + The Initial Developer of the Original Code is + the Mozilla Foundation. + Portions created by the Initial Developer are Copyright (C) 2011 + the Initial Developer. All Rights Reserved. + + Contributor(s): + Nicholas Nethercote + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __DMDV_H +#define __DMDV_H + +#include "valgrind/valgrind.h" + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { + VG_USERREQ__DMDV_REPORT = VG_USERREQ_TOOL_BASE('D','M'), + VG_USERREQ__DMDV_UNREPORT, + VG_USERREQ__DMDV_CHECK_REPORTING + } Vg_DMDVClientRequest; + + +/* Mark heap block at _qzz_addr as reported for _qzz_len bytes. + * _qzz_name is the name of the reporter. */ +#define VALGRIND_DMDV_REPORT(_qzz_addr,_qzz_len,_qzz_name) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__DMDV_REPORT, \ + (_qzz_addr), (_qzz_len), (_qzz_name), 0, 0) + +/* Mark heap block at _qzz_addr as not reported. */ +#define VALGRIND_DMDV_UNREPORT(_qzz_addr) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__DMDV_UNREPORT, \ + (_qzz_addr), 0, 0, 0, 0) + +/* Do a reporting check. */ +#define VALGRIND_DMDV_CHECK_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__DMDV_CHECK_REPORTING, \ + 0, 0, 0, 0, 0) + +#endif + Index: exp-dmdv/tests/basics.c =================================================================== --- exp-dmdv/tests/basics.c (revision 0) +++ exp-dmdv/tests/basics.c (revision 0) @@ -0,0 +1,55 @@ +#include +#include +#include "exp-dmdv/dmdv.h" + +int main(void) +{ + int i; + char *a; + for (i = 0; i < 10; i++) { + a = malloc(100); + } + free(a); // 1 freed, 9 out of 10 unreported + + char *b = malloc(10); + size_t bUsable = malloc_usable_size(b); + VALGRIND_DMDV_REPORT(b, bUsable, "b"); // reported + + char *c = malloc(10); + size_t cUsable = malloc_usable_size(c); + VALGRIND_DMDV_REPORT(c, cUsable, "c"); + VALGRIND_DMDV_REPORT(c, cUsable, "c"); // double-reported + + VALGRIND_DMDV_REPORT(i, 10, "d"); // no such heap block warning + + char *e = malloc(4096); + e = realloc(e, 4097); // jemalloc rounds this up to 8192 + VALGRIND_DMDV_REPORT(e, 4097, "e"); // size mismatch warning + + // jemalloc would leave this as size 2. Valgrind's heap allocator actually + // rounds it up to 8 (or 16?). Make sure that malloc_usable_size() returns + // 2 like jemalloc's would. + char *f = malloc(2); + fprintf(stderr, "mus: %ld\n", malloc_usable_size(f)); + + // Ensure that realloc copies the slop bytes! If it doesn't, we only get + // 10 'a's printed instead of 15. + char *g = malloc(10); // rounds up to 16 + for (i = 0; i < malloc_usable_size(g); i++) + g[i] = 'a'; + g[i-1] = '\0'; + g = realloc(g, 32); + fprintf(stderr, "g = %s\n", g); + + // Check that a freed block isn't considered by the checking. + char *h = malloc(10); + free(h); + + VALGRIND_DMDV_CHECK_REPORTING; // ok + VALGRIND_DMDV_CHECK_REPORTING; // warning, ignored + + // These should be ignored. + VALGRIND_DMDV_REPORT((void*)0x0, 0, "zero"); + + return 0; +} Index: exp-dmdv/tests/heapkeys.stderr.exp =================================================================== --- exp-dmdv/tests/heapkeys.stderr.exp (revision 0) +++ exp-dmdv/tests/heapkeys.stderr.exp (revision 0) @@ -0,0 +1,133 @@ +Copyright (C) 2011-2011, and GNU GPL'd, by Nicholas Nethercote. + +UNREPORTED BLOCKS: + +Unreported: 2 block(s) in record 1 of 3 + 112 bytes (112 requested / 0 slop) + 7.07% of the heap (7.07% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:25) + +Unreported: 2 block(s) in record 2 of 3 + 112 bytes (112 requested / 0 slop) + 7.07% of the heap (14.14% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:26) + +Unreported: 2 block(s) in record 3 of 3 + 112 bytes (112 requested / 0 slop) + 7.07% of the heap (21.21% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:27) + +REPORTED BLOCKS: + +Reported(a01): 2 block(s) in record 1 of 9 + 240 bytes (240 requested / 0 slop) + 15.15% of the heap (15.15% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:26) + Reported + at 0x........: f (heapkeys.c:17) + by 0x........: main (heapkeys.c:26) + +Reported(a01): 2 block(s) in record 2 of 9 + 240 bytes (240 requested / 0 slop) + 15.15% of the heap (30.30% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:25) + Reported + at 0x........: f (heapkeys.c:17) + by 0x........: main (heapkeys.c:25) + +Reported(a01): 2 block(s) in record 3 of 9 + 240 bytes (240 requested / 0 slop) + 15.15% of the heap (45.45% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:27) + Reported + at 0x........: f (heapkeys.c:17) + by 0x........: main (heapkeys.c:27) + +Reported(a23): 1 block(s) in record 4 of 9 + 96 bytes (96 requested / 0 slop) + 6.06% of the heap (51.51% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:25) + Reported + at 0x........: f (heapkeys.c:18) + by 0x........: main (heapkeys.c:25) + +Reported(a23): 1 block(s) in record 5 of 9 + 96 bytes (96 requested / 0 slop) + 6.06% of the heap (57.57% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:26) + Reported + at 0x........: f (heapkeys.c:18) + by 0x........: main (heapkeys.c:26) + +Reported(a23): 1 block(s) in record 6 of 9 + 96 bytes (96 requested / 0 slop) + 6.06% of the heap (63.63% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:27) + Reported + at 0x........: f (heapkeys.c:18) + by 0x........: main (heapkeys.c:27) + +Reported(a23): 1 block(s) in record 7 of 9 + 80 bytes (80 requested / 0 slop) + 5.05% of the heap (68.68% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:25) + Reported + at 0x........: f (heapkeys.c:19) + by 0x........: main (heapkeys.c:25) + +Reported(a23): 1 block(s) in record 8 of 9 + 80 bytes (80 requested / 0 slop) + 5.05% of the heap (73.73% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:27) + Reported + at 0x........: f (heapkeys.c:19) + by 0x........: main (heapkeys.c:27) + +Reported(a23): 1 block(s) in record 9 of 9 + 80 bytes (80 requested / 0 slop) + 5.05% of the heap (78.78% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (heapkeys.c:13) + by 0x........: main (heapkeys.c:26) + Reported + at 0x........: f (heapkeys.c:19) + by 0x........: main (heapkeys.c:26) + +SUMMARY: + Total: 1,584 bytes + Reported: 1,248 bytes ( 78.78%) + Unreported: 336 bytes ( 21.21%) + Suppressed: 0 bytes ( 0.00%) + +ERROR SUMMARY: 12 errors from 12 contexts (suppressed: 0 from 0) Index: exp-dmdv/tests/jesizes.vgtest =================================================================== --- exp-dmdv/tests/jesizes.vgtest (revision 0) +++ exp-dmdv/tests/jesizes.vgtest (revision 0) @@ -0,0 +1 @@ +prog: jesizes Index: exp-dmdv/tests/filter_stderr =================================================================== --- exp-dmdv/tests/filter_stderr (revision 0) +++ exp-dmdv/tests/filter_stderr (revision 0) @@ -0,0 +1,12 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Anonymise addresses +$dir/../../tests/filter_addresses | + +# Remove "DMDV, ..." line and the following copyright line. +sed "/^DMDV, a dark matter detector/ , /./ d" + Index: exp-dmdv/tests/unreport.vgtest =================================================================== --- exp-dmdv/tests/unreport.vgtest (revision 0) +++ exp-dmdv/tests/unreport.vgtest (revision 0) @@ -0,0 +1 @@ +prog: unreport Index: exp-dmdv/tests/Makefile.am =================================================================== --- exp-dmdv/tests/Makefile.am (revision 0) +++ exp-dmdv/tests/Makefile.am (revision 0) @@ -0,0 +1,20 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +dist_noinst_SCRIPTS = filter_stderr + +EXTRA_DIST = \ + basics.stderr.exp basics.vgtest \ + heapkeys.stderr.exp heapkeys.vgtest \ + jesizes.stderr.exp jesizes.vgtest \ + unreport.stderr.exp unreport.vgtest + +check_PROGRAMS = \ + basics \ + heapkeys \ + jesizes \ + unreport + +AM_CFLAGS += $(AM_FLAG_M3264_PRI) +AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) + Index: exp-dmdv/tests/jesizes.stderr.exp =================================================================== --- exp-dmdv/tests/jesizes.stderr.exp (revision 0) +++ exp-dmdv/tests/jesizes.stderr.exp (revision 0) @@ -0,0 +1,129 @@ +Copyright (C) 2011-2011, and GNU GPL'd, by Nicholas Nethercote. + +0 -> 2 (2) +1 -> 2 (1) +2 -> 2 (0) +3 -> 4 (1) +4 -> 4 (0) +5 -> 8 (3) +7 -> 8 (1) +8 -> 8 (0) +9 -> 16 (7) +15 -> 16 (1) +16 -> 16 (0) +17 -> 32 (15) +31 -> 32 (1) +32 -> 32 (0) +33 -> 48 (15) +47 -> 48 (1) +48 -> 48 (0) +49 -> 64 (15) +63 -> 64 (1) +64 -> 64 (0) +65 -> 80 (15) +79 -> 80 (1) +80 -> 80 (0) +113 -> 128 (15) +127 -> 128 (1) +128 -> 128 (0) +129 -> 144 (15) +143 -> 144 (1) +144 -> 144 (0) +241 -> 256 (15) +255 -> 256 (1) +256 -> 256 (0) +257 -> 272 (15) +271 -> 272 (1) +272 -> 272 (0) +481 -> 496 (15) +495 -> 496 (1) +496 -> 496 (0) +497 -> 512 (15) +511 -> 512 (1) +512 -> 512 (0) +513 -> 1024 (511) +1023 -> 1024 (1) +1024 -> 1024 (0) +1025 -> 2048 (1023) +2047 -> 2048 (1) +2048 -> 2048 (0) +2049 -> 4096 (2047) +4095 -> 4096 (1) +4096 -> 4096 (0) +4097 -> 8192 (4095) +8191 -> 8192 (1) +8192 -> 8192 (0) +12289 -> 16384 (4095) +16383 -> 16384 (1) +16384 -> 16384 (0) +28673 -> 32768 (4095) +32767 -> 32768 (1) +32768 -> 32768 (0) +61441 -> 65536 (4095) +65535 -> 65536 (1) +65536 -> 65536 (0) +126977 -> 131072 (4095) +131071 -> 131072 (1) +131072 -> 131072 (0) +258049 -> 262144 (4095) +262143 -> 262144 (1) +262144 -> 262144 (0) +520193 -> 524288 (4095) +524287 -> 524288 (1) +524288 -> 524288 (0) +1044481 -> 1048576 (4095) +1048575 -> 1048576 (1) +1048576 -> 1048576 (0) +1048577 -> 1052672 (4095) +2097151 -> 2097152 (1) +2097152 -> 2097152 (0) +2097153 -> 2101248 (4095) +3145727 -> 3145728 (1) +3145728 -> 3145728 (0) +3145729 -> 3149824 (4095) +4194303 -> 4194304 (1) +4194304 -> 4194304 (0) +4194305 -> 4198400 (4095) +5242879 -> 5242880 (1) +5242880 -> 5242880 (0) +5242881 -> 5246976 (4095) +6291455 -> 6291456 (1) +6291456 -> 6291456 (0) +6291457 -> 6295552 (4095) +7340031 -> 7340032 (1) +7340032 -> 7340032 (0) +7340033 -> 7344128 (4095) +8388607 -> 8388608 (1) +8388608 -> 8388608 (0) +8388609 -> 8392704 (4095) +9437183 -> 9437184 (1) +9437184 -> 9437184 (0) +15728641 -> 15732736 (4095) +16777215 -> 16777216 (1) +16777216 -> 16777216 (0) +32505857 -> 32509952 (4095) +33554431 -> 33554432 (1) +33554432 -> 33554432 (0) +33554433 -> 33558528 (4095) +34603007 -> 34603008 (1) +34603008 -> 34603008 (0) +totals: 387939702 -> 388021286 (81584) +UNREPORTED BLOCKS: + +Unreported: 107 block(s) in record 1 of 1 + 388,021,286 bytes (387,939,702 requested / 81,584 slop) + 100.00% of the heap (100.00% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (jesizes.c:75) + +REPORTED BLOCKS: + +(none) + +SUMMARY: + Total: 388,021,286 bytes + Reported: 0 bytes ( 0.00%) + Unreported: 388,021,286 bytes (100.00%) + Suppressed: 0 bytes ( 0.00%) + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) Index: exp-dmdv/tests/heapkeys.c =================================================================== --- exp-dmdv/tests/heapkeys.c (revision 0) +++ exp-dmdv/tests/heapkeys.c (revision 0) @@ -0,0 +1,32 @@ +#include +#include +#include "exp-dmdv/dmdv.h" + +// This test checks that heap blocks that have the same stack trace but +// different (or no) reporters get handled separately. + +void f(void) +{ + int i; + char *a[6]; + for (i = 0; i < 6; i++) { + a[i] = malloc(128 - 16*i); + } + + for (i = 0; i <= 1; i++) + VALGRIND_DMDV_REPORT(a[i], 128 - 16*i, "a01"); // reported + VALGRIND_DMDV_REPORT(a[2], 128 - 16*2, "a23"); // reported + VALGRIND_DMDV_REPORT(a[3], 128 - 16*3, "a23"); // reported + // a[4], a[5] unreported +} + +int main(void) +{ + f(); + f(); + f(); + + VALGRIND_DMDV_CHECK_REPORTING; // ok + + return 0; +} Index: exp-dmdv/tests/unreport.stderr.exp =================================================================== --- exp-dmdv/tests/unreport.stderr.exp (revision 0) +++ exp-dmdv/tests/unreport.stderr.exp (revision 0) @@ -0,0 +1,29 @@ +Copyright (C) 2011-2011, and GNU GPL'd, by Nicholas Nethercote. + +UNREPORT WARNING: block is not already reported +UNREPORT WARNING: block is not already reported +UNREPORTED BLOCKS: + +Unreported: 1 block(s) in record 1 of 2 + 16 bytes (10 requested / 6 slop) + 50.00% of the heap (50.00% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (unreport.c:11) + +Unreported: 1 block(s) in record 2 of 2 + 16 bytes (10 requested / 6 slop) + 50.00% of the heap (100.00% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (unreport.c:16) + +REPORTED BLOCKS: + +(none) + +SUMMARY: + Total: 32 bytes + Reported: 0 bytes ( 0.00%) + Unreported: 32 bytes (100.00%) + Suppressed: 0 bytes ( 0.00%) + +ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) Index: exp-dmdv/tests/jesizes.c =================================================================== --- exp-dmdv/tests/jesizes.c (revision 0) +++ exp-dmdv/tests/jesizes.c (revision 0) @@ -0,0 +1,88 @@ +#include +#include +#include "exp-dmdv/dmdv.h" + +int main(void) +{ + #define K * 1024 + #define M * 1024 * 1024 + + size_t sizes[] = { + // Small/Tiny: 2, 4, 8 (bytes) + 0, 1, 2, // 2 + 3, 4, // 4 + 5, 7, 8, // 8 + + // Small/Quantum-spaced: 16, 32, 48, ..., 480, 496, 512 (bytes) + 9, 15, 16, // 16 + 17, 31, 32, // 32 + 33, 47, 48, // 48 + 49, 63, 64, // 64 + 65, 79, 80, // 80 + // ... + 113, 127, 128, // 128 + 129, 143, 144, // 144 + // ... + 241, 255, 256, // 256 + 257, 271, 272, // 272 + // ... + 481, 495, 496, // 496 + 497, 511, 512, // 512 + + // Small/Sub-page: 1, 2 (KiB) + 513, 1023, 1024, // 1024 + 1025, 2047, 2048, // 2048 + + // Large: 4, 8, 12, ..., 1012, 1016, 1020 (KiB) + 2049, 4 K - 1, 4 K, // 4 K + 4 K + 1, 8 K -1, 8 K, // 8 K + // ... + 12 K + 1, 16 K - 1, 16 K, // 16 K + // ... + 28 K + 1, 32 K - 1, 32 K, // 32 K + // ... + 60 K + 1, 64 K - 1, 64 K, // 64 K + // ... + 124 K + 1, 128 K - 1, 128 K, // 128 K + // ... + 252 K + 1, 256 K - 1, 256 K, // 256 K + // ... + 508 K + 1, 512 K - 1, 512 K, // 512 K + // ... + 1020 K + 1, 1024 K - 1, 1024 K, // 1024 K + + // Huge: 1, 2, 3, ... (MiB) + 1 M + 1, 2 M - 1, 2 M, // 2 M + 2 M + 1, 3 M - 1, 3 M, // 3 M + 3 M + 1, 4 M - 1, 4 M, // 4 M + 4 M + 1, 5 M - 1, 5 M, // 5 M + 5 M + 1, 6 M - 1, 6 M, // 6 M + 6 M + 1, 7 M - 1, 7 M, // 7 M + 7 M + 1, 8 M - 1, 8 M, // 8 M + 8 M + 1, 9 M - 1, 9 M, // 9 M + // ... + 15 M + 1, 16 M - 1, 16 M, // 16 M + // ... + 31 M + 1, 32 M - 1, 32 M, // 32 M + 32 M + 1, 33 M - 1, 33 M // 33 M + }; + size_t n = sizeof(sizes) / sizeof(sizes[0]); + size_t i; + size_t totalReqSzB = 0; + size_t totalSlopSzB = 0; + + for (i = 0; i < n; i++) { + char* x = malloc(sizes[i]); + size_t req = sizes[i]; + size_t usable = malloc_usable_size(x); + size_t slop = usable - req; + fprintf(stderr, "%ld -> %ld (%ld)\n", req, usable, slop); + totalReqSzB += req; + totalSlopSzB += slop; + } + fprintf(stderr, "totals: %ld -> %ld (%ld)\n", + totalReqSzB, totalReqSzB + totalSlopSzB, totalSlopSzB); + VALGRIND_DMDV_CHECK_REPORTING; + + return 0; +} Index: exp-dmdv/tests/basics.vgtest =================================================================== --- exp-dmdv/tests/basics.vgtest (revision 0) +++ exp-dmdv/tests/basics.vgtest (revision 0) @@ -0,0 +1 @@ +prog: basics Index: exp-dmdv/tests/unreport.c =================================================================== --- exp-dmdv/tests/unreport.c (revision 0) +++ exp-dmdv/tests/unreport.c (revision 0) @@ -0,0 +1,26 @@ + +#include +#include +#include "exp-dmdv/dmdv.h" + +int main(void) +{ + + VALGRIND_DMDV_UNREPORT(NULL); // ignored + + char *b = malloc(10); + size_t usable = malloc_usable_size(b); + VALGRIND_DMDV_REPORT(b, usable, "b"); // ok + VALGRIND_DMDV_UNREPORT(b); // ok + + char *c = malloc(10); + usable = malloc_usable_size(c); + VALGRIND_DMDV_UNREPORT(c); // block not reported warning + VALGRIND_DMDV_REPORT(c, usable, "c"); + VALGRIND_DMDV_UNREPORT(c); + VALGRIND_DMDV_UNREPORT(c); // block not reported warning + + VALGRIND_DMDV_CHECK_REPORTING; + + return 0; +} Index: exp-dmdv/tests/basics.stderr.exp =================================================================== --- exp-dmdv/tests/basics.stderr.exp (revision 0) +++ exp-dmdv/tests/basics.stderr.exp (revision 0) @@ -0,0 +1,76 @@ +Copyright (C) 2011-2011, and GNU GPL'd, by Nicholas Nethercote. + +Double report of heap block 0x........: + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:18) + Previously reported by 'c' + at 0x........: main (basics.c:20) + Now reported by 'c' + at 0x........: main (basics.c:21) + +REPORT WARNING(d): no such heap block 0x........ (length 10): + at 0x........: main (basics.c:23) + +REPORT WARNING(e): size mismatch: reported=4,097, actual=8,192 (using actual) +mus: 2 +g = aaaaaaaaaaaaaaa +UNREPORTED BLOCKS: + +Unreported: 9 block(s) in record 1 of 3 + 1,008 bytes (900 requested / 108 slop) + 10.87% of the heap (10.87% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:10) + +Unreported: 1 block(s) in record 2 of 3 + 32 bytes (32 requested / 0 slop) + 0.34% of the heap (11.22% cumulative unreported) + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:41) + +Unreported: 1 block(s) in record 3 of 3 + 2 bytes (2 requested / 0 slop) + 0.02% of the heap (11.24% cumulative unreported) + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:32) + +REPORTED BLOCKS: + +Reported(e): 1 block(s) in record 1 of 3 + 8,192 bytes (4,097 requested / 4,095 slop) + 88.40% of the heap (88.40% cumulative reported) + Allocated + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:26) + Reported + at 0x........: main (basics.c:27) + +Reported(b): 1 block(s) in record 2 of 3 + 16 bytes (10 requested / 6 slop) + 0.17% of the heap (88.58% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:14) + Reported + at 0x........: main (basics.c:16) + +Reported(c): 1 block(s) in record 3 of 3 + 16 bytes (10 requested / 6 slop) + 0.17% of the heap (88.75% cumulative reported) + Allocated + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (basics.c:18) + Reported + at 0x........: main (basics.c:20) + +SUMMARY: + Total: 9,266 bytes + Reported: 8,224 bytes ( 88.75%) + Unreported: 1,042 bytes ( 11.24%) + Suppressed: 0 bytes ( 0.00%) +Sorry, CHECK_REPORTING will only work once. +DMDV will ignore this request. + + +ERROR SUMMARY: 7 errors from 7 contexts (suppressed: 0 from 0) Index: exp-dmdv/tests/heapkeys.vgtest =================================================================== --- exp-dmdv/tests/heapkeys.vgtest (revision 0) +++ exp-dmdv/tests/heapkeys.vgtest (revision 0) @@ -0,0 +1 @@ +prog: heapkeys Index: exp-dmdv/Makefile.am =================================================================== --- exp-dmdv/Makefile.am (revision 0) +++ exp-dmdv/Makefile.am (revision 0) @@ -0,0 +1,93 @@ +include $(top_srcdir)/Makefile.tool.am + +EXTRA_DIST = docs/dmdv-manual.xml + +#---------------------------------------------------------------------------- +# dmdv- +#---------------------------------------------------------------------------- + +noinst_PROGRAMS = exp-dmdv-@VGCONF_ARCH_PRI@-@VGCONF_OS@ +if VGCONF_HAVE_PLATFORM_SEC +noinst_PROGRAMS += exp-dmdv-@VGCONF_ARCH_SEC@-@VGCONF_OS@ +endif + +NONE_SOURCES_COMMON = dmdv_main.c + +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = \ + $(NONE_SOURCES_COMMON) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \ + $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \ + $(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \ + $(TOOL_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LINK = \ + $(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \ + @VALT_LOAD_ADDRESS_PRI@ \ + $(LINK) \ + $(exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS) \ + $(exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS) + +if VGCONF_HAVE_PLATFORM_SEC +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = \ + $(NONE_SOURCES_COMMON) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_DEPENDENCIES = \ + $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_SEC_CAPS@) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = \ + $(TOOL_LDADD_@VGCONF_PLATFORM_SEC_CAPS@) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = \ + $(TOOL_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LINK = \ + $(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \ + @VALT_LOAD_ADDRESS_SEC@ \ + $(LINK) \ + $(exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS) \ + $(exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS) +endif + +#---------------------------------------------------------------------------- +# vgpreload_exp_dmdv-.so +#---------------------------------------------------------------------------- + +noinst_PROGRAMS += vgpreload_exp-dmdv-@VGCONF_ARCH_PRI@-@VGCONF_OS@.so +if VGCONF_HAVE_PLATFORM_SEC +noinst_PROGRAMS += vgpreload_exp-dmdv-@VGCONF_ARCH_SEC@-@VGCONF_OS@.so +endif + +if VGCONF_OS_IS_DARWIN +noinst_DSYMS = $(noinst_PROGRAMS) +endif + +vgpreload_exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_SOURCES = +vgpreload_exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +vgpreload_exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) $(AM_CFLAGS_PIC) +vgpreload_exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_DEPENDENCIES = \ + $(LIBREPLACEMALLOC_@VGCONF_PLATFORM_PRI_CAPS@) +vgpreload_exp_dmdv_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) \ + $(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) + +if VGCONF_HAVE_PLATFORM_SEC +vgpreload_exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_SOURCES = +vgpreload_exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +vgpreload_exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) $(AM_CFLAGS_PIC) +vgpreload_exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_DEPENDENCIES = \ + $(LIBREPLACEMALLOC_@VGCONF_PLATFORM_SEC_CAPS@) +vgpreload_exp_dmdv_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) \ + $(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +endif + + Index: Makefile.am =================================================================== --- Makefile.am (revision 13036) +++ Makefile.am (working copy) @@ -14,7 +14,8 @@ EXP_TOOLS = exp-sgcheck \ exp-bbv \ - exp-dhat + exp-dhat \ + exp-dmdv # Put docs last because building the HTML is slow and we want to get # everything else working before we try it. @@ -42,6 +43,7 @@ glibc-2.2-LinuxThreads-helgrind.supp \ glibc-2.X-drd.supp \ exp-sgcheck.supp \ + exp-dmdv.supp \ darwin9.supp darwin9-drd.supp \ darwin10.supp darwin10-drd.supp \ darwin11.supp darwin12.supp \ Index: configure.in =================================================================== --- configure.in (revision 13036) +++ configure.in (working copy) @@ -937,7 +937,10 @@ # Add glibc and X11 suppressions for exp-sgcheck DEFAULT_SUPP="exp-sgcheck.supp ${DEFAULT_SUPP}" +# Add suppressions for DMDV +DEFAULT_SUPP="exp-dmdv.supp ${DEFAULT_SUPP}" + #---------------------------------------------------------------------------- # Platform variants? #---------------------------------------------------------------------------- @@ -2468,6 +2471,8 @@ exp-bbv/tests/arm-linux/Makefile exp-dhat/Makefile exp-dhat/tests/Makefile + exp-dmdv/Makefile + exp-dmdv/tests/Makefile ]) AC_CONFIG_FILES([coregrind/link_tool_exe_linux], [chmod +x coregrind/link_tool_exe_linux])