summaryrefslogtreecommitdiffstats
path: root/src/test_malloc.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
commit63847496f14c813a5d80efd5b7de0f1294ffe1e3 (patch)
tree01c7571c7c762ceee70638549a99834fdd7c411b /src/test_malloc.c
parentInitial commit. (diff)
downloadsqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.tar.xz
sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.zip
Adding upstream version 3.45.1.upstream/3.45.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test_malloc.c')
-rw-r--r--src/test_malloc.c1513
1 files changed, 1513 insertions, 0 deletions
diff --git a/src/test_malloc.c b/src/test_malloc.c
new file mode 100644
index 0000000..8146501
--- /dev/null
+++ b/src/test_malloc.c
@@ -0,0 +1,1513 @@
+/*
+** 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;
+}
+
+/*
+** 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){
+ 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 ){
+ sqlite3_mem_methods m = memfault.m;
+ m.xMalloc = faultsimMalloc;
+ m.xRealloc = faultsimRealloc;
+ 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