diff options
author | Robin Watts <robin.watts@artifex.com> | 2011-08-09 15:55:30 +0700 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2011-09-14 17:41:30 +0100 |
commit | cefb81f1886685580a40b17b5e495a8a8a1ebeaf (patch) | |
tree | 737c5f342474cc20464e9ae72a15af69e6bd6f4e /fitz/memento.c | |
parent | 32ee8f594db8c6221db2c42949e0ee58cc47c176 (diff) | |
download | mupdf-cefb81f1886685580a40b17b5e495a8a8a1ebeaf.tar.xz |
Add Memento build option.
Import memento files. Update Makefile to support: make build=memento
(like debug, but with memento too).
Tiny tweak to cmapdump to avoid memento building that utility.
Diffstat (limited to 'fitz/memento.c')
-rw-r--r-- | fitz/memento.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/fitz/memento.c b/fitz/memento.c new file mode 100644 index 00000000..638bb2b2 --- /dev/null +++ b/fitz/memento.c @@ -0,0 +1,1040 @@ +/* Copyright (C) 2011 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 */ +#undef MEMENTO_LEAKONLY + +#ifndef MEMENTO_STACKTRACE_METHOD +#ifdef __GNUC__ +#define MEMENTO_STACKTRACE_METHOD 1 +#endif +#endif + +/* 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 +#include "memento.h" +#include <stdio.h> + +#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 *); +//void *memset(void *,int,size_t); +//int atexit(void (*)(void)); + +#ifdef MEMENTO + +#include "valgrind/memcheck.h" + +enum { + Memento_PreSize = 16, + Memento_PostSize = 16 +}; + +typedef struct Memento_BlkHeader Memento_BlkHeader; + +struct Memento_BlkHeader +{ + size_t rawsize; + int sequence; + int lastCheckedOK; + Memento_BlkHeader *next; + 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 squeezeAt; + int squeezing; + int squeezed; + 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; + } + 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 int Memento_listBlock(Memento_BlkHeader *b, + void *arg) +{ + int *counts = (int *)arg; + fprintf(stderr, " 0x%p:(size=%d,num=%d)\n", + MEMBLK_TOBLK(b), (int)b->rawsize, b->sequence); + counts[0]++; + counts[1]+= b->rawsize; + return 0; +} + +static void Memento_listBlocks(void) { + int counts[2]; + counts[0] = 0; + counts[1] = 0; + fprintf(stderr, "Allocated blocks:\n"); + 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 void Memento_fin(void) +{ + Memento_checkAllMemory(); + fprintf(stderr, "Total memory malloced = %d bytes\n", globals.totalAlloc); + fprintf(stderr, "Peak memory malloced = %d bytes\n", globals.peakAlloc); + fprintf(stderr, "%d mallocs, %d frees, %d reallocs\n", globals.numMallocs, + globals.numFrees, globals.numReallocs); + fprintf(stderr, "Average allocation size %d bytes\n", + globals.totalAlloc/globals.numMallocs); + if (globals.used.head != NULL) { + Memento_listBlocks(); + Memento_breakpoint(); + } + if (globals.squeezed) { + fprintf(stderr, "Memory squeezing @ %d complete\n", globals.squeezed); + } +} + +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); + + atexit(Memento_fin); + + Memento_inited(); +} + +static void Memento_event(void) +{ + globals.sequence++; + if ((globals.sequence >= globals.paranoidAt) && (globals.paranoidAt != 0)) { + globals.paranoia = 1; + globals.countdown = 1; + } + if ((globals.sequence >= globals.failAt) && (globals.failAt != 0)) { + globals.failing = 1; + } + if ((globals.sequence >= globals.squeezeAt) && (globals.squeezeAt != 0)) { + globals.squeezing = 1; + } + + if (--globals.countdown == 0) { + Memento_checkAllMemory(); + globals.countdown = globals.paranoia; + } + + if (globals.sequence == globals.breakAt) + Memento_breakpoint(); +} + +int Memento_breakAt(int event) +{ + globals.breakAt = event; + return event; +} + +#ifdef MEMENTO_HAS_FORK +#include <unistd.h> +#include <sys/wait.h> +#include <signal.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]; + +static void Memento_signal(void) +{ + fprintf(stderr, "SEGV after Memory squeezing @ %d\n", globals.squeezed); + +#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 void squeeze(void) +{ + pid_t pid; + int i, status; + + fprintf(stderr, "Memory squeezing @ %d\n", globals.sequence); + + /* 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); + /* We must fail all new allocations from here */ + globals.failing = 1; + globals.squeezed = globals.sequence; + return; + } + + /* Wait for pid to finish */ + waitpid(pid, &status, 0); + + /* 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; + } + } +} +#else +void squeeze(void) +{ + fprintf(stderr, "Memento memory squeezing disabled as no fork!\n"); +} +#endif + +int Memento_failThisEvent(void) +{ + if (!globals.inited) + Memento_init(); + + Memento_event(); + + if ((globals.squeezing) && (!globals.squeezed)) + squeeze(); + + return globals.failing; +} + +void *Memento_malloc(size_t s) +{ + Memento_BlkHeader *memblk; + size_t smem = MEMBLK_SIZE(s); + + if (!globals.inited) + Memento_init(); + + Memento_event(); + + if ((globals.squeezing) && (!globals.squeezed)) + squeeze(); + + if (globals.failing) + return NULL; + + if (s == 0) + return NULL; + + globals.numMallocs++; + + 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; + 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 0x%p(size=%d,num=%d) not on allocated list!\n", + action, memblk, memblk->rawsize, memblk->sequence); + Memento_breakpoint(); + return 1; + } else if (data.preCorrupt || data.postCorrupt) { + fprintf(stderr, "Block 0x%p(size=%d,num=%d) found to be corrupted on %s!\n", + action, memblk->rawsize, memblk->sequence, 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; + + 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; + + if (!globals.inited) + Memento_init(); + + if (blk == NULL) + return Memento_malloc(newsize); + if (newsize == 0) { + Memento_free(blk); + return NULL; + } + if ((globals.squeezing) && (!globals.squeezed)) + squeeze(); + if (globals.failing) + return NULL; + + memblk = MEMBLK_FROMBLK(blk); + + Memento_event(); + + if (checkBlock(memblk, "realloc")) + return NULL; + + newsizemem = MEMBLK_SIZE(newsize); + Memento_removeBlock(&globals.used, memblk); + 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; + 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 0x%p(size=%d,num=%d)", + memblk, memblk->rawsize, memblk->sequence); + 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; + } + 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, " 0x%p(size=%d,num=%d) ", + MEMBLK_TOBLK(memblk), memblk->rawsize, memblk->sequence); + 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; + } + 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 0x%p(size=%d,num=%d)\n", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of ")), + MEMBLK_TOBLK(data.blk), data.blk->rawsize, data.blk->sequence); + 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 0x%p(size=%d,num=%d)\n", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of ")), + MEMBLK_TOBLK(data.blk), data.blk->rawsize, data.blk->sequence); + return data.blk->sequence; + } + return 0; +} + +int Memento_failAt(int i) +{ + globals.failAt = i; + if ((globals.sequence > globals.failAt) && + (globals.failing != 0)) + globals.failing = 1; + return i; +} + +#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; +} + +#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); +} + + +#endif |