blob: 0cc4911c524e9a0d2a1e90f6001f0150326f7620 [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**
danielk1977ef05f2d2008-06-20 11:05:37 +000016** $Id: test_malloc.c,v 1.27 2008/06/20 11:05:38 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 */
35 sqlite3_mem_methods m; /* 'Real' malloc implementation */
36} memfault;
37
38/*
39** This routine exists as a place to set a breakpoint that will
40** fire on any simulated malloc() failure.
41*/
42static void sqlite3Fault(void){
43 static int cnt = 0;
44 cnt++;
45}
46
47/*
48** Check to see if a fault should be simulated. Return true to simulate
49** the fault. Return false if the fault should not be simulated.
50*/
51static int faultsimStep(){
52 if( likely(!memfault.enable) ){
53 return 0;
54 }
55 if( memfault.iCountdown>0 ){
56 memfault.iCountdown--;
57 return 0;
58 }
59 sqlite3Fault();
60 memfault.nFail++;
61 if( sqlite3FaultIsBenign()>0 ){
62 memfault.nBenign++;
63 }
64 memfault.nRepeat--;
65 if( memfault.nRepeat<=0 ){
66 memfault.enable = 0;
67 }
68 return 1;
69}
70
71/*
72** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation
73** logic.
74*/
75static void *faultsimMalloc(int n){
76 void *p = 0;
77 if( !faultsimStep() ){
78 p = memfault.m.xMalloc(n);
79 }
80 return p;
81}
82
83
84/*
85** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation
86** logic.
87*/
88static void *faultsimRealloc(void *pOld, int n){
89 void *p = 0;
90 if( !faultsimStep() ){
91 p = memfault.m.xRealloc(pOld, n);
92 }
93 return p;
94}
95
96/*
97** The following method calls are passed directly through to the underlying
98** malloc system:
99**
100** xFree
101** xSize
102** xRoundup
103** xInit
104** xShutdown
105*/
106static void faultsimFree(void *p){
107 memfault.m.xFree(p);
108}
109static int faultsimSize(void *p){
110 return memfault.m.xSize(p);
111}
112static int faultsimRoundup(int n){
113 return memfault.m.xRoundup(n);
114}
115static int faultsimInit(void *p){
116 return memfault.m.xInit(memfault.m.pAppData);
117}
118static void faultsimShutdown(void *p){
119 memfault.m.xShutdown(memfault.m.pAppData);
120}
121
122/*
123** This routine configures the malloc failure simulation. After
124** calling this routine, the next nDelay mallocs will succeed, followed
125** by a block of nRepeat failures, after which malloc() calls will begin
126** to succeed again.
127*/
128static void faultsimConfig(int nDelay, int nRepeat){
129 memfault.iCountdown = nDelay;
130 memfault.nRepeat = nRepeat;
131 memfault.nBenign = 0;
132 memfault.nFail = 0;
133 memfault.enable = nDelay>=0;
134}
135
136/*
137** Return the number of faults (both hard and benign faults) that have
138** occurred since the injector was last configured.
139*/
140static int faultsimFailures(void){
141 return memfault.nFail;
142}
143
144/*
145** Return the number of benign faults that have occurred since the
146** injector was last configured.
147*/
148static int faultsimBenignFailures(void){
149 return memfault.nBenign;
150}
151
152/*
153** Return the number of successes that will occur before the next failure.
154** If no failures are scheduled, return -1.
155*/
156static int faultsimPending(void){
157 if( memfault.enable ){
158 return memfault.iCountdown;
159 }else{
160 return -1;
161 }
162}
163
164/*
165** Add or remove the fault-simulation layer using sqlite3_config(). If
166** the argument is non-zero, the
167*/
168static int faultsimInstall(int install){
169 static struct sqlite3_mem_methods m = {
170 faultsimMalloc, /* xMalloc */
171 faultsimFree, /* xFree */
172 faultsimRealloc, /* xRealloc */
173 faultsimSize, /* xSize */
174 faultsimRoundup, /* xRoundup */
175 faultsimInit, /* xInit */
176 faultsimShutdown, /* xShutdown */
177 0 /* pAppData */
178 };
179 int rc;
180
181 install = (install ? 1 : 0);
182 assert(memfault.isInstalled==1 || memfault.isInstalled==0);
183
184 if( install==memfault.isInstalled ){
185 return SQLITE_ERROR;
186 }
187
188 rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m);
189 assert(memfault.m.xMalloc);
190 if( rc==SQLITE_OK ){
191 rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
192 }
193
194 if( rc==SQLITE_OK ){
195 memfault.isInstalled = 1;
196 }
197 return rc;
198}
199
200#ifdef SQLITE_TEST
201
202/*
203** This function is implemented in test1.c. Returns a pointer to a static
204** buffer containing the symbolic SQLite error code that corresponds to
205** the least-significant 8-bits of the integer passed as an argument.
206** For example:
207**
208** sqlite3TestErrorName(1) -> "SQLITE_ERROR"
209*/
danielk1977d09414c2008-06-19 18:17:49 +0000210const char *sqlite3TestErrorName(int);
211
drh2f999a62007-08-15 19:16:43 +0000212/*
213** Transform pointers to text and back again
214*/
215static void pointerToText(void *p, char *z){
216 static const char zHex[] = "0123456789abcdef";
217 int i, k;
drh4a50aac2007-08-23 02:47:53 +0000218 unsigned int u;
219 sqlite3_uint64 n;
220 if( sizeof(n)==sizeof(p) ){
221 memcpy(&n, &p, sizeof(p));
222 }else if( sizeof(u)==sizeof(p) ){
223 memcpy(&u, &p, sizeof(u));
224 n = u;
225 }else{
226 assert( 0 );
227 }
drh2f999a62007-08-15 19:16:43 +0000228 for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
229 z[k] = zHex[n&0xf];
230 n >>= 4;
231 }
232 z[sizeof(p)*2] = 0;
233}
234static int hexToInt(int h){
235 if( h>='0' && h<='9' ){
236 return h - '0';
237 }else if( h>='a' && h<='f' ){
238 return h - 'a' + 10;
239 }else{
240 return -1;
241 }
242}
243static int textToPointer(const char *z, void **pp){
244 sqlite3_uint64 n = 0;
245 int i;
drh4a50aac2007-08-23 02:47:53 +0000246 unsigned int u;
drh2f999a62007-08-15 19:16:43 +0000247 for(i=0; i<sizeof(void*)*2 && z[0]; i++){
248 int v;
249 v = hexToInt(*z++);
250 if( v<0 ) return TCL_ERROR;
251 n = n*16 + v;
252 }
253 if( *z!=0 ) return TCL_ERROR;
drh4a50aac2007-08-23 02:47:53 +0000254 if( sizeof(n)==sizeof(*pp) ){
255 memcpy(pp, &n, sizeof(n));
256 }else if( sizeof(u)==sizeof(*pp) ){
257 u = (unsigned int)n;
258 memcpy(pp, &u, sizeof(u));
259 }else{
260 assert( 0 );
261 }
drh2f999a62007-08-15 19:16:43 +0000262 return TCL_OK;
263}
264
265/*
266** Usage: sqlite3_malloc NBYTES
267**
268** Raw test interface for sqlite3_malloc().
269*/
270static int test_malloc(
271 void * clientData,
272 Tcl_Interp *interp,
273 int objc,
274 Tcl_Obj *CONST objv[]
275){
276 int nByte;
277 void *p;
278 char zOut[100];
279 if( objc!=2 ){
280 Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
281 return TCL_ERROR;
282 }
283 if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
284 p = sqlite3_malloc((unsigned)nByte);
285 pointerToText(p, zOut);
286 Tcl_AppendResult(interp, zOut, NULL);
287 return TCL_OK;
288}
289
290/*
291** Usage: sqlite3_realloc PRIOR NBYTES
292**
293** Raw test interface for sqlite3_realloc().
294*/
295static int test_realloc(
296 void * clientData,
297 Tcl_Interp *interp,
298 int objc,
299 Tcl_Obj *CONST objv[]
300){
301 int nByte;
302 void *pPrior, *p;
303 char zOut[100];
304 if( objc!=3 ){
305 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
306 return TCL_ERROR;
307 }
308 if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
309 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
310 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
311 return TCL_ERROR;
312 }
313 p = sqlite3_realloc(pPrior, (unsigned)nByte);
314 pointerToText(p, zOut);
315 Tcl_AppendResult(interp, zOut, NULL);
316 return TCL_OK;
317}
318
drh2f999a62007-08-15 19:16:43 +0000319/*
320** Usage: sqlite3_free PRIOR
321**
322** Raw test interface for sqlite3_free().
323*/
324static int test_free(
325 void * clientData,
326 Tcl_Interp *interp,
327 int objc,
328 Tcl_Obj *CONST objv[]
329){
330 void *pPrior;
331 if( objc!=2 ){
332 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
333 return TCL_ERROR;
334 }
335 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
336 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
337 return TCL_ERROR;
338 }
339 sqlite3_free(pPrior);
340 return TCL_OK;
341}
342
343/*
drh9c7a60d2007-10-19 17:47:24 +0000344** These routines are in test_hexio.c
345*/
346int sqlite3TestHexToBin(const char *, int, char *);
347int sqlite3TestBinToHex(char*,int);
348
349/*
350** Usage: memset ADDRESS SIZE HEX
351**
352** Set a chunk of memory (obtained from malloc, probably) to a
353** specified hex pattern.
354*/
355static int test_memset(
356 void * clientData,
357 Tcl_Interp *interp,
358 int objc,
359 Tcl_Obj *CONST objv[]
360){
361 void *p;
362 int size, n, i;
363 char *zHex;
364 char *zOut;
365 char zBin[100];
366
367 if( objc!=4 ){
368 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
369 return TCL_ERROR;
370 }
371 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
372 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
373 return TCL_ERROR;
374 }
375 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
376 return TCL_ERROR;
377 }
378 if( size<=0 ){
379 Tcl_AppendResult(interp, "size must be positive", (char*)0);
380 return TCL_ERROR;
381 }
382 zHex = Tcl_GetStringFromObj(objv[3], &n);
383 if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
384 n = sqlite3TestHexToBin(zHex, n, zBin);
385 if( n==0 ){
386 Tcl_AppendResult(interp, "no data", (char*)0);
387 return TCL_ERROR;
388 }
389 zOut = p;
390 for(i=0; i<size; i++){
391 zOut[i] = zBin[i%n];
392 }
393 return TCL_OK;
394}
395
396/*
397** Usage: memget ADDRESS SIZE
398**
399** Return memory as hexadecimal text.
400*/
401static int test_memget(
402 void * clientData,
403 Tcl_Interp *interp,
404 int objc,
405 Tcl_Obj *CONST objv[]
406){
407 void *p;
408 int size, n;
409 char *zBin;
410 char zHex[100];
411
412 if( objc!=3 ){
413 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
414 return TCL_ERROR;
415 }
416 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
417 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
418 return TCL_ERROR;
419 }
420 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
421 return TCL_ERROR;
422 }
423 if( size<=0 ){
424 Tcl_AppendResult(interp, "size must be positive", (char*)0);
425 return TCL_ERROR;
426 }
427 zBin = p;
428 while( size>0 ){
429 if( size>(sizeof(zHex)-1)/2 ){
430 n = (sizeof(zHex)-1)/2;
431 }else{
432 n = size;
433 }
434 memcpy(zHex, zBin, n);
435 zBin += n;
436 size -= n;
437 sqlite3TestBinToHex(zHex, n);
438 Tcl_AppendResult(interp, zHex, (char*)0);
439 }
440 return TCL_OK;
441}
442
443/*
drh2f999a62007-08-15 19:16:43 +0000444** Usage: sqlite3_memory_used
445**
446** Raw test interface for sqlite3_memory_used().
447*/
448static int test_memory_used(
449 void * clientData,
450 Tcl_Interp *interp,
451 int objc,
452 Tcl_Obj *CONST objv[]
453){
454 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
455 return TCL_OK;
456}
457
458/*
459** Usage: sqlite3_memory_highwater ?RESETFLAG?
460**
461** Raw test interface for sqlite3_memory_highwater().
462*/
463static int test_memory_highwater(
464 void * clientData,
465 Tcl_Interp *interp,
466 int objc,
467 Tcl_Obj *CONST objv[]
468){
469 int resetFlag = 0;
470 if( objc!=1 && objc!=2 ){
471 Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
472 return TCL_ERROR;
473 }
474 if( objc==2 ){
475 if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
476 }
477 Tcl_SetObjResult(interp,
478 Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
479 return TCL_OK;
480}
481
482/*
483** Usage: sqlite3_memdebug_backtrace DEPTH
484**
485** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined
486** then this routine is a no-op.
487*/
488static int test_memdebug_backtrace(
489 void * clientData,
490 Tcl_Interp *interp,
491 int objc,
492 Tcl_Obj *CONST objv[]
493){
494 int depth;
495 if( objc!=2 ){
496 Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
497 return TCL_ERROR;
498 }
499 if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
500#ifdef SQLITE_MEMDEBUG
501 {
drh49e4fd72008-02-19 15:15:15 +0000502 extern void sqlite3MemdebugBacktrace(int);
503 sqlite3MemdebugBacktrace(depth);
drh2f999a62007-08-15 19:16:43 +0000504 }
505#endif
506 return TCL_OK;
507}
508
509/*
510** Usage: sqlite3_memdebug_dump FILENAME
511**
512** Write a summary of unfreed memory to FILENAME.
513*/
514static int test_memdebug_dump(
515 void * clientData,
516 Tcl_Interp *interp,
517 int objc,
518 Tcl_Obj *CONST objv[]
519){
520 if( objc!=2 ){
521 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
522 return TCL_ERROR;
523 }
drh2d7636e2008-02-16 16:21:45 +0000524#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
525 || defined(SQLITE_POW2_MEMORY_SIZE)
drh2f999a62007-08-15 19:16:43 +0000526 {
drh49e4fd72008-02-19 15:15:15 +0000527 extern void sqlite3MemdebugDump(const char*);
528 sqlite3MemdebugDump(Tcl_GetString(objv[1]));
drh2f999a62007-08-15 19:16:43 +0000529 }
530#endif
531 return TCL_OK;
532}
533
danielk1977a7a8e142008-02-13 18:25:27 +0000534/*
535** Usage: sqlite3_memdebug_malloc_count
536**
537** Return the total number of times malloc() has been called.
538*/
539static int test_memdebug_malloc_count(
540 void * clientData,
541 Tcl_Interp *interp,
542 int objc,
543 Tcl_Obj *CONST objv[]
544){
545 int nMalloc = -1;
546 if( objc!=1 ){
547 Tcl_WrongNumArgs(interp, 1, objv, "");
548 return TCL_ERROR;
549 }
550#if defined(SQLITE_MEMDEBUG)
551 {
drh49e4fd72008-02-19 15:15:15 +0000552 extern int sqlite3MemdebugMallocCount();
553 nMalloc = sqlite3MemdebugMallocCount();
danielk1977a7a8e142008-02-13 18:25:27 +0000554 }
555#endif
556 Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
557 return TCL_OK;
558}
559
drh2f999a62007-08-15 19:16:43 +0000560
561/*
danielk1977a1644fd2007-08-29 12:31:25 +0000562** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS?
563**
564** where options are:
565**
drh643167f2008-01-22 21:30:53 +0000566** -repeat <count>
danielk1977a1644fd2007-08-29 12:31:25 +0000567** -benigncnt <varname>
drh0e6f1542007-08-15 20:41:28 +0000568**
569** Arrange for a simulated malloc() failure after COUNTER successes.
drh643167f2008-01-22 21:30:53 +0000570** If a repeat count is specified, the fault is repeated that many
571** times.
drh0e6f1542007-08-15 20:41:28 +0000572**
573** Each call to this routine overrides the prior counter value.
574** This routine returns the number of simulated failures that have
575** happened since the previous call to this routine.
576**
577** To disable simulated failures, use a COUNTER of -1.
578*/
579static int test_memdebug_fail(
580 void * clientData,
581 Tcl_Interp *interp,
582 int objc,
583 Tcl_Obj *CONST objv[]
584){
danielk1977a1644fd2007-08-29 12:31:25 +0000585 int ii;
drh0e6f1542007-08-15 20:41:28 +0000586 int iFail;
drh643167f2008-01-22 21:30:53 +0000587 int nRepeat = 1;
danielk1977a1644fd2007-08-29 12:31:25 +0000588 Tcl_Obj *pBenignCnt = 0;
drh643167f2008-01-22 21:30:53 +0000589 int nBenign;
drh0e6f1542007-08-15 20:41:28 +0000590 int nFail = 0;
danielk1977a1644fd2007-08-29 12:31:25 +0000591
592 if( objc<2 ){
593 Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
drh0e6f1542007-08-15 20:41:28 +0000594 return TCL_ERROR;
595 }
596 if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
danielk1977a1644fd2007-08-29 12:31:25 +0000597
598 for(ii=2; ii<objc; ii+=2){
599 int nOption;
600 char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
601 char *zErr = 0;
602
603 if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
604 if( ii==(objc-1) ){
605 zErr = "option requires an argument: ";
606 }else{
drh643167f2008-01-22 21:30:53 +0000607 if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
danielk1977a1644fd2007-08-29 12:31:25 +0000608 return TCL_ERROR;
609 }
610 }
611 }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
612 if( ii==(objc-1) ){
613 zErr = "option requires an argument: ";
614 }else{
615 pBenignCnt = objv[ii+1];
616 }
617 }else{
618 zErr = "unknown option: ";
619 }
620
621 if( zErr ){
622 Tcl_AppendResult(interp, zErr, zOption, 0);
623 return TCL_ERROR;
624 }
drhed138fb2007-08-22 22:04:37 +0000625 }
danielk1977a1644fd2007-08-29 12:31:25 +0000626
danielk1977ef05f2d2008-06-20 11:05:37 +0000627 nBenign = faultsimBenignFailures();
628 nFail = faultsimFailures();
629 faultsimConfig(iFail, nRepeat);
630
drh643167f2008-01-22 21:30:53 +0000631 if( pBenignCnt ){
632 Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
drh0e6f1542007-08-15 20:41:28 +0000633 }
drh0e6f1542007-08-15 20:41:28 +0000634 Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
635 return TCL_OK;
636}
637
danielk1977cd037242007-08-30 15:46:06 +0000638/*
639** Usage: sqlite3_memdebug_pending
640**
641** Return the number of malloc() calls that will succeed before a
642** simulated failure occurs. A negative return value indicates that
643** no malloc() failure is scheduled.
644*/
645static int test_memdebug_pending(
646 void * clientData,
647 Tcl_Interp *interp,
648 int objc,
649 Tcl_Obj *CONST objv[]
650){
drh5efaf072008-03-18 00:07:10 +0000651 int nPending;
danielk1977cd037242007-08-30 15:46:06 +0000652 if( objc!=1 ){
653 Tcl_WrongNumArgs(interp, 1, objv, "");
654 return TCL_ERROR;
655 }
danielk1977ef05f2d2008-06-20 11:05:37 +0000656 nPending = faultsimPending();
drh5efaf072008-03-18 00:07:10 +0000657 Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
danielk1977cd037242007-08-30 15:46:06 +0000658 return TCL_OK;
659}
660
drh0e6f1542007-08-15 20:41:28 +0000661
662/*
drh4a50aac2007-08-23 02:47:53 +0000663** Usage: sqlite3_memdebug_settitle TITLE
664**
665** Set a title string stored with each allocation. The TITLE is
666** typically the name of the test that was running when the
667** allocation occurred. The TITLE is stored with the allocation
668** and can be used to figure out which tests are leaking memory.
669**
670** Each title overwrite the previous.
671*/
672static int test_memdebug_settitle(
673 void * clientData,
674 Tcl_Interp *interp,
675 int objc,
676 Tcl_Obj *CONST objv[]
677){
678 const char *zTitle;
679 if( objc!=2 ){
680 Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
681 return TCL_ERROR;
682 }
683 zTitle = Tcl_GetString(objv[1]);
684#ifdef SQLITE_MEMDEBUG
685 {
drh49e4fd72008-02-19 15:15:15 +0000686 extern int sqlite3MemdebugSettitle(const char*);
687 sqlite3MemdebugSettitle(zTitle);
drh4a50aac2007-08-23 02:47:53 +0000688 }
689#endif
690 return TCL_OK;
691}
692
danielk1977cd3e8f72008-03-25 09:47:35 +0000693#define MALLOC_LOG_FRAMES 10
danielk19776f332c12008-03-21 14:22:44 +0000694static Tcl_HashTable aMallocLog;
695static int mallocLogEnabled = 0;
696
697typedef struct MallocLog MallocLog;
698struct MallocLog {
699 int nCall;
700 int nByte;
701};
702
shaneafdd23a2008-05-29 02:57:47 +0000703#ifdef SQLITE_MEMDEBUG
danielk19776f332c12008-03-21 14:22:44 +0000704static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
705 if( mallocLogEnabled ){
706 MallocLog *pLog;
707 Tcl_HashEntry *pEntry;
708 int isNew;
709
710 int aKey[MALLOC_LOG_FRAMES];
711 int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
712
713 memset(aKey, 0, nKey);
714 if( (sizeof(void*)*nFrame)<nKey ){
715 nKey = nFrame*sizeof(void*);
716 }
717 memcpy(aKey, aFrame, nKey);
718
719 pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
720 if( isNew ){
721 pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
722 memset(pLog, 0, sizeof(MallocLog));
723 Tcl_SetHashValue(pEntry, (ClientData)pLog);
724 }else{
725 pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
726 }
727
728 pLog->nCall++;
729 pLog->nByte += nByte;
730 }
731}
shaneafdd23a2008-05-29 02:57:47 +0000732#endif /* SQLITE_MEMDEBUG */
danielk19776f332c12008-03-21 14:22:44 +0000733
danielk19775f096132008-03-28 15:44:09 +0000734static void test_memdebug_log_clear(){
danielk1977dbdc4d42008-03-28 07:42:53 +0000735 Tcl_HashSearch search;
736 Tcl_HashEntry *pEntry;
737 for(
738 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
739 pEntry;
740 pEntry=Tcl_NextHashEntry(&search)
741 ){
742 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
743 Tcl_Free((char *)pLog);
744 }
745 Tcl_DeleteHashTable(&aMallocLog);
746 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
747}
748
danielk19776f332c12008-03-21 14:22:44 +0000749static int test_memdebug_log(
750 void * clientData,
751 Tcl_Interp *interp,
752 int objc,
753 Tcl_Obj *CONST objv[]
754){
755 static int isInit = 0;
756 int iSub;
757
danielk1977dbdc4d42008-03-28 07:42:53 +0000758 static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
759 enum MB_enum {
760 MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
761 };
danielk19776f332c12008-03-21 14:22:44 +0000762
763 if( !isInit ){
764#ifdef SQLITE_MEMDEBUG
765 extern void sqlite3MemdebugBacktraceCallback(
766 void (*xBacktrace)(int, int, void **));
767 sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
768#endif
769 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
770 isInit = 1;
771 }
772
773 if( objc<2 ){
774 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
775 }
776 if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
777 return TCL_ERROR;
778 }
779
780 switch( (enum MB_enum)iSub ){
781 case MB_LOG_START:
782 mallocLogEnabled = 1;
783 break;
784 case MB_LOG_STOP:
785 mallocLogEnabled = 0;
786 break;
787 case MB_LOG_DUMP: {
788 Tcl_HashSearch search;
789 Tcl_HashEntry *pEntry;
790 Tcl_Obj *pRet = Tcl_NewObj();
791
792 assert(sizeof(int)==sizeof(void*));
793
794 for(
795 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
796 pEntry;
797 pEntry=Tcl_NextHashEntry(&search)
798 ){
799 Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
800 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
801 int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
802 int ii;
803
804 apElem[0] = Tcl_NewIntObj(pLog->nCall);
805 apElem[1] = Tcl_NewIntObj(pLog->nByte);
806 for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
807 apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
808 }
809
810 Tcl_ListObjAppendElement(interp, pRet,
811 Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
812 );
813 }
814
815 Tcl_SetObjResult(interp, pRet);
816 break;
817 }
818 case MB_LOG_CLEAR: {
danielk1977dbdc4d42008-03-28 07:42:53 +0000819 test_memdebug_log_clear();
820 break;
821 }
822
823 case MB_LOG_SYNC: {
drhb9404922008-03-28 12:53:38 +0000824#ifdef SQLITE_MEMDEBUG
danielk1977dbdc4d42008-03-28 07:42:53 +0000825 extern void sqlite3MemdebugSync();
826 test_memdebug_log_clear();
827 mallocLogEnabled = 1;
828 sqlite3MemdebugSync();
drhb9404922008-03-28 12:53:38 +0000829#endif
danielk1977dbdc4d42008-03-28 07:42:53 +0000830 break;
danielk19776f332c12008-03-21 14:22:44 +0000831 }
832 }
833
834 return TCL_OK;
835}
drh4a50aac2007-08-23 02:47:53 +0000836
837/*
drh9ac3fe92008-06-18 18:12:04 +0000838** Usage: sqlite3_config_scratch SIZE N
839**
840** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
841** The buffer is static and is of limited size. N might be
842** adjusted downward as needed to accomodate the requested size.
843** The revised value of N is returned.
844**
845** A negative SIZE causes the buffer pointer to be NULL.
846*/
847static int test_config_scratch(
848 void * clientData,
849 Tcl_Interp *interp,
850 int objc,
851 Tcl_Obj *CONST objv[]
852){
853 int sz, N, rc;
854 Tcl_Obj *pResult;
drhf7141992008-06-19 00:16:08 +0000855 static char buf[30000];
drh9ac3fe92008-06-18 18:12:04 +0000856 if( objc!=3 ){
857 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
858 return TCL_ERROR;
859 }
drhf7141992008-06-19 00:16:08 +0000860 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
861 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000862 if( sz<0 ){
863 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
drhf7141992008-06-19 00:16:08 +0000864 }else{
drh9ac3fe92008-06-18 18:12:04 +0000865 int mx = sizeof(buf)/(sz+4);
866 if( N>mx ) N = mx;
867 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
868 }
869 pResult = Tcl_NewObj();
870 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
871 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
872 Tcl_SetObjResult(interp, pResult);
873 return TCL_OK;
874}
875
876/*
877** Usage: sqlite3_config_pagecache SIZE N
878**
879** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
880** The buffer is static and is of limited size. N might be
881** adjusted downward as needed to accomodate the requested size.
882** The revised value of N is returned.
883**
884** A negative SIZE causes the buffer pointer to be NULL.
885*/
886static int test_config_pagecache(
887 void * clientData,
888 Tcl_Interp *interp,
889 int objc,
890 Tcl_Obj *CONST objv[]
891){
892 int sz, N, rc;
893 Tcl_Obj *pResult;
894 static char buf[100000];
895 if( objc!=3 ){
896 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
897 return TCL_ERROR;
898 }
drhf7141992008-06-19 00:16:08 +0000899 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
900 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000901 if( sz<0 ){
drhf7141992008-06-19 00:16:08 +0000902 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
903 }else{
drh9ac3fe92008-06-18 18:12:04 +0000904 int mx = sizeof(buf)/(sz+4);
905 if( N>mx ) N = mx;
906 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
907 }
908 pResult = Tcl_NewObj();
909 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
910 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
911 Tcl_SetObjResult(interp, pResult);
912 return TCL_OK;
913}
914
drhf7141992008-06-19 00:16:08 +0000915/*
916** Usage: sqlite3_status OPCODE RESETFLAG
917**
918** Return a list of three elements which are the sqlite3_status() return
919** code, the current value, and the high-water mark value.
920*/
921static int test_status(
922 void * clientData,
923 Tcl_Interp *interp,
924 int objc,
925 Tcl_Obj *CONST objv[]
926){
927 int rc, iValue, mxValue;
928 int i, op, resetFlag;
929 const char *zOpName;
930 static const struct {
931 const char *zName;
932 int op;
933 } aOp[] = {
934 { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
935 { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
936 { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
937 { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
938 { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
939 { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
940 };
941 Tcl_Obj *pResult;
942 if( objc!=3 ){
943 Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
944 return TCL_ERROR;
945 }
946 zOpName = Tcl_GetString(objv[1]);
947 for(i=0; i<ArraySize(aOp); i++){
948 if( strcmp(aOp[i].zName, zOpName)==0 ){
949 op = aOp[i].op;
950 break;
951 }
952 }
953 if( i>=ArraySize(aOp) ){
954 if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR;
955 }
956 if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR;
957 rc = sqlite3_status(op, &iValue, &mxValue, resetFlag);
958 pResult = Tcl_NewObj();
959 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
960 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
961 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
962 Tcl_SetObjResult(interp, pResult);
963 return TCL_OK;
964}
drh9ac3fe92008-06-18 18:12:04 +0000965
966/*
danielk1977d09414c2008-06-19 18:17:49 +0000967** install_malloc_faultsim BOOLEAN
968*/
969static int test_install_malloc_faultsim(
970 void * clientData,
971 Tcl_Interp *interp,
972 int objc,
973 Tcl_Obj *CONST objv[]
974){
975 int rc;
976 int isInstall;
977
978 if( objc!=2 ){
979 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
980 return TCL_ERROR;
981 }
982 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
983 return TCL_ERROR;
984 }
danielk1977ef05f2d2008-06-20 11:05:37 +0000985 rc = faultsimInstall(isInstall);
danielk1977d09414c2008-06-19 18:17:49 +0000986 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
987 return TCL_OK;
988}
989
990/*
drh2f999a62007-08-15 19:16:43 +0000991** Register commands with the TCL interpreter.
992*/
993int Sqlitetest_malloc_Init(Tcl_Interp *interp){
994 static struct {
995 char *zName;
996 Tcl_ObjCmdProc *xProc;
997 } aObjCmd[] = {
998 { "sqlite3_malloc", test_malloc },
999 { "sqlite3_realloc", test_realloc },
1000 { "sqlite3_free", test_free },
drh9c7a60d2007-10-19 17:47:24 +00001001 { "memset", test_memset },
1002 { "memget", test_memget },
drh2f999a62007-08-15 19:16:43 +00001003 { "sqlite3_memory_used", test_memory_used },
1004 { "sqlite3_memory_highwater", test_memory_highwater },
1005 { "sqlite3_memdebug_backtrace", test_memdebug_backtrace },
1006 { "sqlite3_memdebug_dump", test_memdebug_dump },
drh0e6f1542007-08-15 20:41:28 +00001007 { "sqlite3_memdebug_fail", test_memdebug_fail },
danielk1977cd037242007-08-30 15:46:06 +00001008 { "sqlite3_memdebug_pending", test_memdebug_pending },
drh4a50aac2007-08-23 02:47:53 +00001009 { "sqlite3_memdebug_settitle", test_memdebug_settitle },
danielk1977a7a8e142008-02-13 18:25:27 +00001010 { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count },
drh9ac3fe92008-06-18 18:12:04 +00001011 { "sqlite3_memdebug_log", test_memdebug_log },
1012 { "sqlite3_config_scratch", test_config_scratch },
1013 { "sqlite3_config_pagecache", test_config_pagecache },
drhf7141992008-06-19 00:16:08 +00001014 { "sqlite3_status", test_status },
danielk1977d09414c2008-06-19 18:17:49 +00001015
1016 { "install_malloc_faultsim", test_install_malloc_faultsim },
drh2f999a62007-08-15 19:16:43 +00001017 };
1018 int i;
1019 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
1020 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
1021 }
1022 return TCL_OK;
1023}
danielk1977ef05f2d2008-06-20 11:05:37 +00001024#endif