xref: /sqlite-3.40.0/src/test_malloc.c (revision 1c826650)
1 /*
2 ** 2007 August 15
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 *************************************************************************
12 **
13 ** This file contains code used to implement test interfaces to the
14 ** memory allocation subsystem.
15 **
16 ** $Id: test_malloc.c,v 1.47 2008/08/05 17:53:24 drh Exp $
17 */
18 #include "sqliteInt.h"
19 #include "tcl.h"
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 
24 /*
25 ** This structure is used to encapsulate the global state variables used
26 ** by malloc() fault simulation.
27 */
28 static struct MemFault {
29   int iCountdown;         /* Number of pending successes before a failure */
30   int nRepeat;            /* Number of times to repeat the failure */
31   int nBenign;            /* Number of benign failures seen since last config */
32   int nFail;              /* Number of failures seen since last config */
33   u8 enable;              /* True if enabled */
34   int isInstalled;        /* True if the fault simulation layer is installed */
35   int isBenignMode;       /* True if malloc failures are considered benign */
36   sqlite3_mem_methods m;  /* 'Real' malloc implementation */
37 } memfault;
38 
39 /*
40 ** This routine exists as a place to set a breakpoint that will
41 ** fire on any simulated malloc() failure.
42 */
43 static void sqlite3Fault(void){
44   static int cnt = 0;
45   cnt++;
46 }
47 
48 /*
49 ** Check to see if a fault should be simulated.  Return true to simulate
50 ** the fault.  Return false if the fault should not be simulated.
51 */
52 static int faultsimStep(){
53   if( likely(!memfault.enable) ){
54     return 0;
55   }
56   if( memfault.iCountdown>0 ){
57     memfault.iCountdown--;
58     return 0;
59   }
60   sqlite3Fault();
61   memfault.nFail++;
62   if( memfault.isBenignMode>0 ){
63     memfault.nBenign++;
64   }
65   memfault.nRepeat--;
66   if( memfault.nRepeat<=0 ){
67     memfault.enable = 0;
68   }
69   return 1;
70 }
71 
72 /*
73 ** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation
74 ** logic.
75 */
76 static void *faultsimMalloc(int n){
77   void *p = 0;
78   if( !faultsimStep() ){
79     p = memfault.m.xMalloc(n);
80   }
81   return p;
82 }
83 
84 
85 /*
86 ** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation
87 ** logic.
88 */
89 static void *faultsimRealloc(void *pOld, int n){
90   void *p = 0;
91   if( !faultsimStep() ){
92     p = memfault.m.xRealloc(pOld, n);
93   }
94   return p;
95 }
96 
97 /*
98 ** The following method calls are passed directly through to the underlying
99 ** malloc system:
100 **
101 **     xFree
102 **     xSize
103 **     xRoundup
104 **     xInit
105 **     xShutdown
106 */
107 static void faultsimFree(void *p){
108   memfault.m.xFree(p);
109 }
110 static int faultsimSize(void *p){
111   return memfault.m.xSize(p);
112 }
113 static int faultsimRoundup(int n){
114   return memfault.m.xRoundup(n);
115 }
116 static int faultsimInit(void *p){
117   return memfault.m.xInit(memfault.m.pAppData);
118 }
119 static void faultsimShutdown(void *p){
120   memfault.m.xShutdown(memfault.m.pAppData);
121 }
122 
123 /*
124 ** This routine configures the malloc failure simulation.  After
125 ** calling this routine, the next nDelay mallocs will succeed, followed
126 ** by a block of nRepeat failures, after which malloc() calls will begin
127 ** to succeed again.
128 */
129 static void faultsimConfig(int nDelay, int nRepeat){
130   memfault.iCountdown = nDelay;
131   memfault.nRepeat = nRepeat;
132   memfault.nBenign = 0;
133   memfault.nFail = 0;
134   memfault.enable = nDelay>=0;
135 }
136 
137 /*
138 ** Return the number of faults (both hard and benign faults) that have
139 ** occurred since the injector was last configured.
140 */
141 static int faultsimFailures(void){
142   return memfault.nFail;
143 }
144 
145 /*
146 ** Return the number of benign faults that have occurred since the
147 ** injector was last configured.
148 */
149 static int faultsimBenignFailures(void){
150   return memfault.nBenign;
151 }
152 
153 /*
154 ** Return the number of successes that will occur before the next failure.
155 ** If no failures are scheduled, return -1.
156 */
157 static int faultsimPending(void){
158   if( memfault.enable ){
159     return memfault.iCountdown;
160   }else{
161     return -1;
162   }
163 }
164 
165 
166 static void faultsimBeginBenign(void){
167   memfault.isBenignMode++;
168 }
169 static void faultsimEndBenign(void){
170   memfault.isBenignMode--;
171 }
172 
173 /*
174 ** Add or remove the fault-simulation layer using sqlite3_config(). If
175 ** the argument is non-zero, the
176 */
177 static int faultsimInstall(int install){
178   static struct sqlite3_mem_methods m = {
179     faultsimMalloc,                   /* xMalloc */
180     faultsimFree,                     /* xFree */
181     faultsimRealloc,                  /* xRealloc */
182     faultsimSize,                     /* xSize */
183     faultsimRoundup,                  /* xRoundup */
184     faultsimInit,                     /* xInit */
185     faultsimShutdown,                 /* xShutdown */
186     0                                 /* pAppData */
187   };
188   int rc;
189 
190   install = (install ? 1 : 0);
191   assert(memfault.isInstalled==1 || memfault.isInstalled==0);
192 
193   if( install==memfault.isInstalled ){
194     return SQLITE_ERROR;
195   }
196 
197   if( install ){
198     rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m);
199     assert(memfault.m.xMalloc);
200     if( rc==SQLITE_OK ){
201       rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
202     }
203     sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,
204         faultsimBeginBenign, faultsimEndBenign
205     );
206   }else{
207     assert(memfault.m.xMalloc);
208     rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m);
209     sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0);
210   }
211 
212   if( rc==SQLITE_OK ){
213     memfault.isInstalled = 1;
214   }
215   return rc;
216 }
217 
218 #ifdef SQLITE_TEST
219 
220 /*
221 ** This function is implemented in test1.c. Returns a pointer to a static
222 ** buffer containing the symbolic SQLite error code that corresponds to
223 ** the least-significant 8-bits of the integer passed as an argument.
224 ** For example:
225 **
226 **   sqlite3TestErrorName(1) -> "SQLITE_ERROR"
227 */
228 const char *sqlite3TestErrorName(int);
229 
230 /*
231 ** Transform pointers to text and back again
232 */
233 static void pointerToText(void *p, char *z){
234   static const char zHex[] = "0123456789abcdef";
235   int i, k;
236   unsigned int u;
237   sqlite3_uint64 n;
238   if( p==0 ){
239     strcpy(z, "0");
240     return;
241   }
242   if( sizeof(n)==sizeof(p) ){
243     memcpy(&n, &p, sizeof(p));
244   }else if( sizeof(u)==sizeof(p) ){
245     memcpy(&u, &p, sizeof(u));
246     n = u;
247   }else{
248     assert( 0 );
249   }
250   for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
251     z[k] = zHex[n&0xf];
252     n >>= 4;
253   }
254   z[sizeof(p)*2] = 0;
255 }
256 static int hexToInt(int h){
257   if( h>='0' && h<='9' ){
258     return h - '0';
259   }else if( h>='a' && h<='f' ){
260     return h - 'a' + 10;
261   }else{
262     return -1;
263   }
264 }
265 static int textToPointer(const char *z, void **pp){
266   sqlite3_uint64 n = 0;
267   int i;
268   unsigned int u;
269   for(i=0; i<sizeof(void*)*2 && z[0]; i++){
270     int v;
271     v = hexToInt(*z++);
272     if( v<0 ) return TCL_ERROR;
273     n = n*16 + v;
274   }
275   if( *z!=0 ) return TCL_ERROR;
276   if( sizeof(n)==sizeof(*pp) ){
277     memcpy(pp, &n, sizeof(n));
278   }else if( sizeof(u)==sizeof(*pp) ){
279     u = (unsigned int)n;
280     memcpy(pp, &u, sizeof(u));
281   }else{
282     assert( 0 );
283   }
284   return TCL_OK;
285 }
286 
287 /*
288 ** Usage:    sqlite3_malloc  NBYTES
289 **
290 ** Raw test interface for sqlite3_malloc().
291 */
292 static int test_malloc(
293   void * clientData,
294   Tcl_Interp *interp,
295   int objc,
296   Tcl_Obj *CONST objv[]
297 ){
298   int nByte;
299   void *p;
300   char zOut[100];
301   if( objc!=2 ){
302     Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
303     return TCL_ERROR;
304   }
305   if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
306   p = sqlite3_malloc((unsigned)nByte);
307   pointerToText(p, zOut);
308   Tcl_AppendResult(interp, zOut, NULL);
309   return TCL_OK;
310 }
311 
312 /*
313 ** Usage:    sqlite3_realloc  PRIOR  NBYTES
314 **
315 ** Raw test interface for sqlite3_realloc().
316 */
317 static int test_realloc(
318   void * clientData,
319   Tcl_Interp *interp,
320   int objc,
321   Tcl_Obj *CONST objv[]
322 ){
323   int nByte;
324   void *pPrior, *p;
325   char zOut[100];
326   if( objc!=3 ){
327     Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
328     return TCL_ERROR;
329   }
330   if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
331   if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
332     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
333     return TCL_ERROR;
334   }
335   p = sqlite3_realloc(pPrior, (unsigned)nByte);
336   pointerToText(p, zOut);
337   Tcl_AppendResult(interp, zOut, NULL);
338   return TCL_OK;
339 }
340 
341 /*
342 ** Usage:    sqlite3_free  PRIOR
343 **
344 ** Raw test interface for sqlite3_free().
345 */
346 static int test_free(
347   void * clientData,
348   Tcl_Interp *interp,
349   int objc,
350   Tcl_Obj *CONST objv[]
351 ){
352   void *pPrior;
353   if( objc!=2 ){
354     Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
355     return TCL_ERROR;
356   }
357   if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
358     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
359     return TCL_ERROR;
360   }
361   sqlite3_free(pPrior);
362   return TCL_OK;
363 }
364 
365 /*
366 ** These routines are in test_hexio.c
367 */
368 int sqlite3TestHexToBin(const char *, int, char *);
369 int sqlite3TestBinToHex(char*,int);
370 
371 /*
372 ** Usage:    memset  ADDRESS  SIZE  HEX
373 **
374 ** Set a chunk of memory (obtained from malloc, probably) to a
375 ** specified hex pattern.
376 */
377 static int test_memset(
378   void * clientData,
379   Tcl_Interp *interp,
380   int objc,
381   Tcl_Obj *CONST objv[]
382 ){
383   void *p;
384   int size, n, i;
385   char *zHex;
386   char *zOut;
387   char zBin[100];
388 
389   if( objc!=4 ){
390     Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
391     return TCL_ERROR;
392   }
393   if( textToPointer(Tcl_GetString(objv[1]), &p) ){
394     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
395     return TCL_ERROR;
396   }
397   if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
398     return TCL_ERROR;
399   }
400   if( size<=0 ){
401     Tcl_AppendResult(interp, "size must be positive", (char*)0);
402     return TCL_ERROR;
403   }
404   zHex = Tcl_GetStringFromObj(objv[3], &n);
405   if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
406   n = sqlite3TestHexToBin(zHex, n, zBin);
407   if( n==0 ){
408     Tcl_AppendResult(interp, "no data", (char*)0);
409     return TCL_ERROR;
410   }
411   zOut = p;
412   for(i=0; i<size; i++){
413     zOut[i] = zBin[i%n];
414   }
415   return TCL_OK;
416 }
417 
418 /*
419 ** Usage:    memget  ADDRESS  SIZE
420 **
421 ** Return memory as hexadecimal text.
422 */
423 static int test_memget(
424   void * clientData,
425   Tcl_Interp *interp,
426   int objc,
427   Tcl_Obj *CONST objv[]
428 ){
429   void *p;
430   int size, n;
431   char *zBin;
432   char zHex[100];
433 
434   if( objc!=3 ){
435     Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
436     return TCL_ERROR;
437   }
438   if( textToPointer(Tcl_GetString(objv[1]), &p) ){
439     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
440     return TCL_ERROR;
441   }
442   if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
443     return TCL_ERROR;
444   }
445   if( size<=0 ){
446     Tcl_AppendResult(interp, "size must be positive", (char*)0);
447     return TCL_ERROR;
448   }
449   zBin = p;
450   while( size>0 ){
451     if( size>(sizeof(zHex)-1)/2 ){
452       n = (sizeof(zHex)-1)/2;
453     }else{
454       n = size;
455     }
456     memcpy(zHex, zBin, n);
457     zBin += n;
458     size -= n;
459     sqlite3TestBinToHex(zHex, n);
460     Tcl_AppendResult(interp, zHex, (char*)0);
461   }
462   return TCL_OK;
463 }
464 
465 /*
466 ** Usage:    sqlite3_memory_used
467 **
468 ** Raw test interface for sqlite3_memory_used().
469 */
470 static int test_memory_used(
471   void * clientData,
472   Tcl_Interp *interp,
473   int objc,
474   Tcl_Obj *CONST objv[]
475 ){
476   Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
477   return TCL_OK;
478 }
479 
480 /*
481 ** Usage:    sqlite3_memory_highwater ?RESETFLAG?
482 **
483 ** Raw test interface for sqlite3_memory_highwater().
484 */
485 static int test_memory_highwater(
486   void * clientData,
487   Tcl_Interp *interp,
488   int objc,
489   Tcl_Obj *CONST objv[]
490 ){
491   int resetFlag = 0;
492   if( objc!=1 && objc!=2 ){
493     Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
494     return TCL_ERROR;
495   }
496   if( objc==2 ){
497     if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
498   }
499   Tcl_SetObjResult(interp,
500      Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
501   return TCL_OK;
502 }
503 
504 /*
505 ** Usage:    sqlite3_memdebug_backtrace DEPTH
506 **
507 ** Set the depth of backtracing.  If SQLITE_MEMDEBUG is not defined
508 ** then this routine is a no-op.
509 */
510 static int test_memdebug_backtrace(
511   void * clientData,
512   Tcl_Interp *interp,
513   int objc,
514   Tcl_Obj *CONST objv[]
515 ){
516   int depth;
517   if( objc!=2 ){
518     Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
519     return TCL_ERROR;
520   }
521   if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
522 #ifdef SQLITE_MEMDEBUG
523   {
524     extern void sqlite3MemdebugBacktrace(int);
525     sqlite3MemdebugBacktrace(depth);
526   }
527 #endif
528   return TCL_OK;
529 }
530 
531 /*
532 ** Usage:    sqlite3_memdebug_dump  FILENAME
533 **
534 ** Write a summary of unfreed memory to FILENAME.
535 */
536 static int test_memdebug_dump(
537   void * clientData,
538   Tcl_Interp *interp,
539   int objc,
540   Tcl_Obj *CONST objv[]
541 ){
542   if( objc!=2 ){
543     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
544     return TCL_ERROR;
545   }
546 #if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
547      || defined(SQLITE_POW2_MEMORY_SIZE)
548   {
549     extern void sqlite3MemdebugDump(const char*);
550     sqlite3MemdebugDump(Tcl_GetString(objv[1]));
551   }
552 #endif
553   return TCL_OK;
554 }
555 
556 /*
557 ** Usage:    sqlite3_memdebug_malloc_count
558 **
559 ** Return the total number of times malloc() has been called.
560 */
561 static int test_memdebug_malloc_count(
562   void * clientData,
563   Tcl_Interp *interp,
564   int objc,
565   Tcl_Obj *CONST objv[]
566 ){
567   int nMalloc = -1;
568   if( objc!=1 ){
569     Tcl_WrongNumArgs(interp, 1, objv, "");
570     return TCL_ERROR;
571   }
572 #if defined(SQLITE_MEMDEBUG)
573   {
574     extern int sqlite3MemdebugMallocCount();
575     nMalloc = sqlite3MemdebugMallocCount();
576   }
577 #endif
578   Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
579   return TCL_OK;
580 }
581 
582 
583 /*
584 ** Usage:    sqlite3_memdebug_fail  COUNTER  ?OPTIONS?
585 **
586 ** where options are:
587 **
588 **     -repeat    <count>
589 **     -benigncnt <varname>
590 **
591 ** Arrange for a simulated malloc() failure after COUNTER successes.
592 ** If a repeat count is specified, the fault is repeated that many
593 ** times.
594 **
595 ** Each call to this routine overrides the prior counter value.
596 ** This routine returns the number of simulated failures that have
597 ** happened since the previous call to this routine.
598 **
599 ** To disable simulated failures, use a COUNTER of -1.
600 */
601 static int test_memdebug_fail(
602   void * clientData,
603   Tcl_Interp *interp,
604   int objc,
605   Tcl_Obj *CONST objv[]
606 ){
607   int ii;
608   int iFail;
609   int nRepeat = 1;
610   Tcl_Obj *pBenignCnt = 0;
611   int nBenign;
612   int nFail = 0;
613 
614   if( objc<2 ){
615     Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
616     return TCL_ERROR;
617   }
618   if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
619 
620   for(ii=2; ii<objc; ii+=2){
621     int nOption;
622     char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
623     char *zErr = 0;
624 
625     if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
626       if( ii==(objc-1) ){
627         zErr = "option requires an argument: ";
628       }else{
629         if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
630           return TCL_ERROR;
631         }
632       }
633     }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
634       if( ii==(objc-1) ){
635         zErr = "option requires an argument: ";
636       }else{
637         pBenignCnt = objv[ii+1];
638       }
639     }else{
640       zErr = "unknown option: ";
641     }
642 
643     if( zErr ){
644       Tcl_AppendResult(interp, zErr, zOption, 0);
645       return TCL_ERROR;
646     }
647   }
648 
649   nBenign = faultsimBenignFailures();
650   nFail = faultsimFailures();
651   faultsimConfig(iFail, nRepeat);
652 
653   if( pBenignCnt ){
654     Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
655   }
656   Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
657   return TCL_OK;
658 }
659 
660 /*
661 ** Usage:    sqlite3_memdebug_pending
662 **
663 ** Return the number of malloc() calls that will succeed before a
664 ** simulated failure occurs. A negative return value indicates that
665 ** no malloc() failure is scheduled.
666 */
667 static int test_memdebug_pending(
668   void * clientData,
669   Tcl_Interp *interp,
670   int objc,
671   Tcl_Obj *CONST objv[]
672 ){
673   int nPending;
674   if( objc!=1 ){
675     Tcl_WrongNumArgs(interp, 1, objv, "");
676     return TCL_ERROR;
677   }
678   nPending = faultsimPending();
679   Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
680   return TCL_OK;
681 }
682 
683 
684 /*
685 ** Usage:    sqlite3_memdebug_settitle TITLE
686 **
687 ** Set a title string stored with each allocation.  The TITLE is
688 ** typically the name of the test that was running when the
689 ** allocation occurred.  The TITLE is stored with the allocation
690 ** and can be used to figure out which tests are leaking memory.
691 **
692 ** Each title overwrite the previous.
693 */
694 static int test_memdebug_settitle(
695   void * clientData,
696   Tcl_Interp *interp,
697   int objc,
698   Tcl_Obj *CONST objv[]
699 ){
700   const char *zTitle;
701   if( objc!=2 ){
702     Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
703     return TCL_ERROR;
704   }
705   zTitle = Tcl_GetString(objv[1]);
706 #ifdef SQLITE_MEMDEBUG
707   {
708     extern int sqlite3MemdebugSettitle(const char*);
709     sqlite3MemdebugSettitle(zTitle);
710   }
711 #endif
712   return TCL_OK;
713 }
714 
715 #define MALLOC_LOG_FRAMES 10
716 static Tcl_HashTable aMallocLog;
717 static int mallocLogEnabled = 0;
718 
719 typedef struct MallocLog MallocLog;
720 struct MallocLog {
721   int nCall;
722   int nByte;
723 };
724 
725 #ifdef SQLITE_MEMDEBUG
726 static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
727   if( mallocLogEnabled ){
728     MallocLog *pLog;
729     Tcl_HashEntry *pEntry;
730     int isNew;
731 
732     int aKey[MALLOC_LOG_FRAMES];
733     int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
734 
735     memset(aKey, 0, nKey);
736     if( (sizeof(void*)*nFrame)<nKey ){
737       nKey = nFrame*sizeof(void*);
738     }
739     memcpy(aKey, aFrame, nKey);
740 
741     pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
742     if( isNew ){
743       pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
744       memset(pLog, 0, sizeof(MallocLog));
745       Tcl_SetHashValue(pEntry, (ClientData)pLog);
746     }else{
747       pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
748     }
749 
750     pLog->nCall++;
751     pLog->nByte += nByte;
752   }
753 }
754 #endif /* SQLITE_MEMDEBUG */
755 
756 static void test_memdebug_log_clear(){
757   Tcl_HashSearch search;
758   Tcl_HashEntry *pEntry;
759   for(
760     pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
761     pEntry;
762     pEntry=Tcl_NextHashEntry(&search)
763   ){
764     MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
765     Tcl_Free((char *)pLog);
766   }
767   Tcl_DeleteHashTable(&aMallocLog);
768   Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
769 }
770 
771 static int test_memdebug_log(
772   void * clientData,
773   Tcl_Interp *interp,
774   int objc,
775   Tcl_Obj *CONST objv[]
776 ){
777   static int isInit = 0;
778   int iSub;
779 
780   static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
781   enum MB_enum {
782       MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
783   };
784 
785   if( !isInit ){
786 #ifdef SQLITE_MEMDEBUG
787     extern void sqlite3MemdebugBacktraceCallback(
788         void (*xBacktrace)(int, int, void **));
789     sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
790 #endif
791     Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
792     isInit = 1;
793   }
794 
795   if( objc<2 ){
796     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
797   }
798   if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
799     return TCL_ERROR;
800   }
801 
802   switch( (enum MB_enum)iSub ){
803     case MB_LOG_START:
804       mallocLogEnabled = 1;
805       break;
806     case MB_LOG_STOP:
807       mallocLogEnabled = 0;
808       break;
809     case MB_LOG_DUMP: {
810       Tcl_HashSearch search;
811       Tcl_HashEntry *pEntry;
812       Tcl_Obj *pRet = Tcl_NewObj();
813 
814       assert(sizeof(int)==sizeof(void*));
815 
816       for(
817         pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
818         pEntry;
819         pEntry=Tcl_NextHashEntry(&search)
820       ){
821         Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
822         MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
823         int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
824         int ii;
825 
826         apElem[0] = Tcl_NewIntObj(pLog->nCall);
827         apElem[1] = Tcl_NewIntObj(pLog->nByte);
828         for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
829           apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
830         }
831 
832         Tcl_ListObjAppendElement(interp, pRet,
833             Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
834         );
835       }
836 
837       Tcl_SetObjResult(interp, pRet);
838       break;
839     }
840     case MB_LOG_CLEAR: {
841       test_memdebug_log_clear();
842       break;
843     }
844 
845     case MB_LOG_SYNC: {
846 #ifdef SQLITE_MEMDEBUG
847       extern void sqlite3MemdebugSync();
848       test_memdebug_log_clear();
849       mallocLogEnabled = 1;
850       sqlite3MemdebugSync();
851 #endif
852       break;
853     }
854   }
855 
856   return TCL_OK;
857 }
858 
859 /*
860 ** Usage:    sqlite3_config_scratch SIZE N
861 **
862 ** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
863 ** The buffer is static and is of limited size.  N might be
864 ** adjusted downward as needed to accomodate the requested size.
865 ** The revised value of N is returned.
866 **
867 ** A negative SIZE causes the buffer pointer to be NULL.
868 */
869 static int test_config_scratch(
870   void * clientData,
871   Tcl_Interp *interp,
872   int objc,
873   Tcl_Obj *CONST objv[]
874 ){
875   int sz, N, rc;
876   Tcl_Obj *pResult;
877   static char *buf = 0;
878   if( objc!=3 ){
879     Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
880     return TCL_ERROR;
881   }
882   if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
883   if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
884   free(buf);
885   if( sz<0 ){
886     buf = 0;
887     rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
888   }else{
889     buf = malloc( sz*N + 1 );
890     rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
891   }
892   pResult = Tcl_NewObj();
893   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
894   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
895   Tcl_SetObjResult(interp, pResult);
896   return TCL_OK;
897 }
898 
899 /*
900 ** Usage:    sqlite3_config_pagecache SIZE N
901 **
902 ** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
903 ** The buffer is static and is of limited size.  N might be
904 ** adjusted downward as needed to accomodate the requested size.
905 ** The revised value of N is returned.
906 **
907 ** A negative SIZE causes the buffer pointer to be NULL.
908 */
909 static int test_config_pagecache(
910   void * clientData,
911   Tcl_Interp *interp,
912   int objc,
913   Tcl_Obj *CONST objv[]
914 ){
915   int sz, N, rc;
916   Tcl_Obj *pResult;
917   static char *buf = 0;
918   if( objc!=3 ){
919     Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
920     return TCL_ERROR;
921   }
922   if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
923   if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
924   free(buf);
925   if( sz<0 ){
926     buf = 0;
927     rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
928   }else{
929     buf = malloc( sz*N );
930     rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
931   }
932   pResult = Tcl_NewObj();
933   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
934   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
935   Tcl_SetObjResult(interp, pResult);
936   return TCL_OK;
937 }
938 
939 /*
940 ** Usage:    sqlite3_config_memstatus BOOLEAN
941 **
942 ** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
943 */
944 static int test_config_memstatus(
945   void * clientData,
946   Tcl_Interp *interp,
947   int objc,
948   Tcl_Obj *CONST objv[]
949 ){
950   int enable, rc;
951   if( objc!=2 ){
952     Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
953     return TCL_ERROR;
954   }
955   if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR;
956   rc = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, enable);
957   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
958   return TCL_OK;
959 }
960 
961 /*
962 ** Usage:    sqlite3_config_chunkalloc
963 **
964 */
965 static int test_config_chunkalloc(
966   void * clientData,
967   Tcl_Interp *interp,
968   int objc,
969   Tcl_Obj *CONST objv[]
970 ){
971   int rc;
972   int nThreshold;
973   if( objc!=2 ){
974     Tcl_WrongNumArgs(interp, 1, objv, "THRESHOLD");
975     return TCL_ERROR;
976   }
977   if( Tcl_GetIntFromObj(interp, objv[1], &nThreshold) ) return TCL_ERROR;
978   rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC, nThreshold);
979   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
980   return TCL_OK;
981 }
982 
983 /*
984 ** Usage:    sqlite3_config_lookaside  SIZE  COUNT
985 **
986 */
987 static int test_config_lookaside(
988   void * clientData,
989   Tcl_Interp *interp,
990   int objc,
991   Tcl_Obj *CONST objv[]
992 ){
993   int rc;
994   int sz, cnt;
995   if( objc!=3 ){
996     Tcl_WrongNumArgs(interp, 1, objv, "SIZE COUNT");
997     return TCL_ERROR;
998   }
999   if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
1000   if( Tcl_GetIntFromObj(interp, objv[2], &cnt) ) return TCL_ERROR;
1001   rc = sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt);
1002   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1003   return TCL_OK;
1004 }
1005 
1006 
1007 /*
1008 ** Usage:    sqlite3_db_config_lookaside  CONNECTION  BUFID  SIZE  COUNT
1009 **
1010 ** There are two static buffers with BUFID 1 and 2.   Each static buffer
1011 ** is 10KB in size.  A BUFID of 0 indicates that the buffer should be NULL
1012 ** which will cause sqlite3_db_config() to allocate space on its own.
1013 */
1014 static int test_db_config_lookaside(
1015   void * clientData,
1016   Tcl_Interp *interp,
1017   int objc,
1018   Tcl_Obj *CONST objv[]
1019 ){
1020   int rc;
1021   int sz, cnt;
1022   sqlite3 *db;
1023   int bufid;
1024   static char azBuf[2][10000];
1025   int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
1026   if( objc!=5 ){
1027     Tcl_WrongNumArgs(interp, 1, objv, "BUFID SIZE COUNT");
1028     return TCL_ERROR;
1029   }
1030   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
1031   if( Tcl_GetIntFromObj(interp, objv[2], &bufid) ) return TCL_ERROR;
1032   if( Tcl_GetIntFromObj(interp, objv[3], &sz) ) return TCL_ERROR;
1033   if( Tcl_GetIntFromObj(interp, objv[4], &cnt) ) return TCL_ERROR;
1034   if( bufid==0 ){
1035     rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, 0, sz, cnt);
1036   }else if( bufid>=1 && bufid<=2 && sz*cnt<=sizeof(azBuf[0]) ){
1037     rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, azBuf[bufid], sz,cnt);
1038   }else{
1039     Tcl_AppendResult(interp, "illegal arguments - see documentation", (char*)0);
1040     return TCL_ERROR;
1041   }
1042   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1043   return TCL_OK;
1044 }
1045 
1046 /*
1047 ** Usage:
1048 **
1049 **   sqlite3_config_heap NBYTE NMINALLOC
1050 */
1051 static int test_config_heap(
1052   void * clientData,
1053   Tcl_Interp *interp,
1054   int objc,
1055   Tcl_Obj *CONST objv[]
1056 ){
1057   static char *zBuf; /* Use this memory */
1058   static int szBuf;  /* Bytes allocated for zBuf */
1059   int nByte;         /* Size of buffer to pass to sqlite3_config() */
1060   int nMinAlloc;     /* Size of minimum allocation */
1061   int rc;            /* Return code of sqlite3_config() */
1062 
1063   Tcl_Obj * CONST *aArg = &objv[1];
1064   int nArg = objc-1;
1065 
1066   if( nArg!=2 ){
1067     Tcl_WrongNumArgs(interp, 1, objv, "NBYTE NMINALLOC");
1068     return TCL_ERROR;
1069   }
1070   if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR;
1071   if( Tcl_GetIntFromObj(interp, aArg[1], &nMinAlloc) ) return TCL_ERROR;
1072 
1073   if( nByte==0 ){
1074     free( zBuf );
1075     zBuf = 0;
1076     szBuf = 0;
1077     rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
1078   }else{
1079     zBuf = realloc(zBuf, nByte);
1080     szBuf = nByte;
1081     rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc);
1082   }
1083 
1084   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
1085   return TCL_OK;
1086 }
1087 
1088 /*
1089 ** tclcmd:     sqlite3_config_error  [DB]
1090 **
1091 ** Invoke sqlite3_config() or sqlite3_db_config() with invalid
1092 ** opcodes and verify that they return errors.
1093 */
1094 static int test_config_error(
1095   void * clientData,
1096   Tcl_Interp *interp,
1097   int objc,
1098   Tcl_Obj *CONST objv[]
1099 ){
1100   sqlite3 *db;
1101   int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
1102 
1103   if( objc!=2 && objc!=1 ){
1104     Tcl_WrongNumArgs(interp, 1, objv, "[DB]");
1105     return TCL_ERROR;
1106   }
1107   if( objc==2 ){
1108     if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
1109     if( sqlite3_db_config(db, 99999)!=SQLITE_ERROR ){
1110       Tcl_AppendResult(interp,
1111             "sqlite3_db_config(db, 99999) does not return SQLITE_ERROR",
1112             (char*)0);
1113       return TCL_ERROR;
1114     }
1115   }else{
1116     if( sqlite3_config(99999)!=SQLITE_ERROR ){
1117       Tcl_AppendResult(interp,
1118           "sqlite3_config(99999) does not return SQLITE_ERROR",
1119           (char*)0);
1120       return TCL_ERROR;
1121     }
1122   }
1123   return TCL_OK;
1124 }
1125 
1126 /*
1127 ** Usage:
1128 **
1129 **   sqlite3_dump_memsys3  FILENAME
1130 **   sqlite3_dump_memsys5  FILENAME
1131 **
1132 ** Write a summary of unfreed memsys3 allocations to FILENAME.
1133 */
1134 static int test_dump_memsys3(
1135   void * clientData,
1136   Tcl_Interp *interp,
1137   int objc,
1138   Tcl_Obj *CONST objv[]
1139 ){
1140   if( objc!=2 ){
1141     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
1142     return TCL_ERROR;
1143   }
1144 
1145   switch( (int)clientData ){
1146     case 3: {
1147 #ifdef SQLITE_ENABLE_MEMSYS3
1148       extern void sqlite3Memsys3Dump(const char*);
1149       sqlite3Memsys3Dump(Tcl_GetString(objv[1]));
1150       break;
1151 #endif
1152     }
1153     case 5: {
1154 #ifdef SQLITE_ENABLE_MEMSYS5
1155       extern void sqlite3Memsys5Dump(const char*);
1156       sqlite3Memsys5Dump(Tcl_GetString(objv[1]));
1157       break;
1158 #endif
1159     }
1160   }
1161   return TCL_OK;
1162 }
1163 
1164 /*
1165 ** Usage:    sqlite3_status  OPCODE  RESETFLAG
1166 **
1167 ** Return a list of three elements which are the sqlite3_status() return
1168 ** code, the current value, and the high-water mark value.
1169 */
1170 static int test_status(
1171   void * clientData,
1172   Tcl_Interp *interp,
1173   int objc,
1174   Tcl_Obj *CONST objv[]
1175 ){
1176   int rc, iValue, mxValue;
1177   int i, op, resetFlag;
1178   const char *zOpName;
1179   static const struct {
1180     const char *zName;
1181     int op;
1182   } aOp[] = {
1183     { "SQLITE_STATUS_MEMORY_USED",         SQLITE_STATUS_MEMORY_USED         },
1184     { "SQLITE_STATUS_MALLOC_SIZE",         SQLITE_STATUS_MALLOC_SIZE         },
1185     { "SQLITE_STATUS_PAGECACHE_USED",      SQLITE_STATUS_PAGECACHE_USED      },
1186     { "SQLITE_STATUS_PAGECACHE_OVERFLOW",  SQLITE_STATUS_PAGECACHE_OVERFLOW  },
1187     { "SQLITE_STATUS_PAGECACHE_SIZE",      SQLITE_STATUS_PAGECACHE_SIZE      },
1188     { "SQLITE_STATUS_SCRATCH_USED",        SQLITE_STATUS_SCRATCH_USED        },
1189     { "SQLITE_STATUS_SCRATCH_OVERFLOW",    SQLITE_STATUS_SCRATCH_OVERFLOW    },
1190     { "SQLITE_STATUS_SCRATCH_SIZE",        SQLITE_STATUS_SCRATCH_SIZE        },
1191     { "SQLITE_STATUS_PARSER_STACK",        SQLITE_STATUS_PARSER_STACK        },
1192   };
1193   Tcl_Obj *pResult;
1194   if( objc!=3 ){
1195     Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
1196     return TCL_ERROR;
1197   }
1198   zOpName = Tcl_GetString(objv[1]);
1199   for(i=0; i<ArraySize(aOp); i++){
1200     if( strcmp(aOp[i].zName, zOpName)==0 ){
1201       op = aOp[i].op;
1202       break;
1203     }
1204   }
1205   if( i>=ArraySize(aOp) ){
1206     if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR;
1207   }
1208   if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR;
1209   iValue = 0;
1210   mxValue = 0;
1211   rc = sqlite3_status(op, &iValue, &mxValue, resetFlag);
1212   pResult = Tcl_NewObj();
1213   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
1214   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
1215   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
1216   Tcl_SetObjResult(interp, pResult);
1217   return TCL_OK;
1218 }
1219 
1220 /*
1221 ** Usage:    sqlite3_db_status  DATABASE  OPCODE  RESETFLAG
1222 **
1223 ** Return a list of three elements which are the sqlite3_db_status() return
1224 ** code, the current value, and the high-water mark value.
1225 */
1226 static int test_db_status(
1227   void * clientData,
1228   Tcl_Interp *interp,
1229   int objc,
1230   Tcl_Obj *CONST objv[]
1231 ){
1232   int rc, iValue, mxValue;
1233   int i, op, resetFlag;
1234   const char *zOpName;
1235   sqlite3 *db;
1236   int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
1237   static const struct {
1238     const char *zName;
1239     int op;
1240   } aOp[] = {
1241     { "SQLITE_DBSTATUS_LOOKASIDE_USED",    SQLITE_DBSTATUS_LOOKASIDE_USED   },
1242   };
1243   Tcl_Obj *pResult;
1244   if( objc!=4 ){
1245     Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
1246     return TCL_ERROR;
1247   }
1248   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
1249   zOpName = Tcl_GetString(objv[2]);
1250   for(i=0; i<ArraySize(aOp); i++){
1251     if( strcmp(aOp[i].zName, zOpName)==0 ){
1252       op = aOp[i].op;
1253       break;
1254     }
1255   }
1256   if( i>=ArraySize(aOp) ){
1257     if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR;
1258   }
1259   if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR;
1260   iValue = 0;
1261   mxValue = 0;
1262   rc = sqlite3_db_status(db, op, &iValue, &mxValue, resetFlag);
1263   pResult = Tcl_NewObj();
1264   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
1265   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
1266   Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
1267   Tcl_SetObjResult(interp, pResult);
1268   return TCL_OK;
1269 }
1270 
1271 /*
1272 ** install_malloc_faultsim BOOLEAN
1273 */
1274 static int test_install_malloc_faultsim(
1275   void * clientData,
1276   Tcl_Interp *interp,
1277   int objc,
1278   Tcl_Obj *CONST objv[]
1279 ){
1280   int rc;
1281   int isInstall;
1282 
1283   if( objc!=2 ){
1284     Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
1285     return TCL_ERROR;
1286   }
1287   if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
1288     return TCL_ERROR;
1289   }
1290   rc = faultsimInstall(isInstall);
1291   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
1292   return TCL_OK;
1293 }
1294 
1295 /*
1296 ** Register commands with the TCL interpreter.
1297 */
1298 int Sqlitetest_malloc_Init(Tcl_Interp *interp){
1299   static struct {
1300      char *zName;
1301      Tcl_ObjCmdProc *xProc;
1302      int clientData;
1303   } aObjCmd[] = {
1304      { "sqlite3_malloc",             test_malloc                   ,0 },
1305      { "sqlite3_realloc",            test_realloc                  ,0 },
1306      { "sqlite3_free",               test_free                     ,0 },
1307      { "memset",                     test_memset                   ,0 },
1308      { "memget",                     test_memget                   ,0 },
1309      { "sqlite3_memory_used",        test_memory_used              ,0 },
1310      { "sqlite3_memory_highwater",   test_memory_highwater         ,0 },
1311      { "sqlite3_memdebug_backtrace", test_memdebug_backtrace       ,0 },
1312      { "sqlite3_memdebug_dump",      test_memdebug_dump            ,0 },
1313      { "sqlite3_memdebug_fail",      test_memdebug_fail            ,0 },
1314      { "sqlite3_memdebug_pending",   test_memdebug_pending         ,0 },
1315      { "sqlite3_memdebug_settitle",  test_memdebug_settitle        ,0 },
1316      { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 },
1317      { "sqlite3_memdebug_log",       test_memdebug_log             ,0 },
1318      { "sqlite3_config_scratch",     test_config_scratch           ,0 },
1319      { "sqlite3_config_pagecache",   test_config_pagecache         ,0 },
1320      { "sqlite3_status",             test_status                   ,0 },
1321      { "sqlite3_db_status",          test_db_status                ,0 },
1322      { "install_malloc_faultsim",    test_install_malloc_faultsim  ,0 },
1323      { "sqlite3_config_heap",        test_config_heap              ,0 },
1324      { "sqlite3_config_memstatus",   test_config_memstatus         ,0 },
1325      { "sqlite3_config_chunkalloc",  test_config_chunkalloc        ,0 },
1326      { "sqlite3_config_lookaside",   test_config_lookaside         ,0 },
1327      { "sqlite3_config_error",       test_config_error             ,0 },
1328      { "sqlite3_db_config_lookaside",test_db_config_lookaside      ,0 },
1329      { "sqlite3_dump_memsys3",       test_dump_memsys3             ,3 },
1330      { "sqlite3_dump_memsys5",       test_dump_memsys3             ,5 }
1331   };
1332   int i;
1333   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
1334     ClientData c = (ClientData)aObjCmd[i].clientData;
1335     Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
1336   }
1337   return TCL_OK;
1338 }
1339 #endif
1340