| /* | |
| ** 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 code used to implement test interfaces to the | |
| ** memory allocation subsystem. | |
| */ | |
| #include "sqliteInt.h" | |
| #if defined(INCLUDE_SQLITE_TCL_H) | |
| # include "sqlite_tcl.h" | |
| #else | |
| # include "tcl.h" | |
| #endif | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <assert.h> | |
| /* | |
| ** This structure is used to encapsulate the global state variables used | |
| ** by malloc() fault simulation. | |
| */ | |
| static struct MemFault { | |
| int iCountdown; /* Number of pending successes before a failure */ | |
| int nRepeat; /* Number of times to repeat the failure */ | |
| int nBenign; /* Number of benign failures seen since last config */ | |
| int nFail; /* Number of failures seen since last config */ | |
| int nOkBefore; /* Successful allocations prior to the first fault */ | |
| int nOkAfter; /* Successful allocations after a fault */ | |
| u8 enable; /* True if enabled */ | |
| int isInstalled; /* True if the fault simulation layer is installed */ | |
| int isBenignMode; /* True if malloc failures are considered benign */ | |
| sqlite3_mem_methods m; /* 'Real' malloc implementation */ | |
| } memfault; | |
| /* | |
| ** This routine exists as a place to set a breakpoint that will | |
| ** fire on any simulated malloc() failure. | |
| */ | |
| static void sqlite3Fault(void){ | |
| static int cnt = 0; | |
| cnt++; | |
| } | |
| /* | |
| ** This routine exists as a place to set a breakpoint that will | |
| ** fire the first time any malloc() fails on a single test case. | |
| ** The sqlite3Fault() routine above runs on every malloc() failure. | |
| ** This routine only runs on the first such failure. | |
| */ | |
| static void sqlite3FirstFault(void){ | |
| static int cnt2 = 0; | |
| cnt2++; | |
| } | |
| /* | |
| ** Check to see if a fault should be simulated. Return true to simulate | |
| ** the fault. Return false if the fault should not be simulated. | |
| */ | |
| static int faultsimStep(void){ | |
| if( likely(!memfault.enable) ){ | |
| memfault.nOkAfter++; | |
| return 0; | |
| } | |
| if( memfault.iCountdown>0 ){ | |
| memfault.iCountdown--; | |
| memfault.nOkBefore++; | |
| return 0; | |
| } | |
| if( memfault.nFail==0 ) sqlite3FirstFault(); | |
| sqlite3Fault(); | |
| memfault.nFail++; | |
| if( memfault.isBenignMode>0 ){ | |
| memfault.nBenign++; | |
| } | |
| memfault.nRepeat--; | |
| if( memfault.nRepeat<=0 ){ | |
| memfault.enable = 0; | |
| } | |
| return 1; | |
| } | |
| /* | |
| ** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation | |
| ** logic. | |
| */ | |
| static void *faultsimMalloc(int n){ | |
| void *p = 0; | |
| if( !faultsimStep() ){ | |
| p = memfault.m.xMalloc(n); | |
| } | |
| return p; | |
| } | |
| /* | |
| ** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation | |
| ** logic. | |
| */ | |
| static void *faultsimRealloc(void *pOld, int n){ | |
| void *p = 0; | |
| if( !faultsimStep() ){ | |
| p = memfault.m.xRealloc(pOld, n); | |
| } | |
| return p; | |
| } | |
| /* | |
| ** The following method calls are passed directly through to the underlying | |
| ** malloc system: | |
| ** | |
| ** xFree | |
| ** xSize | |
| ** xRoundup | |
| ** xInit | |
| ** xShutdown | |
| */ | |
| static void faultsimFree(void *p){ | |
| memfault.m.xFree(p); | |
| } | |
| static int faultsimSize(void *p){ | |
| return memfault.m.xSize(p); | |
| } | |
| static int faultsimRoundup(int n){ | |
| return memfault.m.xRoundup(n); | |
| } | |
| static int faultsimInit(void *p){ | |
| return memfault.m.xInit(memfault.m.pAppData); | |
| } | |
| static void faultsimShutdown(void *p){ | |
| memfault.m.xShutdown(memfault.m.pAppData); | |
| } | |
| /* | |
| ** This routine configures the malloc failure simulation. After | |
| ** calling this routine, the next nDelay mallocs will succeed, followed | |
| ** by a block of nRepeat failures, after which malloc() calls will begin | |
| ** to succeed again. | |
| */ | |
| static void faultsimConfig(int nDelay, int nRepeat){ | |
| memfault.iCountdown = nDelay; | |
| memfault.nRepeat = nRepeat; | |
| memfault.nBenign = 0; | |
| memfault.nFail = 0; | |
| memfault.nOkBefore = 0; | |
| memfault.nOkAfter = 0; | |
| memfault.enable = nDelay>=0; | |
| /* Sometimes, when running multi-threaded tests, the isBenignMode | |
| ** variable is not properly incremented/decremented so that it is | |
| ** 0 when not inside a benign malloc block. This doesn't affect | |
| ** the multi-threaded tests, as they do not use this system. But | |
| ** it does affect OOM tests run later in the same process. So | |
| ** zero the variable here, just to be sure. | |
| */ | |
| memfault.isBenignMode = 0; | |
| } | |
| /* | |
| ** Return the number of faults (both hard and benign faults) that have | |
| ** occurred since the injector was last configured. | |
| */ | |
| static int faultsimFailures(void){ | |
| return memfault.nFail; | |
| } | |
| /* | |
| ** Return the number of benign faults that have occurred since the | |
| ** injector was last configured. | |
| */ | |
| static int faultsimBenignFailures(void){ | |
| return memfault.nBenign; | |
| } | |
| /* | |
| ** Return the number of successes that will occur before the next failure. | |
| ** If no failures are scheduled, return -1. | |
| */ | |
| static int faultsimPending(void){ | |
| if( memfault.enable ){ | |
| return memfault.iCountdown; | |
| }else{ | |
| return -1; | |
| } | |
| } | |
| static void faultsimBeginBenign(void){ | |
| memfault.isBenignMode++; | |
| } | |
| static void faultsimEndBenign(void){ | |
| memfault.isBenignMode--; | |
| } | |
| /* | |
| ** Add or remove the fault-simulation layer using sqlite3_config(). If | |
| ** the argument is non-zero, the | |
| */ | |
| static int faultsimInstall(int install){ | |
| static struct sqlite3_mem_methods m = { | |
| faultsimMalloc, /* xMalloc */ | |
| faultsimFree, /* xFree */ | |
| faultsimRealloc, /* xRealloc */ | |
| faultsimSize, /* xSize */ | |
| faultsimRoundup, /* xRoundup */ | |
| faultsimInit, /* xInit */ | |
| faultsimShutdown, /* xShutdown */ | |
| 0 /* pAppData */ | |
| }; | |
| int rc; | |
| install = (install ? 1 : 0); | |
| assert(memfault.isInstalled==1 || memfault.isInstalled==0); | |
| if( install==memfault.isInstalled ){ | |
| return SQLITE_ERROR; | |
| } | |
| if( install ){ | |
| rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m); | |
| assert(memfault.m.xMalloc); | |
| if( rc==SQLITE_OK ){ | |
| rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m); | |
| } | |
| sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, | |
| faultsimBeginBenign, faultsimEndBenign | |
| ); | |
| }else{ | |
| sqlite3_mem_methods m2; | |
| assert(memfault.m.xMalloc); | |
| /* One should be able to reset the default memory allocator by storing | |
| ** a zeroed allocator then calling GETMALLOC. */ | |
| memset(&m2, 0, sizeof(m2)); | |
| sqlite3_config(SQLITE_CONFIG_MALLOC, &m2); | |
| sqlite3_config(SQLITE_CONFIG_GETMALLOC, &m2); | |
| assert( memcmp(&m2, &memfault.m, sizeof(m2))==0 ); | |
| rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m); | |
| sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, | |
| (void*)0, (void*)0); | |
| } | |
| if( rc==SQLITE_OK ){ | |
| memfault.isInstalled = 1; | |
| } | |
| return rc; | |
| } | |
| #ifdef SQLITE_TEST | |
| /* | |
| ** This function is implemented in main.c. Returns a pointer to a static | |
| ** buffer containing the symbolic SQLite error code that corresponds to | |
| ** the least-significant 8-bits of the integer passed as an argument. | |
| ** For example: | |
| ** | |
| ** sqlite3ErrName(1) -> "SQLITE_ERROR" | |
| */ | |
| extern const char *sqlite3ErrName(int); | |
| /* | |
| ** Transform pointers to text and back again | |
| */ | |
| static void pointerToText(void *p, char *z){ | |
| static const char zHex[] = "0123456789abcdef"; | |
| int i, k; | |
| unsigned int u; | |
| sqlite3_uint64 n; | |
| if( p==0 ){ | |
| strcpy(z, "0"); | |
| return; | |
| } | |
| if( sizeof(n)==sizeof(p) ){ | |
| memcpy(&n, &p, sizeof(p)); | |
| }else if( sizeof(u)==sizeof(p) ){ | |
| memcpy(&u, &p, sizeof(u)); | |
| n = u; | |
| }else{ | |
| assert( 0 ); | |
| } | |
| for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){ | |
| z[k] = zHex[n&0xf]; | |
| n >>= 4; | |
| } | |
| z[sizeof(p)*2] = 0; | |
| } | |
| static int hexToInt(int h){ | |
| if( h>='0' && h<='9' ){ | |
| return h - '0'; | |
| }else if( h>='a' && h<='f' ){ | |
| return h - 'a' + 10; | |
| }else{ | |
| return -1; | |
| } | |
| } | |
| static int textToPointer(const char *z, void **pp){ | |
| sqlite3_uint64 n = 0; | |
| int i; | |
| unsigned int u; | |
| for(i=0; i<sizeof(void*)*2 && z[0]; i++){ | |
| int v; | |
| v = hexToInt(*z++); | |
| if( v<0 ) return TCL_ERROR; | |
| n = n*16 + v; | |
| } | |
| if( *z!=0 ) return TCL_ERROR; | |
| if( sizeof(n)==sizeof(*pp) ){ | |
| memcpy(pp, &n, sizeof(n)); | |
| }else if( sizeof(u)==sizeof(*pp) ){ | |
| u = (unsigned int)n; | |
| memcpy(pp, &u, sizeof(u)); | |
| }else{ | |
| assert( 0 ); | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_malloc NBYTES | |
| ** | |
| ** Raw test interface for sqlite3_malloc(). | |
| */ | |
| static int SQLITE_TCLAPI test_malloc( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int nByte; | |
| void *p; | |
| char zOut[100]; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "NBYTES"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR; | |
| p = sqlite3_malloc((unsigned)nByte); | |
| pointerToText(p, zOut); | |
| Tcl_AppendResult(interp, zOut, NULL); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_realloc PRIOR NBYTES | |
| ** | |
| ** Raw test interface for sqlite3_realloc(). | |
| */ | |
| static int SQLITE_TCLAPI test_realloc( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int nByte; | |
| void *pPrior, *p; | |
| char zOut[100]; | |
| if( objc!=3 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR; | |
| if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){ | |
| Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0); | |
| return TCL_ERROR; | |
| } | |
| p = sqlite3_realloc(pPrior, (unsigned)nByte); | |
| pointerToText(p, zOut); | |
| Tcl_AppendResult(interp, zOut, NULL); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_free PRIOR | |
| ** | |
| ** Raw test interface for sqlite3_free(). | |
| */ | |
| static int SQLITE_TCLAPI test_free( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| void *pPrior; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "PRIOR"); | |
| return TCL_ERROR; | |
| } | |
| if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){ | |
| Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0); | |
| return TCL_ERROR; | |
| } | |
| sqlite3_free(pPrior); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** These routines are in test_hexio.c | |
| */ | |
| int sqlite3TestHexToBin(const char *, int, char *); | |
| int sqlite3TestBinToHex(char*,int); | |
| /* | |
| ** Usage: memset ADDRESS SIZE HEX | |
| ** | |
| ** Set a chunk of memory (obtained from malloc, probably) to a | |
| ** specified hex pattern. | |
| */ | |
| static int SQLITE_TCLAPI test_memset( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| void *p; | |
| int size, n, i; | |
| char *zHex; | |
| char *zOut; | |
| char zBin[100]; | |
| if( objc!=4 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX"); | |
| return TCL_ERROR; | |
| } | |
| if( textToPointer(Tcl_GetString(objv[1]), &p) ){ | |
| Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[2], &size) ){ | |
| return TCL_ERROR; | |
| } | |
| if( size<=0 ){ | |
| Tcl_AppendResult(interp, "size must be positive", (char*)0); | |
| return TCL_ERROR; | |
| } | |
| zHex = Tcl_GetStringFromObj(objv[3], &n); | |
| if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2; | |
| n = sqlite3TestHexToBin(zHex, n, zBin); | |
| if( n==0 ){ | |
| Tcl_AppendResult(interp, "no data", (char*)0); | |
| return TCL_ERROR; | |
| } | |
| zOut = p; | |
| for(i=0; i<size; i++){ | |
| zOut[i] = zBin[i%n]; | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: memget ADDRESS SIZE | |
| ** | |
| ** Return memory as hexadecimal text. | |
| */ | |
| static int SQLITE_TCLAPI test_memget( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| void *p; | |
| int size, n; | |
| char *zBin; | |
| char zHex[100]; | |
| if( objc!=3 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE"); | |
| return TCL_ERROR; | |
| } | |
| if( textToPointer(Tcl_GetString(objv[1]), &p) ){ | |
| Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[2], &size) ){ | |
| return TCL_ERROR; | |
| } | |
| if( size<=0 ){ | |
| Tcl_AppendResult(interp, "size must be positive", (char*)0); | |
| return TCL_ERROR; | |
| } | |
| zBin = p; | |
| while( size>0 ){ | |
| if( size>(sizeof(zHex)-1)/2 ){ | |
| n = (sizeof(zHex)-1)/2; | |
| }else{ | |
| n = size; | |
| } | |
| memcpy(zHex, zBin, n); | |
| zBin += n; | |
| size -= n; | |
| sqlite3TestBinToHex(zHex, n); | |
| Tcl_AppendResult(interp, zHex, (char*)0); | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memory_used | |
| ** | |
| ** Raw test interface for sqlite3_memory_used(). | |
| */ | |
| static int SQLITE_TCLAPI test_memory_used( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used())); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memory_highwater ?RESETFLAG? | |
| ** | |
| ** Raw test interface for sqlite3_memory_highwater(). | |
| */ | |
| static int SQLITE_TCLAPI test_memory_highwater( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int resetFlag = 0; | |
| if( objc!=1 && objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "?RESET?"); | |
| return TCL_ERROR; | |
| } | |
| if( objc==2 ){ | |
| if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR; | |
| } | |
| Tcl_SetObjResult(interp, | |
| Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag))); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memdebug_backtrace DEPTH | |
| ** | |
| ** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined | |
| ** then this routine is a no-op. | |
| */ | |
| static int SQLITE_TCLAPI test_memdebug_backtrace( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int depth; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "DEPT"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR; | |
| #ifdef SQLITE_MEMDEBUG | |
| { | |
| extern void sqlite3MemdebugBacktrace(int); | |
| sqlite3MemdebugBacktrace(depth); | |
| } | |
| #endif | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memdebug_dump FILENAME | |
| ** | |
| ** Write a summary of unfreed memory to FILENAME. | |
| */ | |
| static int SQLITE_TCLAPI test_memdebug_dump( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); | |
| return TCL_ERROR; | |
| } | |
| #if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \ | |
| || defined(SQLITE_POW2_MEMORY_SIZE) | |
| { | |
| extern void sqlite3MemdebugDump(const char*); | |
| sqlite3MemdebugDump(Tcl_GetString(objv[1])); | |
| } | |
| #endif | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memdebug_malloc_count | |
| ** | |
| ** Return the total number of times malloc() has been called. | |
| */ | |
| static int SQLITE_TCLAPI test_memdebug_malloc_count( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int nMalloc = -1; | |
| if( objc!=1 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, ""); | |
| return TCL_ERROR; | |
| } | |
| #if defined(SQLITE_MEMDEBUG) | |
| { | |
| extern int sqlite3MemdebugMallocCount(); | |
| nMalloc = sqlite3MemdebugMallocCount(); | |
| } | |
| #endif | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS? | |
| ** | |
| ** where options are: | |
| ** | |
| ** -repeat <count> | |
| ** -benigncnt <varname> | |
| ** | |
| ** Arrange for a simulated malloc() failure after COUNTER successes. | |
| ** If a repeat count is specified, the fault is repeated that many | |
| ** times. | |
| ** | |
| ** 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 SQLITE_TCLAPI test_memdebug_fail( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int ii; | |
| int iFail; | |
| int nRepeat = 1; | |
| Tcl_Obj *pBenignCnt = 0; | |
| int nBenign; | |
| int nFail = 0; | |
| if( objc<2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR; | |
| for(ii=2; ii<objc; ii+=2){ | |
| int nOption; | |
| char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption); | |
| char *zErr = 0; | |
| if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){ | |
| if( ii==(objc-1) ){ | |
| zErr = "option requires an argument: "; | |
| }else{ | |
| if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){ | |
| return TCL_ERROR; | |
| } | |
| } | |
| }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){ | |
| if( ii==(objc-1) ){ | |
| zErr = "option requires an argument: "; | |
| }else{ | |
| pBenignCnt = objv[ii+1]; | |
| } | |
| }else{ | |
| zErr = "unknown option: "; | |
| } | |
| if( zErr ){ | |
| Tcl_AppendResult(interp, zErr, zOption, 0); | |
| return TCL_ERROR; | |
| } | |
| } | |
| nBenign = faultsimBenignFailures(); | |
| nFail = faultsimFailures(); | |
| faultsimConfig(iFail, nRepeat); | |
| if( pBenignCnt ){ | |
| Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0); | |
| } | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_memdebug_pending | |
| ** | |
| ** Return the number of malloc() calls that will succeed before a | |
| ** simulated failure occurs. A negative return value indicates that | |
| ** no malloc() failure is scheduled. | |
| */ | |
| static int SQLITE_TCLAPI test_memdebug_pending( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int nPending; | |
| if( objc!=1 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, ""); | |
| return TCL_ERROR; | |
| } | |
| nPending = faultsimPending(); | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** The following global variable keeps track of the number of tests | |
| ** that have run. This variable is only useful when running in the | |
| ** debugger. | |
| */ | |
| static int sqlite3_memdebug_title_count = 0; | |
| /* | |
| ** Usage: sqlite3_memdebug_settitle TITLE | |
| ** | |
| ** Set a title string stored with each allocation. The TITLE is | |
| ** typically the name of the test that was running when the | |
| ** allocation occurred. The TITLE is stored with the allocation | |
| ** and can be used to figure out which tests are leaking memory. | |
| ** | |
| ** Each title overwrite the previous. | |
| */ | |
| static int SQLITE_TCLAPI test_memdebug_settitle( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| sqlite3_memdebug_title_count++; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "TITLE"); | |
| return TCL_ERROR; | |
| } | |
| #ifdef SQLITE_MEMDEBUG | |
| { | |
| const char *zTitle; | |
| extern int sqlite3MemdebugSettitle(const char*); | |
| zTitle = Tcl_GetString(objv[1]); | |
| sqlite3MemdebugSettitle(zTitle); | |
| } | |
| #endif | |
| return TCL_OK; | |
| } | |
| #define MALLOC_LOG_FRAMES 10 | |
| #define MALLOC_LOG_KEYINTS ( \ | |
| 10 * ((sizeof(int)>=sizeof(void*)) ? 1 : sizeof(void*)/sizeof(int)) \ | |
| ) | |
| static Tcl_HashTable aMallocLog; | |
| static int mallocLogEnabled = 0; | |
| typedef struct MallocLog MallocLog; | |
| struct MallocLog { | |
| int nCall; | |
| int nByte; | |
| }; | |
| #ifdef SQLITE_MEMDEBUG | |
| static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){ | |
| if( mallocLogEnabled ){ | |
| MallocLog *pLog; | |
| Tcl_HashEntry *pEntry; | |
| int isNew; | |
| int aKey[MALLOC_LOG_KEYINTS]; | |
| unsigned int nKey = sizeof(int)*MALLOC_LOG_KEYINTS; | |
| memset(aKey, 0, nKey); | |
| if( (sizeof(void*)*nFrame)<nKey ){ | |
| nKey = nFrame*sizeof(void*); | |
| } | |
| memcpy(aKey, aFrame, nKey); | |
| pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew); | |
| if( isNew ){ | |
| pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog)); | |
| memset(pLog, 0, sizeof(MallocLog)); | |
| Tcl_SetHashValue(pEntry, (ClientData)pLog); | |
| }else{ | |
| pLog = (MallocLog *)Tcl_GetHashValue(pEntry); | |
| } | |
| pLog->nCall++; | |
| pLog->nByte += nByte; | |
| } | |
| } | |
| #endif /* SQLITE_MEMDEBUG */ | |
| static void test_memdebug_log_clear(void){ | |
| Tcl_HashSearch search; | |
| Tcl_HashEntry *pEntry; | |
| for( | |
| pEntry=Tcl_FirstHashEntry(&aMallocLog, &search); | |
| pEntry; | |
| pEntry=Tcl_NextHashEntry(&search) | |
| ){ | |
| MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry); | |
| Tcl_Free((char *)pLog); | |
| } | |
| Tcl_DeleteHashTable(&aMallocLog); | |
| Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_KEYINTS); | |
| } | |
| static int SQLITE_TCLAPI test_memdebug_log( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| static int isInit = 0; | |
| int iSub; | |
| static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" }; | |
| enum MB_enum { | |
| MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC | |
| }; | |
| if( !isInit ){ | |
| #ifdef SQLITE_MEMDEBUG | |
| extern void sqlite3MemdebugBacktraceCallback( | |
| void (*xBacktrace)(int, int, void **)); | |
| sqlite3MemdebugBacktraceCallback(test_memdebug_callback); | |
| #endif | |
| Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_KEYINTS); | |
| isInit = 1; | |
| } | |
| if( objc<2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); | |
| } | |
| if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){ | |
| return TCL_ERROR; | |
| } | |
| switch( (enum MB_enum)iSub ){ | |
| case MB_LOG_START: | |
| mallocLogEnabled = 1; | |
| break; | |
| case MB_LOG_STOP: | |
| mallocLogEnabled = 0; | |
| break; | |
| case MB_LOG_DUMP: { | |
| Tcl_HashSearch search; | |
| Tcl_HashEntry *pEntry; | |
| Tcl_Obj *pRet = Tcl_NewObj(); | |
| assert(sizeof(Tcl_WideInt)>=sizeof(void*)); | |
| for( | |
| pEntry=Tcl_FirstHashEntry(&aMallocLog, &search); | |
| pEntry; | |
| pEntry=Tcl_NextHashEntry(&search) | |
| ){ | |
| Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2]; | |
| MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry); | |
| Tcl_WideInt *aKey = (Tcl_WideInt *)Tcl_GetHashKey(&aMallocLog, pEntry); | |
| int ii; | |
| apElem[0] = Tcl_NewIntObj(pLog->nCall); | |
| apElem[1] = Tcl_NewIntObj(pLog->nByte); | |
| for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){ | |
| apElem[ii+2] = Tcl_NewWideIntObj(aKey[ii]); | |
| } | |
| Tcl_ListObjAppendElement(interp, pRet, | |
| Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem) | |
| ); | |
| } | |
| Tcl_SetObjResult(interp, pRet); | |
| break; | |
| } | |
| case MB_LOG_CLEAR: { | |
| test_memdebug_log_clear(); | |
| break; | |
| } | |
| case MB_LOG_SYNC: { | |
| #ifdef SQLITE_MEMDEBUG | |
| extern void sqlite3MemdebugSync(); | |
| test_memdebug_log_clear(); | |
| mallocLogEnabled = 1; | |
| sqlite3MemdebugSync(); | |
| #endif | |
| break; | |
| } | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_pagecache SIZE N | |
| ** | |
| ** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE. | |
| ** The buffer is static and is of limited size. N might be | |
| ** adjusted downward as needed to accommodate the requested size. | |
| ** The revised value of N is returned. | |
| ** | |
| ** A negative SIZE causes the buffer pointer to be NULL. | |
| */ | |
| static int SQLITE_TCLAPI test_config_pagecache( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int sz, N; | |
| Tcl_Obj *pRes; | |
| static char *buf = 0; | |
| if( objc!=3 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "SIZE N"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR; | |
| free(buf); | |
| buf = 0; | |
| /* Set the return value */ | |
| pRes = Tcl_NewObj(); | |
| Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(sqlite3GlobalConfig.szPage)); | |
| Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(sqlite3GlobalConfig.nPage)); | |
| Tcl_SetObjResult(interp, pRes); | |
| if( sz<0 ){ | |
| sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, 0); | |
| }else{ | |
| buf = malloc( sz*N ); | |
| sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N); | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_alt_pcache INSTALL_FLAG DISCARD_CHANCE PRNG_SEED | |
| ** | |
| ** Set up the alternative test page cache. Install if INSTALL_FLAG is | |
| ** true and uninstall (reverting to the default page cache) if INSTALL_FLAG | |
| ** is false. DISCARD_CHANGE is an integer between 0 and 100 inclusive | |
| ** which determines the chance of discarding a page when unpinned. 100 | |
| ** is certainty. 0 is never. PRNG_SEED is the pseudo-random number generator | |
| ** seed. | |
| */ | |
| static int SQLITE_TCLAPI test_alt_pcache( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int installFlag; | |
| int discardChance = 0; | |
| int prngSeed = 0; | |
| int highStress = 0; | |
| extern void installTestPCache(int,unsigned,unsigned,unsigned); | |
| if( objc<2 || objc>5 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, | |
| "INSTALLFLAG DISCARDCHANCE PRNGSEEED HIGHSTRESS"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &installFlag) ) return TCL_ERROR; | |
| if( objc>=3 && Tcl_GetIntFromObj(interp, objv[2], &discardChance) ){ | |
| return TCL_ERROR; | |
| } | |
| if( objc>=4 && Tcl_GetIntFromObj(interp, objv[3], &prngSeed) ){ | |
| return TCL_ERROR; | |
| } | |
| if( objc>=5 && Tcl_GetIntFromObj(interp, objv[4], &highStress) ){ | |
| return TCL_ERROR; | |
| } | |
| if( discardChance<0 || discardChance>100 ){ | |
| Tcl_AppendResult(interp, "discard-chance should be between 0 and 100", | |
| (char*)0); | |
| return TCL_ERROR; | |
| } | |
| installTestPCache(installFlag, (unsigned)discardChance, (unsigned)prngSeed, | |
| (unsigned)highStress); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_memstatus BOOLEAN | |
| ** | |
| ** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS. | |
| */ | |
| static int SQLITE_TCLAPI test_config_memstatus( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int enable, rc; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR; | |
| rc = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, enable); | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_lookaside SIZE COUNT | |
| ** | |
| */ | |
| static int SQLITE_TCLAPI test_config_lookaside( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int sz, cnt; | |
| Tcl_Obj *pRet; | |
| if( objc!=3 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "SIZE COUNT"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, objv[2], &cnt) ) return TCL_ERROR; | |
| pRet = Tcl_NewObj(); | |
| Tcl_ListObjAppendElement( | |
| interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.szLookaside) | |
| ); | |
| Tcl_ListObjAppendElement( | |
| interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.nLookaside) | |
| ); | |
| sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt); | |
| Tcl_SetObjResult(interp, pRet); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_db_config_lookaside CONNECTION BUFID SIZE COUNT | |
| ** | |
| ** There are two static buffers with BUFID 1 and 2. Each static buffer | |
| ** is 10KB in size. A BUFID of 0 indicates that the buffer should be NULL | |
| ** which will cause sqlite3_db_config() to allocate space on its own. | |
| */ | |
| static int SQLITE_TCLAPI test_db_config_lookaside( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc; | |
| int sz, cnt; | |
| sqlite3 *db; | |
| int bufid; | |
| static char azBuf[2][10000]; | |
| extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); | |
| if( objc!=5 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BUFID SIZE COUNT"); | |
| return TCL_ERROR; | |
| } | |
| if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, objv[2], &bufid) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, objv[3], &sz) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, objv[4], &cnt) ) return TCL_ERROR; | |
| if( bufid==0 ){ | |
| rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, (void*)0, sz, cnt); | |
| }else if( bufid>=1 && bufid<=2 && sz*cnt<=sizeof(azBuf[0]) ){ | |
| rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, azBuf[bufid], sz,cnt); | |
| }else{ | |
| Tcl_AppendResult(interp, "illegal arguments - see documentation", (char*)0); | |
| return TCL_ERROR; | |
| } | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_heap NBYTE NMINALLOC | |
| */ | |
| static int SQLITE_TCLAPI test_config_heap( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| static char *zBuf; /* Use this memory */ | |
| int nByte; /* Size of buffer to pass to sqlite3_config() */ | |
| int nMinAlloc; /* Size of minimum allocation */ | |
| int rc; /* Return code of sqlite3_config() */ | |
| Tcl_Obj * CONST *aArg = &objv[1]; | |
| int nArg = objc-1; | |
| if( nArg!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "NBYTE NMINALLOC"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR; | |
| if( Tcl_GetIntFromObj(interp, aArg[1], &nMinAlloc) ) return TCL_ERROR; | |
| if( nByte==0 ){ | |
| free( zBuf ); | |
| zBuf = 0; | |
| rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0); | |
| }else{ | |
| zBuf = realloc(zBuf, nByte); | |
| rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc); | |
| } | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_heap_size NBYTE | |
| */ | |
| static int SQLITE_TCLAPI test_config_heap_size( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int nByte; /* Size to pass to sqlite3_config() */ | |
| int rc; /* Return code of sqlite3_config() */ | |
| Tcl_Obj * CONST *aArg = &objv[1]; | |
| int nArg = objc-1; | |
| if( nArg!=1 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "NBYTE"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR; | |
| rc = sqlite3_config(SQLITE_CONFIG_WIN32_HEAPSIZE, nByte); | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_error [DB] | |
| ** | |
| ** Invoke sqlite3_config() or sqlite3_db_config() with invalid | |
| ** opcodes and verify that they return errors. | |
| */ | |
| static int SQLITE_TCLAPI test_config_error( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| sqlite3 *db; | |
| extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); | |
| if( objc!=2 && objc!=1 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "[DB]"); | |
| return TCL_ERROR; | |
| } | |
| if( objc==2 ){ | |
| if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | |
| if( sqlite3_db_config(db, 99999)!=SQLITE_ERROR ){ | |
| Tcl_AppendResult(interp, | |
| "sqlite3_db_config(db, 99999) does not return SQLITE_ERROR", | |
| (char*)0); | |
| return TCL_ERROR; | |
| } | |
| }else{ | |
| if( sqlite3_config(99999)!=SQLITE_ERROR ){ | |
| Tcl_AppendResult(interp, | |
| "sqlite3_config(99999) does not return SQLITE_ERROR", | |
| (char*)0); | |
| return TCL_ERROR; | |
| } | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_uri BOOLEAN | |
| ** | |
| ** Enables or disables interpretation of URI parameters by default using | |
| ** SQLITE_CONFIG_URI. | |
| */ | |
| static int SQLITE_TCLAPI test_config_uri( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc; | |
| int bOpenUri; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetBooleanFromObj(interp, objv[1], &bOpenUri) ){ | |
| return TCL_ERROR; | |
| } | |
| rc = sqlite3_config(SQLITE_CONFIG_URI, bOpenUri); | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_cis BOOLEAN | |
| ** | |
| ** Enables or disables the use of the covering-index scan optimization. | |
| ** SQLITE_CONFIG_COVERING_INDEX_SCAN. | |
| */ | |
| static int SQLITE_TCLAPI test_config_cis( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc; | |
| int bUseCis; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetBooleanFromObj(interp, objv[1], &bUseCis) ){ | |
| return TCL_ERROR; | |
| } | |
| rc = sqlite3_config(SQLITE_CONFIG_COVERING_INDEX_SCAN, bUseCis); | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_config_pmasz INTEGER | |
| ** | |
| ** Set the minimum PMA size. | |
| */ | |
| static int SQLITE_TCLAPI test_config_pmasz( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc; | |
| int iPmaSz; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); | |
| return TCL_ERROR; | |
| } | |
| if( Tcl_GetIntFromObj(interp, objv[1], &iPmaSz) ){ | |
| return TCL_ERROR; | |
| } | |
| rc = sqlite3_config(SQLITE_CONFIG_PMASZ, iPmaSz); | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_dump_memsys3 FILENAME | |
| ** sqlite3_dump_memsys5 FILENAME | |
| ** | |
| ** Write a summary of unfreed memsys3 allocations to FILENAME. | |
| */ | |
| static int SQLITE_TCLAPI test_dump_memsys3( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); | |
| return TCL_ERROR; | |
| } | |
| switch( SQLITE_PTR_TO_INT(clientData) ){ | |
| case 3: { | |
| #ifdef SQLITE_ENABLE_MEMSYS3 | |
| extern void sqlite3Memsys3Dump(const char*); | |
| sqlite3Memsys3Dump(Tcl_GetString(objv[1])); | |
| break; | |
| #endif | |
| } | |
| case 5: { | |
| #ifdef SQLITE_ENABLE_MEMSYS5 | |
| extern void sqlite3Memsys5Dump(const char*); | |
| sqlite3Memsys5Dump(Tcl_GetString(objv[1])); | |
| break; | |
| #endif | |
| } | |
| } | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_status OPCODE RESETFLAG | |
| ** | |
| ** Return a list of three elements which are the sqlite3_status() return | |
| ** code, the current value, and the high-water mark value. | |
| */ | |
| static int SQLITE_TCLAPI test_status( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc, iValue, mxValue; | |
| int i, op = 0, resetFlag; | |
| const char *zOpName; | |
| static const struct { | |
| const char *zName; | |
| int op; | |
| } aOp[] = { | |
| { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED }, | |
| { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE }, | |
| { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED }, | |
| { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW }, | |
| { "SQLITE_STATUS_PAGECACHE_SIZE", SQLITE_STATUS_PAGECACHE_SIZE }, | |
| { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED }, | |
| { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW }, | |
| { "SQLITE_STATUS_SCRATCH_SIZE", SQLITE_STATUS_SCRATCH_SIZE }, | |
| { "SQLITE_STATUS_PARSER_STACK", SQLITE_STATUS_PARSER_STACK }, | |
| { "SQLITE_STATUS_MALLOC_COUNT", SQLITE_STATUS_MALLOC_COUNT }, | |
| }; | |
| Tcl_Obj *pResult; | |
| if( objc!=3 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG"); | |
| return TCL_ERROR; | |
| } | |
| zOpName = Tcl_GetString(objv[1]); | |
| for(i=0; i<ArraySize(aOp); i++){ | |
| if( strcmp(aOp[i].zName, zOpName)==0 ){ | |
| op = aOp[i].op; | |
| break; | |
| } | |
| } | |
| if( i>=ArraySize(aOp) ){ | |
| if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR; | |
| } | |
| if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR; | |
| iValue = 0; | |
| mxValue = 0; | |
| rc = sqlite3_status(op, &iValue, &mxValue, resetFlag); | |
| pResult = Tcl_NewObj(); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc)); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue)); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue)); | |
| Tcl_SetObjResult(interp, pResult); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Usage: sqlite3_db_status DATABASE OPCODE RESETFLAG | |
| ** | |
| ** Return a list of three elements which are the sqlite3_db_status() return | |
| ** code, the current value, and the high-water mark value. | |
| */ | |
| static int SQLITE_TCLAPI test_db_status( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc, iValue, mxValue; | |
| int i, op = 0, resetFlag; | |
| const char *zOpName; | |
| sqlite3 *db; | |
| extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); | |
| static const struct { | |
| const char *zName; | |
| int op; | |
| } aOp[] = { | |
| { "LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED }, | |
| { "CACHE_USED", SQLITE_DBSTATUS_CACHE_USED }, | |
| { "SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED }, | |
| { "STMT_USED", SQLITE_DBSTATUS_STMT_USED }, | |
| { "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT }, | |
| { "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE }, | |
| { "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }, | |
| { "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT }, | |
| { "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }, | |
| { "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }, | |
| { "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS }, | |
| { "CACHE_USED_SHARED", SQLITE_DBSTATUS_CACHE_USED_SHARED }, | |
| { "CACHE_SPILL", SQLITE_DBSTATUS_CACHE_SPILL }, | |
| }; | |
| Tcl_Obj *pResult; | |
| if( objc!=4 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "DB PARAMETER RESETFLAG"); | |
| return TCL_ERROR; | |
| } | |
| if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | |
| zOpName = Tcl_GetString(objv[2]); | |
| if( memcmp(zOpName, "SQLITE_", 7)==0 ) zOpName += 7; | |
| if( memcmp(zOpName, "DBSTATUS_", 9)==0 ) zOpName += 9; | |
| for(i=0; i<ArraySize(aOp); i++){ | |
| if( strcmp(aOp[i].zName, zOpName)==0 ){ | |
| op = aOp[i].op; | |
| break; | |
| } | |
| } | |
| if( i>=ArraySize(aOp) ){ | |
| if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR; | |
| } | |
| if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR; | |
| iValue = 0; | |
| mxValue = 0; | |
| rc = sqlite3_db_status(db, op, &iValue, &mxValue, resetFlag); | |
| pResult = Tcl_NewObj(); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc)); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue)); | |
| Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue)); | |
| Tcl_SetObjResult(interp, pResult); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** install_malloc_faultsim BOOLEAN | |
| */ | |
| static int SQLITE_TCLAPI test_install_malloc_faultsim( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc; | |
| int isInstall; | |
| if( objc!=2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); | |
| return TCL_ERROR; | |
| } | |
| if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){ | |
| return TCL_ERROR; | |
| } | |
| rc = faultsimInstall(isInstall); | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** sqlite3_install_memsys3 | |
| */ | |
| static int SQLITE_TCLAPI test_install_memsys3( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| int rc = SQLITE_MISUSE; | |
| #ifdef SQLITE_ENABLE_MEMSYS3 | |
| const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); | |
| rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); | |
| #endif | |
| Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); | |
| return TCL_OK; | |
| } | |
| static int SQLITE_TCLAPI test_vfs_oom_test( | |
| void * clientData, | |
| Tcl_Interp *interp, | |
| int objc, | |
| Tcl_Obj *CONST objv[] | |
| ){ | |
| extern int sqlite3_memdebug_vfs_oom_test; | |
| if( objc>2 ){ | |
| Tcl_WrongNumArgs(interp, 1, objv, "?INTEGER?"); | |
| return TCL_ERROR; | |
| }else if( objc==2 ){ | |
| int iNew; | |
| if( Tcl_GetIntFromObj(interp, objv[1], &iNew) ) return TCL_ERROR; | |
| sqlite3_memdebug_vfs_oom_test = iNew; | |
| } | |
| Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_memdebug_vfs_oom_test)); | |
| return TCL_OK; | |
| } | |
| /* | |
| ** Register commands with the TCL interpreter. | |
| */ | |
| int Sqlitetest_malloc_Init(Tcl_Interp *interp){ | |
| static struct { | |
| char *zName; | |
| Tcl_ObjCmdProc *xProc; | |
| int clientData; | |
| } aObjCmd[] = { | |
| { "sqlite3_malloc", test_malloc ,0 }, | |
| { "sqlite3_realloc", test_realloc ,0 }, | |
| { "sqlite3_free", test_free ,0 }, | |
| { "memset", test_memset ,0 }, | |
| { "memget", test_memget ,0 }, | |
| { "sqlite3_memory_used", test_memory_used ,0 }, | |
| { "sqlite3_memory_highwater", test_memory_highwater ,0 }, | |
| { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 }, | |
| { "sqlite3_memdebug_dump", test_memdebug_dump ,0 }, | |
| { "sqlite3_memdebug_fail", test_memdebug_fail ,0 }, | |
| { "sqlite3_memdebug_pending", test_memdebug_pending ,0 }, | |
| { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 }, | |
| { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 }, | |
| { "sqlite3_memdebug_log", test_memdebug_log ,0 }, | |
| { "sqlite3_config_pagecache", test_config_pagecache ,0 }, | |
| { "sqlite3_config_alt_pcache", test_alt_pcache ,0 }, | |
| { "sqlite3_status", test_status ,0 }, | |
| { "sqlite3_db_status", test_db_status ,0 }, | |
| { "install_malloc_faultsim", test_install_malloc_faultsim ,0 }, | |
| { "sqlite3_config_heap", test_config_heap ,0 }, | |
| { "sqlite3_config_heap_size", test_config_heap_size ,0 }, | |
| { "sqlite3_config_memstatus", test_config_memstatus ,0 }, | |
| { "sqlite3_config_lookaside", test_config_lookaside ,0 }, | |
| { "sqlite3_config_error", test_config_error ,0 }, | |
| { "sqlite3_config_uri", test_config_uri ,0 }, | |
| { "sqlite3_config_cis", test_config_cis ,0 }, | |
| { "sqlite3_config_pmasz", test_config_pmasz ,0 }, | |
| { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, | |
| { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, | |
| { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, | |
| { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, | |
| { "sqlite3_memdebug_vfs_oom_test", test_vfs_oom_test ,0 }, | |
| }; | |
| int i; | |
| for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ | |
| ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData); | |
| Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); | |
| } | |
| return TCL_OK; | |
| } | |
| #endif |