persistentstorage/sqlite3api/TEST/SRC/test_malloc.c
changeset 0 08ec8eefde2f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sqlite3api/TEST/SRC/test_malloc.c	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1349 @@
+/*
+** 2007 August 15
+**
+** Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
+** 
+** 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.
+**
+** $Id: test_malloc.c,v 1.47 2008/08/05 17:53:24 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/param.h>
+
+/* Symbian OS */
+extern char* GetFullFilePath(char* aPath, const char* aFileName);
+
+/*
+** 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 */
+  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++;
+}
+
+/*
+** 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(){
+  if( likely(!memfault.enable) ){
+    return 0;
+  }
+  if( memfault.iCountdown>0 ){
+    memfault.iCountdown--;
+    return 0;
+  }
+  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.enable = nDelay>=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{
+    assert(memfault.m.xMalloc);
+    rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m);
+    sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0);
+  }
+
+  if( rc==SQLITE_OK ){
+    memfault.isInstalled = 1;
+  }
+  return rc;
+}
+
+#ifdef SQLITE_TEST
+
+/*
+** This function is implemented in test1.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:
+**
+**   sqlite3TestErrorName(1) -> "SQLITE_ERROR"
+*/
+const char *sqlite3TestErrorName(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 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 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 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 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 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 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 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 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 test_memdebug_dump(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  char fnamebuf[MAXPATHLEN + 1];
+  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*);
+    if(GetFullFilePath(fnamebuf, Tcl_GetString(objv[1])) == 0)
+      return TCL_ERROR;
+    sqlite3MemdebugDump(fnamebuf);
+  }
+#endif
+  return TCL_OK;
+}
+
+/*
+** Usage:    sqlite3_memdebug_malloc_count
+**
+** Return the total number of times malloc() has been called.
+*/
+static int 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 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 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;
+}
+
+
+/*
+** 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 test_memdebug_settitle(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  const char *zTitle;
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
+    return TCL_ERROR;
+  }
+  zTitle = Tcl_GetString(objv[1]);
+#ifdef SQLITE_MEMDEBUG
+  {
+    extern int sqlite3MemdebugSettitle(const char*);
+    sqlite3MemdebugSettitle(zTitle);
+  }
+#endif
+  return TCL_OK;
+}
+
+#define MALLOC_LOG_FRAMES 10 
+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_FRAMES];
+    int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
+
+    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(){
+  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_FRAMES);
+}
+
+static int 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_FRAMES);
+    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(int)==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);
+        int *aKey = (int *)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_NewIntObj(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_scratch SIZE N
+**
+** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
+** The buffer is static and is of limited size.  N might be
+** adjusted downward as needed to accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_scratch(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int sz, N, rc;
+  Tcl_Obj *pResult;
+  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);
+  if( sz<0 ){
+    buf = 0;
+    rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
+  }else{
+    buf = malloc( sz*N + 1 );
+    rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
+  }
+  pResult = Tcl_NewObj();
+  Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+  Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+  Tcl_SetObjResult(interp, pResult);
+  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 accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_pagecache(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int sz, N, rc;
+  Tcl_Obj *pResult;
+  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);
+  if( sz<0 ){
+    buf = 0;
+    rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
+  }else{
+    buf = malloc( sz*N );
+    rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
+  }
+  pResult = Tcl_NewObj();
+  Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+  Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+  Tcl_SetObjResult(interp, pResult);
+  return TCL_OK;
+}
+
+/*
+** Usage:    sqlite3_config_memstatus BOOLEAN
+**
+** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
+*/
+static int 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_chunkalloc 
+**
+*/
+static int test_config_chunkalloc(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int rc;
+  int nThreshold;
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "THRESHOLD");
+    return TCL_ERROR;
+  }
+  if( Tcl_GetIntFromObj(interp, objv[1], &nThreshold) ) return TCL_ERROR;
+  rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC, nThreshold);
+  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+  return TCL_OK;
+}
+
+/*
+** Usage:    sqlite3_config_lookaside  SIZE  COUNT
+**
+*/
+static int test_config_lookaside(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int rc;
+  int sz, cnt;
+  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;
+  rc = sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt);
+  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+  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 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];
+  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, 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 test_config_heap(
+  void * clientData, 
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  static char *zBuf; /* Use this memory */
+  static int szBuf;  /* Bytes allocated for zBuf */
+  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;
+    szBuf = 0;
+    rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
+  }else{
+    zBuf = realloc(zBuf, nByte);
+    szBuf = nByte;
+    rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc);
+  }
+
+  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+  return TCL_OK;
+}
+
+/*
+** tclcmd:     sqlite3_config_error  [DB]
+**
+** Invoke sqlite3_config() or sqlite3_db_config() with invalid
+** opcodes and verify that they return errors.
+*/
+static int test_config_error(
+  void * clientData, 
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3 *db;
+  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_dump_memsys3  FILENAME
+**   sqlite3_dump_memsys5  FILENAME
+**
+** Write a summary of unfreed memsys3 allocations to FILENAME.
+*/
+static int 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( (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 test_status(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int rc, iValue, mxValue;
+  int i, op, 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        },
+  };
+  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 test_db_status(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int rc, iValue, mxValue;
+  int i, op, resetFlag;
+  const char *zOpName;
+  sqlite3 *db;
+  int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
+  static const struct {
+    const char *zName;
+    int op;
+  } aOp[] = {
+    { "SQLITE_DBSTATUS_LOOKASIDE_USED",    SQLITE_DBSTATUS_LOOKASIDE_USED   },
+  };
+  Tcl_Obj *pResult;
+  if( objc!=4 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  zOpName = Tcl_GetString(objv[2]);
+  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 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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+  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_scratch",     test_config_scratch           ,0 },
+     { "sqlite3_config_pagecache",   test_config_pagecache         ,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_memstatus",   test_config_memstatus         ,0 },
+     { "sqlite3_config_chunkalloc",  test_config_chunkalloc        ,0 },
+     { "sqlite3_config_lookaside",   test_config_lookaside         ,0 },
+     { "sqlite3_config_error",       test_config_error             ,0 },
+     { "sqlite3_db_config_lookaside",test_db_config_lookaside      ,0 },
+     { "sqlite3_dump_memsys3",       test_dump_memsys3             ,3 },
+     { "sqlite3_dump_memsys5",       test_dump_memsys3             ,5 }
+  };
+  int i;
+  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+    ClientData c = (ClientData)aObjCmd[i].clientData;
+    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
+  }
+  return TCL_OK;
+}
+#endif