summaryrefslogtreecommitdiff
path: root/source/fitz/memento.c
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2013-06-19 15:29:44 +0200
committerTor Andersson <tor.andersson@artifex.com>2013-06-20 16:45:35 +0200
commit0a927854a10e1e6b9770a81e2e1d9f3093631757 (patch)
tree3d65d820d9fdba2d0d394d99c36290c851b78ca0 /source/fitz/memento.c
parent1ae8f19179c5f0f8c6352b3c7855465325d5449a (diff)
downloadmupdf-0a927854a10e1e6b9770a81e2e1d9f3093631757.tar.xz
Rearrange source files.
Diffstat (limited to 'source/fitz/memento.c')
-rw-r--r--source/fitz/memento.c1535
1 files changed, 1535 insertions, 0 deletions
diff --git a/source/fitz/memento.c b/source/fitz/memento.c
new file mode 100644
index 00000000..6a159f10
--- /dev/null
+++ b/source/fitz/memento.c
@@ -0,0 +1,1535 @@
+/* Copyright (C) 2001-2013 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Inspired by Fortify by Simon P Bullen. */
+
+/* Set the following if you're only looking for leaks, not memory overwrites
+ * to speed the operation */
+/* #define MEMENTO_LEAKONLY */
+
+/* Don't keep blocks around if they'd mean losing more than a quarter of
+ * the freelist. */
+#define MEMENTO_FREELIST_MAX_SINGLE_BLOCK (MEMENTO_FREELIST_MAX/4)
+
+#define COMPILING_MEMENTO_C
+
+/* We have some GS specific tweaks; more for the GS build environment than
+ * anything else. */
+#undef MEMENTO_GS_HACKS
+
+#ifdef MEMENTO_GS_HACKS
+/* For GS we include malloc_.h. Anyone else would just include memento.h */
+#include "malloc_.h"
+#ifdef __MACH__
+#include <string.h>
+#else
+#ifndef memset
+void *memset(void *,int,size_t);
+#endif
+#endif
+int atexit(void (*)(void));
+#else
+#include "mupdf/memento.h"
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#ifdef MEMENTO_ANDROID
+#include <android/log.h>
+
+static int
+android_fprintf(FILE *file, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ __android_log_vprint(ANDROID_LOG_ERROR,"memento", fmt, args);
+ va_end(args);
+}
+
+#define fprintf android_fprintf
+#define MEMENTO_STACKTRACE_METHOD 0
+#endif
+
+#ifndef MEMENTO_STACKTRACE_METHOD
+#ifdef __GNUC__
+#define MEMENTO_STACKTRACE_METHOD 1
+#endif
+#endif
+
+#if defined(__linux__)
+#define MEMENTO_HAS_FORK
+#elif defined(__APPLE__) && defined(__MACH__)
+#define MEMENTO_HAS_FORK
+#endif
+
+/* Define the underlying allocators, just in case */
+void *MEMENTO_UNDERLYING_MALLOC(size_t);
+void MEMENTO_UNDERLYING_FREE(void *);
+void *MEMENTO_UNDERLYING_REALLOC(void *,size_t);
+void *MEMENTO_UNDERLYING_CALLOC(size_t,size_t);
+
+/* And some other standard functions we use. We don't include the header
+ * files, just in case they pull in unexpected others. */
+int atoi(const char *);
+char *getenv(const char *);
+
+/* How far to search for pointers in each block when calculating nestings */
+/* mupdf needs at least 34000ish (sizeof(fz_shade))/ */
+#define MEMENTO_PTRSEARCH 65536
+
+#ifndef MEMENTO_MAXPATTERN
+#define MEMENTO_MAXPATTERN 0
+#endif
+
+#ifdef MEMENTO
+
+#ifdef MEMENTO_GS_HACKS
+#include "valgrind.h"
+#else
+#ifdef HAVE_VALGRIND
+#include "valgrind/memcheck.h"
+#else
+#define VALGRIND_MAKE_MEM_NOACCESS(p,s) do { } while (0==1)
+#define VALGRIND_MAKE_MEM_UNDEFINED(p,s) do { } while (0==1)
+#define VALGRIND_MAKE_MEM_DEFINED(p,s) do { } while (0==1)
+#endif
+#endif
+
+enum {
+ Memento_PreSize = 16,
+ Memento_PostSize = 16
+};
+
+enum {
+ Memento_Flag_OldBlock = 1,
+ Memento_Flag_HasParent = 2,
+ Memento_Flag_BreakOnFree = 4,
+ Memento_Flag_BreakOnRealloc = 8
+};
+
+/* When we list leaked blocks at the end of execution, we search for pointers
+ * between blocks in order to be able to give a nice nested view.
+ * Unfortunately, if you have are running your own allocator (such as
+ * ghostscripts chunk allocator) you can often find that the header of the
+ * block always contains pointers to next or previous blocks. This tends to
+ * mean the nesting displayed is "uninteresting" at best :)
+ *
+ * As a hack to get around this, we have a define MEMENTO_SKIP_SEARCH that
+ * indicates how many bytes to skip over at the start of the chunk.
+ * This may cause us to miss true nestings, but such is life...
+ */
+#ifndef MEMENTO_SEARCH_SKIP
+#ifdef MEMENTO_GS_HACKS
+#define MEMENTO_SEARCH_SKIP (2*sizeof(void *))
+#else
+#define MEMENTO_SEARCH_SKIP 0
+#endif
+#endif
+
+typedef struct Memento_BlkHeader Memento_BlkHeader;
+
+struct Memento_BlkHeader
+{
+ size_t rawsize;
+ int sequence;
+ int lastCheckedOK;
+ int flags;
+ Memento_BlkHeader *next;
+ Memento_BlkHeader *parent; /* Only used while printing out nested list */
+
+ const char *label;
+
+ /* Entries for nesting display calculations */
+ Memento_BlkHeader *child;
+ Memento_BlkHeader *sibling;
+
+ char preblk[Memento_PreSize];
+};
+
+/* In future this could (should) be a smarter data structure, like, say,
+ * splay trees. For now, we use a list.
+ */
+typedef struct Memento_Blocks
+{
+ Memento_BlkHeader *head;
+ Memento_BlkHeader **tail;
+} Memento_Blocks;
+
+/* And our global structure */
+static struct {
+ int inited;
+ Memento_Blocks used;
+ Memento_Blocks free;
+ size_t freeListSize;
+ int sequence;
+ int paranoia;
+ int paranoidAt;
+ int countdown;
+ int lastChecked;
+ int breakAt;
+ int failAt;
+ int failing;
+ int nextFailAt;
+ int squeezeAt;
+ int squeezing;
+ int segv;
+ int pattern;
+ int nextPattern;
+ int patternBit;
+ size_t maxMemory;
+ size_t alloc;
+ size_t peakAlloc;
+ size_t totalAlloc;
+ size_t numMallocs;
+ size_t numFrees;
+ size_t numReallocs;
+} globals;
+
+#define MEMENTO_EXTRASIZE (sizeof(Memento_BlkHeader) + Memento_PostSize)
+
+/* Round up size S to the next multiple of N (where N is a power of 2) */
+#define MEMENTO_ROUNDUP(S,N) ((S + N-1)&~(N-1))
+
+#define MEMBLK_SIZE(s) MEMENTO_ROUNDUP(s + MEMENTO_EXTRASIZE, MEMENTO_MAXALIGN)
+
+#define MEMBLK_FROMBLK(B) (&((Memento_BlkHeader*)(void *)(B))[-1])
+#define MEMBLK_TOBLK(B) ((void*)(&((Memento_BlkHeader*)(void*)(B))[1]))
+#define MEMBLK_POSTPTR(B) \
+ (&((char *)(void *)(B))[(B)->rawsize + sizeof(Memento_BlkHeader)])
+
+void Memento_breakpoint(void)
+{
+ /* A handy externally visible function for breakpointing */
+#if 0 /* Enable this to force automatic breakpointing */
+#ifdef DEBUG
+#ifdef _MSC_VER
+ __asm int 3;
+#endif
+#endif
+#endif
+}
+
+static void Memento_addBlockHead(Memento_Blocks *blks,
+ Memento_BlkHeader *b,
+ int type)
+{
+ if (blks->tail == &blks->head) {
+ /* Adding into an empty list, means the tail changes too */
+ blks->tail = &b->next;
+ }
+ b->next = blks->head;
+ blks->head = b;
+#ifndef MEMENTO_LEAKONLY
+ memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize);
+ memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize);
+#endif
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize);
+ if (type == 0) { /* malloc */
+ VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize);
+ } else if (type == 1) { /* free */
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
+ }
+ VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
+}
+
+static void Memento_addBlockTail(Memento_Blocks *blks,
+ Memento_BlkHeader *b,
+ int type)
+{
+ VALGRIND_MAKE_MEM_DEFINED(blks->tail, sizeof(Memento_BlkHeader *));
+ *blks->tail = b;
+ blks->tail = &b->next;
+ b->next = NULL;
+ VALGRIND_MAKE_MEM_NOACCESS(blks->tail, sizeof(Memento_BlkHeader *));
+#ifndef MEMENTO_LEAKONLY
+ memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize);
+ memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize);
+#endif
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize);
+ if (type == 0) { /* malloc */
+ VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize);
+ } else if (type == 1) { /* free */
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
+ }
+ VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
+}
+
+typedef struct BlkCheckData {
+ int found;
+ int preCorrupt;
+ int postCorrupt;
+ int freeCorrupt;
+ int index;
+} BlkCheckData;
+
+static int Memento_Internal_checkAllocedBlock(Memento_BlkHeader *b, void *arg)
+{
+#ifndef MEMENTO_LEAKONLY
+ int i;
+ char *p;
+ int corrupt = 0;
+ BlkCheckData *data = (BlkCheckData *)arg;
+
+ p = b->preblk;
+ i = Memento_PreSize;
+ do {
+ corrupt |= (*p++ ^ (char)MEMENTO_PREFILL);
+ } while (--i);
+ if (corrupt) {
+ data->preCorrupt = 1;
+ }
+ p = MEMBLK_POSTPTR(b);
+ i = Memento_PreSize;
+ do {
+ corrupt |= (*p++ ^ (char)MEMENTO_POSTFILL);
+ } while (--i);
+ if (corrupt) {
+ data->postCorrupt = 1;
+ }
+ if ((data->freeCorrupt | data->preCorrupt | data->postCorrupt) == 0) {
+ b->lastCheckedOK = globals.sequence;
+ }
+ data->found |= 1;
+#endif
+ return 0;
+}
+
+static int Memento_Internal_checkFreedBlock(Memento_BlkHeader *b, void *arg)
+{
+#ifndef MEMENTO_LEAKONLY
+ int i;
+ char *p;
+ BlkCheckData *data = (BlkCheckData *)arg;
+
+ p = MEMBLK_TOBLK(b);
+ i = b->rawsize;
+ /* Attempt to speed this up by checking an (aligned) int at a time */
+ do {
+ if (((size_t)p) & 1) {
+ if (*p++ != (char)MEMENTO_FREEFILL)
+ break;
+ i--;
+ if (i == 0)
+ break;
+ }
+ if ((i >= 2) && (((size_t)p) & 2)) {
+ if (*(short *)p != (short)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8)))
+ goto mismatch;
+ p += 2;
+ i -= 2;
+ if (i == 0)
+ break;
+ }
+ i -= 4;
+ while (i >= 0) {
+ if (*(int *)p != (MEMENTO_FREEFILL |
+ (MEMENTO_FREEFILL<<8) |
+ (MEMENTO_FREEFILL<<16) |
+ (MEMENTO_FREEFILL<<24)))
+ goto mismatch;
+ p += 4;
+ i -= 4;
+ }
+ i += 4;
+ if ((i >= 2) && (((size_t)p) & 2)) {
+ if (*(short *)p != (short)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8)))
+ goto mismatch;
+ p += 2;
+ i -= 2;
+ }
+mismatch:
+ while (i) {
+ if (*p++ != (char)MEMENTO_FREEFILL)
+ break;
+ i--;
+ }
+ } while (0);
+ if (i) {
+ data->freeCorrupt = 1;
+ data->index = b->rawsize-i;
+ }
+ return Memento_Internal_checkAllocedBlock(b, arg);
+#else
+ return 0;
+#endif
+}
+
+static void Memento_removeBlock(Memento_Blocks *blks,
+ Memento_BlkHeader *b)
+{
+ Memento_BlkHeader *head = blks->head;
+ Memento_BlkHeader *prev = NULL;
+ while ((head) && (head != b)) {
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
+ prev = head;
+ head = head->next;
+ VALGRIND_MAKE_MEM_NOACCESS(prev, sizeof(*prev));
+ }
+ if (head == NULL) {
+ /* FAIL! Will have been reported to user earlier, so just exit. */
+ return;
+ }
+ VALGRIND_MAKE_MEM_DEFINED(blks->tail, sizeof(*blks->tail));
+ if (*blks->tail == head) {
+ /* Removing the tail of the list */
+ if (prev == NULL) {
+ /* Which is also the head */
+ blks->tail = &blks->head;
+ } else {
+ /* Which isn't the head */
+ blks->tail = &prev->next;
+ }
+ }
+ if (prev == NULL) {
+ /* Removing from the head of the list */
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
+ blks->head = head->next;
+ VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(*head));
+ } else {
+ /* Removing from not-the-head */
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
+ VALGRIND_MAKE_MEM_DEFINED(prev, sizeof(*prev));
+ prev->next = head->next;
+ VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(*head));
+ VALGRIND_MAKE_MEM_NOACCESS(prev, sizeof(*prev));
+ }
+}
+
+static int Memento_Internal_makeSpace(size_t space)
+{
+ /* If too big, it can never go on the freelist */
+ if (space > MEMENTO_FREELIST_MAX_SINGLE_BLOCK)
+ return 0;
+ /* Pretend we added it on. */
+ globals.freeListSize += space;
+ /* Ditch blocks until it fits within our limit */
+ while (globals.freeListSize > MEMENTO_FREELIST_MAX) {
+ Memento_BlkHeader *head = globals.free.head;
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
+ globals.free.head = head->next;
+ globals.freeListSize -= MEMBLK_SIZE(head->rawsize);
+ MEMENTO_UNDERLYING_FREE(head);
+ }
+ /* Make sure we haven't just completely emptied the free list */
+ /* (This should never happen, but belt and braces... */
+ if (globals.free.head == NULL)
+ globals.free.tail = &globals.free.head;
+ return 1;
+}
+
+static int Memento_appBlocks(Memento_Blocks *blks,
+ int (*app)(Memento_BlkHeader *,
+ void *),
+ void *arg)
+{
+ Memento_BlkHeader *head = blks->head;
+ Memento_BlkHeader *next;
+ int result;
+ while (head) {
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
+ VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head),
+ head->rawsize + Memento_PostSize);
+ result = app(head, arg);
+ next = head->next;
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
+ VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader));
+ if (result)
+ return result;
+ head = next;
+ }
+ return 0;
+}
+
+static int Memento_appBlock(Memento_Blocks *blks,
+ int (*app)(Memento_BlkHeader *,
+ void *),
+ void *arg,
+ Memento_BlkHeader *b)
+{
+ Memento_BlkHeader *head = blks->head;
+ Memento_BlkHeader *next;
+ int result;
+ while (head && head != b) {
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
+ next = head->next;
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
+ head = next;
+ }
+ if (head == b) {
+ VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
+ VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head),
+ head->rawsize + Memento_PostSize);
+ result = app(head, arg);
+ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
+ VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader));
+ return result;
+ }
+ return 0;
+}
+
+static void showBlock(Memento_BlkHeader *b, int space)
+{
+ fprintf(stderr, "0x%p:(size=%d,num=%d)",
+ MEMBLK_TOBLK(b), (int)b->rawsize, b->sequence);
+ if (b->label)
+ fprintf(stderr, "%c(%s)", space, b->label);
+}
+
+static void blockDisplay(Memento_BlkHeader *b, int n)
+{
+ n++;
+ while (n > 40)
+ {
+ fprintf(stderr, "*");
+ n -= 40;
+ }
+ while(n > 0)
+ {
+ int i = n;
+ if (i > 32)
+ i = 32;
+ n -= i;
+ fprintf(stderr, "%s", &" "[32-i]);
+ }
+ showBlock(b, '\t');
+ fprintf(stderr, "\n");
+}
+
+static int Memento_listBlock(Memento_BlkHeader *b,
+ void *arg)
+{
+ int *counts = (int *)arg;
+ blockDisplay(b, 0);
+ counts[0]++;
+ counts[1]+= b->rawsize;
+ return 0;
+}
+
+static void doNestedDisplay(Memento_BlkHeader *b,
+ int depth)
+{
+ /* Try and avoid recursion if we can help it */
+ do {
+ blockDisplay(b, depth);
+ if (b->sibling) {
+ if (b->child)
+ doNestedDisplay(b->child, depth+1);
+ b = b->sibling;
+ } else {
+ b = b->child;
+ depth++;
+ }
+ } while (b);
+}
+
+static int ptrcmp(const void *a_, const void *b_)
+{
+ const char **a = (const char **)a_;
+ const char **b = (const char **)b_;
+ return (int)(*a-*b);
+}
+
+static
+int Memento_listBlocksNested(void)
+{
+ int count, size, i;
+ Memento_BlkHeader *b;
+ void **blocks, *minptr, *maxptr;
+ long mask;
+
+ /* Count the blocks */
+ count = 0;
+ size = 0;
+ for (b = globals.used.head; b; b = b->next) {
+ size += b->rawsize;
+ count++;
+ }
+
+ /* Make our block list */
+ blocks = MEMENTO_UNDERLYING_MALLOC(sizeof(void *) * count);
+ if (blocks == NULL)
+ return 1;
+
+ /* Populate our block list */
+ b = globals.used.head;
+ minptr = maxptr = MEMBLK_TOBLK(b);
+ mask = (long)minptr;
+ for (i = 0; b; b = b->next, i++) {
+ void *p = MEMBLK_TOBLK(b);
+ mask &= (long)p;
+ if (p < minptr)
+ minptr = p;
+ if (p > maxptr)
+ maxptr = p;
+ blocks[i] = p;
+ b->flags &= ~Memento_Flag_HasParent;
+ b->child = NULL;
+ b->sibling = NULL;
+ b->parent = NULL;
+ }
+ qsort(blocks, count, sizeof(void *), ptrcmp);
+
+ /* Now, calculate tree */
+ for (b = globals.used.head; b; b = b->next) {
+ char *p = MEMBLK_TOBLK(b);
+ int end = (b->rawsize < MEMENTO_PTRSEARCH ? b->rawsize : MEMENTO_PTRSEARCH);
+ for (i = MEMENTO_SEARCH_SKIP; i < end; i += sizeof(void *)) {
+ void *q = *(void **)(&p[i]);
+ void **r;
+
+ /* Do trivial checks on pointer */
+ if ((mask & (int)q) != mask || q < minptr || q > maxptr)
+ continue;
+
+ /* Search for pointer */
+ r = bsearch(&q, blocks, count, sizeof(void *), ptrcmp);
+ if (r) {
+ /* Found child */
+ Memento_BlkHeader *child = MEMBLK_FROMBLK(*r);
+ Memento_BlkHeader *parent;
+
+ /* We're assuming tree structure, not graph - ignore second
+ * and subsequent pointers. */
+ if (child->parent != NULL)
+ continue;
+ if (child->flags & Memento_Flag_HasParent)
+ continue;
+
+ /* We're also assuming acyclicness here. If this is one of
+ * our parents, ignore it. */
+ parent = b->parent;
+ while (parent != NULL && parent != child)
+ parent = parent->parent;
+ if (parent == child)
+ continue;
+
+ child->sibling = b->child;
+ b->child = child;
+ child->parent = b;
+ child->flags |= Memento_Flag_HasParent;
+ }
+ }
+ }
+
+ /* Now display with nesting */
+ for (b = globals.used.head; b; b = b->next) {
+ if ((b->flags & Memento_Flag_HasParent) == 0)
+ doNestedDisplay(b, 0);
+ }
+ fprintf(stderr, " Total number of blocks = %d\n", count);
+ fprintf(stderr, " Total size of blocks = %d\n", size);
+
+ MEMENTO_UNDERLYING_FREE(blocks);
+ return 0;
+}
+
+void Memento_listBlocks(void)
+{
+ fprintf(stderr, "Allocated blocks:\n");
+ if (Memento_listBlocksNested())
+ {
+ int counts[2];
+ counts[0] = 0;
+ counts[1] = 0;
+ Memento_appBlocks(&globals.used, Memento_listBlock, &counts[0]);
+ fprintf(stderr, " Total number of blocks = %d\n", counts[0]);
+ fprintf(stderr, " Total size of blocks = %d\n", counts[1]);
+ }
+}
+
+static int Memento_listNewBlock(Memento_BlkHeader *b,
+ void *arg)
+{
+ if (b->flags & Memento_Flag_OldBlock)
+ return 0;
+ b->flags |= Memento_Flag_OldBlock;
+ return Memento_listBlock(b, arg);
+}
+
+void Memento_listNewBlocks(void) {
+ int counts[2];
+ counts[0] = 0;
+ counts[1] = 0;
+ fprintf(stderr, "Blocks allocated and still extant since last list:\n");
+ Memento_appBlocks(&globals.used, Memento_listNewBlock, &counts[0]);
+ fprintf(stderr, " Total number of blocks = %d\n", counts[0]);
+ fprintf(stderr, " Total size of blocks = %d\n", counts[1]);
+}
+
+static void Memento_endStats(void)
+{
+ fprintf(stderr, "Total memory malloced = %u bytes\n", (unsigned int)globals.totalAlloc);
+ fprintf(stderr, "Peak memory malloced = %u bytes\n", (unsigned int)globals.peakAlloc);
+ fprintf(stderr, "%u mallocs, %u frees, %u reallocs\n", (unsigned int)globals.numMallocs,
+ (unsigned int)globals.numFrees, (unsigned int)globals.numReallocs);
+ fprintf(stderr, "Average allocation size %u bytes\n", (unsigned int)
+ (globals.numMallocs != 0 ? globals.totalAlloc/globals.numMallocs: 0));
+}
+
+void Memento_stats(void)
+{
+ fprintf(stderr, "Current memory malloced = %u bytes\n", (unsigned int)globals.alloc);
+ Memento_endStats();
+}
+
+static void Memento_fin(void)
+{
+ Memento_checkAllMemory();
+ Memento_endStats();
+ if (globals.used.head != NULL) {
+ Memento_listBlocks();
+ Memento_breakpoint();
+ }
+ if (globals.segv) {
+ fprintf(stderr, "Memory dumped on SEGV while squeezing @ %d\n", globals.failAt);
+ } else if (globals.squeezing) {
+ if (globals.pattern == 0)
+ fprintf(stderr, "Memory squeezing @ %d complete\n", globals.squeezeAt);
+ else
+ fprintf(stderr, "Memory squeezing @ %d (%d) complete\n", globals.squeezeAt, globals.pattern);
+ }
+ if (globals.failing)
+ {
+ fprintf(stderr, "MEMENTO_FAILAT=%d\n", globals.failAt);
+ fprintf(stderr, "MEMENTO_PATTERN=%d\n", globals.pattern);
+ }
+ if (globals.nextFailAt != 0)
+ {
+ fprintf(stderr, "MEMENTO_NEXTFAILAT=%d\n", globals.nextFailAt);
+ fprintf(stderr, "MEMENTO_NEXTPATTERN=%d\n", globals.nextPattern);
+ }
+}
+
+static void Memento_inited(void)
+{
+ /* A good place for a breakpoint */
+}
+
+static void Memento_init(void)
+{
+ char *env;
+ memset(&globals, 0, sizeof(globals));
+ globals.inited = 1;
+ globals.used.head = NULL;
+ globals.used.tail = &globals.used.head;
+ globals.free.head = NULL;
+ globals.free.tail = &globals.free.head;
+ globals.sequence = 0;
+ globals.countdown = 1024;
+
+ env = getenv("MEMENTO_FAILAT");
+ globals.failAt = (env ? atoi(env) : 0);
+
+ env = getenv("MEMENTO_PARANOIA");
+ globals.paranoia = (env ? atoi(env) : 0);
+ if (globals.paranoia == 0)
+ globals.paranoia = 1024;
+
+ env = getenv("MEMENTO_PARANOIDAT");
+ globals.paranoidAt = (env ? atoi(env) : 0);
+
+ env = getenv("MEMENTO_SQUEEZEAT");
+ globals.squeezeAt = (env ? atoi(env) : 0);
+
+ env = getenv("MEMENTO_PATTERN");
+ globals.pattern = (env ? atoi(env) : 0);
+
+ env = getenv("MEMENTO_MAXMEMORY");
+ globals.maxMemory = (env ? atoi(env) : 0);
+
+ atexit(Memento_fin);
+
+ Memento_inited();
+}
+
+#ifdef MEMENTO_HAS_FORK
+#include <unistd.h>
+#include <sys/wait.h>
+#ifdef MEMENTO_STACKTRACE_METHOD
+#if MEMENTO_STACKTRACE_METHOD == 1
+#include <signal.h>
+#endif
+#endif
+
+/* FIXME: Find some portable way of getting this */
+/* MacOSX has 10240, Ubuntu seems to have 256 */
+#define OPEN_MAX 10240
+
+/* stashed_map[j] = i means that filedescriptor i-1 was duplicated to j */
+int stashed_map[OPEN_MAX];
+
+#ifdef MEMENTO_STACKTRACE_METHOD
+#if MEMENTO_STACKTRACE_METHOD == 1
+extern size_t backtrace(void **, int);
+extern void backtrace_symbols_fd(void **, size_t, int);
+#endif
+#endif
+
+static void Memento_signal(void)
+{
+ fprintf(stderr, "SEGV after Memory squeezing @ %d\n", globals.squeezeAt);
+
+#ifdef MEMENTO_STACKTRACE_METHOD
+#if MEMENTO_STACKTRACE_METHOD == 1
+ {
+ void *array[100];
+ size_t size;
+
+ size = backtrace(array, 100);
+ fprintf(stderr, "------------------------------------------------------------------------\n");
+ fprintf(stderr, "Backtrace:\n");
+ backtrace_symbols_fd(array, size, 2);
+ fprintf(stderr, "------------------------------------------------------------------------\n");
+ }
+#endif
+#endif
+
+ exit(1);
+}
+
+static int squeeze(void)
+{
+ pid_t pid;
+ int i, status;
+
+ if (globals.patternBit < 0)
+ return 1;
+ if (globals.squeezing && globals.patternBit >= MEMENTO_MAXPATTERN)
+ return 1;
+
+ if (globals.patternBit == 0)
+ globals.squeezeAt = globals.sequence;
+
+ if (!globals.squeezing) {
+ fprintf(stderr, "Memory squeezing @ %d\n", globals.squeezeAt);
+ } else
+ fprintf(stderr, "Memory squeezing @ %d (%x,%x)\n", globals.squeezeAt, globals.pattern, globals.patternBit);
+
+ /* When we fork below, the child is going to snaffle all our file pointers
+ * and potentially corrupt them. Let's make copies of all of them before
+ * we fork, so we can restore them when we restart. */
+ for (i = 0; i < OPEN_MAX; i++) {
+ if (stashed_map[i] == 0) {
+ int j = dup(i);
+ stashed_map[j] = i+1;
+ }
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ /* Child */
+ signal(SIGSEGV, Memento_signal);
+ /* In the child, we always fail the next allocation. */
+ if (globals.patternBit == 0) {
+ globals.patternBit = 1;
+ } else
+ globals.patternBit <<= 1;
+ globals.squeezing = 1;
+ return 1;
+ }
+
+ /* In the parent if we hit another allocation, pass it (and record the
+ * fact we passed it in the pattern. */
+ globals.pattern |= globals.patternBit;
+ globals.patternBit <<= 1;
+
+ /* Wait for pid to finish */
+ waitpid(pid, &status, 0);
+
+ if (status != 0) {
+ fprintf(stderr, "Child status=%d\n", status);
+ }
+
+ /* Put the files back */
+ for (i = 0; i < OPEN_MAX; i++) {
+ if (stashed_map[i] != 0) {
+ dup2(i, stashed_map[i]-1);
+ close(i);
+ stashed_map[i] = 0;
+ }
+ }
+
+ return 0;
+}
+#else
+#include <signal.h>
+
+static void Memento_signal(void)
+{
+ globals.segv = 1;
+ /* If we just return from this function the SEGV will be unhandled, and
+ * we'll launch into whatever JIT debugging system the OS provides. At
+ * least fprintf(stderr, something useful first. If MEMENTO_NOJIT is set, then
+ * just exit to avoid the JIT (and get the usual atexit handling). */
+ if (getenv("MEMENTO_NOJIT"))
+ exit(1);
+ else
+ Memento_fin();
+}
+
+int squeeze(void)
+{
+ fprintf(stderr, "Memento memory squeezing disabled as no fork!\n");
+ return 0;
+}
+#endif
+
+static void Memento_startFailing(void)
+{
+ if (!globals.failing) {
+ fprintf(stderr, "Starting to fail...\n");
+ fflush(stderr);
+ globals.failing = 1;
+ globals.failAt = globals.sequence;
+ globals.nextFailAt = globals.sequence+1;
+ globals.pattern = 0;
+ globals.patternBit = 0;
+ signal(SIGSEGV, Memento_signal);
+ signal(SIGABRT, Memento_signal);
+ Memento_breakpoint();
+ }
+}
+
+static void Memento_event(void)
+{
+ globals.sequence++;
+ if ((globals.sequence >= globals.paranoidAt) && (globals.paranoidAt != 0)) {
+ globals.paranoia = 1;
+ globals.countdown = 1;
+ }
+ if (--globals.countdown == 0) {
+ Memento_checkAllMemory();
+ globals.countdown = globals.paranoia;
+ }
+
+ if (globals.sequence == globals.breakAt) {
+ fprintf(stderr, "Breaking at event %d\n", globals.breakAt);
+ Memento_breakpoint();
+ }
+}
+
+int Memento_breakAt(int event)
+{
+ globals.breakAt = event;
+ return event;
+}
+
+void *Memento_label(void *ptr, const char *label)
+{
+ Memento_BlkHeader *block;
+
+ if (ptr == NULL)
+ return NULL;
+ block = MEMBLK_FROMBLK(ptr);
+ block->label = label;
+ return ptr;
+}
+
+int Memento_failThisEvent(void)
+{
+ int failThisOne;
+
+ if (!globals.inited)
+ Memento_init();
+
+ Memento_event();
+
+ if ((globals.sequence >= globals.failAt) && (globals.failAt != 0))
+ Memento_startFailing();
+ if ((globals.sequence >= globals.squeezeAt) && (globals.squeezeAt != 0)) {
+ return squeeze();
+ }
+
+ if (!globals.failing)
+ return 0;
+ failThisOne = ((globals.patternBit & globals.pattern) == 0);
+ /* If we are failing, and we've reached the end of the pattern and we've
+ * still got bits available in the pattern word, and we haven't already
+ * set a nextPattern, then extend the pattern. */
+ if (globals.failing &&
+ ((~(globals.patternBit-1) & globals.pattern) == 0) &&
+ (globals.patternBit != 0) &&
+ globals.nextPattern == 0)
+ {
+ /* We'll fail this one, and set the 'next' one to pass it. */
+ globals.nextFailAt = globals.failAt;
+ globals.nextPattern = globals.pattern | globals.patternBit;
+ }
+ globals.patternBit = (globals.patternBit ? globals.patternBit << 1 : 1);
+
+ return failThisOne;
+}
+
+void *Memento_malloc(size_t s)
+{
+ Memento_BlkHeader *memblk;
+ size_t smem = MEMBLK_SIZE(s);
+
+ if (Memento_failThisEvent())
+ return NULL;
+
+ if (s == 0)
+ return NULL;
+
+ globals.numMallocs++;
+
+ if (globals.maxMemory != 0 && globals.alloc + s > globals.maxMemory)
+ return NULL;
+
+ memblk = MEMENTO_UNDERLYING_MALLOC(smem);
+ if (memblk == NULL)
+ return NULL;
+
+ globals.alloc += s;
+ globals.totalAlloc += s;
+ if (globals.peakAlloc < globals.alloc)
+ globals.peakAlloc = globals.alloc;
+#ifndef MEMENTO_LEAKONLY
+ memset(MEMBLK_TOBLK(memblk), MEMENTO_ALLOCFILL, s);
+#endif
+ memblk->rawsize = s;
+ memblk->sequence = globals.sequence;
+ memblk->lastCheckedOK = memblk->sequence;
+ memblk->flags = 0;
+ memblk->label = 0;
+ memblk->child = NULL;
+ memblk->sibling = NULL;
+ Memento_addBlockHead(&globals.used, memblk, 0);
+ return MEMBLK_TOBLK(memblk);
+}
+
+void *Memento_calloc(size_t n, size_t s)
+{
+ void *block = Memento_malloc(n*s);
+
+ if (block)
+ memset(block, 0, n*s);
+ return block;
+}
+
+static int checkBlock(Memento_BlkHeader *memblk, const char *action)
+{
+#ifndef MEMENTO_LEAKONLY
+ BlkCheckData data;
+
+ memset(&data, 0, sizeof(data));
+ Memento_appBlock(&globals.used, Memento_Internal_checkAllocedBlock,
+ &data, memblk);
+ if (!data.found) {
+ /* Failure! */
+ fprintf(stderr, "Attempt to %s block ", action);
+ showBlock(memblk, 32);
+ Memento_breakpoint();
+ return 1;
+ } else if (data.preCorrupt || data.postCorrupt) {
+ fprintf(stderr, "Block ");
+ showBlock(memblk, ' ');
+ fprintf(stderr, " found to be corrupted on %s!\n", action);
+ if (data.preCorrupt) {
+ fprintf(stderr, "Preguard corrupted\n");
+ }
+ if (data.postCorrupt) {
+ fprintf(stderr, "Postguard corrupted\n");
+ }
+ fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n",
+ memblk->lastCheckedOK, globals.sequence);
+ Memento_breakpoint();
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void Memento_free(void *blk)
+{
+ Memento_BlkHeader *memblk;
+
+ if (!globals.inited)
+ Memento_init();
+
+ Memento_event();
+
+ if (blk == NULL)
+ return;
+
+ memblk = MEMBLK_FROMBLK(blk);
+ VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
+ if (checkBlock(memblk, "free"))
+ return;
+
+ if (memblk->flags & Memento_Flag_BreakOnFree)
+ Memento_breakpoint();
+
+ VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
+ globals.alloc -= memblk->rawsize;
+ globals.numFrees++;
+
+ Memento_removeBlock(&globals.used, memblk);
+
+ VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
+ if (Memento_Internal_makeSpace(MEMBLK_SIZE(memblk->rawsize))) {
+ VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
+ VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(memblk),
+ memblk->rawsize + Memento_PostSize);
+#ifndef MEMENTO_LEAKONLY
+ memset(MEMBLK_TOBLK(memblk), MEMENTO_FREEFILL, memblk->rawsize);
+#endif
+ Memento_addBlockTail(&globals.free, memblk, 1);
+ } else {
+ MEMENTO_UNDERLYING_FREE(memblk);
+ }
+}
+
+void *Memento_realloc(void *blk, size_t newsize)
+{
+ Memento_BlkHeader *memblk, *newmemblk;
+ size_t newsizemem;
+ int flags;
+
+ if (blk == NULL)
+ return Memento_malloc(newsize);
+ if (newsize == 0) {
+ Memento_free(blk);
+ return NULL;
+ }
+
+ if (Memento_failThisEvent())
+ return NULL;
+
+ memblk = MEMBLK_FROMBLK(blk);
+ if (checkBlock(memblk, "realloc"))
+ return NULL;
+
+ if (memblk->flags & Memento_Flag_BreakOnRealloc)
+ Memento_breakpoint();
+
+ if (globals.maxMemory != 0 && globals.alloc - memblk->rawsize + newsize > globals.maxMemory)
+ return NULL;
+
+ newsizemem = MEMBLK_SIZE(newsize);
+ Memento_removeBlock(&globals.used, memblk);
+ flags = memblk->flags;
+ newmemblk = MEMENTO_UNDERLYING_REALLOC(memblk, newsizemem);
+ if (newmemblk == NULL)
+ {
+ Memento_addBlockHead(&globals.used, memblk, 2);
+ return NULL;
+ }
+ globals.numReallocs++;
+ globals.totalAlloc += newsize;
+ globals.alloc -= newmemblk->rawsize;
+ globals.alloc += newsize;
+ if (globals.peakAlloc < globals.alloc)
+ globals.peakAlloc = globals.alloc;
+ newmemblk->flags = flags;
+ if (newmemblk->rawsize < newsize) {
+ char *newbytes = ((char *)MEMBLK_TOBLK(newmemblk))+newmemblk->rawsize;
+#ifndef MEMENTO_LEAKONLY
+ memset(newbytes, MEMENTO_ALLOCFILL, newsize - newmemblk->rawsize);
+#endif
+ VALGRIND_MAKE_MEM_UNDEFINED(newbytes, newsize - newmemblk->rawsize);
+ }
+ newmemblk->rawsize = newsize;
+#ifndef MEMENTO_LEAKONLY
+ memset(newmemblk->preblk, MEMENTO_PREFILL, Memento_PreSize);
+ memset(MEMBLK_POSTPTR(newmemblk), MEMENTO_POSTFILL, Memento_PostSize);
+#endif
+ Memento_addBlockHead(&globals.used, newmemblk, 2);
+ return MEMBLK_TOBLK(newmemblk);
+}
+
+int Memento_checkBlock(void *blk)
+{
+ Memento_BlkHeader *memblk;
+
+ if (blk == NULL)
+ return 0;
+ memblk = MEMBLK_FROMBLK(blk);
+ return checkBlock(memblk, "check");
+}
+
+static int Memento_Internal_checkAllAlloced(Memento_BlkHeader *memblk, void *arg)
+{
+ BlkCheckData *data = (BlkCheckData *)arg;
+
+ Memento_Internal_checkAllocedBlock(memblk, data);
+ if (data->preCorrupt || data->postCorrupt) {
+ if ((data->found & 2) == 0) {
+ fprintf(stderr, "Allocated blocks:\n");
+ data->found |= 2;
+ }
+ fprintf(stderr, " Block ");
+ showBlock(memblk, ' ');
+ if (data->preCorrupt) {
+ fprintf(stderr, " Preguard ");
+ }
+ if (data->postCorrupt) {
+ fprintf(stderr, "%s Postguard ",
+ (data->preCorrupt ? "&" : ""));
+ }
+ fprintf(stderr, "corrupted.\n "
+ "Block last checked OK at allocation %d. Now %d.\n",
+ memblk->lastCheckedOK, globals.sequence);
+ data->preCorrupt = 0;
+ data->postCorrupt = 0;
+ data->freeCorrupt = 0;
+ }
+ else
+ memblk->lastCheckedOK = globals.sequence;
+ return 0;
+}
+
+static int Memento_Internal_checkAllFreed(Memento_BlkHeader *memblk, void *arg)
+{
+ BlkCheckData *data = (BlkCheckData *)arg;
+
+ Memento_Internal_checkFreedBlock(memblk, data);
+ if (data->preCorrupt || data->postCorrupt || data->freeCorrupt) {
+ if ((data->found & 4) == 0) {
+ fprintf(stderr, "Freed blocks:\n");
+ data->found |= 4;
+ }
+ fprintf(stderr, " ");
+ showBlock(memblk, ' ');
+ if (data->freeCorrupt) {
+ fprintf(stderr, " index %d (address 0x%p) onwards", data->index,
+ &((char *)MEMBLK_TOBLK(memblk))[data->index]);
+ if (data->preCorrupt) {
+ fprintf(stderr, "+ preguard");
+ }
+ if (data->postCorrupt) {
+ fprintf(stderr, "+ postguard");
+ }
+ } else {
+ if (data->preCorrupt) {
+ fprintf(stderr, " preguard");
+ }
+ if (data->postCorrupt) {
+ fprintf(stderr, "%s Postguard",
+ (data->preCorrupt ? "+" : ""));
+ }
+ }
+ fprintf(stderr, " corrupted.\n"
+ " Block last checked OK at allocation %d. Now %d.\n",
+ memblk->lastCheckedOK, globals.sequence);
+ data->preCorrupt = 0;
+ data->postCorrupt = 0;
+ data->freeCorrupt = 0;
+ }
+ else
+ memblk->lastCheckedOK = globals.sequence;
+ return 0;
+}
+
+int Memento_checkAllMemory(void)
+{
+#ifndef MEMENTO_LEAKONLY
+ BlkCheckData data;
+
+ memset(&data, 0, sizeof(data));
+ Memento_appBlocks(&globals.used, Memento_Internal_checkAllAlloced, &data);
+ Memento_appBlocks(&globals.free, Memento_Internal_checkAllFreed, &data);
+ if (data.found & 6) {
+ Memento_breakpoint();
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+int Memento_setParanoia(int i)
+{
+ globals.paranoia = i;
+ globals.countdown = globals.paranoia;
+ return i;
+}
+
+int Memento_paranoidAt(int i)
+{
+ globals.paranoidAt = i;
+ return i;
+}
+
+int Memento_getBlockNum(void *b)
+{
+ Memento_BlkHeader *memblk;
+ if (b == NULL)
+ return 0;
+ memblk = MEMBLK_FROMBLK(b);
+ return (memblk->sequence);
+}
+
+int Memento_check(void)
+{
+ int result;
+
+ fprintf(stderr, "Checking memory\n");
+ result = Memento_checkAllMemory();
+ fprintf(stderr, "Memory checked!\n");
+ return result;
+}
+
+typedef struct findBlkData {
+ void *addr;
+ Memento_BlkHeader *blk;
+ int flags;
+} findBlkData;
+
+static int Memento_containsAddr(Memento_BlkHeader *b,
+ void *arg)
+{
+ findBlkData *data = (findBlkData *)arg;
+ char *blkend = &((char *)MEMBLK_TOBLK(b))[b->rawsize];
+ if ((MEMBLK_TOBLK(b) <= data->addr) &&
+ ((void *)blkend > data->addr)) {
+ data->blk = b;
+ data->flags = 1;
+ return 1;
+ }
+ if (((void *)b <= data->addr) &&
+ (MEMBLK_TOBLK(b) > data->addr)) {
+ data->blk = b;
+ data->flags = 2;
+ return 1;
+ }
+ if (((void *)blkend <= data->addr) &&
+ ((void *)(blkend + Memento_PostSize) > data->addr)) {
+ data->blk = b;
+ data->flags = 3;
+ return 1;
+ }
+ return 0;
+}
+
+int Memento_find(void *a)
+{
+ findBlkData data;
+
+ data.addr = a;
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.used, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Address 0x%p is in %sallocated block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, "\n");
+ return data.blk->sequence;
+ }
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.free, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Address 0x%p is in %sfreed block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, "\n");
+ return data.blk->sequence;
+ }
+ return 0;
+}
+
+void Memento_breakOnFree(void *a)
+{
+ findBlkData data;
+
+ data.addr = a;
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.used, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, ") is freed\n");
+ data.blk->flags |= Memento_Flag_BreakOnFree;
+ return;
+ }
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.free, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Can't stop on free; address 0x%p is in %sfreed block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, "\n");
+ return;
+ }
+ fprintf(stderr, "Can't stop on free; address 0x%p is not in a known block.\n", a);
+}
+
+void Memento_breakOnRealloc(void *a)
+{
+ findBlkData data;
+
+ data.addr = a;
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.used, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, ") is freed (or realloced)\n");
+ data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc;
+ return;
+ }
+ data.blk = NULL;
+ data.flags = 0;
+ Memento_appBlocks(&globals.free, Memento_containsAddr, &data);
+ if (data.blk != NULL) {
+ fprintf(stderr, "Can't stop on free/realloc; address 0x%p is in %sfreed block ",
+ data.addr,
+ (data.flags == 1 ? "" : (data.flags == 2 ?
+ "preguard of " : "postguard of ")));
+ showBlock(data.blk, ' ');
+ fprintf(stderr, "\n");
+ return;
+ }
+ fprintf(stderr, "Can't stop on free/realloc; address 0x%p is not in a known block.\n", a);
+}
+
+int Memento_failAt(int i)
+{
+ globals.failAt = i;
+ if ((globals.sequence > globals.failAt) &&
+ (globals.failing != 0))
+ Memento_startFailing();
+ return i;
+}
+
+size_t Memento_setMax(size_t max)
+{
+ globals.maxMemory = max;
+ return max;
+}
+
+#else
+
+/* Just in case anyone has left some debugging code in... */
+void (Memento_breakpoint)(void)
+{
+}
+
+int (Memento_checkBlock)(void *b)
+{
+ return 0;
+}
+
+int (Memento_checkAllMemory)(void)
+{
+ return 0;
+}
+
+int (Memento_check)(void)
+{
+ return 0;
+}
+
+int (Memento_setParanoia)(int i)
+{
+ return 0;
+}
+
+int (Memento_paranoidAt)(int i)
+{
+ return 0;
+}
+
+int (Memento_breakAt)(int i)
+{
+ return 0;
+}
+
+int (Memento_getBlockNum)(void *i)
+{
+ return 0;
+}
+
+int (Memento_find)(void *a)
+{
+ return 0;
+}
+
+int (Memento_failAt)(int i)
+{
+ return 0;
+}
+
+void (Memento_breakOnFree)(void *a)
+{
+}
+
+void (Memento_breakOnRealloc)(void *a)
+{
+}
+
+#undef Memento_malloc
+#undef Memento_free
+#undef Memento_realloc
+#undef Memento_calloc
+
+void *Memento_malloc(size_t size)
+{
+ return MEMENTO_UNDERLYING_MALLOC(size);
+}
+
+void Memento_free(void *b)
+{
+ MEMENTO_UNDERLYING_FREE(b);
+}
+
+void *Memento_realloc(void *b, size_t s)
+{
+ return MEMENTO_UNDERLYING_REALLOC(b, s);
+}
+
+void *Memento_calloc(size_t n, size_t s)
+{
+ return MEMENTO_UNDERLYING_CALLOC(n, s);
+}
+
+void (Memento_listBlocks)(void)
+{
+}
+
+void (Memento_listNewBlocks)(void)
+{
+}
+
+size_t (Memento_setMax)(size_t max)
+{
+ return 0;
+}
+
+void (Memento_stats)(void)
+{
+}
+
+void *(Memento_label)(void *ptr, const char *label)
+{
+ return ptr;
+}
+
+#endif