summaryrefslogtreecommitdiff
path: root/fitz/memento.c
diff options
context:
space:
mode:
Diffstat (limited to 'fitz/memento.c')
-rw-r--r--fitz/memento.c1535
1 files changed, 0 insertions, 1535 deletions
diff --git a/fitz/memento.c b/fitz/memento.c
deleted file mode 100644
index 6a159f10..00000000
--- a/fitz/memento.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/* 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