blob: 95b44ac5f4b8b5bc2ab5d8929a017ab895213e56 [file] [log] [blame]
drh2f999a62007-08-15 19:16:43 +00001/*
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**
danielk197732155ef2008-06-25 10:34:34 +000016** $Id: test_malloc.c,v 1.30 2008/06/25 10:34:35 danielk1977 Exp $
drh2f999a62007-08-15 19:16:43 +000017*/
18#include "sqliteInt.h"
19#include "tcl.h"
20#include <stdlib.h>
21#include <string.h>
22#include <assert.h>
23
danielk1977ef05f2d2008-06-20 11:05:37 +000024/*
25** This structure is used to encapsulate the global state variables used
26** by malloc() fault simulation.
27*/
28static 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 */
danielk19772d1d86f2008-06-20 14:59:51 +000035 int isBenignMode; /* True if malloc failures are considered benign */
danielk1977ef05f2d2008-06-20 11:05:37 +000036 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*/
43static 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*/
52static 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++;
danielk19772d1d86f2008-06-20 14:59:51 +000062 if( memfault.isBenignMode>0 ){
danielk1977ef05f2d2008-06-20 11:05:37 +000063 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*/
76static 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*/
89static 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*/
107static void faultsimFree(void *p){
108 memfault.m.xFree(p);
109}
110static int faultsimSize(void *p){
111 return memfault.m.xSize(p);
112}
113static int faultsimRoundup(int n){
114 return memfault.m.xRoundup(n);
115}
116static int faultsimInit(void *p){
117 return memfault.m.xInit(memfault.m.pAppData);
118}
119static 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*/
129static 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*/
141static 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*/
149static 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*/
157static int faultsimPending(void){
158 if( memfault.enable ){
159 return memfault.iCountdown;
160 }else{
161 return -1;
162 }
163}
164
danielk19772d1d86f2008-06-20 14:59:51 +0000165
166static void faultsimBeginBenign(void){
167 memfault.isBenignMode++;
168}
169static void faultsimEndBenign(void){
170 memfault.isBenignMode--;
171}
172
danielk1977ef05f2d2008-06-20 11:05:37 +0000173/*
174** Add or remove the fault-simulation layer using sqlite3_config(). If
175** the argument is non-zero, the
176*/
177static 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
danielk19772d1d86f2008-06-20 14:59:51 +0000197 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);
danielk1977ef05f2d2008-06-20 11:05:37 +0000210 }
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*/
danielk1977d09414c2008-06-19 18:17:49 +0000228const char *sqlite3TestErrorName(int);
229
drh2f999a62007-08-15 19:16:43 +0000230/*
231** Transform pointers to text and back again
232*/
233static void pointerToText(void *p, char *z){
234 static const char zHex[] = "0123456789abcdef";
235 int i, k;
drh4a50aac2007-08-23 02:47:53 +0000236 unsigned int u;
237 sqlite3_uint64 n;
238 if( sizeof(n)==sizeof(p) ){
239 memcpy(&n, &p, sizeof(p));
240 }else if( sizeof(u)==sizeof(p) ){
241 memcpy(&u, &p, sizeof(u));
242 n = u;
243 }else{
244 assert( 0 );
245 }
drh2f999a62007-08-15 19:16:43 +0000246 for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
247 z[k] = zHex[n&0xf];
248 n >>= 4;
249 }
250 z[sizeof(p)*2] = 0;
251}
252static int hexToInt(int h){
253 if( h>='0' && h<='9' ){
254 return h - '0';
255 }else if( h>='a' && h<='f' ){
256 return h - 'a' + 10;
257 }else{
258 return -1;
259 }
260}
261static int textToPointer(const char *z, void **pp){
262 sqlite3_uint64 n = 0;
263 int i;
drh4a50aac2007-08-23 02:47:53 +0000264 unsigned int u;
drh2f999a62007-08-15 19:16:43 +0000265 for(i=0; i<sizeof(void*)*2 && z[0]; i++){
266 int v;
267 v = hexToInt(*z++);
268 if( v<0 ) return TCL_ERROR;
269 n = n*16 + v;
270 }
271 if( *z!=0 ) return TCL_ERROR;
drh4a50aac2007-08-23 02:47:53 +0000272 if( sizeof(n)==sizeof(*pp) ){
273 memcpy(pp, &n, sizeof(n));
274 }else if( sizeof(u)==sizeof(*pp) ){
275 u = (unsigned int)n;
276 memcpy(pp, &u, sizeof(u));
277 }else{
278 assert( 0 );
279 }
drh2f999a62007-08-15 19:16:43 +0000280 return TCL_OK;
281}
282
283/*
284** Usage: sqlite3_malloc NBYTES
285**
286** Raw test interface for sqlite3_malloc().
287*/
288static int test_malloc(
289 void * clientData,
290 Tcl_Interp *interp,
291 int objc,
292 Tcl_Obj *CONST objv[]
293){
294 int nByte;
295 void *p;
296 char zOut[100];
297 if( objc!=2 ){
298 Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
299 return TCL_ERROR;
300 }
301 if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
302 p = sqlite3_malloc((unsigned)nByte);
303 pointerToText(p, zOut);
304 Tcl_AppendResult(interp, zOut, NULL);
305 return TCL_OK;
306}
307
308/*
309** Usage: sqlite3_realloc PRIOR NBYTES
310**
311** Raw test interface for sqlite3_realloc().
312*/
313static int test_realloc(
314 void * clientData,
315 Tcl_Interp *interp,
316 int objc,
317 Tcl_Obj *CONST objv[]
318){
319 int nByte;
320 void *pPrior, *p;
321 char zOut[100];
322 if( objc!=3 ){
323 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
324 return TCL_ERROR;
325 }
326 if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
327 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
328 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
329 return TCL_ERROR;
330 }
331 p = sqlite3_realloc(pPrior, (unsigned)nByte);
332 pointerToText(p, zOut);
333 Tcl_AppendResult(interp, zOut, NULL);
334 return TCL_OK;
335}
336
drh2f999a62007-08-15 19:16:43 +0000337/*
338** Usage: sqlite3_free PRIOR
339**
340** Raw test interface for sqlite3_free().
341*/
342static int test_free(
343 void * clientData,
344 Tcl_Interp *interp,
345 int objc,
346 Tcl_Obj *CONST objv[]
347){
348 void *pPrior;
349 if( objc!=2 ){
350 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
351 return TCL_ERROR;
352 }
353 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
354 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
355 return TCL_ERROR;
356 }
357 sqlite3_free(pPrior);
358 return TCL_OK;
359}
360
361/*
drh9c7a60d2007-10-19 17:47:24 +0000362** These routines are in test_hexio.c
363*/
364int sqlite3TestHexToBin(const char *, int, char *);
365int sqlite3TestBinToHex(char*,int);
366
367/*
368** Usage: memset ADDRESS SIZE HEX
369**
370** Set a chunk of memory (obtained from malloc, probably) to a
371** specified hex pattern.
372*/
373static int test_memset(
374 void * clientData,
375 Tcl_Interp *interp,
376 int objc,
377 Tcl_Obj *CONST objv[]
378){
379 void *p;
380 int size, n, i;
381 char *zHex;
382 char *zOut;
383 char zBin[100];
384
385 if( objc!=4 ){
386 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
387 return TCL_ERROR;
388 }
389 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
390 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
391 return TCL_ERROR;
392 }
393 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
394 return TCL_ERROR;
395 }
396 if( size<=0 ){
397 Tcl_AppendResult(interp, "size must be positive", (char*)0);
398 return TCL_ERROR;
399 }
400 zHex = Tcl_GetStringFromObj(objv[3], &n);
401 if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
402 n = sqlite3TestHexToBin(zHex, n, zBin);
403 if( n==0 ){
404 Tcl_AppendResult(interp, "no data", (char*)0);
405 return TCL_ERROR;
406 }
407 zOut = p;
408 for(i=0; i<size; i++){
409 zOut[i] = zBin[i%n];
410 }
411 return TCL_OK;
412}
413
414/*
415** Usage: memget ADDRESS SIZE
416**
417** Return memory as hexadecimal text.
418*/
419static int test_memget(
420 void * clientData,
421 Tcl_Interp *interp,
422 int objc,
423 Tcl_Obj *CONST objv[]
424){
425 void *p;
426 int size, n;
427 char *zBin;
428 char zHex[100];
429
430 if( objc!=3 ){
431 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
432 return TCL_ERROR;
433 }
434 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
435 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
436 return TCL_ERROR;
437 }
438 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
439 return TCL_ERROR;
440 }
441 if( size<=0 ){
442 Tcl_AppendResult(interp, "size must be positive", (char*)0);
443 return TCL_ERROR;
444 }
445 zBin = p;
446 while( size>0 ){
447 if( size>(sizeof(zHex)-1)/2 ){
448 n = (sizeof(zHex)-1)/2;
449 }else{
450 n = size;
451 }
452 memcpy(zHex, zBin, n);
453 zBin += n;
454 size -= n;
455 sqlite3TestBinToHex(zHex, n);
456 Tcl_AppendResult(interp, zHex, (char*)0);
457 }
458 return TCL_OK;
459}
460
461/*
drh2f999a62007-08-15 19:16:43 +0000462** Usage: sqlite3_memory_used
463**
464** Raw test interface for sqlite3_memory_used().
465*/
466static int test_memory_used(
467 void * clientData,
468 Tcl_Interp *interp,
469 int objc,
470 Tcl_Obj *CONST objv[]
471){
472 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
473 return TCL_OK;
474}
475
476/*
477** Usage: sqlite3_memory_highwater ?RESETFLAG?
478**
479** Raw test interface for sqlite3_memory_highwater().
480*/
481static int test_memory_highwater(
482 void * clientData,
483 Tcl_Interp *interp,
484 int objc,
485 Tcl_Obj *CONST objv[]
486){
487 int resetFlag = 0;
488 if( objc!=1 && objc!=2 ){
489 Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
490 return TCL_ERROR;
491 }
492 if( objc==2 ){
493 if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
494 }
495 Tcl_SetObjResult(interp,
496 Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
497 return TCL_OK;
498}
499
500/*
501** Usage: sqlite3_memdebug_backtrace DEPTH
502**
503** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined
504** then this routine is a no-op.
505*/
506static int test_memdebug_backtrace(
507 void * clientData,
508 Tcl_Interp *interp,
509 int objc,
510 Tcl_Obj *CONST objv[]
511){
512 int depth;
513 if( objc!=2 ){
514 Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
515 return TCL_ERROR;
516 }
517 if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
518#ifdef SQLITE_MEMDEBUG
519 {
drh49e4fd72008-02-19 15:15:15 +0000520 extern void sqlite3MemdebugBacktrace(int);
521 sqlite3MemdebugBacktrace(depth);
drh2f999a62007-08-15 19:16:43 +0000522 }
523#endif
524 return TCL_OK;
525}
526
527/*
528** Usage: sqlite3_memdebug_dump FILENAME
529**
530** Write a summary of unfreed memory to FILENAME.
531*/
532static int test_memdebug_dump(
533 void * clientData,
534 Tcl_Interp *interp,
535 int objc,
536 Tcl_Obj *CONST objv[]
537){
538 if( objc!=2 ){
539 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
540 return TCL_ERROR;
541 }
drh2d7636e2008-02-16 16:21:45 +0000542#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
543 || defined(SQLITE_POW2_MEMORY_SIZE)
drh2f999a62007-08-15 19:16:43 +0000544 {
drh49e4fd72008-02-19 15:15:15 +0000545 extern void sqlite3MemdebugDump(const char*);
546 sqlite3MemdebugDump(Tcl_GetString(objv[1]));
drh2f999a62007-08-15 19:16:43 +0000547 }
548#endif
549 return TCL_OK;
550}
551
danielk1977a7a8e142008-02-13 18:25:27 +0000552/*
553** Usage: sqlite3_memdebug_malloc_count
554**
555** Return the total number of times malloc() has been called.
556*/
557static int test_memdebug_malloc_count(
558 void * clientData,
559 Tcl_Interp *interp,
560 int objc,
561 Tcl_Obj *CONST objv[]
562){
563 int nMalloc = -1;
564 if( objc!=1 ){
565 Tcl_WrongNumArgs(interp, 1, objv, "");
566 return TCL_ERROR;
567 }
568#if defined(SQLITE_MEMDEBUG)
569 {
drh49e4fd72008-02-19 15:15:15 +0000570 extern int sqlite3MemdebugMallocCount();
571 nMalloc = sqlite3MemdebugMallocCount();
danielk1977a7a8e142008-02-13 18:25:27 +0000572 }
573#endif
574 Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
575 return TCL_OK;
576}
577
drh2f999a62007-08-15 19:16:43 +0000578
579/*
danielk1977a1644fd2007-08-29 12:31:25 +0000580** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS?
581**
582** where options are:
583**
drh643167f2008-01-22 21:30:53 +0000584** -repeat <count>
danielk1977a1644fd2007-08-29 12:31:25 +0000585** -benigncnt <varname>
drh0e6f1542007-08-15 20:41:28 +0000586**
587** Arrange for a simulated malloc() failure after COUNTER successes.
drh643167f2008-01-22 21:30:53 +0000588** If a repeat count is specified, the fault is repeated that many
589** times.
drh0e6f1542007-08-15 20:41:28 +0000590**
591** Each call to this routine overrides the prior counter value.
592** This routine returns the number of simulated failures that have
593** happened since the previous call to this routine.
594**
595** To disable simulated failures, use a COUNTER of -1.
596*/
597static int test_memdebug_fail(
598 void * clientData,
599 Tcl_Interp *interp,
600 int objc,
601 Tcl_Obj *CONST objv[]
602){
danielk1977a1644fd2007-08-29 12:31:25 +0000603 int ii;
drh0e6f1542007-08-15 20:41:28 +0000604 int iFail;
drh643167f2008-01-22 21:30:53 +0000605 int nRepeat = 1;
danielk1977a1644fd2007-08-29 12:31:25 +0000606 Tcl_Obj *pBenignCnt = 0;
drh643167f2008-01-22 21:30:53 +0000607 int nBenign;
drh0e6f1542007-08-15 20:41:28 +0000608 int nFail = 0;
danielk1977a1644fd2007-08-29 12:31:25 +0000609
610 if( objc<2 ){
611 Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
drh0e6f1542007-08-15 20:41:28 +0000612 return TCL_ERROR;
613 }
614 if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
danielk1977a1644fd2007-08-29 12:31:25 +0000615
616 for(ii=2; ii<objc; ii+=2){
617 int nOption;
618 char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
619 char *zErr = 0;
620
621 if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
622 if( ii==(objc-1) ){
623 zErr = "option requires an argument: ";
624 }else{
drh643167f2008-01-22 21:30:53 +0000625 if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
danielk1977a1644fd2007-08-29 12:31:25 +0000626 return TCL_ERROR;
627 }
628 }
629 }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
630 if( ii==(objc-1) ){
631 zErr = "option requires an argument: ";
632 }else{
633 pBenignCnt = objv[ii+1];
634 }
635 }else{
636 zErr = "unknown option: ";
637 }
638
639 if( zErr ){
640 Tcl_AppendResult(interp, zErr, zOption, 0);
641 return TCL_ERROR;
642 }
drhed138fb2007-08-22 22:04:37 +0000643 }
danielk1977a1644fd2007-08-29 12:31:25 +0000644
danielk1977ef05f2d2008-06-20 11:05:37 +0000645 nBenign = faultsimBenignFailures();
646 nFail = faultsimFailures();
647 faultsimConfig(iFail, nRepeat);
648
drh643167f2008-01-22 21:30:53 +0000649 if( pBenignCnt ){
650 Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
drh0e6f1542007-08-15 20:41:28 +0000651 }
drh0e6f1542007-08-15 20:41:28 +0000652 Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
653 return TCL_OK;
654}
655
danielk1977cd037242007-08-30 15:46:06 +0000656/*
657** Usage: sqlite3_memdebug_pending
658**
659** Return the number of malloc() calls that will succeed before a
660** simulated failure occurs. A negative return value indicates that
661** no malloc() failure is scheduled.
662*/
663static int test_memdebug_pending(
664 void * clientData,
665 Tcl_Interp *interp,
666 int objc,
667 Tcl_Obj *CONST objv[]
668){
drh5efaf072008-03-18 00:07:10 +0000669 int nPending;
danielk1977cd037242007-08-30 15:46:06 +0000670 if( objc!=1 ){
671 Tcl_WrongNumArgs(interp, 1, objv, "");
672 return TCL_ERROR;
673 }
danielk1977ef05f2d2008-06-20 11:05:37 +0000674 nPending = faultsimPending();
drh5efaf072008-03-18 00:07:10 +0000675 Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
danielk1977cd037242007-08-30 15:46:06 +0000676 return TCL_OK;
677}
678
drh0e6f1542007-08-15 20:41:28 +0000679
680/*
drh4a50aac2007-08-23 02:47:53 +0000681** Usage: sqlite3_memdebug_settitle TITLE
682**
683** Set a title string stored with each allocation. The TITLE is
684** typically the name of the test that was running when the
685** allocation occurred. The TITLE is stored with the allocation
686** and can be used to figure out which tests are leaking memory.
687**
688** Each title overwrite the previous.
689*/
690static int test_memdebug_settitle(
691 void * clientData,
692 Tcl_Interp *interp,
693 int objc,
694 Tcl_Obj *CONST objv[]
695){
696 const char *zTitle;
697 if( objc!=2 ){
698 Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
699 return TCL_ERROR;
700 }
701 zTitle = Tcl_GetString(objv[1]);
702#ifdef SQLITE_MEMDEBUG
703 {
drh49e4fd72008-02-19 15:15:15 +0000704 extern int sqlite3MemdebugSettitle(const char*);
705 sqlite3MemdebugSettitle(zTitle);
drh4a50aac2007-08-23 02:47:53 +0000706 }
707#endif
708 return TCL_OK;
709}
710
danielk1977cd3e8f72008-03-25 09:47:35 +0000711#define MALLOC_LOG_FRAMES 10
danielk19776f332c12008-03-21 14:22:44 +0000712static Tcl_HashTable aMallocLog;
713static int mallocLogEnabled = 0;
714
715typedef struct MallocLog MallocLog;
716struct MallocLog {
717 int nCall;
718 int nByte;
719};
720
shaneafdd23a2008-05-29 02:57:47 +0000721#ifdef SQLITE_MEMDEBUG
danielk19776f332c12008-03-21 14:22:44 +0000722static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
723 if( mallocLogEnabled ){
724 MallocLog *pLog;
725 Tcl_HashEntry *pEntry;
726 int isNew;
727
728 int aKey[MALLOC_LOG_FRAMES];
729 int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
730
731 memset(aKey, 0, nKey);
732 if( (sizeof(void*)*nFrame)<nKey ){
733 nKey = nFrame*sizeof(void*);
734 }
735 memcpy(aKey, aFrame, nKey);
736
737 pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
738 if( isNew ){
739 pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
740 memset(pLog, 0, sizeof(MallocLog));
741 Tcl_SetHashValue(pEntry, (ClientData)pLog);
742 }else{
743 pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
744 }
745
746 pLog->nCall++;
747 pLog->nByte += nByte;
748 }
749}
shaneafdd23a2008-05-29 02:57:47 +0000750#endif /* SQLITE_MEMDEBUG */
danielk19776f332c12008-03-21 14:22:44 +0000751
danielk19775f096132008-03-28 15:44:09 +0000752static void test_memdebug_log_clear(){
danielk1977dbdc4d42008-03-28 07:42:53 +0000753 Tcl_HashSearch search;
754 Tcl_HashEntry *pEntry;
755 for(
756 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
757 pEntry;
758 pEntry=Tcl_NextHashEntry(&search)
759 ){
760 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
761 Tcl_Free((char *)pLog);
762 }
763 Tcl_DeleteHashTable(&aMallocLog);
764 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
765}
766
danielk19776f332c12008-03-21 14:22:44 +0000767static int test_memdebug_log(
768 void * clientData,
769 Tcl_Interp *interp,
770 int objc,
771 Tcl_Obj *CONST objv[]
772){
773 static int isInit = 0;
774 int iSub;
775
danielk1977dbdc4d42008-03-28 07:42:53 +0000776 static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
777 enum MB_enum {
778 MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
779 };
danielk19776f332c12008-03-21 14:22:44 +0000780
781 if( !isInit ){
782#ifdef SQLITE_MEMDEBUG
783 extern void sqlite3MemdebugBacktraceCallback(
784 void (*xBacktrace)(int, int, void **));
785 sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
786#endif
787 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
788 isInit = 1;
789 }
790
791 if( objc<2 ){
792 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
793 }
794 if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
795 return TCL_ERROR;
796 }
797
798 switch( (enum MB_enum)iSub ){
799 case MB_LOG_START:
800 mallocLogEnabled = 1;
801 break;
802 case MB_LOG_STOP:
803 mallocLogEnabled = 0;
804 break;
805 case MB_LOG_DUMP: {
806 Tcl_HashSearch search;
807 Tcl_HashEntry *pEntry;
808 Tcl_Obj *pRet = Tcl_NewObj();
809
810 assert(sizeof(int)==sizeof(void*));
811
812 for(
813 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
814 pEntry;
815 pEntry=Tcl_NextHashEntry(&search)
816 ){
817 Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
818 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
819 int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
820 int ii;
821
822 apElem[0] = Tcl_NewIntObj(pLog->nCall);
823 apElem[1] = Tcl_NewIntObj(pLog->nByte);
824 for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
825 apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
826 }
827
828 Tcl_ListObjAppendElement(interp, pRet,
829 Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
830 );
831 }
832
833 Tcl_SetObjResult(interp, pRet);
834 break;
835 }
836 case MB_LOG_CLEAR: {
danielk1977dbdc4d42008-03-28 07:42:53 +0000837 test_memdebug_log_clear();
838 break;
839 }
840
841 case MB_LOG_SYNC: {
drhb9404922008-03-28 12:53:38 +0000842#ifdef SQLITE_MEMDEBUG
danielk1977dbdc4d42008-03-28 07:42:53 +0000843 extern void sqlite3MemdebugSync();
844 test_memdebug_log_clear();
845 mallocLogEnabled = 1;
846 sqlite3MemdebugSync();
drhb9404922008-03-28 12:53:38 +0000847#endif
danielk1977dbdc4d42008-03-28 07:42:53 +0000848 break;
danielk19776f332c12008-03-21 14:22:44 +0000849 }
850 }
851
852 return TCL_OK;
853}
drh4a50aac2007-08-23 02:47:53 +0000854
855/*
drh9ac3fe92008-06-18 18:12:04 +0000856** Usage: sqlite3_config_scratch SIZE N
857**
858** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
859** The buffer is static and is of limited size. N might be
860** adjusted downward as needed to accomodate the requested size.
861** The revised value of N is returned.
862**
863** A negative SIZE causes the buffer pointer to be NULL.
864*/
865static int test_config_scratch(
866 void * clientData,
867 Tcl_Interp *interp,
868 int objc,
869 Tcl_Obj *CONST objv[]
870){
871 int sz, N, rc;
872 Tcl_Obj *pResult;
drhf7141992008-06-19 00:16:08 +0000873 static char buf[30000];
drh9ac3fe92008-06-18 18:12:04 +0000874 if( objc!=3 ){
875 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
876 return TCL_ERROR;
877 }
drhf7141992008-06-19 00:16:08 +0000878 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
879 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000880 if( sz<0 ){
881 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
drhf7141992008-06-19 00:16:08 +0000882 }else{
drh9ac3fe92008-06-18 18:12:04 +0000883 int mx = sizeof(buf)/(sz+4);
884 if( N>mx ) N = mx;
885 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
886 }
887 pResult = Tcl_NewObj();
888 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
889 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
890 Tcl_SetObjResult(interp, pResult);
891 return TCL_OK;
892}
893
894/*
895** Usage: sqlite3_config_pagecache SIZE N
896**
897** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
898** The buffer is static and is of limited size. N might be
899** adjusted downward as needed to accomodate the requested size.
900** The revised value of N is returned.
901**
902** A negative SIZE causes the buffer pointer to be NULL.
903*/
904static int test_config_pagecache(
905 void * clientData,
906 Tcl_Interp *interp,
907 int objc,
908 Tcl_Obj *CONST objv[]
909){
910 int sz, N, rc;
911 Tcl_Obj *pResult;
912 static char buf[100000];
913 if( objc!=3 ){
914 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
915 return TCL_ERROR;
916 }
drhf7141992008-06-19 00:16:08 +0000917 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
918 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000919 if( sz<0 ){
drhf7141992008-06-19 00:16:08 +0000920 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
921 }else{
drh9ac3fe92008-06-18 18:12:04 +0000922 int mx = sizeof(buf)/(sz+4);
923 if( N>mx ) N = mx;
924 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
925 }
926 pResult = Tcl_NewObj();
927 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
928 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
929 Tcl_SetObjResult(interp, pResult);
930 return TCL_OK;
931}
932
drhf7141992008-06-19 00:16:08 +0000933/*
danielk197732155ef2008-06-25 10:34:34 +0000934** Usage: sqlite3_config_memsys3 NBYTE
danielk197757e5ea92008-06-24 19:02:55 +0000935**
936*/
danielk197732155ef2008-06-25 10:34:34 +0000937static int test_config_memsys3(
danielk197757e5ea92008-06-24 19:02:55 +0000938 void * clientData,
939 Tcl_Interp *interp,
940 int objc,
941 Tcl_Obj *CONST objv[]
942){
943 int sz, rc;
944 Tcl_Obj *pResult;
945 static char buf[1000000];
946 if( objc!=2 ){
947 Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
948 return TCL_ERROR;
949 }
950 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
951 if( sz<=0 ){
952 sqlite3_mem_methods m;
953 memset(&m, 0, sizeof(sqlite3_mem_methods));
954 rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
955 }else{
956 if( sz>sizeof(buf) ){
957 sz = sizeof(buf);
958 }
danielk197732155ef2008-06-25 10:34:34 +0000959 rc = sqlite3_config(SQLITE_CONFIG_MEMSYS3, buf, sz);
danielk197757e5ea92008-06-24 19:02:55 +0000960 }
961 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
962 return TCL_OK;
963}
964
965/*
danielk197732155ef2008-06-25 10:34:34 +0000966** Usage: sqlite3_dump_memsys3 FILENAME
967**
968** Write a summary of unfreed memsys3 allocations to FILENAME.
969*/
970static int test_dump_memsys3(
971 void * clientData,
972 Tcl_Interp *interp,
973 int objc,
974 Tcl_Obj *CONST objv[]
975){
976 if( objc!=2 ){
977 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
978 return TCL_ERROR;
979 }
980#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
981 || defined(SQLITE_POW2_MEMORY_SIZE)
982 {
983 extern void sqlite3Memsys3Dump(const char*);
984 sqlite3Memsys3Dump(Tcl_GetString(objv[1]));
985 }
986#endif
987 return TCL_OK;
988}
989
990/*
drhf7141992008-06-19 00:16:08 +0000991** Usage: sqlite3_status OPCODE RESETFLAG
992**
993** Return a list of three elements which are the sqlite3_status() return
994** code, the current value, and the high-water mark value.
995*/
996static int test_status(
997 void * clientData,
998 Tcl_Interp *interp,
999 int objc,
1000 Tcl_Obj *CONST objv[]
1001){
1002 int rc, iValue, mxValue;
1003 int i, op, resetFlag;
1004 const char *zOpName;
1005 static const struct {
1006 const char *zName;
1007 int op;
1008 } aOp[] = {
1009 { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
1010 { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
1011 { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
1012 { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
1013 { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
1014 { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
1015 };
1016 Tcl_Obj *pResult;
1017 if( objc!=3 ){
1018 Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
1019 return TCL_ERROR;
1020 }
1021 zOpName = Tcl_GetString(objv[1]);
1022 for(i=0; i<ArraySize(aOp); i++){
1023 if( strcmp(aOp[i].zName, zOpName)==0 ){
1024 op = aOp[i].op;
1025 break;
1026 }
1027 }
1028 if( i>=ArraySize(aOp) ){
1029 if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR;
1030 }
1031 if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR;
1032 rc = sqlite3_status(op, &iValue, &mxValue, resetFlag);
1033 pResult = Tcl_NewObj();
1034 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
1035 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
1036 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
1037 Tcl_SetObjResult(interp, pResult);
1038 return TCL_OK;
1039}
drh9ac3fe92008-06-18 18:12:04 +00001040
1041/*
danielk1977d09414c2008-06-19 18:17:49 +00001042** install_malloc_faultsim BOOLEAN
1043*/
1044static int test_install_malloc_faultsim(
1045 void * clientData,
1046 Tcl_Interp *interp,
1047 int objc,
1048 Tcl_Obj *CONST objv[]
1049){
1050 int rc;
1051 int isInstall;
1052
1053 if( objc!=2 ){
1054 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
1055 return TCL_ERROR;
1056 }
1057 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
1058 return TCL_ERROR;
1059 }
danielk1977ef05f2d2008-06-20 11:05:37 +00001060 rc = faultsimInstall(isInstall);
danielk1977d09414c2008-06-19 18:17:49 +00001061 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
1062 return TCL_OK;
1063}
1064
1065/*
drh2f999a62007-08-15 19:16:43 +00001066** Register commands with the TCL interpreter.
1067*/
1068int Sqlitetest_malloc_Init(Tcl_Interp *interp){
1069 static struct {
1070 char *zName;
1071 Tcl_ObjCmdProc *xProc;
1072 } aObjCmd[] = {
1073 { "sqlite3_malloc", test_malloc },
1074 { "sqlite3_realloc", test_realloc },
1075 { "sqlite3_free", test_free },
drh9c7a60d2007-10-19 17:47:24 +00001076 { "memset", test_memset },
1077 { "memget", test_memget },
drh2f999a62007-08-15 19:16:43 +00001078 { "sqlite3_memory_used", test_memory_used },
1079 { "sqlite3_memory_highwater", test_memory_highwater },
1080 { "sqlite3_memdebug_backtrace", test_memdebug_backtrace },
1081 { "sqlite3_memdebug_dump", test_memdebug_dump },
drh0e6f1542007-08-15 20:41:28 +00001082 { "sqlite3_memdebug_fail", test_memdebug_fail },
danielk1977cd037242007-08-30 15:46:06 +00001083 { "sqlite3_memdebug_pending", test_memdebug_pending },
drh4a50aac2007-08-23 02:47:53 +00001084 { "sqlite3_memdebug_settitle", test_memdebug_settitle },
danielk1977a7a8e142008-02-13 18:25:27 +00001085 { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count },
drh9ac3fe92008-06-18 18:12:04 +00001086 { "sqlite3_memdebug_log", test_memdebug_log },
1087 { "sqlite3_config_scratch", test_config_scratch },
1088 { "sqlite3_config_pagecache", test_config_pagecache },
danielk197732155ef2008-06-25 10:34:34 +00001089 { "sqlite3_config_memsys3", test_config_memsys3 },
1090 { "sqlite3_dump_memsys3", test_dump_memsys3 },
drhf7141992008-06-19 00:16:08 +00001091 { "sqlite3_status", test_status },
danielk1977d09414c2008-06-19 18:17:49 +00001092 { "install_malloc_faultsim", test_install_malloc_faultsim },
drh2f999a62007-08-15 19:16:43 +00001093 };
1094 int i;
1095 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
1096 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
1097 }
1098 return TCL_OK;
1099}
danielk1977ef05f2d2008-06-20 11:05:37 +00001100#endif