Enhancements and smoke testing of the new memory allocation subsystem.

Have not yet cut it over to the core, though. (CVS 4230)

FossilOrigin-Name: 1dad2c0a1f00596b13b02ccef664bd2346a677a4
This commit is contained in:
drh 2007-08-15 20:41:28 +00:00
parent 2f999a6758
commit 0e6f1546b0
5 changed files with 318 additions and 197 deletions

View File

@ -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

View File

@ -1 +1 @@
9e506656720fb3a3205b8cc398152272ce56f6f3
1dad2c0a1f00596b13b02ccef664bd2346a677a4

View File

@ -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;
}

View File

@ -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 */

View File

@ -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<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){