gecko/tools/dmdv/dmdv.patch
Nicholas Nethercote e37c1e657a Bug 800703 - Rename the existing, Valgrind-based DMD as "DMDV". r=khuey.
--HG--
rename : xpcom/base/dmd.h => tools/dmdv/dmdv.h
2012-10-08 15:31:17 -07:00

2033 lines
65 KiB
Diff

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 <nnethercote@mozilla.com>
+
+ 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 <stdlib.h>
+#include <malloc.h>
+#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 <stdlib.h>
+#include <malloc.h>
+#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 <stdlib.h>
+#include <malloc.h>
+#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 <stdlib.h>
+#include <malloc.h>
+#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-<platform>
+#----------------------------------------------------------------------------
+
+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-<platform>.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])