sl@0: /* sl@0: ** 2007 August 15 sl@0: ** sl@0: ** Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: ** sl@0: ** The author disclaims copyright to this source code. In place of sl@0: ** a legal notice, here is a blessing: sl@0: ** sl@0: ** May you do good and not evil. sl@0: ** May you find forgiveness for yourself and forgive others. sl@0: ** May you share freely, never taking more than you give. sl@0: ** sl@0: ************************************************************************* sl@0: ** sl@0: ** This file contains code used to implement test interfaces to the sl@0: ** memory allocation subsystem. sl@0: ** sl@0: ** $Id: test_malloc.c,v 1.47 2008/08/05 17:53:24 drh Exp $ sl@0: */ sl@0: #include "sqliteInt.h" sl@0: #include "tcl.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /* Symbian OS */ sl@0: extern char* GetFullFilePath(char* aPath, const char* aFileName); sl@0: sl@0: /* sl@0: ** This structure is used to encapsulate the global state variables used sl@0: ** by malloc() fault simulation. sl@0: */ sl@0: static struct MemFault { sl@0: int iCountdown; /* Number of pending successes before a failure */ sl@0: int nRepeat; /* Number of times to repeat the failure */ sl@0: int nBenign; /* Number of benign failures seen since last config */ sl@0: int nFail; /* Number of failures seen since last config */ sl@0: u8 enable; /* True if enabled */ sl@0: int isInstalled; /* True if the fault simulation layer is installed */ sl@0: int isBenignMode; /* True if malloc failures are considered benign */ sl@0: sqlite3_mem_methods m; /* 'Real' malloc implementation */ sl@0: } memfault; sl@0: sl@0: /* sl@0: ** This routine exists as a place to set a breakpoint that will sl@0: ** fire on any simulated malloc() failure. sl@0: */ sl@0: static void sqlite3Fault(void){ sl@0: static int cnt = 0; sl@0: cnt++; sl@0: } sl@0: sl@0: /* sl@0: ** Check to see if a fault should be simulated. Return true to simulate sl@0: ** the fault. Return false if the fault should not be simulated. sl@0: */ sl@0: static int faultsimStep(){ sl@0: if( likely(!memfault.enable) ){ sl@0: return 0; sl@0: } sl@0: if( memfault.iCountdown>0 ){ sl@0: memfault.iCountdown--; sl@0: return 0; sl@0: } sl@0: sqlite3Fault(); sl@0: memfault.nFail++; sl@0: if( memfault.isBenignMode>0 ){ sl@0: memfault.nBenign++; sl@0: } sl@0: memfault.nRepeat--; sl@0: if( memfault.nRepeat<=0 ){ sl@0: memfault.enable = 0; sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation sl@0: ** logic. sl@0: */ sl@0: static void *faultsimMalloc(int n){ sl@0: void *p = 0; sl@0: if( !faultsimStep() ){ sl@0: p = memfault.m.xMalloc(n); sl@0: } sl@0: return p; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation sl@0: ** logic. sl@0: */ sl@0: static void *faultsimRealloc(void *pOld, int n){ sl@0: void *p = 0; sl@0: if( !faultsimStep() ){ sl@0: p = memfault.m.xRealloc(pOld, n); sl@0: } sl@0: return p; sl@0: } sl@0: sl@0: /* sl@0: ** The following method calls are passed directly through to the underlying sl@0: ** malloc system: sl@0: ** sl@0: ** xFree sl@0: ** xSize sl@0: ** xRoundup sl@0: ** xInit sl@0: ** xShutdown sl@0: */ sl@0: static void faultsimFree(void *p){ sl@0: memfault.m.xFree(p); sl@0: } sl@0: static int faultsimSize(void *p){ sl@0: return memfault.m.xSize(p); sl@0: } sl@0: static int faultsimRoundup(int n){ sl@0: return memfault.m.xRoundup(n); sl@0: } sl@0: static int faultsimInit(void *p){ sl@0: return memfault.m.xInit(memfault.m.pAppData); sl@0: } sl@0: static void faultsimShutdown(void *p){ sl@0: memfault.m.xShutdown(memfault.m.pAppData); sl@0: } sl@0: sl@0: /* sl@0: ** This routine configures the malloc failure simulation. After sl@0: ** calling this routine, the next nDelay mallocs will succeed, followed sl@0: ** by a block of nRepeat failures, after which malloc() calls will begin sl@0: ** to succeed again. sl@0: */ sl@0: static void faultsimConfig(int nDelay, int nRepeat){ sl@0: memfault.iCountdown = nDelay; sl@0: memfault.nRepeat = nRepeat; sl@0: memfault.nBenign = 0; sl@0: memfault.nFail = 0; sl@0: memfault.enable = nDelay>=0; sl@0: } sl@0: sl@0: /* sl@0: ** Return the number of faults (both hard and benign faults) that have sl@0: ** occurred since the injector was last configured. sl@0: */ sl@0: static int faultsimFailures(void){ sl@0: return memfault.nFail; sl@0: } sl@0: sl@0: /* sl@0: ** Return the number of benign faults that have occurred since the sl@0: ** injector was last configured. sl@0: */ sl@0: static int faultsimBenignFailures(void){ sl@0: return memfault.nBenign; sl@0: } sl@0: sl@0: /* sl@0: ** Return the number of successes that will occur before the next failure. sl@0: ** If no failures are scheduled, return -1. sl@0: */ sl@0: static int faultsimPending(void){ sl@0: if( memfault.enable ){ sl@0: return memfault.iCountdown; sl@0: }else{ sl@0: return -1; sl@0: } sl@0: } sl@0: sl@0: sl@0: static void faultsimBeginBenign(void){ sl@0: memfault.isBenignMode++; sl@0: } sl@0: static void faultsimEndBenign(void){ sl@0: memfault.isBenignMode--; sl@0: } sl@0: sl@0: /* sl@0: ** Add or remove the fault-simulation layer using sqlite3_config(). If sl@0: ** the argument is non-zero, the sl@0: */ sl@0: static int faultsimInstall(int install){ sl@0: static struct sqlite3_mem_methods m = { sl@0: faultsimMalloc, /* xMalloc */ sl@0: faultsimFree, /* xFree */ sl@0: faultsimRealloc, /* xRealloc */ sl@0: faultsimSize, /* xSize */ sl@0: faultsimRoundup, /* xRoundup */ sl@0: faultsimInit, /* xInit */ sl@0: faultsimShutdown, /* xShutdown */ sl@0: 0 /* pAppData */ sl@0: }; sl@0: int rc; sl@0: sl@0: install = (install ? 1 : 0); sl@0: assert(memfault.isInstalled==1 || memfault.isInstalled==0); sl@0: sl@0: if( install==memfault.isInstalled ){ sl@0: return SQLITE_ERROR; sl@0: } sl@0: sl@0: if( install ){ sl@0: rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m); sl@0: assert(memfault.m.xMalloc); sl@0: if( rc==SQLITE_OK ){ sl@0: rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m); sl@0: } sl@0: sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, sl@0: faultsimBeginBenign, faultsimEndBenign sl@0: ); sl@0: }else{ sl@0: assert(memfault.m.xMalloc); sl@0: rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m); sl@0: sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0); sl@0: } sl@0: sl@0: if( rc==SQLITE_OK ){ sl@0: memfault.isInstalled = 1; sl@0: } sl@0: return rc; sl@0: } sl@0: sl@0: #ifdef SQLITE_TEST sl@0: sl@0: /* sl@0: ** This function is implemented in test1.c. Returns a pointer to a static sl@0: ** buffer containing the symbolic SQLite error code that corresponds to sl@0: ** the least-significant 8-bits of the integer passed as an argument. sl@0: ** For example: sl@0: ** sl@0: ** sqlite3TestErrorName(1) -> "SQLITE_ERROR" sl@0: */ sl@0: const char *sqlite3TestErrorName(int); sl@0: sl@0: /* sl@0: ** Transform pointers to text and back again sl@0: */ sl@0: static void pointerToText(void *p, char *z){ sl@0: static const char zHex[] = "0123456789abcdef"; sl@0: int i, k; sl@0: unsigned int u; sl@0: sqlite3_uint64 n; sl@0: if( p==0 ){ sl@0: strcpy(z, "0"); sl@0: return; sl@0: } sl@0: if( sizeof(n)==sizeof(p) ){ sl@0: memcpy(&n, &p, sizeof(p)); sl@0: }else if( sizeof(u)==sizeof(p) ){ sl@0: memcpy(&u, &p, sizeof(u)); sl@0: n = u; sl@0: }else{ sl@0: assert( 0 ); sl@0: } sl@0: for(i=0, k=sizeof(p)*2-1; i>= 4; sl@0: } sl@0: z[sizeof(p)*2] = 0; sl@0: } sl@0: static int hexToInt(int h){ sl@0: if( h>='0' && h<='9' ){ sl@0: return h - '0'; sl@0: }else if( h>='a' && h<='f' ){ sl@0: return h - 'a' + 10; sl@0: }else{ sl@0: return -1; sl@0: } sl@0: } sl@0: static int textToPointer(const char *z, void **pp){ sl@0: sqlite3_uint64 n = 0; sl@0: int i; sl@0: unsigned int u; sl@0: for(i=0; isizeof(zBin)*2 ) n = sizeof(zBin)*2; sl@0: n = sqlite3TestHexToBin(zHex, n, zBin); sl@0: if( n==0 ){ sl@0: Tcl_AppendResult(interp, "no data", (char*)0); sl@0: return TCL_ERROR; sl@0: } sl@0: zOut = p; sl@0: for(i=0; i0 ){ sl@0: if( size>(sizeof(zHex)-1)/2 ){ sl@0: n = (sizeof(zHex)-1)/2; sl@0: }else{ sl@0: n = size; sl@0: } sl@0: memcpy(zHex, zBin, n); sl@0: zBin += n; sl@0: size -= n; sl@0: sqlite3TestBinToHex(zHex, n); sl@0: Tcl_AppendResult(interp, zHex, (char*)0); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memory_used sl@0: ** sl@0: ** Raw test interface for sqlite3_memory_used(). sl@0: */ sl@0: static int test_memory_used( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used())); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memory_highwater ?RESETFLAG? sl@0: ** sl@0: ** Raw test interface for sqlite3_memory_highwater(). sl@0: */ sl@0: static int test_memory_highwater( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int resetFlag = 0; sl@0: if( objc!=1 && objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "?RESET?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( objc==2 ){ sl@0: if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR; sl@0: } sl@0: Tcl_SetObjResult(interp, sl@0: Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag))); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_backtrace DEPTH sl@0: ** sl@0: ** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined sl@0: ** then this routine is a no-op. sl@0: */ sl@0: static int test_memdebug_backtrace( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int depth; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "DEPT"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR; sl@0: #ifdef SQLITE_MEMDEBUG sl@0: { sl@0: extern void sqlite3MemdebugBacktrace(int); sl@0: sqlite3MemdebugBacktrace(depth); sl@0: } sl@0: #endif sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_dump FILENAME sl@0: ** sl@0: ** Write a summary of unfreed memory to FILENAME. sl@0: */ sl@0: static int test_memdebug_dump( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: char fnamebuf[MAXPATHLEN + 1]; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); sl@0: return TCL_ERROR; sl@0: } sl@0: #if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \ sl@0: || defined(SQLITE_POW2_MEMORY_SIZE) sl@0: { sl@0: extern void sqlite3MemdebugDump(const char*); sl@0: if(GetFullFilePath(fnamebuf, Tcl_GetString(objv[1])) == 0) sl@0: return TCL_ERROR; sl@0: sqlite3MemdebugDump(fnamebuf); sl@0: } sl@0: #endif sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_malloc_count sl@0: ** sl@0: ** Return the total number of times malloc() has been called. sl@0: */ sl@0: static int test_memdebug_malloc_count( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int nMalloc = -1; sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: #if defined(SQLITE_MEMDEBUG) sl@0: { sl@0: extern int sqlite3MemdebugMallocCount(); sl@0: nMalloc = sqlite3MemdebugMallocCount(); sl@0: } sl@0: #endif sl@0: Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc)); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS? sl@0: ** sl@0: ** where options are: sl@0: ** sl@0: ** -repeat sl@0: ** -benigncnt sl@0: ** sl@0: ** Arrange for a simulated malloc() failure after COUNTER successes. sl@0: ** If a repeat count is specified, the fault is repeated that many sl@0: ** times. sl@0: ** sl@0: ** Each call to this routine overrides the prior counter value. sl@0: ** This routine returns the number of simulated failures that have sl@0: ** happened since the previous call to this routine. sl@0: ** sl@0: ** To disable simulated failures, use a COUNTER of -1. sl@0: */ sl@0: static int test_memdebug_fail( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int ii; sl@0: int iFail; sl@0: int nRepeat = 1; sl@0: Tcl_Obj *pBenignCnt = 0; sl@0: int nBenign; sl@0: int nFail = 0; sl@0: sl@0: if( objc<2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR; sl@0: sl@0: for(ii=2; ii1 && strncmp(zOption, "-repeat", nOption)==0 ){ sl@0: if( ii==(objc-1) ){ sl@0: zErr = "option requires an argument: "; sl@0: }else{ sl@0: if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){ sl@0: if( ii==(objc-1) ){ sl@0: zErr = "option requires an argument: "; sl@0: }else{ sl@0: pBenignCnt = objv[ii+1]; sl@0: } sl@0: }else{ sl@0: zErr = "unknown option: "; sl@0: } sl@0: sl@0: if( zErr ){ sl@0: Tcl_AppendResult(interp, zErr, zOption, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: sl@0: nBenign = faultsimBenignFailures(); sl@0: nFail = faultsimFailures(); sl@0: faultsimConfig(iFail, nRepeat); sl@0: sl@0: if( pBenignCnt ){ sl@0: Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0); sl@0: } sl@0: Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail)); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_pending sl@0: ** sl@0: ** Return the number of malloc() calls that will succeed before a sl@0: ** simulated failure occurs. A negative return value indicates that sl@0: ** no malloc() failure is scheduled. sl@0: */ sl@0: static int test_memdebug_pending( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int nPending; sl@0: if( objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: nPending = faultsimPending(); sl@0: Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending)); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Usage: sqlite3_memdebug_settitle TITLE sl@0: ** sl@0: ** Set a title string stored with each allocation. The TITLE is sl@0: ** typically the name of the test that was running when the sl@0: ** allocation occurred. The TITLE is stored with the allocation sl@0: ** and can be used to figure out which tests are leaking memory. sl@0: ** sl@0: ** Each title overwrite the previous. sl@0: */ sl@0: static int test_memdebug_settitle( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: const char *zTitle; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "TITLE"); sl@0: return TCL_ERROR; sl@0: } sl@0: zTitle = Tcl_GetString(objv[1]); sl@0: #ifdef SQLITE_MEMDEBUG sl@0: { sl@0: extern int sqlite3MemdebugSettitle(const char*); sl@0: sqlite3MemdebugSettitle(zTitle); sl@0: } sl@0: #endif sl@0: return TCL_OK; sl@0: } sl@0: sl@0: #define MALLOC_LOG_FRAMES 10 sl@0: static Tcl_HashTable aMallocLog; sl@0: static int mallocLogEnabled = 0; sl@0: sl@0: typedef struct MallocLog MallocLog; sl@0: struct MallocLog { sl@0: int nCall; sl@0: int nByte; sl@0: }; sl@0: sl@0: #ifdef SQLITE_MEMDEBUG sl@0: static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){ sl@0: if( mallocLogEnabled ){ sl@0: MallocLog *pLog; sl@0: Tcl_HashEntry *pEntry; sl@0: int isNew; sl@0: sl@0: int aKey[MALLOC_LOG_FRAMES]; sl@0: int nKey = sizeof(int)*MALLOC_LOG_FRAMES; sl@0: sl@0: memset(aKey, 0, nKey); sl@0: if( (sizeof(void*)*nFrame)nCall++; sl@0: pLog->nByte += nByte; sl@0: } sl@0: } sl@0: #endif /* SQLITE_MEMDEBUG */ sl@0: sl@0: static void test_memdebug_log_clear(){ sl@0: Tcl_HashSearch search; sl@0: Tcl_HashEntry *pEntry; sl@0: for( sl@0: pEntry=Tcl_FirstHashEntry(&aMallocLog, &search); sl@0: pEntry; sl@0: pEntry=Tcl_NextHashEntry(&search) sl@0: ){ sl@0: MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry); sl@0: Tcl_Free((char *)pLog); sl@0: } sl@0: Tcl_DeleteHashTable(&aMallocLog); sl@0: Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES); sl@0: } sl@0: sl@0: static int test_memdebug_log( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: static int isInit = 0; sl@0: int iSub; sl@0: sl@0: static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" }; sl@0: enum MB_enum { sl@0: MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC sl@0: }; sl@0: sl@0: if( !isInit ){ sl@0: #ifdef SQLITE_MEMDEBUG sl@0: extern void sqlite3MemdebugBacktraceCallback( sl@0: void (*xBacktrace)(int, int, void **)); sl@0: sqlite3MemdebugBacktraceCallback(test_memdebug_callback); sl@0: #endif sl@0: Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES); sl@0: isInit = 1; sl@0: } sl@0: sl@0: if( objc<2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); sl@0: } sl@0: if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: switch( (enum MB_enum)iSub ){ sl@0: case MB_LOG_START: sl@0: mallocLogEnabled = 1; sl@0: break; sl@0: case MB_LOG_STOP: sl@0: mallocLogEnabled = 0; sl@0: break; sl@0: case MB_LOG_DUMP: { sl@0: Tcl_HashSearch search; sl@0: Tcl_HashEntry *pEntry; sl@0: Tcl_Obj *pRet = Tcl_NewObj(); sl@0: sl@0: assert(sizeof(int)==sizeof(void*)); sl@0: sl@0: for( sl@0: pEntry=Tcl_FirstHashEntry(&aMallocLog, &search); sl@0: pEntry; sl@0: pEntry=Tcl_NextHashEntry(&search) sl@0: ){ sl@0: Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2]; sl@0: MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry); sl@0: int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry); sl@0: int ii; sl@0: sl@0: apElem[0] = Tcl_NewIntObj(pLog->nCall); sl@0: apElem[1] = Tcl_NewIntObj(pLog->nByte); sl@0: for(ii=0; ii=1 && bufid<=2 && sz*cnt<=sizeof(azBuf[0]) ){ sl@0: rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, azBuf[bufid], sz,cnt); sl@0: }else{ sl@0: Tcl_AppendResult(interp, "illegal arguments - see documentation", (char*)0); sl@0: return TCL_ERROR; sl@0: } sl@0: Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sl@0: ** sl@0: ** sqlite3_config_heap NBYTE NMINALLOC sl@0: */ sl@0: static int test_config_heap( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: static char *zBuf; /* Use this memory */ sl@0: static int szBuf; /* Bytes allocated for zBuf */ sl@0: int nByte; /* Size of buffer to pass to sqlite3_config() */ sl@0: int nMinAlloc; /* Size of minimum allocation */ sl@0: int rc; /* Return code of sqlite3_config() */ sl@0: sl@0: Tcl_Obj * CONST *aArg = &objv[1]; sl@0: int nArg = objc-1; sl@0: sl@0: if( nArg!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "NBYTE NMINALLOC"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR; sl@0: if( Tcl_GetIntFromObj(interp, aArg[1], &nMinAlloc) ) return TCL_ERROR; sl@0: sl@0: if( nByte==0 ){ sl@0: free( zBuf ); sl@0: zBuf = 0; sl@0: szBuf = 0; sl@0: rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0); sl@0: }else{ sl@0: zBuf = realloc(zBuf, nByte); sl@0: szBuf = nByte; sl@0: rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc); sl@0: } sl@0: sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** tclcmd: sqlite3_config_error [DB] sl@0: ** sl@0: ** Invoke sqlite3_config() or sqlite3_db_config() with invalid sl@0: ** opcodes and verify that they return errors. sl@0: */ sl@0: static int test_config_error( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: sqlite3 *db; sl@0: int getDbPointer(Tcl_Interp*, const char*, sqlite3**); sl@0: sl@0: if( objc!=2 && objc!=1 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "[DB]"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( objc==2 ){ sl@0: if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; sl@0: if( sqlite3_db_config(db, 99999)!=SQLITE_ERROR ){ sl@0: Tcl_AppendResult(interp, sl@0: "sqlite3_db_config(db, 99999) does not return SQLITE_ERROR", sl@0: (char*)0); sl@0: return TCL_ERROR; sl@0: } sl@0: }else{ sl@0: if( sqlite3_config(99999)!=SQLITE_ERROR ){ sl@0: Tcl_AppendResult(interp, sl@0: "sqlite3_config(99999) does not return SQLITE_ERROR", sl@0: (char*)0); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sl@0: ** sl@0: ** sqlite3_dump_memsys3 FILENAME sl@0: ** sqlite3_dump_memsys5 FILENAME sl@0: ** sl@0: ** Write a summary of unfreed memsys3 allocations to FILENAME. sl@0: */ sl@0: static int test_dump_memsys3( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: switch( (int)clientData ){ sl@0: case 3: { sl@0: #ifdef SQLITE_ENABLE_MEMSYS3 sl@0: extern void sqlite3Memsys3Dump(const char*); sl@0: sqlite3Memsys3Dump(Tcl_GetString(objv[1])); sl@0: break; sl@0: #endif sl@0: } sl@0: case 5: { sl@0: #ifdef SQLITE_ENABLE_MEMSYS5 sl@0: extern void sqlite3Memsys5Dump(const char*); sl@0: sqlite3Memsys5Dump(Tcl_GetString(objv[1])); sl@0: break; sl@0: #endif sl@0: } sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_status OPCODE RESETFLAG sl@0: ** sl@0: ** Return a list of three elements which are the sqlite3_status() return sl@0: ** code, the current value, and the high-water mark value. sl@0: */ sl@0: static int test_status( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc, iValue, mxValue; sl@0: int i, op, resetFlag; sl@0: const char *zOpName; sl@0: static const struct { sl@0: const char *zName; sl@0: int op; sl@0: } aOp[] = { sl@0: { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED }, sl@0: { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE }, sl@0: { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED }, sl@0: { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW }, sl@0: { "SQLITE_STATUS_PAGECACHE_SIZE", SQLITE_STATUS_PAGECACHE_SIZE }, sl@0: { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED }, sl@0: { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW }, sl@0: { "SQLITE_STATUS_SCRATCH_SIZE", SQLITE_STATUS_SCRATCH_SIZE }, sl@0: { "SQLITE_STATUS_PARSER_STACK", SQLITE_STATUS_PARSER_STACK }, sl@0: }; sl@0: Tcl_Obj *pResult; sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG"); sl@0: return TCL_ERROR; sl@0: } sl@0: zOpName = Tcl_GetString(objv[1]); sl@0: for(i=0; i=ArraySize(aOp) ){ sl@0: if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR; sl@0: iValue = 0; sl@0: mxValue = 0; sl@0: rc = sqlite3_status(op, &iValue, &mxValue, resetFlag); sl@0: pResult = Tcl_NewObj(); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc)); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue)); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue)); sl@0: Tcl_SetObjResult(interp, pResult); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: sqlite3_db_status DATABASE OPCODE RESETFLAG sl@0: ** sl@0: ** Return a list of three elements which are the sqlite3_db_status() return sl@0: ** code, the current value, and the high-water mark value. sl@0: */ sl@0: static int test_db_status( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc, iValue, mxValue; sl@0: int i, op, resetFlag; sl@0: const char *zOpName; sl@0: sqlite3 *db; sl@0: int getDbPointer(Tcl_Interp*, const char*, sqlite3**); sl@0: static const struct { sl@0: const char *zName; sl@0: int op; sl@0: } aOp[] = { sl@0: { "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED }, sl@0: }; sl@0: Tcl_Obj *pResult; sl@0: if( objc!=4 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; sl@0: zOpName = Tcl_GetString(objv[2]); sl@0: for(i=0; i=ArraySize(aOp) ){ sl@0: if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR; sl@0: iValue = 0; sl@0: mxValue = 0; sl@0: rc = sqlite3_db_status(db, op, &iValue, &mxValue, resetFlag); sl@0: pResult = Tcl_NewObj(); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc)); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue)); sl@0: Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue)); sl@0: Tcl_SetObjResult(interp, pResult); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** install_malloc_faultsim BOOLEAN sl@0: */ sl@0: static int test_install_malloc_faultsim( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: int rc; sl@0: int isInstall; sl@0: sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: rc = faultsimInstall(isInstall); sl@0: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Register commands with the TCL interpreter. sl@0: */ sl@0: int Sqlitetest_malloc_Init(Tcl_Interp *interp){ sl@0: static struct { sl@0: char *zName; sl@0: Tcl_ObjCmdProc *xProc; sl@0: int clientData; sl@0: } aObjCmd[] = { sl@0: { "sqlite3_malloc", test_malloc ,0 }, sl@0: { "sqlite3_realloc", test_realloc ,0 }, sl@0: { "sqlite3_free", test_free ,0 }, sl@0: { "memset", test_memset ,0 }, sl@0: { "memget", test_memget ,0 }, sl@0: { "sqlite3_memory_used", test_memory_used ,0 }, sl@0: { "sqlite3_memory_highwater", test_memory_highwater ,0 }, sl@0: { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 }, sl@0: { "sqlite3_memdebug_dump", test_memdebug_dump ,0 }, sl@0: { "sqlite3_memdebug_fail", test_memdebug_fail ,0 }, sl@0: { "sqlite3_memdebug_pending", test_memdebug_pending ,0 }, sl@0: { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 }, sl@0: { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 }, sl@0: { "sqlite3_memdebug_log", test_memdebug_log ,0 }, sl@0: { "sqlite3_config_scratch", test_config_scratch ,0 }, sl@0: { "sqlite3_config_pagecache", test_config_pagecache ,0 }, sl@0: { "sqlite3_status", test_status ,0 }, sl@0: { "sqlite3_db_status", test_db_status ,0 }, sl@0: { "install_malloc_faultsim", test_install_malloc_faultsim ,0 }, sl@0: { "sqlite3_config_heap", test_config_heap ,0 }, sl@0: { "sqlite3_config_memstatus", test_config_memstatus ,0 }, sl@0: { "sqlite3_config_chunkalloc", test_config_chunkalloc ,0 }, sl@0: { "sqlite3_config_lookaside", test_config_lookaside ,0 }, sl@0: { "sqlite3_config_error", test_config_error ,0 }, sl@0: { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, sl@0: { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, sl@0: { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 } sl@0: }; sl@0: int i; sl@0: for(i=0; i