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