Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -56,11 +56,11 @@ # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \ callback.o complete.o date.o delete.o \ expr.o func.o hash.o insert.o loadext.o \ - main.o malloc.o mem1.o mutex.o \ + main.o malloc.o mem1.o mem2.o mutex.o \ opcodes.o os.o os_os2.o os_unix.o os_win.o \ pager.o parse.o pragma.o prepare.o printf.o random.o \ select.o table.o tclsqlite.o tokenize.o trigger.o \ update.o util.o vacuum.o \ vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \ @@ -90,11 +90,12 @@ $(TOP)/src/insert.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ - $(TOP)/src/mem.c \ + $(TOP)/src/mem1.c \ + $(TOP)/src/mem2.c \ $(TOP)/src/mutex.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ @@ -344,10 +345,13 @@ malloc.o: $(TOP)/src/malloc.c $(HDR) $(TCCX) -c $(TOP)/src/malloc.c mem1.o: $(TOP)/src/mem1.c $(HDR) $(TCCX) -c $(TOP)/src/mem1.c + +mem2.o: $(TOP)/src/mem2.c $(HDR) + $(TCCX) -c $(TOP)/src/mem2.c mutex.o: $(TOP)/src/mutex.c $(HDR) $(TCCX) -c $(TOP)/src/mutex.c pager.o: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h Index: src/mem1.c ================================================================== --- src/mem1.c +++ src/mem1.c @@ -10,12 +10,19 @@ ** ************************************************************************* ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem1.c,v 1.1 2007/08/15 13:04:54 drh Exp $ +** $Id: mem1.c,v 1.2 2007/08/15 17:07:57 drh Exp $ +*/ + +/* +** This version of the memory allocator is the default. It is +** used when no other memory allocator is specified using compile-time +** macros. */ +#if !defined(SQLITE_MEMDEBUG) && !defined(SQLITE_OMIT_MEMORY_ALLOCATION) /* ** We will eventually construct multiple memory allocation subsystems ** suitable for use in various contexts: ** @@ -200,5 +207,7 @@ } } sqlite3_mutex_leave(memMutex); return (void*)p; } + +#endif /* !SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */ ADDED src/mem2.c Index: src/mem2.c ================================================================== --- /dev/null +++ src/mem2.c @@ -0,0 +1,366 @@ +/* +** 2007 August 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement a memory +** allocation subsystem for use by SQLite. +** +** $Id: mem2.c,v 1.1 2007/08/15 17:07:57 drh Exp $ +*/ + +/* +** This version of the memory allocator is used only if the +** SQLITE_MEMDEBUG macro is defined and SQLITE_OMIT_MEMORY_ALLOCATION +** is not defined. +*/ +#if defined(SQLITE_MEMDEBUG) && !defined(SQLITE_OMIT_MEMORY_ALLOCATION) + +/* +** We will eventually construct multiple memory allocation subsystems +** suitable for use in various contexts: +** +** * Normal multi-threaded builds +** * Normal single-threaded builds +** * Debugging builds +** +** This version is suitable for use in debugging builds. +** +** Features: +** +** * Every allocate has guards at both ends. +** * New allocations are initialized with randomness +** * Allocations are overwritten with randomness when freed +** * Optional logs of malloc activity generated +** * Summary of outstanding allocations with backtraces to the +** point of allocation. +** * The ability to simulate memory allocation failure +*/ +#include "sqliteInt.h" +#include + +/* +** The backtrace functionality is only available with GLIBC +*/ +#ifdef __GLIBC__ + extern int backtrace(void**,int); + extern void backtrace_symbols_fd(void*const*,int,int); +#else +# define backtrace(A,B) 0 +# define backtrace_symbols_fd(A,B,C) +#endif + + +/* +** Mutex to control access to the memory allocation subsystem. +*/ +static sqlite3_mutex *memMutex = 0; + +/* +** Current allocation and high-water mark. +*/ +static sqlite3_uint64 nowUsed = 0; +static sqlite3_uint64 mxUsed = 0; + +/* +** The alarm callback and its arguments. The memMutex lock will +** be held while the callback is running. Recursive calls into +** the memory subsystem are allowed, but no new callbacks will be +** issued. The alarmBusy variable is set to prevent recursive +** callbacks. +*/ +static void (*alarmCallback)(void*, sqlite3_uint64, unsigned) = 0; +static void *alarmArg = 0; +static sqlite3_uint64 alarmThreshold = (((sqlite3_uint64)1)<<63); +static int alarmBusy = 0; + + +/* +** Return the amount of memory currently checked out. +*/ +sqlite3_uint64 sqlite3_memory_used(void){ + sqlite3_uint64 n; + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + n = nowUsed; + sqlite3_mutex_leave(memMutex); + return n; +} + +/* +** Return the maximum amount of memory that has ever been +** checked out since either the beginning of this process +** or since the most recent reset. +*/ +sqlite3_uint64 sqlite3_memory_highwater(int resetFlag){ + sqlite3_uint64 n; + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + n = mxUsed; + if( resetFlag ){ + mxUsed = nowUsed; + } + sqlite3_mutex_leave(memMutex); + return n; +} + +/* +** Change the alarm callback +*/ +int sqlite3_memory_alarm( + void(*xCallback)(void *pArg, sqlite3_uint64 used, unsigned int N), + void *pArg, + sqlite3_uint64 iThreshold +){ + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + alarmCallback = xCallback; + alarmArg = pArg; + alarmThreshold = iThreshold; + sqlite3_mutex_leave(memMutex); + return SQLITE_OK; +} + +/* +** Trigger the alarm +*/ +static void sqlite3MemsysAlarm(unsigned nByte){ + if( alarmCallback==0 || alarmBusy ) return; + alarmBusy = 1; + alarmCallback(alarmArg, nowUsed, nByte); + alarmBusy = 0; +} + +/* +** Each memory allocation looks like this: +** +** ---------------------------------------------------------------- +** | backtrace pointers | MemBlockHdr | allocation | EndGuard | +** ---------------------------------------------------------------- +** +** The application code sees only a pointer to the allocation. We have +** to back up from the allocation pointer to find the MemBlockHdr. The +** MemBlockHdr tells us the size of the allocation and the number of +** backtrace pointers. There is also a guard word at the end of the +** MemBlockHdr. +*/ +struct MemBlockHdr { + struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ + unsigned int iSize; /* Size of this allocation */ + unsigned short nBacktrace; /* Number of backtraces on this alloc */ + unsigned short nBacktraceSlots; /* Available backtrace slots */ + unsigned int iForeGuard; /* Guard word for sanity */ +}; + +/* +** Guard words +*/ +#define FOREGUARD 0x80F5E153 +#define REARGUARD 0xE4676B53 + +/* +** Head and tail of a linked list of all outstanding allocations +*/ +static struct MemBlockHdr *pFirst = 0; +static struct MemBlockHdr *pLast = 0; + +/* +** The number of levels of backtrace to save in new allocations. +*/ +static int backtraceLevels = 0; + +/* +** Given an allocation, find the MemBlockHdr for that allocation. +** +** This routine checks the guards at either end of the allocation and +** if they are incorrect it asserts. +*/ +static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ + struct MemBlockHdr *p; + unsigned int *pInt; + + p = (struct MemBlockHdr*)pAllocation; + p--; + assert( p->iForeGuard==FOREGUARD ); + assert( (p->iSize & 3)==0 ); + pInt = (unsigned int*)pAllocation; + assert( pInt[p->iSize/sizeof(unsigned int)]==REARGUARD ); + return p; +} + +/* +** Allocate nByte of memory +*/ +void *sqlite3_malloc(unsigned int nByte){ + struct MemBlockHdr *pHdr; + void **pBt; + unsigned int *pInt; + void *p; + unsigned int totalSize; + + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + if( nowUsed+nByte>=alarmThreshold ){ + sqlite3MemsysAlarm(nByte); + } + nByte = (nByte+3)&~3; + totalSize = nByte + sizeof(*pHdr) + sizeof(unsigned int) + + backtraceLevels*sizeof(void*); + p = malloc(totalSize); + if( p==0 ){ + sqlite3MemsysAlarm(nByte); + p = malloc(totalSize); + } + if( p ){ + pBt = p; + pHdr = (struct MemBlockHdr*)&pBt[backtraceLevels]; + pHdr->pNext = 0; + pHdr->pPrev = pLast; + if( pLast ){ + pLast->pNext = pHdr; + }else{ + pFirst = pHdr; + } + pLast = pHdr; + pHdr->iForeGuard = FOREGUARD; + pHdr->nBacktraceSlots = backtraceLevels; + if( backtraceLevels ){ + pHdr->nBacktrace = backtrace(pBt, backtraceLevels); + }else{ + pHdr->nBacktrace = 0; + } + pHdr->iSize = nByte; + pInt = (unsigned int *)&pHdr[1]; + pInt[nByte/sizeof(unsigned int)] = REARGUARD; + memset(pInt, 0x65, nByte); + nowUsed += nByte; + if( nowUsed>mxUsed ){ + mxUsed = nowUsed; + } + p = (void*)pInt; + } + sqlite3_mutex_leave(memMutex); + return p; +} + +/* +** Free memory. +*/ +void sqlite3_free(void *pPrior){ + struct MemBlockHdr *pHdr; + void **pBt; + if( pPrior==0 ){ + return; + } + assert( memMutex!=0 ); + pHdr = sqlite3MemsysGetHeader(pPrior); + pBt = (void**)pHdr; + pBt -= pHdr->nBacktraceSlots; + sqlite3_mutex_enter(memMutex, 1); + nowUsed -= pHdr->iSize; + if( pHdr->pPrev ){ + assert( pHdr->pPrev->pNext==pHdr ); + pHdr->pPrev->pNext = pHdr->pNext; + }else{ + assert( pFirst==pHdr ); + pFirst = pHdr->pNext; + } + if( pHdr->pNext ){ + assert( pHdr->pNext->pPrev==pHdr ); + pHdr->pNext->pPrev = pHdr->pPrev; + }else{ + assert( pLast==pHdr ); + pLast = pHdr->pPrev; + } + memset(pBt, 0x2b, sizeof(void*)*pHdr->nBacktrace + sizeof(*pHdr) + + pHdr->iSize + sizeof(unsigned int)); + free(pBt); + sqlite3_mutex_leave(memMutex); +} + +/* +** Change the size of an existing memory allocation. +** +** For this debugging implementation, we *always* make a copy of the +** allocation into a new place in memory. In this way, if the +** higher level code is using pointer to the old allocation, it is +** much more likely to break and we are much more liking to find +** the error. +*/ +void *sqlite3_realloc(void *pPrior, unsigned int nByte){ + struct MemBlockHdr *pOldHdr; + void *pNew; + unsigned nOld; + if( pPrior==0 ){ + return sqlite3_malloc(nByte); + } + if( nByte==0 ){ + sqlite3_free(pPrior); + return; + } + pOldHdr = sqlite3MemsysGetHeader(pPrior); + pNew = sqlite3_malloc(nByte); + if( pNew ){ + memcpy(pNew, pPrior, nByteiSize ? nByte : pOldHdr->iSize); + if( nByte>pOldHdr->iSize ){ + memset(&((char*)pNew)[pOldHdr->iSize], 0x2b, nByte - pOldHdr->iSize); + } + sqlite3_free(pPrior); + } + return pNew; +} + +/* +** Set the number of backtrace levels kept for each allocation. +** A value of zero turns of backtracing. The number is always rounded +** up to a multiple of 2. +*/ +void sqlite3_memdebug_backtrace_depth(int depth){ + if( depth<0 ){ depth = 0; } + if( depth>20 ){ depth = 20; } + depth = (depth+1)&~1; + backtraceLevels = depth; +} + +/* +** Open the file indicated and write a log of all unfreed memory +** allocations into that log. +*/ +void sqlite3_memdebug_dump(const char *zFilename){ + FILE *out; + struct MemBlockHdr *pHdr; + void **pBt; + out = fopen(zFilename, "w"); + if( out==0 ){ + fprintf(stderr, "** Unable to output memory debug output log: %s **\n", + zFilename); + return; + } + for(pHdr=pFirst; pHdr; pHdr=pHdr->pNext){ + fprintf(out, "**** %d bytes at %p ****\n", pHdr->iSize, &pHdr[1]); + if( pHdr->nBacktrace ){ + fflush(out); + pBt = (void**)pHdr; + pBt -= pHdr->nBacktraceSlots; + backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out)); + fprintf(out, "\n"); + } + } + fclose(out); +} + +#endif /* SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */