diff --git a/manifest b/manifest index 58c15ea59e..1e3ffd30e1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\sinfrastructure\sfor\sthe\snew\smemory\ssubsystem.\s(CVS\s4229) -D 2007-08-15T19:16:43 +C Enhancements\sand\ssmoke\stesting\sof\sthe\snew\smemory\sallocation\ssubsystem.\nHave\snot\syet\scut\sit\sover\sto\sthe\score,\sthough.\s(CVS\s4230) +D 2007-08-15T20:41:29 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -88,8 +88,8 @@ F src/loadext.c 6c24ee62adfe7fbfb2f2dd43ff18e5534b19010f F src/main.c f12d230c1226d3f43c1f4595af1c25ccbe3017c7 F src/malloc.c 3850ab4a2edfb190ffee353c5674ebd8c6b4ccc7 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 -F src/mem1.c ef73642a171d174cd556d0168f8edbceaf94933b -F src/mem2.c 1f3745eae124b08706a9e582b1e75bae614d0f5a +F src/mem1.c 6d4b9efe51242fcc63d410fb326824f1208b3d4e +F src/mem2.c d0ba3b23da2e95bced1818ade8a8a2dc9526111c F src/mutex.c 667dae0de95f8fb92a3ffc8c3f20c0d26115a1a6 F src/os.c e2faefbe0f5a8ca5e3b1c49ee1b5c6cfa0f0e279 F src/os.h 8eff07babf74e5bc3f895f8a6c7c294dad5ff997 @@ -133,7 +133,7 @@ F src/test_btree.c 882d59acad48bab3b1fe3daf3645059b590cfc79 F src/test_config.c 26389b032216e0fb2b544ff48a5e9101bd7b1fb4 F src/test_hexio.c 14c007252285c6dabcec4a28fcf08e9177e85178 F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8 -F src/test_malloc.c 3f47498aba913d0d555e392211b9d3355f69c21b +F src/test_malloc.c d9ba6be85f9c4a439b19f6e0a72d91c369d72c63 F src/test_md5.c d9f828765b242ff86f58cd879259c3da4eaede02 F src/test_schema.c 89c526e4b1e9a8fb540550f6ebc69242bf57d3ce F src/test_server.c 76c0baf509abe65ca6e5c7974ab0097cfdd8b833 @@ -529,7 +529,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P af9503daf3f7703fcddad754bc1dc9e179830b6e -R 637b616a5b01e055657d69b5613c0e1b +P 9e506656720fb3a3205b8cc398152272ce56f6f3 +R 1bc235d902bd5b92ca3cb78565b1c6fd U drh -Z 56bc577909e0d6bde5f25aac7f6e7b86 +Z 15d4eec20da6957d2d37ec345d76fef8 diff --git a/manifest.uuid b/manifest.uuid index 9adba8fb59..1b1739c4de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9e506656720fb3a3205b8cc398152272ce56f6f3 \ No newline at end of file +1dad2c0a1f00596b13b02ccef664bd2346a677a4 \ No newline at end of file diff --git a/src/mem1.c b/src/mem1.c index 46732ca262..64b45d2196 100644 --- a/src/mem1.c +++ b/src/mem1.c @@ -12,7 +12,7 @@ ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem1.c,v 1.2 2007/08/15 17:07:57 drh Exp $ +** $Id: mem1.c,v 1.3 2007/08/15 20:41:29 drh Exp $ */ /* @@ -37,29 +37,42 @@ */ #include "sqliteInt.h" - /* -** Mutex to control access to the memory allocation subsystem. +** All of the static variables used by this module are collected +** into a single structure named "mem". This is to keep the +** static variables organized and to reduce namespace pollution +** when this module is combined with other in the amalgamation. */ -static sqlite3_mutex *memMutex = 0; +static struct { + /* + ** The alarm callback and its arguments. The mem.mutex 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. + */ + sqlite3_uint64 alarmThreshold; + void (*alarmCallback)(void*, sqlite3_uint64, unsigned); + void *alarmArg; + int alarmBusy; + + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlite3_mutex *mutex; + + /* + ** Current allocation and high-water mark. + */ + sqlite3_uint64 nowUsed; + sqlite3_uint64 mxUsed; + + +} mem = { /* This variable holds all of the local data */ + ((sqlite3_uint64)1)<<63, /* alarmThreshold */ + /* Everything else is initialized to zero */ +}; -/* -** 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; /* @@ -67,12 +80,12 @@ static int alarmBusy = 0; */ sqlite3_uint64 sqlite3_memory_used(void){ sqlite3_uint64 n; - if( memMutex==0 ){ - memMutex = sqlite3_mutex_alloc(1); + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); } - sqlite3_mutex_enter(memMutex, 1); - n = nowUsed; - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_enter(mem.mutex, 1); + n = mem.nowUsed; + sqlite3_mutex_leave(mem.mutex); return n; } @@ -83,15 +96,15 @@ sqlite3_uint64 sqlite3_memory_used(void){ */ sqlite3_uint64 sqlite3_memory_highwater(int resetFlag){ sqlite3_uint64 n; - if( memMutex==0 ){ - memMutex = sqlite3_mutex_alloc(1); + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); } - sqlite3_mutex_enter(memMutex, 1); - n = mxUsed; + sqlite3_mutex_enter(mem.mutex, 1); + n = mem.mxUsed; if( resetFlag ){ - mxUsed = nowUsed; + mem.mxUsed = mem.nowUsed; } - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); return n; } @@ -103,14 +116,14 @@ int sqlite3_memory_alarm( void *pArg, sqlite3_uint64 iThreshold ){ - if( memMutex==0 ){ - memMutex = sqlite3_mutex_alloc(1); + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); } - sqlite3_mutex_enter(memMutex, 1); - alarmCallback = xCallback; - alarmArg = pArg; - alarmThreshold = iThreshold; - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_enter(mem.mutex, 1); + mem.alarmCallback = xCallback; + mem.alarmArg = pArg; + mem.alarmThreshold = iThreshold; + sqlite3_mutex_leave(mem.mutex); return SQLITE_OK; } @@ -118,10 +131,10 @@ int sqlite3_memory_alarm( ** Trigger the alarm */ static void sqlite3MemsysAlarm(unsigned nByte){ - if( alarmCallback==0 || alarmBusy ) return; - alarmBusy = 1; - alarmCallback(alarmArg, nowUsed, nByte); - alarmBusy = 0; + if( mem.alarmCallback==0 || mem.alarmBusy ) return; + mem.alarmBusy = 1; + mem.alarmCallback(mem.alarmArg, mem.nowUsed, nByte); + mem.alarmBusy = 0; } /* @@ -129,11 +142,11 @@ static void sqlite3MemsysAlarm(unsigned nByte){ */ void *sqlite3_malloc(unsigned int nBytes){ sqlite3_uint64 *p; - if( memMutex==0 ){ - memMutex = sqlite3_mutex_alloc(1); + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); } - sqlite3_mutex_enter(memMutex, 1); - if( nowUsed+nBytes>=alarmThreshold ){ + sqlite3_mutex_enter(mem.mutex, 1); + if( mem.nowUsed+nBytes>=mem.alarmThreshold ){ sqlite3MemsysAlarm(nBytes); } p = malloc(nBytes+8); @@ -144,12 +157,12 @@ void *sqlite3_malloc(unsigned int nBytes){ if( p ){ p[0] = nBytes; p++; - nowUsed += nBytes; - if( nowUsed>mxUsed ){ - mxUsed = nowUsed; + mem.nowUsed += nBytes; + if( mem.nowUsed>mem.mxUsed ){ + mem.mxUsed = mem.nowUsed; } } - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); return (void*)p; } @@ -162,14 +175,14 @@ void sqlite3_free(void *pPrior){ if( pPrior==0 ){ return; } - assert( memMutex!=0 ); + assert( mem.mutex!=0 ); p = pPrior; p--; nByte = (unsigned int)*p; - sqlite3_mutex_enter(memMutex, 1); - nowUsed -= nByte; + sqlite3_mutex_enter(mem.mutex, 1); + mem.nowUsed -= nByte; free(p); - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); } /* @@ -188,9 +201,9 @@ void *sqlite3_realloc(void *pPrior, unsigned int nBytes){ p = pPrior; p--; nOld = (unsigned int)p[0]; - assert( memMutex!=0 ); - sqlite3_mutex_enter(memMutex, 1); - if( nowUsed+nBytes-nOld>=alarmThreshold ){ + assert( mem.mutex!=0 ); + sqlite3_mutex_enter(mem.mutex, 1); + if( mem.nowUsed+nBytes-nOld>=mem.alarmThreshold ){ sqlite3MemsysAlarm(nBytes-nOld); } p = realloc(p, nBytes+8); @@ -201,12 +214,12 @@ void *sqlite3_realloc(void *pPrior, unsigned int nBytes){ if( p ){ p[0] = nBytes; p++; - nowUsed += nBytes-nOld; - if( nowUsed>mxUsed ){ - mxUsed = nowUsed; + mem.nowUsed += nBytes-nOld; + if( mem.nowUsed>mem.mxUsed ){ + mem.mxUsed = mem.nowUsed; } } - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); return (void*)p; } diff --git a/src/mem2.c b/src/mem2.c index d538619645..5135096b96 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -12,7 +12,7 @@ ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem2.c,v 1.2 2007/08/15 19:16:43 drh Exp $ +** $Id: mem2.c,v 1.3 2007/08/15 20:41:29 drh Exp $ */ /* @@ -56,93 +56,6 @@ # 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: ** @@ -171,15 +84,124 @@ struct MemBlockHdr { #define REARGUARD 0xE4676B53 /* -** Head and tail of a linked list of all outstanding allocations +** All of the static variables used by this module are collected +** into a single structure named "mem". This is to keep the +** static variables organized and to reduce namespace pollution +** when this module is combined with other in the amalgamation. */ -static struct MemBlockHdr *pFirst = 0; -static struct MemBlockHdr *pLast = 0; +static struct { + /* + ** The alarm callback and its arguments. The mem.mutex 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. + */ + sqlite3_uint64 alarmThreshold; + void (*alarmCallback)(void*, sqlite3_uint64, unsigned); + void *alarmArg; + int alarmBusy; + + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlite3_mutex *mutex; + + /* + ** Current allocation and high-water mark. + */ + sqlite3_uint64 nowUsed; + sqlite3_uint64 mxUsed; + + /* + ** Head and tail of a linked list of all outstanding allocations + */ + struct MemBlockHdr *pFirst; + struct MemBlockHdr *pLast; + + /* + ** The number of levels of backtrace to save in new allocations. + */ + int nBacktrace; + + /* + ** These values are used to simulate malloc failures. When + ** iFail is 1, simulate a malloc failures and reset the value + ** to iReset. + */ + int iFail; /* Decrement and fail malloc when this is 1 */ + int iReset; /* When malloc fails set iiFail to this value */ + int iFailCnt; /* Number of failures */ + + +} mem = { /* This variable holds all of the local data */ + ((sqlite3_uint64)1)<<63, /* alarmThreshold */ + /* Everything else is initialized to zero */ +}; + + /* -** The number of levels of backtrace to save in new allocations. +** Return the amount of memory currently checked out. */ -static int backtraceLevels = 0; +sqlite3_uint64 sqlite3_memory_used(void){ + sqlite3_uint64 n; + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(mem.mutex, 1); + n = mem.nowUsed; + sqlite3_mutex_leave(mem.mutex); + 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( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(mem.mutex, 1); + n = mem.mxUsed; + if( resetFlag ){ + mem.mxUsed = mem.nowUsed; + } + sqlite3_mutex_leave(mem.mutex); + 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( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(mem.mutex, 1); + mem.alarmCallback = xCallback; + mem.alarmArg = pArg; + mem.alarmThreshold = iThreshold; + sqlite3_mutex_leave(mem.mutex); + return SQLITE_OK; +} + +/* +** Trigger the alarm +*/ +static void sqlite3MemsysAlarm(unsigned nByte){ + if( mem.alarmCallback==0 || mem.alarmBusy ) return; + mem.alarmBusy = 1; + mem.alarmCallback(mem.alarmArg, mem.nowUsed, nByte); + mem.alarmBusy = 0; +} /* ** Given an allocation, find the MemBlockHdr for that allocation. @@ -201,7 +223,17 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ } /* -** Allocate nByte of memory +** This routine is called once the first time a simulated memory +** failure occurs. The sole purpose of this routine is to provide +** a convenient place to set a debugger breakpoint when debugging +** errors related to malloc() failures. +*/ +static void sqlite3MemsysFailed(void){ + mem.iFailCnt = 0; +} + +/* +** Allocate nByte bytes of memory. */ void *sqlite3_malloc(unsigned int nByte){ struct MemBlockHdr *pHdr; @@ -210,37 +242,51 @@ void *sqlite3_malloc(unsigned int nByte){ void *p; unsigned int totalSize; - if( memMutex==0 ){ - memMutex = sqlite3_mutex_alloc(1); + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(1); } - sqlite3_mutex_enter(memMutex, 1); - if( nowUsed+nByte>=alarmThreshold ){ + sqlite3_mutex_enter(mem.mutex, 1); + if( mem.nowUsed+nByte>=mem.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); + mem.nBacktrace*sizeof(void*); + if( mem.iFail>0 ){ + if( mem.iFail==1 ){ + p = 0; + mem.iFail = mem.iReset; + if( mem.iFailCnt==0 ){ + sqlite3MemsysFailed(); /* A place to set a breakpoint */ + } + mem.iFailCnt++; + }else{ + p = malloc(totalSize); + mem.iFail--; + } + }else{ p = malloc(totalSize); + if( p==0 ){ + sqlite3MemsysAlarm(nByte); + p = malloc(totalSize); + } } if( p ){ pBt = p; - pHdr = (struct MemBlockHdr*)&pBt[backtraceLevels]; + pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; pHdr->pNext = 0; - pHdr->pPrev = pLast; - if( pLast ){ - pLast->pNext = pHdr; + pHdr->pPrev = mem.pLast; + if( mem.pLast ){ + mem.pLast->pNext = pHdr; }else{ - pFirst = pHdr; + mem.pFirst = pHdr; } - pLast = pHdr; + mem.pLast = pHdr; pHdr->iForeGuard = FOREGUARD; - pHdr->nBacktraceSlots = backtraceLevels; - if( backtraceLevels ){ + pHdr->nBacktraceSlots = mem.nBacktrace; + if( mem.nBacktrace ){ void *aAddr[40]; - pHdr->nBacktrace = backtrace(aAddr, backtraceLevels+1)-1; + pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); }else{ pHdr->nBacktrace = 0; @@ -249,13 +295,13 @@ void *sqlite3_malloc(unsigned int nByte){ pInt = (unsigned int *)&pHdr[1]; pInt[nByte/sizeof(unsigned int)] = REARGUARD; memset(pInt, 0x65, nByte); - nowUsed += nByte; - if( nowUsed>mxUsed ){ - mxUsed = nowUsed; + mem.nowUsed += nByte; + if( mem.nowUsed>mem.mxUsed ){ + mem.mxUsed = mem.nowUsed; } p = (void*)pInt; } - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); return p; } @@ -268,30 +314,30 @@ void sqlite3_free(void *pPrior){ if( pPrior==0 ){ return; } - assert( memMutex!=0 ); + assert( mem.mutex!=0 ); pHdr = sqlite3MemsysGetHeader(pPrior); pBt = (void**)pHdr; pBt -= pHdr->nBacktraceSlots; - sqlite3_mutex_enter(memMutex, 1); - nowUsed -= pHdr->iSize; + sqlite3_mutex_enter(mem.mutex, 1); + mem.nowUsed -= pHdr->iSize; if( pHdr->pPrev ){ assert( pHdr->pPrev->pNext==pHdr ); pHdr->pPrev->pNext = pHdr->pNext; }else{ - assert( pFirst==pHdr ); - pFirst = pHdr->pNext; + assert( mem.pFirst==pHdr ); + mem.pFirst = pHdr->pNext; } if( pHdr->pNext ){ assert( pHdr->pNext->pPrev==pHdr ); pHdr->pNext->pPrev = pHdr->pPrev; }else{ - assert( pLast==pHdr ); - pLast = pHdr->pPrev; + assert( mem.pLast==pHdr ); + mem.pLast = pHdr->pPrev; } memset(pBt, 0x2b, sizeof(void*)*pHdr->nBacktrace + sizeof(*pHdr) + pHdr->iSize + sizeof(unsigned int)); free(pBt); - sqlite3_mutex_leave(memMutex); + sqlite3_mutex_leave(mem.mutex); } /* @@ -334,7 +380,7 @@ void sqlite3_memdebug_backtrace(int depth){ if( depth<0 ){ depth = 0; } if( depth>20 ){ depth = 20; } depth = (depth+1)&0xfe; - backtraceLevels = depth; + mem.nBacktrace = depth; } /* @@ -351,7 +397,7 @@ void sqlite3_memdebug_dump(const char *zFilename){ zFilename); return; } - for(pHdr=pFirst; pHdr; pHdr=pHdr->pNext){ + for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ fprintf(out, "**** %d bytes at %p ****\n", pHdr->iSize, &pHdr[1]); if( pHdr->nBacktrace ){ fflush(out); @@ -364,4 +410,26 @@ void sqlite3_memdebug_dump(const char *zFilename){ fclose(out); } +/* +** This routine is used to simulate malloc failures. +** +** After calling this routine, there will be iFail successful +** memory allocations and then a failure. If iRepeat is true, +** all subsequent memory allocations will fail. If iRepeat is +** false, only a single allocation will fail. +** +** Each call to this routine overrides the previous. To disable +** the simulated allocation failure mechanism, set iFail to -1. +** +** This routine returns the number of simulated failures that have +** occurred since the previous call. +*/ +int sqlite3_memdebug_fail(int iFail, int iRepeat){ + int n = mem.iFailCnt; + mem.iFail = iFail+1; + mem.iReset = iRepeat; + mem.iFailCnt = 0; + return n; +} + #endif /* SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */ diff --git a/src/test_malloc.c b/src/test_malloc.c index 0b2ed77b48..6560fc9dd6 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -13,7 +13,7 @@ ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.1 2007/08/15 19:16:43 drh Exp $ +** $Id: test_malloc.c,v 1.2 2007/08/15 20:41:29 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -227,6 +227,45 @@ static int test_memdebug_dump( } +/* +** Usage: sqlite3_memdebug_fail COUNTER REPEAT +** +** Arrange for a simulated malloc() failure after COUNTER successes. +** If REPEAT is 1 then all subsequent malloc()s fail. If REPEAT is +** 0 then only a single failure occurs. +** +** Each call to this routine overrides the prior counter value. +** This routine returns the number of simulated failures that have +** happened since the previous call to this routine. +** +** To disable simulated failures, use a COUNTER of -1. +*/ +static int test_memdebug_fail( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int iFail; + int iRepeat; + int nFail = 0; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "COUNTER REPEAT"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &iRepeat) ) return TCL_ERROR; +#ifdef SQLITE_MEMDEBUG + { + extern int sqlite3_memdebug_fail(int,int); + nFail = sqlite3_memdebug_fail(iFail, iRepeat); + } +#endif + Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail)); + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ @@ -242,6 +281,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_memory_highwater", test_memory_highwater }, { "sqlite3_memdebug_backtrace", test_memdebug_backtrace }, { "sqlite3_memdebug_dump", test_memdebug_dump }, + { "sqlite3_memdebug_fail", test_memdebug_fail }, }; int i; for(i=0; i