SQLite4
Check-in [75b8ccc0a8]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Re-enable memory statistics and backtraces.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 75b8ccc0a84cc687d1ab45756f9fe18c76cff5df
User & Date: dan 2013-05-21 18:19:02
Context
2013-05-21
20:08
Remove sqlite4_mem_methods from sqlite.h.in. Other modifications so that src4.test works again. check-in: 7091e990cd user: dan tags: trunk
18:19
Re-enable memory statistics and backtraces. check-in: 75b8ccc0a8 user: dan tags: trunk
2013-05-18
19:42
Use the default sqlite4_mm object for malloc() and free(). check-in: e3280f4585 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/env.c.

    38     38   struct sqlite4_env sqlite4DefaultEnv = {
    39     39      sizeof(sqlite4_env),       /* nByte */
    40     40      1,                         /* iVersion */
    41     41      SQLITE4_DEFAULT_MEMSTATUS, /* bMemstat */
    42     42      1,                         /* bCoreMutex */
    43     43      SQLITE4_THREADSAFE==1,     /* bFullMutex */
    44     44      0x7ffffffe,                /* mxStrlen */
    45         -   128,                       /* szLookaside */
    46         -   500,                       /* nLookaside */
           45  +   0,                         /* szLookaside */
           46  +   0,                         /* nLookaside */
    47     47      &sqlite4MMSystem,          /* pMM */
    48     48   #if 0
    49     49      {0,0,0,0,0,0,0,0,0},       /* m */
    50     50   #endif
    51     51      {0,0,0,0,0,0,0,0,0,0},     /* mutex */
    52     52      (void*)0,                  /* pHeap */
    53     53      0,                         /* nHeap */
................................................................................
   300    300         *va_arg(ap, sqlite4_mutex_methods*) = pEnv->mutex;
   301    301         break;
   302    302       }
   303    303   #endif
   304    304   
   305    305   
   306    306       /*
   307         -    ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_MALLOC, sqlite4_mem_methods*)
          307  +    ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_SETMM, sqlite4_mm*)
   308    308       **
   309         -    ** Set the memory allocation routines to be used by this environment.
          309  +    ** Set the memory allocator to be used by this environment.
   310    310       */
   311         -    case SQLITE4_ENVCONFIG_MALLOC: {
   312         -#if 0
   313         -      /* Specify an alternative malloc implementation */
          311  +    case SQLITE4_ENVCONFIG_SETMM: {
   314    312         if( pEnv->isInit ) return SQLITE4_MISUSE;
   315         -      pEnv->m = *va_arg(ap, sqlite4_mem_methods*);
   316         -#endif
          313  +      pEnv->pMM = va_arg(ap, sqlite4_mm*);
   317    314         break;
   318    315       }
   319    316   
   320    317       /*
   321         -    ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_GETMALLOC, sqlite4_mem_methods*)
          318  +    ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_GETMM, sqlite4_mm**)
   322    319       **
   323    320       ** Copy the memory allocation routines in use by this environment
   324    321       ** into the structure given in the argument.
   325    322       */
   326         -    case SQLITE4_ENVCONFIG_GETMALLOC: {
   327         -#if 0
   328         -      /* Retrieve the current malloc() implementation */
   329         -      if( pEnv->m.xMalloc==0 ) sqlite4MemSetDefault(pEnv);
   330         -      *va_arg(ap, sqlite4_mem_methods*) = pEnv->m;
   331         -#endif
          323  +    case SQLITE4_ENVCONFIG_GETMM: {
          324  +      *va_arg(ap, sqlite4_mm**) = pEnv->pMM;
   332    325         break;
   333    326       }
   334    327   
   335    328       /* sqlite4_env_config(p, SQLITE4_ENVCONFIG_MEMSTAT, int onoff);
   336    329       **
   337    330       ** Enable or disable collection of memory usage statistics according to
   338    331       ** the onoff parameter.  

Changes to src/mem.c.

   281    281       pOvfl->xMemberOfA = pA->pMethods->xMember;
   282    282       pOvfl->pA = pA;
   283    283       pOvfl->pB = pB;
   284    284     }
   285    285     return &pOvfl->base;
   286    286   }
   287    287   
          288  +/*************************************************************************
          289  +** The SQLITE4_MM_STATS memory allocator.
          290  +*/
          291  +
          292  +/*
          293  +** Number of available statistics. Statistics are assigned ids starting at
          294  +** one, not zero.
          295  +*/
          296  +#define MM_STATS_NSTAT 8
          297  +
          298  +struct mmStats {
          299  +  sqlite4_mm base;                /* Base class.  Must be first. */
          300  +  sqlite4_mm *p;                  /* Underlying allocator object */
          301  +  sqlite4_mutex *mutex;           /* Mutex protecting aStat[] (or NULL) */
          302  +
          303  +  i64 nOut;                       /* Number of bytes outstanding */
          304  +  i64 nOutHw;                     /* Highwater mark of nOut */
          305  +  i64 nUnit;                      /* Number of allocations outstanding */
          306  +  i64 nUnitHw;                    /* Highwater mark of nUnit */
          307  +  i64 nMaxRequest;                /* Largest request seen so far */
          308  +  i64 nFault;                     /* Number of malloc or realloc failures */
          309  +};
          310  +
          311  +static void updateStatsMalloc(
          312  +  struct mmStats *pStats, 
          313  +  void *pNew, 
          314  +  sqlite4_size_t iSz
          315  +){
          316  +  /* Statistic SQLITE4_MMSTAT_SIZE records the largest allocation request
          317  +  ** that has been made so far. So if iSz is larger than the current value,
          318  +  ** set MMSTAT_SIZE to iSz now. This statistic is updated regardless of 
          319  +  ** whether or not the allocation succeeded.  */ 
          320  +  if( iSz>pStats->nMaxRequest ){
          321  +    pStats->nMaxRequest = iSz;
          322  +  }
          323  +
          324  +  /* If the allocation succeeded, increase the number of allocations and
          325  +  ** bytes outstanding accordingly. Also update the highwater marks if
          326  +  ** required. If the allocation failed, increment the fault count.  */
          327  +  if( pNew ){
          328  +    pStats->nOut += sqlite4_mm_msize(pStats->p, pNew);
          329  +    pStats->nUnit += 1;
          330  +    if( pStats->nOut>pStats->nOutHw ) pStats->nOutHw = pStats->nOut;
          331  +    if( pStats->nUnit>pStats->nUnitHw ) pStats->nUnitHw = pStats->nUnit;
          332  +  }else{
          333  +    pStats->nFault++;
          334  +  }
          335  +}
          336  +
          337  +static void *mmStatsMalloc(sqlite4_mm *pMM, sqlite4_size_t iSz){
          338  +  struct mmStats *pStats = (struct mmStats*)pMM;
          339  +  void *pRet;
          340  +
          341  +  pRet = sqlite4_mm_malloc(pStats->p, iSz);
          342  +  sqlite4_mutex_enter(pStats->mutex);
          343  +  updateStatsMalloc(pStats, pRet, iSz);
          344  +  sqlite4_mutex_leave(pStats->mutex);
          345  +  return pRet;
          346  +}
          347  +
          348  +static void mmStatsFree(sqlite4_mm *pMM, void *pOld){
          349  +  struct mmStats *pStats = (struct mmStats*)pMM;
          350  +  sqlite4_mutex_enter(pStats->mutex);
          351  +  if( pOld ){
          352  +    sqlite4_size_t nByte = sqlite4_mm_msize(pMM, pOld);
          353  +    pStats->nOut -= nByte;
          354  +    pStats->nUnit -= 1;
          355  +  }
          356  +  sqlite4_mutex_leave(pStats->mutex);
          357  +  sqlite4_mm_free(pStats->p, pOld);
          358  +}
          359  +
          360  +static void *mmStatsRealloc(sqlite4_mm *pMM, void *pOld, sqlite4_size_t iSz){
          361  +  struct mmStats *pStats = (struct mmStats*)pMM;
          362  +  sqlite4_size_t nOrig = (pOld ? sqlite4_mm_msize(pStats->p, pOld) : 0);
          363  +  void *pRet;
          364  +
          365  +  pRet = sqlite4_mm_realloc(pStats->p, pOld, iSz);
          366  +
          367  +  sqlite4_mutex_enter(pStats->mutex);
          368  +  if( pRet ){
          369  +    pStats->nOut -= nOrig;
          370  +    if( pOld ) pStats->nUnit--;
          371  +  }
          372  +  updateStatsMalloc(pStats, pRet, iSz);
          373  +  sqlite4_mutex_leave(pStats->mutex);
          374  +
          375  +  return pRet;
          376  +}
          377  +
          378  +static sqlite4_size_t mmStatsMsize(sqlite4_mm *pMM, void *pOld){
          379  +  struct mmStats *pStats = (struct mmStats*)pMM;
          380  +  return sqlite4_mm_msize(pStats->p, pOld);
          381  +}
          382  +
          383  +static int mmStatsMember(sqlite4_mm *pMM, const void *pOld){
          384  +  struct mmStats *pStats = (struct mmStats*)pMM;
          385  +  return sqlite4_mm_member(pStats->p, pOld);
          386  +}
          387  +
          388  +
          389  +static sqlite4_int64 mmStatsStat(
          390  +  sqlite4_mm *pMM, 
          391  +  unsigned int eType, 
          392  +  unsigned int flags
          393  +){
          394  +  struct mmStats *pStats = (struct mmStats*)pMM;
          395  +  i64 iRet = 0;
          396  +  sqlite4_mutex_enter(pStats->mutex);
          397  +  switch( eType ){
          398  +    case SQLITE4_MMSTAT_OUT: {
          399  +      iRet = pStats->nOut;
          400  +      break;
          401  +    }
          402  +    case SQLITE4_MMSTAT_OUT_HW: {
          403  +      iRet = pStats->nOutHw;
          404  +      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nOutHw = pStats->nOut;
          405  +      break;
          406  +    }
          407  +    case SQLITE4_MMSTAT_UNITS: {
          408  +      iRet = pStats->nUnit;
          409  +      break;
          410  +    }
          411  +    case SQLITE4_MMSTAT_UNITS_HW: {
          412  +      iRet = pStats->nUnitHw;
          413  +      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nUnitHw = pStats->nUnit;
          414  +      break;
          415  +    }
          416  +    case SQLITE4_MMSTAT_SIZE: {
          417  +      iRet = pStats->nMaxRequest;
          418  +      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nMaxRequest = 0;
          419  +      break;
          420  +    }
          421  +    case SQLITE4_MMSTAT_MEMFAULT:
          422  +    case SQLITE4_MMSTAT_FAULT: {
          423  +      iRet = pStats->nFault;
          424  +      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nFault = 0;
          425  +      break;
          426  +    }
          427  +  }
          428  +  sqlite4_mutex_leave(pStats->mutex);
          429  +  return iRet;
          430  +}
          431  +
          432  +/*
          433  +** Destroy the allocator object passed as the first argument.
          434  +*/
          435  +static void mmStatsFinal(sqlite4_mm *pMM){
          436  +  struct mmStats *pStats = (struct mmStats*)pMM;
          437  +  sqlite4_mm *p = pStats->p;
          438  +  sqlite4_mm_free(p, pStats);
          439  +  sqlite4_mm_destroy(p);
          440  +}
          441  +
          442  +static const sqlite4_mm_methods mmStatsMethods = {
          443  +  /* iVersion */    1,
          444  +  /* xMalloc  */    mmStatsMalloc,
          445  +  /* xRealloc */    mmStatsRealloc,
          446  +  /* xFree    */    mmStatsFree,
          447  +  /* xMsize   */    mmStatsMsize,
          448  +  /* xMember  */    mmStatsMember,
          449  +  /* xBenign  */    0,
          450  +  /* xStat    */    mmStatsStat,
          451  +  /* xCtrl    */    0,
          452  +  /* xFinal   */    mmStatsFinal
          453  +};
          454  +
          455  +/*
          456  +** Allocate a new stats allocator.
          457  +*/
          458  +static sqlite4_mm *mmStatsNew(sqlite4_mm *p){
          459  +  struct mmStats *pNew;
          460  +
          461  +  pNew = (struct mmStats *)sqlite4_mm_malloc(p, sizeof(*pNew));
          462  +  if( pNew ){
          463  +    memset(pNew, 0, sizeof(*pNew));
          464  +    pNew->p = p;
          465  +    pNew->base.pMethods = &mmStatsMethods;
          466  +  }
          467  +
          468  +  return (sqlite4_mm *)pNew;
          469  +}
          470  +
   288    471   
   289    472   /*************************************************************************
   290    473   ** The SQLITE4_MM_ONESIZE memory allocator.
   291    474   **
   292    475   ** All memory allocations are rounded up to a single size, "sz".  A request
   293    476   ** for an allocation larger than sz bytes fails.  All allocations come out
   294    477   ** of a single initial buffer with "cnt" chunks of "sz" bytes each.
................................................................................
   350    533     struct mmOnesz *pOnesz = (struct mmOnesz*)pMM;
   351    534     return pOld ? pOnesz->sz : 0;  
   352    535   }
   353    536   static int mmOneszMember(sqlite4_mm *pMM, const void *pOld){
   354    537     struct mmOnesz *pOnesz = (struct mmOnesz*)pMM;
   355    538     return pOld && pOld>=pOnesz->pSpace && pOld<=pOnesz->pLast;
   356    539   }
   357         -static sqlite4_int64 mmOneszStat(sqlite4_mm *pMM, int eType, unsigned flgs){
          540  +static sqlite4_int64 mmOneszStat(
          541  +  sqlite4_mm *pMM, 
          542  +  unsigned int eType, 
          543  +  unsigned int flgs
          544  +){
   358    545     struct mmOnesz *pOnesz = (struct mmOnesz*)pMM;
   359    546     sqlite4_int64 x = -1;
   360    547     switch( eType ){
   361    548       case SQLITE4_MMSTAT_OUT: {
   362    549         x = pOnesz->nUsed*pOnesz->sz;
   363    550         break;
   364    551       }
................................................................................
   510    697       }
   511    698       case SQLITE4_MM_ONESIZE: {
   512    699         void *pSpace = va_arg(ap, void*);
   513    700         int sz = va_arg(ap, int);
   514    701         int cnt = va_arg(ap, int);
   515    702         pMM = mmOneszNew(pSpace, sz, cnt);
   516    703         break;
          704  +    }
          705  +    case SQLITE4_MM_STATS: {
          706  +      sqlite4_mm *p = va_arg(ap, sqlite4_mm*);
          707  +      pMM = mmStatsNew(p);
          708  +      break;
   517    709       }
   518    710       default: {
   519    711         pMM = 0;
   520    712         break;
   521    713       }
   522    714     }
   523    715     va_end(ap);
   524    716     return pMM;
   525    717   }

Changes to src/prepare.c.

   592    592     Vdbe *pOld,               /* VM being reprepared */
   593    593     sqlite4_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
   594    594     int *pnUsed               /* OUT: Bytes read from zSql */
   595    595   ){
   596    596     int rc;
   597    597     assert( ppStmt!=0 );
   598    598     *ppStmt = 0;
          599  +  if( pnUsed ){
          600  +    *pnUsed = 0;
          601  +  }
   599    602     if( !sqlite4SafetyCheckOk(db) ){
   600    603       return SQLITE4_MISUSE_BKPT;
   601    604     }
   602    605     sqlite4_mutex_enter(db->mutex);
   603    606     rc = sqlite4Prepare(db, zSql, nBytes, pOld, ppStmt, pnUsed);
   604    607     if( rc==SQLITE4_SCHEMA ){
   605    608       sqlite4_finalize(*ppStmt);

Changes to src/sqlite.h.in.

   166    166   **
   167    167   ** If pOld is not a valid memory allocation or is a memory allocation that
   168    168   ** has previously been freed, then the result of this routine is undefined.
   169    169   */
   170    170   int sqlite4_mm_member(sqlite4_mm *pMM, const void *pOld);
   171    171   
   172    172   /*
   173         -** Allowed values for the second parameter ("eType") to sqlite4_mm_type().
          173  +** Allowed values for the second parameter ("eType") to sqlite4_mm_stat().
   174    174   */
   175    175   #define SQLITE4_MMSTAT_OUT        1
   176    176   #define SQLITE4_MMSTAT_OUT_HW     2
   177    177   #define SQLITE4_MMSTAT_UNITS      3
   178    178   #define SQLITE4_MMSTAT_UNITS_HW   4
   179    179   #define SQLITE4_MMSTAT_SIZE       5
   180    180   #define SQLITE4_MMSTAT_SZFAULT    6
................................................................................
   193    193   ** a memory allocator is unavailable, then -1 is returned.
   194    194   */
   195    195   sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eType, unsigned flags);
   196    196   
   197    197   /*
   198    198   ** Send a control message into a memory allocator.
   199    199   */
   200         -int sqlit4_mm_control(sqlite4_mm *pMM, int eType, ...);
          200  +int sqlite4_mm_control(sqlite4_mm *pMM, int eType, ...);
   201    201   
   202    202   /*
   203    203   ** Enable or disable benign failure mode.  Benign failure mode can be
   204    204   ** nested.  In benign failure mode, OOM errors do not necessarily propagate
   205    205   ** back out to the application but can be dealt with internally.  Memory
   206    206   ** allocations that occur in benign failure mode are considered "optional".
   207    207   */
................................................................................
   245    245   */
   246    246   #define SQLITE4_ENVCONFIG_INIT          1   /* size, template */
   247    247   #define SQLITE4_ENVCONFIG_SINGLETHREAD  2   /* */
   248    248   #define SQLITE4_ENVCONFIG_MULTITHREAD   3   /* */
   249    249   #define SQLITE4_ENVCONFIG_SERIALIZED    4   /* */
   250    250   #define SQLITE4_ENVCONFIG_MUTEX         5   /* sqlite4_mutex_methods* */
   251    251   #define SQLITE4_ENVCONFIG_GETMUTEX      6   /* sqlite4_mutex_methods* */
   252         -#define SQLITE4_ENVCONFIG_MALLOC        7   /* sqlite4_mem_methods* */
   253         -#define SQLITE4_ENVCONFIG_GETMALLOC     8   /* sqlite4_mem_methods* */
          252  +#define SQLITE4_ENVCONFIG_SETMM         7   /* sqlite4_mm* */
          253  +#define SQLITE4_ENVCONFIG_GETMM         8   /* sqlite4_mm** */
   254    254   #define SQLITE4_ENVCONFIG_MEMSTATUS     9   /* boolean */
   255    255   #define SQLITE4_ENVCONFIG_LOOKASIDE    10   /* size, count */
   256    256   #define SQLITE4_ENVCONFIG_LOG          11   /* xLog, pArg */
   257    257   #define SQLITE4_ENVCONFIG_KVSTORE_PUSH 12   /* name, factory */
   258    258   #define SQLITE4_ENVCONFIG_KVSTORE_POP  13   /* name */
   259    259   #define SQLITE4_ENVCONFIG_KVSTORE_GET  14   /* name, *factory */
   260    260   

Changes to src/tclsqlite.c.

  2875   2875       extern int Sqlitetestrtree_Init(Tcl_Interp*);
  2876   2876       extern int Sqlitequota_Init(Tcl_Interp*);
  2877   2877       extern int SqliteSuperlock_Init(Tcl_Interp*);
  2878   2878       extern int SqlitetestSyscall_Init(Tcl_Interp*);
  2879   2879       extern int Sqliteteststorage_Init(Tcl_Interp*);
  2880   2880       extern int Sqliteteststorage2_Init(Tcl_Interp*);
  2881   2881       extern int SqlitetestLsm_Init(Tcl_Interp*);
  2882         -    extern int Sqlitetest_mem_Init(Tcl_Interp*);
  2883   2882   
  2884   2883       extern void sqlite4TestInit(Tcl_Interp*);
  2885   2884   
  2886   2885       Sqliteconfig_Init(interp);
  2887   2886       Sqlitetest1_Init(interp);
  2888   2887       Sqlitetest4_Init(interp);
  2889   2888       Sqlitetest5_Init(interp);
................................................................................
  2891   2890       Sqlitetest_hexio_Init(interp);
  2892   2891       Sqlitetest_malloc_Init(interp);
  2893   2892       Sqlitetest_mutex_Init(interp);
  2894   2893       SqlitetestThread_Init(interp);
  2895   2894       Sqliteteststorage_Init(interp);
  2896   2895       Sqliteteststorage2_Init(interp);
  2897   2896       SqlitetestLsm_Init(interp);
  2898         -    Sqlitetest_mem_Init(interp);
  2899   2897       sqlite4TestInit(interp);
  2900         -
  2901   2898   
  2902   2899       Tcl_CreateObjCommand(
  2903   2900           interp, "load_testfixture_extensions", init_all_cmd, 0, 0
  2904   2901       );
  2905   2902       Tcl_CreateObjCommand(
  2906   2903           interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0
  2907   2904       );

Changes to test/testInt.h.

    21     21   
    22     22   /* test_main.c */
    23     23   void sqlite4TestInit(Tcl_Interp*);
    24     24   void *sqlite4TestTextToPtr(const char *z);
    25     25   int sqlite4TestDbHandle(Tcl_Interp *, Tcl_Obj *, sqlite4 **);
    26     26   int sqlite4TestSetResult(Tcl_Interp *interp, int rc);
    27     27   
           28  +sqlite4_mm *test_mm_debug(sqlite4_mm *p);
           29  +void test_mm_debug_report(sqlite4_mm *p, FILE *pFile);
           30  +
    28     31   #endif
    29     32   

Changes to test/test_malloc.c.

    15     15   */
    16     16   #include "sqliteInt.h"
    17     17   #include "tcl.h"
    18     18   #include <stdlib.h>
    19     19   #include <string.h>
    20     20   #include <assert.h>
    21     21   
           22  +#include "testInt.h"
           23  +
    22     24   /*
    23     25   ** This structure is used to encapsulate the global state variables used 
    24     26   ** by malloc() fault simulation.
    25     27   */
    26     28   static struct MemFault {
    27     29     int iCountdown;         /* Number of pending successes before a failure */
    28     30     int nRepeat;            /* Number of times to repeat the failure */
................................................................................
   178    180   }
   179    181   
   180    182   /*
   181    183   ** Add or remove the fault-simulation layer using sqlite4_env_config(). If
   182    184   ** the argument is non-zero, the 
   183    185   */
   184    186   static int faultsimInstall(int install){
          187  +#if 0
   185    188     static struct sqlite4_mem_methods m = {
   186    189       faultsimMalloc,                   /* xMalloc */
   187    190       faultsimFree,                     /* xFree */
   188    191       faultsimRealloc,                  /* xRealloc */
   189    192       faultsimSize,                     /* xSize */
   190    193       faultsimInit,                     /* xInit */
   191    194       faultsimShutdown,                 /* xShutdown */
................................................................................
   223    226       rc = sqlite4_env_config(0, SQLITE4_ENVCONFIG_MALLOC, &memfault.m);
   224    227     }
   225    228   
   226    229     if( rc==SQLITE4_OK ){
   227    230       memfault.isInstalled = 1;
   228    231     }
   229    232     return rc;
          233  +#endif
   230    234   }
   231    235   
   232    236   #ifdef SQLITE4_TEST
   233    237   
   234    238   /*
   235    239   ** This function is implemented in test1.c. Returns a pointer to a static
   236    240   ** buffer containing the symbolic SQLite error code that corresponds to
................................................................................
  1116   1120   #ifdef SQLITE4_ENABLE_MEMSYS3
  1117   1121     const sqlite4_mem_methods *sqlite4MemGetMemsys3(void);
  1118   1122     rc = sqlite4_env_config(0, SQLITE4_ENVCONFIG_MALLOC, sqlite4MemGetMemsys3());
  1119   1123   #endif
  1120   1124     Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE);
  1121   1125     return TCL_OK;
  1122   1126   }
         1127  +
         1128  +static sqlite4_mm *pMMDebug = 0;
         1129  +
         1130  +/*
         1131  +** tclcmd: test_mm_install ?debug? ?backtrace? ?stats?
         1132  +*/
         1133  +static int test_mm_install(
         1134  +  void * clientData,
         1135  +  Tcl_Interp *interp,
         1136  +  int objc,
         1137  +  Tcl_Obj *CONST objv[]
         1138  +){
         1139  +  const char *azOpt[] = { 
         1140  +    "debug",                      /* 0 */
         1141  +    "backtrace",                  /* 1 */
         1142  +    "stats",                      /* 2 */
         1143  +    0 
         1144  +  };
         1145  +  sqlite4_mm *pMM = 0;
         1146  +  int i;
         1147  +  int bDebug = 0;
         1148  +  int bBacktrace = 0;
         1149  +  int bStats = 0;
         1150  +
         1151  +  for(i=1; i<objc; i++){
         1152  +    int iOpt = 0;
         1153  +    int rc = Tcl_GetIndexFromObj(interp, objv[i], azOpt, "option", 0, &iOpt); 
         1154  +    if( rc!=TCL_OK ) return rc;
         1155  +
         1156  +    switch( iOpt ){
         1157  +      case 0:
         1158  +        bDebug = 1;
         1159  +        break;
         1160  +      case 1:
         1161  +        bBacktrace = 1;
         1162  +        break;
         1163  +      case 2:
         1164  +        bStats = 1;
         1165  +        break;
         1166  +    }
         1167  +  }
         1168  +
         1169  +  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
         1170  +  if( pMM==sqlite4_mm_default() ){
         1171  +    if( bDebug ){
         1172  +      pMMDebug = pMM = test_mm_debug(pMM);
         1173  +    }
         1174  +    if( bStats ){
         1175  +      pMM = sqlite4_mm_new(SQLITE4_MM_STATS, pMM);
         1176  +    }
         1177  +
         1178  +    sqlite4_env_config(0, SQLITE4_ENVCONFIG_SETMM, pMM);
         1179  +  }
         1180  +
         1181  +  return TCL_OK;
         1182  +}
         1183  +
         1184  +/*
         1185  +** tclcmd: test_mm_stat ?-reset? STATISTIC
         1186  +*/
         1187  +static int test_mm_stat(
         1188  +  void * clientData,
         1189  +  Tcl_Interp *interp,
         1190  +  int objc,
         1191  +  Tcl_Obj *CONST objv[]
         1192  +){
         1193  +  int flags = 0;
         1194  +  sqlite4_mm *pMM = 0;
         1195  +  sqlite4_int64 iRet = -1;
         1196  +  Tcl_Obj *pOpt;
         1197  +
         1198  +  if( objc!=2 && objc!=3 ){
         1199  +    Tcl_WrongNumArgs(interp, 1, objv, "?-reset? STATISTIC");
         1200  +    return TCL_ERROR;
         1201  +  }
         1202  +
         1203  +  if( objc==3 ){
         1204  +    const char *azOpt[] = { "-reset", 0 };
         1205  +    int iOpt = 0;
         1206  +    int rc = Tcl_GetIndexFromObj(interp, objv[1], azOpt, "option", 0, &iOpt); 
         1207  +    if( rc!=TCL_OK ) return rc;
         1208  +    flags = SQLITE4_MMSTAT_RESET;
         1209  +  }
         1210  +  pOpt = objv[objc-1];
         1211  +
         1212  +  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
         1213  +  if( pMM ){
         1214  +    const char *azOpt[] = {
         1215  +      "out", "out_hw", "units", "units_hw", 
         1216  +      "size", "szfault", "memfault", "fault", 
         1217  +      0
         1218  +    };
         1219  +
         1220  +    int iOpt = 0;
         1221  +    int rc = Tcl_GetIndexFromObj(interp, pOpt, azOpt, "option", 0, &iOpt); 
         1222  +    if( rc!=TCL_OK ) return rc;
         1223  +
         1224  +    iRet = sqlite4_mm_stat(pMM, iOpt+1, flags);
         1225  +  }
         1226  +
         1227  +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRet));
         1228  +  return TCL_OK;
         1229  +}
         1230  +
         1231  +/*
         1232  +** tclcmd: test_mm_report FILENAME
         1233  +*/
         1234  +static int test_mm_report(
         1235  +  void * clientData,
         1236  +  Tcl_Interp *interp,
         1237  +  int objc,
         1238  +  Tcl_Obj *CONST objv[]
         1239  +){
         1240  +  FILE *pFile;
         1241  +  const char *zFile;
         1242  +
         1243  +  if( objc!=2 ){
         1244  +    Tcl_WrongNumArgs(interp, 1, objv, "FILE");
         1245  +    return TCL_ERROR;
         1246  +  }
         1247  +
         1248  +  if( pMMDebug ){
         1249  +    zFile = Tcl_GetString(objv[1]);
         1250  +    pFile = fopen(zFile, "w");
         1251  +    if( pFile==0 ){
         1252  +      Tcl_AppendResult(interp, "Failed to open file: ", zFile, 0);
         1253  +      return TCL_ERROR;
         1254  +    }
         1255  +
         1256  +    test_mm_debug_report(pMMDebug, pFile);
         1257  +  }
         1258  +  return TCL_OK;
         1259  +}
         1260  +
  1123   1261   
  1124   1262   /*
  1125   1263   ** Register commands with the TCL interpreter.
  1126   1264   */
  1127   1265   int Sqlitetest_malloc_Init(Tcl_Interp *interp){
  1128   1266     static struct {
  1129   1267        char *zName;
................................................................................
  1146   1284        { "sqlite4_db_status",              test_db_status                ,0 },
  1147   1285        { "install_malloc_faultsim",        test_install_malloc_faultsim  ,0 },
  1148   1286        { "sqlite4_env_config_memstatus",   test_config_memstatus         ,0 },
  1149   1287        { "sqlite4_envconfig_lookaside",    test_envconfig_lookaside      ,0 },
  1150   1288        { "sqlite4_config_error",           test_config_error             ,0 },
  1151   1289        { "sqlite4_db_config_lookaside",    test_db_config_lookaside      ,0 },
  1152   1290        { "sqlite4_install_memsys3",        test_install_memsys3          ,0 },
         1291  +
         1292  +     { "test_mm_install",                test_mm_install               ,0 },
         1293  +     { "test_mm_stat",                   test_mm_stat                  ,0 },
         1294  +     { "test_mm_report",                 test_mm_report                ,0 },
  1153   1295     };
  1154   1296     int i;
  1155   1297     for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
  1156   1298       ClientData c = (ClientData)SQLITE4_INT_TO_PTR(aObjCmd[i].clientData);
  1157   1299       Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
  1158   1300     }
  1159   1301     return TCL_OK;
  1160   1302   }
  1161   1303   #endif

Changes to test/test_mem.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   */
    12     12   #include <stdio.h>
    13     13   #include <assert.h>
    14     14   #include <string.h>
    15     15   
    16         -#include "sqlite4.h"
           16  +#include "sqliteInt.h"
           17  +#include "testInt.h"
    17     18   
    18     19   #define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))
    19     20   
    20     21   #define MIN(x,y) ((x)<(y) ? (x) : (y))
    21     22   
    22     23   typedef unsigned int  u32;
    23     24   typedef unsigned char u8;
................................................................................
    32     33   # define backtrace(A,B) 1
    33     34   # define backtrace_symbols_fd(A,B,C)
    34     35   #endif
    35     36   
    36     37   
    37     38   typedef struct TmBlockHdr TmBlockHdr;
    38     39   typedef struct TmAgg TmAgg;
    39         -typedef struct TmGlobal TmGlobal;
           40  +typedef struct Testmem Testmem;
    40     41   
    41         -struct TmGlobal {
    42         -  /* Linked list of all currently outstanding allocations. And a table of
    43         -  ** all allocations, past and present, indexed by backtrace() info.  */
    44         -  TmBlockHdr *pFirst;
           42  +/*
           43  +** The object that implements the sqlite4_mm interface for this allocator.
           44  +*/
           45  +struct Testmem {
           46  +  sqlite4_mm base;                /* Base class.  Must be first. */
           47  +  sqlite4_mm *p;                  /* Underlying allocator object */
           48  +  sqlite4_mutex *mutex;           /* Mutex protecting this object (or NULL) */
           49  +
           50  +  TmBlockHdr *pFirst;             /* List of all outstanding allocations */
    45     51   #ifdef TM_BACKTRACE
    46         -  TmAgg *aHash[10000];
           52  +  TmAgg *aHash[10000];            /* Table of all allocations by backtrace() */
    47     53   #endif
    48         -
    49         -  /* Underlying malloc/realloc/free functions */
    50         -  sqlite4_mem_methods mem;
    51     54   };
    52     55   
    53     56   struct TmBlockHdr {
    54     57     TmBlockHdr *pNext;
    55     58     TmBlockHdr *pPrev;
    56     59     int nByte;
    57     60   #ifdef TM_BACKTRACE
................................................................................
    74     77   #define FOREGUARD 0x80F5E153
    75     78   #define REARGUARD 0xE4676B53
    76     79   static const u32 rearguard = REARGUARD;
    77     80   
    78     81   #define ROUND8(x) (((x)+7)&~7)
    79     82   #define BLOCK_HDR_SIZE (ROUND8( sizeof(TmBlockHdr) ))
    80     83   
    81         -static void tmEnterMutex(TmGlobal *pTm){
    82         -  /*pTm->xEnterMutex(pTm);*/
    83         -}
    84         -static void tmLeaveMutex(TmGlobal *pTm){
    85         -  /* pTm->xLeaveMutex(pTm); */
           84  +/*
           85  +** Given a user data pointer, return a pointer to the associated 
           86  +** TmBlockHdr structure.
           87  +*/
           88  +static TmBlockHdr *userToBlock(const void *p){
           89  +  return (TmBlockHdr *)(((const u8 *)p) - BLOCK_HDR_SIZE);
    86     90   }
    87     91   
    88         -static void *tmMalloc(TmGlobal *pTm, int nByte){
           92  +/*
           93  +** sqlite4_mm_methods.xMalloc method.
           94  +*/
           95  +static void *mmDebugMalloc(sqlite4_mm *pMM, sqlite4_size_t nByte){
           96  +  Testmem *pTest = (Testmem *)pMM;
    89     97     TmBlockHdr *pNew;               /* New allocation header block */
    90     98     u8 *pUser;                      /* Return value */
    91     99     int nReq;                       /* Total number of bytes requested */
    92    100   
          101  +  /* Allocate space for the users allocation, the TmBlockHdr object that
          102  +  ** located immediately before the users allocation in memory, and the
          103  +  ** 4-byte 'rearguard' located immediately following it. */
    93    104     assert( sizeof(rearguard)==4 );
    94    105     nReq = BLOCK_HDR_SIZE + nByte + 4;
    95         -  pNew = (TmBlockHdr *)pTm->mem.xMalloc(pTm->mem.pMemEnv, nReq);
          106  +  pNew = (TmBlockHdr *)sqlite4_mm_malloc(pTest->p, nReq);
    96    107     memset(pNew, 0, sizeof(TmBlockHdr));
    97    108   
    98         -  tmEnterMutex(pTm);
          109  +  sqlite4_mutex_enter(pTest->mutex);
    99    110   
   100    111     pNew->iForeGuard = FOREGUARD;
   101    112     pNew->nByte = nByte;
   102         -  pNew->pNext = pTm->pFirst;
          113  +  pNew->pNext = pTest->pFirst;
   103    114   
   104         -  if( pTm->pFirst ){
   105         -    pTm->pFirst->pPrev = pNew;
          115  +  if( pTest->pFirst ){
          116  +    pTest->pFirst->pPrev = pNew;
   106    117     }
   107         -  pTm->pFirst = pNew;
          118  +  pTest->pFirst = pNew;
   108    119   
   109    120     pUser = &((u8 *)pNew)[BLOCK_HDR_SIZE];
   110    121     memset(pUser, 0x56, nByte);
   111    122     memcpy(&pUser[nByte], &rearguard, 4);
   112    123   
   113    124   #ifdef TM_BACKTRACE
   114    125     {
................................................................................
   118    129       void *aFrame[TM_BACKTRACE];
   119    130       memset(aFrame, 0, sizeof(aFrame));
   120    131       backtrace(aFrame, TM_BACKTRACE);
   121    132   
   122    133       for(i=0; i<ArraySize(aFrame); i++){
   123    134         iHash += (u64)(aFrame[i]) + (iHash<<3);
   124    135       }
   125         -    iHash = iHash % ArraySize(pTm->aHash);
          136  +    iHash = iHash % ArraySize(pTest->aHash);
   126    137   
   127         -    for(pAgg=pTm->aHash[iHash]; pAgg; pAgg=pAgg->pNext){
          138  +    for(pAgg=pTest->aHash[iHash]; pAgg; pAgg=pAgg->pNext){
   128    139         if( memcmp(pAgg->aFrame, aFrame, sizeof(aFrame))==0 ) break;
   129    140       }
   130    141       if( !pAgg ){
   131         -      pAgg = (TmAgg *)pTm->mem.xMalloc(pTm->mem.pMemEnv, sizeof(TmAgg));
          142  +      pAgg = (TmAgg *)sqlite4_mm_malloc(pTest->p, sizeof(TmAgg));
   132    143         memset(pAgg, 0, sizeof(TmAgg));
   133    144         memcpy(pAgg->aFrame, aFrame, sizeof(aFrame));
   134         -      pAgg->pNext = pTm->aHash[iHash];
   135         -      pTm->aHash[iHash] = pAgg;
          145  +      pAgg->pNext = pTest->aHash[iHash];
          146  +      pTest->aHash[iHash] = pAgg;
   136    147       }
   137    148       pAgg->nAlloc++;
   138    149       pAgg->nByte += nByte;
   139    150       pAgg->nOutAlloc++;
   140    151       pAgg->nOutByte += nByte;
   141    152       pNew->pAgg = pAgg;
   142    153     }
   143    154   #endif
   144    155   
   145         -  tmLeaveMutex(pTm);
          156  +  sqlite4_mutex_leave(pTest->mutex);
   146    157     return pUser;
   147    158   }
   148    159   
   149         -static void tmFree(TmGlobal *pTm, void *p){
          160  +/*
          161  +** sqlite4_mm_methods.xFree method.
          162  +*/
          163  +static void mmDebugFree(sqlite4_mm *pMM, void *p){
          164  +  Testmem *pTest = (Testmem *)pMM;
   150    165     if( p ){
   151         -    TmBlockHdr *pHdr;
          166  +    TmBlockHdr *pHdr = userToBlock(p);
   152    167       u8 *pUser = (u8 *)p;
   153    168   
   154         -    tmEnterMutex(pTm);
   155         -    pHdr = (TmBlockHdr *)&pUser[BLOCK_HDR_SIZE * -1];
          169  +    sqlite4_mutex_enter(pTest->mutex);
          170  +
   156    171       assert( pHdr->iForeGuard==FOREGUARD );
   157    172       assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) );
   158    173   
          174  +    /* Unlink the TmBlockHdr object from the (Testmem.pFirst) list. */
   159    175       if( pHdr->pPrev ){
   160    176         assert( pHdr->pPrev->pNext==pHdr );
   161    177         pHdr->pPrev->pNext = pHdr->pNext;
   162    178       }else{
   163         -      assert( pHdr==pTm->pFirst );
   164         -      pTm->pFirst = pHdr->pNext;
          179  +      assert( pHdr==pTest->pFirst );
          180  +      pTest->pFirst = pHdr->pNext;
   165    181       }
   166    182       if( pHdr->pNext ){
   167    183         assert( pHdr->pNext->pPrev==pHdr );
   168    184         pHdr->pNext->pPrev = pHdr->pPrev;
   169    185       }
   170    186   
   171    187   #ifdef TM_BACKTRACE
   172    188       pHdr->pAgg->nOutAlloc--;
   173    189       pHdr->pAgg->nOutByte -= pHdr->nByte;
   174    190   #endif
   175    191   
   176         -    tmLeaveMutex(pTm);
          192  +    sqlite4_mutex_leave(pTest->mutex);
          193  +
   177    194       memset(pUser, 0x58, pHdr->nByte);
   178    195       memset(pHdr, 0x57, sizeof(TmBlockHdr));
   179         -    pTm->mem.xFree(pTm->mem.pMemEnv, pHdr);
          196  +    sqlite4_mm_free(pTest->p, pHdr);
   180    197     }
   181    198   }
   182    199   
   183         -static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){
          200  +/*
          201  +** sqlite4_mm_methods.xRealloc method.
          202  +*/
          203  +static void *mmDebugRealloc(sqlite4_mm *pMM, void *p, int nByte){
   184    204     void *pNew;
   185    205   
   186         -  pNew = tmMalloc(pTm, nByte);
          206  +  pNew = sqlite4_mm_malloc(pMM, nByte);
   187    207     if( pNew && p ){
   188         -    TmBlockHdr *pHdr;
   189         -    u8 *pUser = (u8 *)p;
   190         -    pHdr = (TmBlockHdr *)&pUser[BLOCK_HDR_SIZE * -1];
   191         -    memcpy(pNew, p, MIN(nByte, pHdr->nByte));
   192         -    tmFree(pTm, p);
          208  +    int nOrig = sqlite4_mm_msize(pMM, p);
          209  +    memcpy(pNew, p, MIN(nByte, nOrig));
          210  +    sqlite4_mm_free(pMM, p);
   193    211     }
          212  +
   194    213     return pNew;
   195    214   }
   196    215   
   197         -static void tmMallocCheck(
   198         -  TmGlobal *pTm,
   199         -  int *pnLeakAlloc,
   200         -  int *pnLeakByte,
   201         -  FILE *pFile
   202         -){
   203         -  TmBlockHdr *pHdr;
   204         -  int nLeak = 0;
   205         -  int nByte = 0;
          216  +/*
          217  +** sqlite4_mm_methods.xMsize method.
          218  +*/
          219  +static sqlite4_size_t mmDebugMsize(sqlite4_mm *pMM, void *p){
          220  +  if( p==0 ) return 0;
          221  +  TmBlockHdr *pHdr = userToBlock(p);
          222  +  return pHdr->nByte;
          223  +}
          224  +
          225  +/*
          226  +** sqlite4_mm_methods.xMember method.
          227  +*/
          228  +static int mmDebugMember(sqlite4_mm *pMM, const void *p){
          229  +  Testmem *pTest = (Testmem *)pMM;
          230  +  return sqlite4_mm_member(pTest->p, (const void *)userToBlock(p));
          231  +}
   206    232   
   207         -  if( pTm==0 ) return;
   208         -
   209         -  for(pHdr=pTm->pFirst; pHdr; pHdr=pHdr->pNext){
   210         -    nLeak++; 
   211         -    nByte += pHdr->nByte;
   212         -  }
   213         -  if( pnLeakAlloc ) *pnLeakAlloc = nLeak;
   214         -  if( pnLeakByte ) *pnLeakByte = nByte;
          233  +/*
          234  +** sqlite4_mm_methods.xStat method.
          235  +*/
          236  +static sqlite4_int64 mmDebugStat(
          237  +  sqlite4_mm *pMM, 
          238  +  unsigned int eType, 
          239  +  unsigned int flags
          240  +){
          241  +  Testmem *pTest = (Testmem *)pMM;
          242  +  return sqlite4_mm_stat(pTest->p, eType, flags);
          243  +}
   215    244   
          245  +/*
          246  +** Write a report of all mallocs, outstanding and otherwise, to the
          247  +** file passed as the second argument. 
          248  +*/
          249  +static void mmDebugReport(Testmem *pTest, FILE *pFile){
   216    250   #ifdef TM_BACKTRACE
   217         -  if( pFile ){
   218         -    int i;
   219         -    fprintf(pFile, "LEAKS\n");
   220         -    for(i=0; i<ArraySize(pTm->aHash); i++){
   221         -      TmAgg *pAgg;
   222         -      for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
   223         -        if( pAgg->nOutAlloc ){
   224         -          int j;
   225         -          fprintf(pFile, "%d %d ", pAgg->nOutByte, pAgg->nOutAlloc);
   226         -          for(j=0; j<TM_BACKTRACE; j++){
   227         -            fprintf(pFile, "%p ", pAgg->aFrame[j]);
   228         -          }
   229         -          fprintf(pFile, "\n");
   230         -        }
   231         -      }
   232         -    }
   233         -    fprintf(pFile, "\nALLOCATIONS\n");
   234         -    for(i=0; i<ArraySize(pTm->aHash); i++){
   235         -      TmAgg *pAgg;
   236         -      for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
          251  +  int i;
          252  +  fprintf(pFile, "LEAKS\n");
          253  +  for(i=0; i<ArraySize(pTest->aHash); i++){
          254  +    TmAgg *pAgg;
          255  +    for(pAgg=pTest->aHash[i]; pAgg; pAgg=pAgg->pNext){
          256  +      if( pAgg->nOutAlloc ){
   237    257           int j;
   238         -        fprintf(pFile, "%d %d ", pAgg->nByte, pAgg->nAlloc);
   239         -        for(j=0; j<TM_BACKTRACE; j++) fprintf(pFile, "%p ", pAgg->aFrame[j]);
          258  +        fprintf(pFile, "%d %d ", pAgg->nOutByte, pAgg->nOutAlloc);
          259  +        for(j=0; j<TM_BACKTRACE; j++){
          260  +          fprintf(pFile, "%p ", pAgg->aFrame[j]);
          261  +        }
   240    262           fprintf(pFile, "\n");
   241    263         }
   242    264       }
   243    265     }
          266  +  fprintf(pFile, "\nALLOCATIONS\n");
          267  +  for(i=0; i<ArraySize(pTest->aHash); i++){
          268  +    TmAgg *pAgg;
          269  +    for(pAgg=pTest->aHash[i]; pAgg; pAgg=pAgg->pNext){
          270  +      int j;
          271  +      fprintf(pFile, "%d %d ", pAgg->nByte, pAgg->nAlloc);
          272  +      for(j=0; j<TM_BACKTRACE; j++) fprintf(pFile, "%p ", pAgg->aFrame[j]);
          273  +      fprintf(pFile, "\n");
          274  +    }
          275  +  }
   244    276   #else
   245    277     (void)pFile;
          278  +  (void)pTest;
   246    279   #endif
   247    280   }
   248    281   
   249         -
   250         -static void *tmLsmEnvXMalloc(void *p, sqlite4_size_t n){
   251         -  return tmMalloc( (TmGlobal*) p, (int)n );
   252         -}
   253         -
   254         -static void tmLsmEnvXFree(void *p, void *ptr){ 
   255         -  tmFree( (TmGlobal *)p, ptr );
   256         -}
   257         -
   258         -static void *tmLsmEnvXRealloc(void *ptr, void * mem, int n){
   259         -  return tmRealloc((TmGlobal*)ptr, mem, n);
          282  +/*
          283  +** Destroy the allocator object passed as the first argument.
          284  +*/
          285  +static void mmDebugFinal(sqlite4_mm *pMM){
          286  +  Testmem *pTest = (Testmem *)pMM;
          287  +  sqlite4_mm *p = pTest->p;
          288  +  sqlite4_mm_free(p, (void *)pTest);
          289  +  sqlite4_mm_destroy(p);
   260    290   }
   261    291   
   262         -
   263         -static sqlite4_size_t tmLsmXSize(void *p, void *ptr){
   264         -  if(NULL==ptr){
   265         -    return 0;
   266         -  }else{
   267         -    unsigned char * pUc = (unsigned char *)ptr;
   268         -    TmBlockHdr * pBlock = (TmBlockHdr*)(pUc-BLOCK_HDR_SIZE);
   269         -    assert( pBlock->nByte > 0 );
   270         -    return (sqlite4_size_t) pBlock->nByte;
          292  +/*
          293  +** Create a new debug allocator wrapper around the allocator passed as the
          294  +** first argument.
          295  +*/
          296  +sqlite4_mm *test_mm_debug(sqlite4_mm *p){
          297  +  static const sqlite4_mm_methods mmDebugMethods = {
          298  +    /* iVersion */    1,
          299  +    /* xMalloc  */    mmDebugMalloc,
          300  +    /* xRealloc */    mmDebugRealloc,
          301  +    /* xFree    */    mmDebugFree,
          302  +    /* xMsize   */    mmDebugMsize,
          303  +    /* xMember  */    mmDebugMember,
          304  +    /* xBenign  */    0,
          305  +    /* xStat    */    mmDebugStat,
          306  +    /* xCtrl    */    0,
          307  +    /* xFinal   */    mmDebugFinal
          308  +  };
          309  +  Testmem *pTest;
          310  +  pTest = (Testmem *)sqlite4_mm_malloc(p, sizeof(Testmem));
          311  +  if( pTest ){
          312  +    memset(pTest, 0, sizeof(Testmem));
          313  +    pTest->base.pMethods = &mmDebugMethods;
          314  +    pTest->p = p;
   271    315     }
   272         -}
   273         -
   274         -static int tmInitStub(void* ignored){
   275         -  assert("Set breakpoint here.");
   276         -  return 0;
   277         -}
   278         -static void tmVoidStub(void* ignored){}
   279         -
   280         -
   281         -int testMallocInstall(sqlite4_env *pEnv){
   282         -  TmGlobal *pGlobal;              /* Object containing allocation hash */
   283         -  sqlite4_mem_methods allocator;  /* This malloc system */
   284         -  sqlite4_mem_methods orig;       /* Underlying malloc system */
   285         -
   286         -  /* Allocate and populate a TmGlobal structure. sqlite4_malloc cannot be
   287         -  ** used to allocate the TmGlobal struct as this would cause the environment
   288         -  ** to move to "initialized" state and the SQLITE4_ENVCONFIG_MALLOC 
   289         -  ** to fail. */
   290         -  sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_GETMALLOC, &orig);
   291         -  pGlobal = (TmGlobal *)orig.xMalloc(orig.pMemEnv, sizeof(TmGlobal));
   292         -  memset(pGlobal, 0, sizeof(TmGlobal));
   293         -  memcpy(&pGlobal->mem, &orig, sizeof(orig));
   294         -
   295         -  /* Set up pEnv to the use the new TmGlobal */
   296         -  allocator.xRealloc = tmLsmEnvXRealloc;
   297         -  allocator.xMalloc = tmLsmEnvXMalloc;
   298         -  allocator.xFree = tmLsmEnvXFree;
   299         -  allocator.xSize = tmLsmXSize;
   300         -  allocator.xInit = tmInitStub;
   301         -  allocator.xShutdown = tmVoidStub;
   302         -  allocator.xBeginBenign = tmVoidStub;
   303         -  allocator.xEndBenign = tmVoidStub;
   304         -  allocator.pMemEnv = pGlobal;
   305         -  return sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_MALLOC, &allocator);
          316  +  return (sqlite4_mm *)pTest;
   306    317   }
   307    318   
   308         -int testMallocUninstall(sqlite4_env *pEnv){
   309         -  TmGlobal *pGlobal;              /* Object containing allocation hash */
   310         -  sqlite4_mem_methods allocator;  /* This malloc system */
   311         -  int rc;
   312         -
   313         -  sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_GETMALLOC, &allocator);
   314         -  assert( allocator.xMalloc==tmLsmEnvXMalloc );
   315         -  pGlobal = (TmGlobal *)allocator.pMemEnv;
   316         -
   317         -  rc = sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_MALLOC, &pGlobal->mem);
   318         -  if( rc==SQLITE4_OK ){
   319         -    sqlite4_free(pEnv, pGlobal);
   320         -  }
   321         -  return rc;
   322         -}
   323         -
   324         -void testMallocCheck(
   325         -  sqlite4_env *pEnv,
   326         -  int *pnLeakAlloc,
   327         -  int *pnLeakByte,
   328         -  FILE *pFile
   329         -){
   330         -  TmGlobal *pGlobal;
   331         -  sqlite4_mem_methods allocator;  /* This malloc system */
   332         -
   333         -  sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_GETMALLOC, &allocator);
   334         -  assert( allocator.xMalloc==tmLsmEnvXMalloc );
   335         -  pGlobal = (TmGlobal *)allocator.pMemEnv;
   336         -
   337         -  tmMallocCheck(pGlobal, pnLeakAlloc, pnLeakByte, pFile);
   338         -}
   339         -
   340         -#include <tcl.h>
   341         -
   342         -/*
   343         -** testmem install
   344         -** testmem uninstall
   345         -** testmem report ?FILENAME?
   346         -*/
   347         -static int testmem_cmd(
   348         -  void * clientData,
   349         -  Tcl_Interp *interp,
   350         -  int objc,
   351         -  Tcl_Obj *CONST objv[]
   352         -){
   353         -  sqlite4_env *pEnv;              /* SQLite 4 environment to work with */
   354         -  int iOpt;
   355         -  const char *azSub[] = {"install", "uninstall", "report", 0};
   356         -
   357         -  if( objc<2 ){
   358         -    Tcl_WrongNumArgs(interp, 1, objv, "sub-command");
   359         -    return TCL_ERROR;
   360         -  }
   361         -  if( Tcl_GetIndexFromObj(interp, objv[1], azSub, "sub-command", 0, &iOpt) ){
   362         -    return TCL_ERROR;
   363         -  }
   364         -
   365         -  pEnv = sqlite4_env_default();
   366         -  switch( iOpt ){
   367         -    case 0: {
   368         -      int rc;
   369         -      if( objc!=2 ){
   370         -        Tcl_WrongNumArgs(interp, 2, objv, "");
   371         -        return TCL_ERROR;
   372         -      }
   373         -      rc = testMallocInstall(pEnv);
   374         -      if( rc!=SQLITE4_OK ){
   375         -        Tcl_AppendResult(interp, "Failed to install testmem wrapper", 0);
   376         -        return TCL_ERROR;
   377         -      }
   378         -      break;
   379         -    }
   380         -
   381         -    case 1:
   382         -      if( objc!=2 ){
   383         -        Tcl_WrongNumArgs(interp, 2, objv, "");
   384         -        return TCL_ERROR;
   385         -      }
   386         -      testMallocUninstall(pEnv);
   387         -      break;
   388         -
   389         -    case 2: {
   390         -      int nLeakAlloc = 0;
   391         -      int nLeakByte = 0;
   392         -      FILE *pReport = 0;
   393         -      Tcl_Obj *pRes;
   394         -
   395         -      if( objc!=2 && objc!=3 ){
   396         -        Tcl_WrongNumArgs(interp, 2, objv, "?filename?");
   397         -        return TCL_ERROR;
   398         -      }
   399         -      if( objc==3 ){
   400         -        const char *zFile = Tcl_GetString(objv[2]);
   401         -        pReport = fopen(zFile, "w");
   402         -        if( !pReport ){
   403         -          Tcl_AppendResult(interp, "Failed to open file: ", zFile, 0);
   404         -          return TCL_ERROR;
   405         -        }
   406         -      }
   407         -
   408         -      testMallocCheck(pEnv, &nLeakAlloc, &nLeakByte, pReport);
   409         -      if( pReport ) fclose(pReport);
   410         -
   411         -      pRes = Tcl_NewObj();
   412         -      Tcl_ListObjAppendElement(interp, pRes, Tcl_NewIntObj(nLeakAlloc));
   413         -      Tcl_ListObjAppendElement(interp, pRes, Tcl_NewIntObj(nLeakByte));
   414         -      Tcl_SetObjResult(interp, pRes);
   415         -      break;
   416         -    }
   417         -  }
   418         -
   419         -  return TCL_OK;
   420         -}
   421         -
   422         -int Sqlitetest_mem_Init(Tcl_Interp *interp){
   423         -  Tcl_CreateObjCommand(interp, "testmem", testmem_cmd, 0, 0);
   424         -  return TCL_OK;
          319  +void test_mm_debug_report(sqlite4_mm *p, FILE *pFile){
          320  +  Testmem *pTest = (Testmem *)p;
          321  +  assert( pTest->base.pMethods->xMalloc==mmDebugMalloc );
          322  +  mmDebugReport(pTest, pFile);
   425    323   }
   426    324   
   427    325   

Changes to test/tester.tcl.

    81     81   # 64KB into the database file instead of the one 1GB in. This means
    82     82   # the code that handles that special case can be tested without creating
    83     83   # very large database files.
    84     84   #
    85     85   set tcl_precision 15
    86     86   #sqlite4_test_control_pending_byte 0x0010000
    87     87   
           88  +test_mm_install stats debug
           89  +
    88     90   
    89     91   # If the pager codec is available, create a wrapper for the [sqlite4] 
    90     92   # command that appends "-key {xyzzy}" to the command line. i.e. this:
    91     93   #
    92     94   #     sqlite4 db test.db
    93     95   #
    94     96   # becomes
................................................................................
   759    761       puts "in your TCL build."
   760    762       puts "******************************************************************"
   761    763     }
   762    764     if {$::cmdlinearg(binarylog)} {
   763    765       vfslog finalize binarylog
   764    766     }
   765    767     kvwrap uninstall
          768  +
          769  +  set nOut  [test_mm_stat out]
          770  +  set nUnit [test_mm_stat units]
          771  +  if {$nOut!=0 || $nUnit!=0} {
          772  +    puts "Unfreed memory: $nOut bytes in $nUnit allocations"
          773  +  } else {
          774  +    puts "All memory allocations freed - no leaks"
          775  +  }
          776  +  show_memstats
          777  +  test_mm_report malloc.txt
          778  +
   766    779   #  if {[lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1]>0 ||
   767    780   #              [sqlite4_memory_used]>0} {
   768    781   #    puts "Unfreed memory: [sqlite4_memory_used] bytes in\
   769    782   #         [lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1] allocations"
   770    783   #    incr nErr
   771    784   #    ifcapable memdebug||mem5||(mem3&&debug) {
   772    785   #      puts "Writing unfreed memory log to \"./memleak.txt\""
................................................................................
   774    787   #    }
   775    788   #  } else {
   776    789   #    puts "All memory allocations freed - no leaks"
   777    790   #    ifcapable memdebug||mem5 {
   778    791   #      sqlite4_memdebug_dump ./memusage.txt
   779    792   #    }
   780    793   #  }
   781         -  show_memstats
   782    794     #puts "Maximum memory usage: [sqlite4_memory_highwater 1] bytes"
   783    795     #puts "Current memory usage: [sqlite4_memory_highwater] bytes"
   784         -  if {[info commands sqlite4_memdebug_malloc_count] ne ""} {
   785         -    puts "Number of malloc()  : [sqlite4_memdebug_malloc_count] calls"
   786         -  }
          796  +  #if {[info commands sqlite4_memdebug_malloc_count] ne ""} {
          797  +  #  puts "Number of malloc()  : [sqlite4_memdebug_malloc_count] calls"
          798  +  #}
   787    799     if {$::cmdlinearg(malloctrace)} {
   788    800       puts "Writing malloc() report to malloc.txt..."
   789    801       testmem report malloc.txt
   790    802     }
   791    803     foreach f [glob -nocomplain test.db-*-journal] {
   792    804       forcedelete $f
   793    805     }
................................................................................
   796    808     }
   797    809     exit [expr {$nErr>0}]
   798    810   }
   799    811   
   800    812   # Display memory statistics for analysis and debugging purposes.
   801    813   #
   802    814   proc show_memstats {} {
   803         -  set x [sqlite4_env_status SQLITE4_ENVSTATUS_MEMORY_USED 0]
   804         -  set y [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_SIZE 0]
   805         -  set val [format {now %10d  max %10d  max-size %10d} \
   806         -              [lindex $x 1] [lindex $x 2] [lindex $y 2]]
   807         -  puts "Memory used:          $val"
   808         -  set x [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0]
   809         -  set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
   810         -  puts "Allocation count:     $val"
          815  +  set nOut        [test_mm_stat out]
          816  +  set nOutHw      [test_mm_stat out_hw]
          817  +  set nMaxRequest [test_mm_stat size]
          818  +  set nUnit       [test_mm_stat units]
          819  +  set nUnitHw     [test_mm_stat units_hw]
          820  +
          821  +  puts [format                                        \
          822  +    "Memory used: now %-10d max %-10d max-size %-10d" \
          823  +    $nOut $nOutHw $nMaxRequest                        \
          824  +  ]
          825  +  puts [format "Allocations: now %-10d max %-10d" $nUnit $nUnitHw]
          826  +
   811    827     ifcapable yytrackmaxstackdepth {
   812    828       set x [sqlite4_env_status SQLITE4_ENVSTATUS_PARSER_STACK 0]
   813    829       set val [format {               max %10d} [lindex $x 2]]
   814    830       puts "Parser stack depth:    $val"
   815    831     }
   816    832   }
   817    833