Add some test logic to the new memory allocation subsystem.  (Lots more needed.)
The test suite is currently indicating memory leaks, though it is unclear if
this is a true code problem or just an instrumentation problem. (CVS 5240)

FossilOrigin-Name: cb1f11cd9764cf0275e88e1f6342e366e5536bfd
diff --git a/src/btree.c b/src/btree.c
index 3e3beb3..9bc560d 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.466 2008/06/18 17:09:10 danielk1977 Exp $
+** $Id: btree.c,v 1.467 2008/06/19 00:16:08 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -1401,6 +1401,24 @@
 }
 
 /*
+** Make sure pBt->pTmpSpace points to an allocation of 
+** MX_CELL_SIZE(pBt) bytes.
+*/
+static void allocateTempSpace(BtShared *pBt){
+  if( !pBt->pTmpSpace ){
+    pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+  }
+}
+
+/*
+** Free the pBt->pTmpSpace allocation
+*/
+static void freeTempSpace(BtShared *pBt){
+  sqlite3PageFree( pBt->pTmpSpace);
+  pBt->pTmpSpace = 0;
+}
+
+/*
 ** Close an open database and invalidate all cursors.
 */
 int sqlite3BtreeClose(Btree *p){
@@ -1444,8 +1462,7 @@
       pBt->xFreeSchema(pBt->pSchema);
     }
     sqlite3_free(pBt->pSchema);
-    sqlite3_free(pBt->pTmpSpace);
-    sqlite3_free(pBt);
+    freeTempSpace(pBt);
   }
 
 #ifndef SQLITE_OMIT_SHARED_CACHE
@@ -1549,8 +1566,7 @@
     assert( (pageSize & 7)==0 );
     assert( !pBt->pPage1 && !pBt->pCursor );
     pBt->pageSize = pageSize;
-    sqlite3_free(pBt->pTmpSpace);
-    pBt->pTmpSpace = 0;
+    freeTempSpace(pBt);
     rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
   }
   pBt->usableSize = pBt->pageSize - nReserve;
@@ -1698,8 +1714,7 @@
       releasePage(pPage1);
       pBt->usableSize = usableSize;
       pBt->pageSize = pageSize;
-      sqlite3_free(pBt->pTmpSpace);
-      pBt->pTmpSpace = 0;
+      freeTempSpace(pBt);
       sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
       return SQLITE_OK;
     }
@@ -5678,16 +5693,6 @@
 }
 
 /*
-** Make sure pBt->pTmpSpace points to an allocation of 
-** MX_CELL_SIZE(pBt) bytes.
-*/
-static void allocateTempSpace(BtShared *pBt){
-  if( !pBt->pTmpSpace ){
-    pBt->pTmpSpace = sqlite3Malloc(MX_CELL_SIZE(pBt));
-  }
-}
-
-/*
 ** Insert a new record into the BTree.  The key is given by (pKey,nKey)
 ** and the data is given by (pData,nData).  The cursor is used only to
 ** define what table the record should be inserted into.  The cursor
@@ -6643,8 +6648,9 @@
   */
   data = pPage->aData;
   hdr = pPage->hdrOffset;
-  hit = sqlite3MallocZero( usableSize );
+  hit = sqlite3PageMalloc( pBt->pageSize );
   if( hit ){
+    memset(hit, 0, usableSize );
     memset(hit, 1, get2byte(&data[hdr+5]));
     nCell = get2byte(&data[hdr+3]);
     cellStart = hdr + 12 - 4*pPage->leaf;
@@ -6686,7 +6692,7 @@
           cnt, data[hdr+7], iPage);
     }
   }
-  sqlite3_free(hit);
+  sqlite3PageFree(hit);
 
   releasePage(pPage);
   return depth+1;
diff --git a/src/main.c b/src/main.c
index 433aabd..4f61cd2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.450 2008/06/18 18:57:42 danielk1977 Exp $
+** $Id: main.c,v 1.451 2008/06/19 00:16:08 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -80,6 +80,7 @@
     sqlite3_mutex_enter(pMutex);
     if( sqlite3IsInit==0 ){
       sqlite3IsInit = 1;
+      sqlite3StatusReset();
       if( rc==SQLITE_OK ) rc = sqlite3MallocInit();
       if( rc==SQLITE_OK ) rc = sqlite3_os_init();
       if( rc!=SQLITE_OK ){
diff --git a/src/malloc.c b/src/malloc.c
index a278e48..3441123 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -12,7 +12,7 @@
 **
 ** Memory allocation functions used throughout sqlite.
 **
-** $Id: malloc.c,v 1.20 2008/06/18 18:12:04 drh Exp $
+** $Id: malloc.c,v 1.21 2008/06/19 00:16:08 drh Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
@@ -97,14 +97,6 @@
   /* Number of free pages for scratch and page-cache memory */
   u32 nScratchFree;
   u32 nPageFree;
-
-  /*
-  ** Performance statistics
-  */
-  sqlite3_int64 nowUsed;  /* Main memory currently in use */
-  sqlite3_int64 mxUsed;   /* Highwater mark for nowUsed */
-  int mxReq;              /* Max request size for ordinary mallocs */
-  int mxScratchReq;       /* Max request size for xTemp mallocs */
 } mem0;
 
 /*
@@ -127,6 +119,7 @@
     mem0.nScratchFree = sqlite3Config.nScratch;
   }else{
     sqlite3Config.pScratch = 0;
+    sqlite3Config.szScratch = 0;
   }
   if( sqlite3Config.pPage && sqlite3Config.szPage>=512
       && sqlite3Config.nPage>0 ){
@@ -137,6 +130,7 @@
     mem0.nPageFree = sqlite3Config.nPage;
   }else{
     sqlite3Config.pPage = 0;
+    sqlite3Config.szPage = 0;
   }
   return sqlite3Config.m.xInit(sqlite3Config.m.pAppData);
 }
@@ -153,12 +147,9 @@
 ** Return the amount of memory currently checked out.
 */
 sqlite3_int64 sqlite3_memory_used(void){
-  sqlite3_int64 n;
-  sqlite3_initialize();
-  sqlite3_mutex_enter(mem0.mutex);
-  n = mem0.nowUsed;
-  sqlite3_mutex_leave(mem0.mutex);  
-  return n;
+  int n, mx;
+  sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
+  return (sqlite3_int64)n;
 }
 
 /*
@@ -167,15 +158,9 @@
 ** or since the most recent reset.
 */
 sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
-  sqlite3_int64 n;
-  sqlite3_initialize();
-  sqlite3_mutex_enter(mem0.mutex);
-  n = mem0.mxUsed;
-  if( resetFlag ){
-    mem0.mxUsed = mem0.nowUsed;
-  }
-  sqlite3_mutex_leave(mem0.mutex);  
-  return n;
+  int n, mx;
+  sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag);
+  return (sqlite3_int64)mx;
 }
 
 /*
@@ -204,7 +189,7 @@
   if( mem0.alarmCallback==0 || mem0.alarmBusy  ) return;
   mem0.alarmBusy = 1;
   xCallback = mem0.alarmCallback;
-  nowUsed = mem0.nowUsed;
+  nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
   pArg = mem0.alarmArg;
   sqlite3_mutex_leave(mem0.mutex);
   xCallback(pArg, nowUsed, nByte);
@@ -212,6 +197,35 @@
   mem0.alarmBusy = 0;
 }
 
+/*
+** Do a memory allocation with statistics and alarms.  Assume the
+** lock is already held.
+*/
+static int mallocWithAlarm(int n, void **pp){
+  int nFull;
+  void *p;
+  assert( sqlite3_mutex_held(mem0.mutex) );
+  nFull = sqlite3Config.m.xRoundup(n);
+  sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
+  if( mem0.alarmCallback!=0 ){
+    int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+    if( nUsed+nFull >= mem0.alarmThreshold ){
+      sqlite3MallocAlarm(nFull);
+    }
+  }
+  if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
+    p = 0;
+  }else{
+    p = sqlite3Config.m.xMalloc(nFull);
+    if( p==0 ){
+      sqlite3MallocAlarm(nFull);
+      p = malloc(nFull);
+    }
+  }
+  if( p ) sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
+  *pp = p;
+  return nFull;
+}
 
 /*
 ** Allocate memory.  This routine is like sqlite3_malloc() except that it
@@ -219,31 +233,11 @@
 */
 void *sqlite3Malloc(int n){
   void *p;
-  int nFull;
   if( n<=0 ){
-    return 0;
+    p = 0;
   }else if( sqlite3Config.bMemstat ){
-    nFull = sqlite3Config.m.xRoundup(n);
     sqlite3_mutex_enter(mem0.mutex);
-    if( n>mem0.mxReq ) mem0.mxReq = n;
-    if( mem0.alarmCallback!=0 && mem0.nowUsed+nFull>=mem0.alarmThreshold ){
-      sqlite3MallocAlarm(nFull);
-    }
-    if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
-      p = 0;
-    }else{
-      p = sqlite3Config.m.xMalloc(nFull);
-      if( p==0 ){
-        sqlite3MallocAlarm(nFull);
-        p = malloc(nFull);
-      }
-    }
-    if( p ){
-      mem0.nowUsed += nFull;
-      if( mem0.nowUsed>mem0.mxUsed ){
-        mem0.mxUsed = mem0.nowUsed;
-      }
-    }
+    mallocWithAlarm(n, &p);
     sqlite3_mutex_leave(mem0.mutex);
   }else{
     p = sqlite3Config.m.xMalloc(n);
@@ -295,21 +289,43 @@
   ** single-threaded case since checking in the multi-threaded case
   ** would be much more complicated.) */
   assert( scratchAllocOut==0 );
-  scratchAllocOut = 1;
 #endif
 
-  sqlite3_mutex_enter(mem0.mutex);
-  if( n>mem0.mxScratchReq ) mem0.mxScratchReq = n;
-  if( mem0.nScratchFree==0 || sqlite3Config.szScratch>=n ){
-    p = sqlite3Config.m.xMalloc(n);
-  }else{
-    int i;
-    i = mem0.aScratchFree[--mem0.nScratchFree];
-    i *= sqlite3Config.szScratch;
-    p = (void*)&((char*)sqlite3Config.pScratch)[i];
+  if( sqlite3Config.szScratch<n ){
+    goto scratch_overflow;
+  }else{  
+    sqlite3_mutex_enter(mem0.mutex);
+    if( mem0.nScratchFree==0 ){
+      sqlite3_mutex_leave(mem0.mutex);
+      goto scratch_overflow;
+    }else{
+      int i;
+      i = mem0.aScratchFree[--mem0.nScratchFree];
+      sqlite3_mutex_leave(mem0.mutex);
+      i *= sqlite3Config.szScratch;
+      sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
+      p = (void*)&((char*)sqlite3Config.pScratch)[i];
+    }
   }
-  sqlite3_mutex_leave(mem0.mutex);
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+  scratchAllocOut = p!=0;
+#endif
+
   return p;
+
+scratch_overflow:
+  if( sqlite3Config.bMemstat ){
+    sqlite3_mutex_enter(mem0.mutex);
+    n = mallocWithAlarm(n, &p);
+    if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
+    sqlite3_mutex_leave(mem0.mutex);
+  }else{
+    p = sqlite3Config.m.xMalloc(n);
+  }
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+  scratchAllocOut = p!=0;
+#endif
+  return p;    
 }
 void sqlite3ScratchFree(void *p){
   if( p ){
@@ -324,30 +340,103 @@
 #endif
 
     if( sqlite3Config.pScratch==0
-       || p<sqlite3Config.pScratch
-       || p>=(void*)mem0.aScratchFree ){
-      sqlite3Config.m.xFree(p);
+           || p<sqlite3Config.pScratch
+           || p>=(void*)mem0.aScratchFree ){
+      if( sqlite3Config.bMemstat ){
+        int iSize = sqlite3MallocSize(p);
+        sqlite3_mutex_enter(mem0.mutex);
+        sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
+        sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
+        sqlite3Config.m.xFree(p);
+        sqlite3_mutex_leave(mem0.mutex);
+      }else{
+        sqlite3Config.m.xFree(p);
+      }
     }else{
       int i;
-      sqlite3_mutex_enter(mem0.mutex);
-      assert( mem0.nScratchFree<sqlite3Config.nScratch );
       i = p - sqlite3Config.pScratch;
       i /= sqlite3Config.szScratch;
       assert( i>=0 && i<sqlite3Config.nScratch );
+      sqlite3_mutex_enter(mem0.mutex);
+      assert( mem0.nScratchFree<sqlite3Config.nScratch );
       mem0.aScratchFree[mem0.nScratchFree++] = i;
+      sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
       sqlite3_mutex_leave(mem0.mutex);
     }
   }
 }
 
 /*
-** Place holders for the page-cache memory allocator.
+** Allocate memory to be used by the page cache.  Make use of the
+** memory buffer provided by SQLITE_CONFIG_PAGECACHE if there is one
+** and that memory is of the right size and is not completely
+** consumed.  Otherwise, failover to sqlite3Malloc().
 */
-void *sqlite3PageMalloc(int iSize){
-  return sqlite3Malloc(iSize);
+void *sqlite3PageMalloc(int n){
+  void *p;
+  assert( n>0 );
+  assert( (n & (n-1))==0 );
+  assert( n>=512 && n<=32768 );
+  if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
+    return 0;
+  }
+
+  if( sqlite3Config.szPage<n ){
+    goto page_overflow;
+  }else{  
+    sqlite3_mutex_enter(mem0.mutex);
+    if( mem0.nPageFree==0 ){
+      sqlite3_mutex_leave(mem0.mutex);
+      goto page_overflow;
+    }else{
+      int i;
+      i = mem0.aPageFree[--mem0.nPageFree];
+      sqlite3_mutex_leave(mem0.mutex);
+      i *= sqlite3Config.szPage;
+      sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
+      p = (void*)&((char*)sqlite3Config.pPage)[i];
+    }
+  }
+  return p;
+
+page_overflow:
+  if( sqlite3Config.bMemstat ){
+    sqlite3_mutex_enter(mem0.mutex);
+    n = mallocWithAlarm(n, &p);
+    if( p ) sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, n);
+    sqlite3_mutex_leave(mem0.mutex);
+  }else{
+    p = sqlite3Config.m.xMalloc(n);
+  }
+  return p;    
 }
-void sqlite3PageFree(void *pOld){
-  sqlite3_free(pOld);
+void sqlite3PageFree(void *p){
+  if( p ){
+    if( sqlite3Config.pPage==0
+           || p<sqlite3Config.pPage
+           || p>=(void*)mem0.aPageFree ){
+      if( sqlite3Config.bMemstat ){
+        int iSize = sqlite3MallocSize(p);
+        sqlite3_mutex_enter(mem0.mutex);
+        sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
+        sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
+        sqlite3Config.m.xFree(p);
+        sqlite3_mutex_leave(mem0.mutex);
+      }else{
+        sqlite3Config.m.xFree(p);
+      }
+    }else{
+      int i;
+      i = p - sqlite3Config.pPage;
+      i /= sqlite3Config.szPage;
+      assert( i>=0 && i<sqlite3Config.nPage );
+      sqlite3_mutex_enter(mem0.mutex);
+      assert( mem0.nPageFree<sqlite3Config.nPage );
+      mem0.aPageFree[mem0.nPageFree++] = i;
+      sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
+      sqlite3_mutex_leave(mem0.mutex);
+    }
+  }
 }
 
 /*
@@ -365,7 +454,7 @@
   if( p==0 ) return;
   if( sqlite3Config.bMemstat ){
     sqlite3_mutex_enter(mem0.mutex);
-    mem0.nowUsed -= sqlite3MallocSize(p);
+    sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
     sqlite3Config.m.xFree(p);
     sqlite3_mutex_leave(mem0.mutex);
   }else{
@@ -389,12 +478,13 @@
   nOld = sqlite3MallocSize(pOld);
   if( sqlite3Config.bMemstat ){
     sqlite3_mutex_enter(mem0.mutex);
-    if( nBytes>mem0.mxReq ) mem0.mxReq = nBytes;
+    sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
     nNew = sqlite3Config.m.xRoundup(nBytes);
     if( nOld==nNew ){
       pNew = pOld;
     }else{
-      if( mem0.nowUsed+nNew-nOld>=mem0.alarmThreshold ){
+      if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= 
+            mem0.alarmThreshold ){
         sqlite3MallocAlarm(nNew-nOld);
       }
       if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
@@ -407,10 +497,7 @@
         }
       }
       if( pNew ){
-        mem0.nowUsed += nNew-nOld;
-        if( mem0.nowUsed>mem0.mxUsed ){
-          mem0.mxUsed = mem0.nowUsed;
-        }
+        sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
       }
     }
     sqlite3_mutex_leave(mem0.mutex);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 5adaf5b..b072c06 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -30,7 +30,7 @@
 ** the version number) and changes its name to "sqlite3.h" as
 ** part of the build process.
 **
-** @(#) $Id: sqlite.h.in,v 1.336 2008/06/18 18:57:42 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.337 2008/06/19 00:16:08 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1057,7 +1057,7 @@
 ** scratch memory.  There are three arguments:  A pointer to the memory, the
 ** size of each scratch buffer (sz), and the number of buffers (N).  The sz
 ** argument must be a multiple of 16. The first
-** argument should point to an allocation of at least (sz+1)*N bytes of memory.
+** argument should point to an allocation of at least (sz+4)*N bytes of memory.
 ** SQLite will use no more than one scratch buffer at once per thread, so
 ** N should be set to the expected maximum number of threads.  The sz 
 ** parameter should be 6 times the size of the largest database page size.
@@ -5998,6 +5998,95 @@
 #define SQLITE_TESTCTRL_PRNG_RESET               7
 #define SQLITE_TESTCTRL_BITVEC_TEST              8
 
+/*
+** CAPI3REF: SQLite Runtime Status {F17200}
+**
+** This interface is used to retrieve run-time status information
+** about the preformance of SQLite, and optionally to reset various
+** highwater marks.  The first argument is an integer code for
+** the specific parameter to measure.  Recognized integer codes
+** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].
+** The current value of the parameter is returned into *pCurrent.
+** The highest recorded value is returned in *pHighwater.  If the
+** resetFlag is true, then the highest record value is reset after
+** *pHighwater is written. Some parameters do not record the highest
+** value.  For those parameters
+** nothing is written into *pHighwater and the resetFlag is ignored.
+** Other parameters record only the highwater mark and not the current
+** value.  For these latter parameters nothing is written into *pCurrent.
+**
+** This routine returns SQLITE_OK on success and a non-zero
+** [error code] on failure.
+**
+** This routine is threadsafe but is not atomic.  This routine can
+** called while other threads are running the same or different SQLite
+** interfaces.  However the values returned in *pCurrent and
+** *pHighwater reflect the status of SQLite at different points in time
+** and it is possible that another thread might change the parameter
+** in between the times when *pCurrent and *pHighwater are written.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+
+/*
+** CAPI3REF: Status Parameters {F17250}
+**
+** These integer constants designate various run-time status parameters
+** that can be returned by [sqlite3_status()].
+**
+** <dl>
+** <dt>SQLITE_STATUS_MEMORY_USED</dt>
+** <dd>This parameter is the current amount of memory checked out
+** using [sqlite3_malloc()], either directly or indirectly.  The 
+** figure includes calls made to [sqlite3_malloc()] by the application
+** and internal memory usage by the SQLite library.  Scratch memory
+** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
+** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
+** this parameter.  The amount returned is the sum of the allocation
+** sizes as are reported by the xSize method in [sqlite3_mem_methods].</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_USED</dt>
+** <dd>This parameter returns the number of pages used out of the
+** page cache buffer configured using [SQLITE_CONFIG_PAGECACHE].  The
+** value returned is in pages, not in bytes.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of page cache
+** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE]
+** buffer and where forced to overflow to [sqlite3_malloc()].</dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_USED</dt>
+** <dd>This parameter returns the number of allocations used out of the
+** scratch allocation lookaside buffer configured using
+** [SQLITE_CONFIG_SCRATCH].  The value returned is in allocations, not
+** in bytes.  Since a single thread may only have one allocation
+** outstanding at time, this parameter also reports the number of threads
+** using scratch memory at the same time.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of scratch memory
+** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH]
+** buffer and where forced to overflow to [sqlite3_malloc()].</dd>
+**
+** <dt>SQLITE_STATUS_MALLOC_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
+** internal equivalents).  The value of interest is return in the
+** *pHighwater parameter to [sqlite3_status()].  The value written
+** into the *pCurrent parameter is undefined.</dd>
+** </dl>
+**
+** New status parameters may be added from time to time.
+*/
+#define SQLITE_STATUS_MEMORY_USED          0
+#define SQLITE_STATUS_PAGECACHE_USED       1
+#define SQLITE_STATUS_PAGECACHE_OVERFLOW   2
+#define SQLITE_STATUS_SCRATCH_USED         3
+#define SQLITE_STATUS_SCRATCH_OVERFLOW     4
+#define SQLITE_STATUS_MALLOC_SIZE          5
+
 
 /*
 ** Undo the hack that converts floating point types to integer for
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 3496200..7097bf6 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.715 2008/06/18 18:57:42 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.716 2008/06/19 00:16:08 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1810,6 +1810,11 @@
 int sqlite3MutexInit(void);
 int sqlite3MutexEnd(void);
 
+void sqlite3StatusReset(void);
+int sqlite3StatusValue(int);
+void sqlite3StatusAdd(int, int);
+void sqlite3StatusSet(int, int);
+
 int sqlite3IsNaN(double);
 
 char *sqlite3MPrintf(sqlite3*,const char*, ...);
diff --git a/src/test_malloc.c b/src/test_malloc.c
index 95ee365..4643f2e 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -13,7 +13,7 @@
 ** This file contains code used to implement test interfaces to the
 ** memory allocation subsystem.
 **
-** $Id: test_malloc.c,v 1.24 2008/06/18 18:12:04 drh Exp $
+** $Id: test_malloc.c,v 1.25 2008/06/19 00:16:08 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -669,16 +669,16 @@
 ){
   int sz, N, rc;
   Tcl_Obj *pResult;
-  static char buf[20000];
+  static char buf[30000];
   if( objc!=3 ){
     Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
     return TCL_ERROR;
   }
-  if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
-  if( Tcl_GetIntFromObj(interp, objv[3], &N) ) return TCL_ERROR;
+  if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
+  if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
   if( sz<0 ){
     rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
-  }else if( sz==0 ){
+  }else{
     int mx = sizeof(buf)/(sz+4);
     if( N>mx ) N = mx;
     rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
@@ -713,11 +713,11 @@
     Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
     return TCL_ERROR;
   }
-  if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
-  if( Tcl_GetIntFromObj(interp, objv[3], &N) ) return TCL_ERROR;
+  if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
+  if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
   if( sz<0 ){
-    rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
-  }else if( sz==0 ){
+    rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
+  }else{
     int mx = sizeof(buf)/(sz+4);
     if( N>mx ) N = mx;
     rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
@@ -729,6 +729,56 @@
   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_PAGECACHE_USED",      SQLITE_STATUS_PAGECACHE_USED      },
+    { "SQLITE_STATUS_PAGECACHE_OVERFLOW",  SQLITE_STATUS_PAGECACHE_OVERFLOW  },
+    { "SQLITE_STATUS_SCRATCH_USED",        SQLITE_STATUS_SCRATCH_USED        },
+    { "SQLITE_STATUS_SCRATCH_OVERFLOW",    SQLITE_STATUS_SCRATCH_OVERFLOW    },
+    { "SQLITE_STATUS_MALLOC_SIZE",         SQLITE_STATUS_MALLOC_SIZE         },
+  };
+  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;
+  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;
+}
 
 /*
 ** Register commands with the TCL interpreter.
@@ -754,6 +804,7 @@
      { "sqlite3_memdebug_log",       test_memdebug_log             },
      { "sqlite3_config_scratch",     test_config_scratch           },
      { "sqlite3_config_pagecache",   test_config_pagecache         },
+     { "sqlite3_status",             test_status                   },
   };
   int i;
   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){