xref: /sqlite-3.40.0/src/test_malloc.c (revision 8a29dfde)
1 /*
2 ** 2007 August 15
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 *************************************************************************
12 **
13 ** This file contains code used to implement test interfaces to the
14 ** memory allocation subsystem.
15 **
16 ** $Id: test_malloc.c,v 1.22 2008/03/28 15:44:10 danielk1977 Exp $
17 */
18 #include "sqliteInt.h"
19 #include "tcl.h"
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 
24 /*
25 ** Transform pointers to text and back again
26 */
27 static void pointerToText(void *p, char *z){
28   static const char zHex[] = "0123456789abcdef";
29   int i, k;
30   unsigned int u;
31   sqlite3_uint64 n;
32   if( sizeof(n)==sizeof(p) ){
33     memcpy(&n, &p, sizeof(p));
34   }else if( sizeof(u)==sizeof(p) ){
35     memcpy(&u, &p, sizeof(u));
36     n = u;
37   }else{
38     assert( 0 );
39   }
40   for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
41     z[k] = zHex[n&0xf];
42     n >>= 4;
43   }
44   z[sizeof(p)*2] = 0;
45 }
46 static int hexToInt(int h){
47   if( h>='0' && h<='9' ){
48     return h - '0';
49   }else if( h>='a' && h<='f' ){
50     return h - 'a' + 10;
51   }else{
52     return -1;
53   }
54 }
55 static int textToPointer(const char *z, void **pp){
56   sqlite3_uint64 n = 0;
57   int i;
58   unsigned int u;
59   for(i=0; i<sizeof(void*)*2 && z[0]; i++){
60     int v;
61     v = hexToInt(*z++);
62     if( v<0 ) return TCL_ERROR;
63     n = n*16 + v;
64   }
65   if( *z!=0 ) return TCL_ERROR;
66   if( sizeof(n)==sizeof(*pp) ){
67     memcpy(pp, &n, sizeof(n));
68   }else if( sizeof(u)==sizeof(*pp) ){
69     u = (unsigned int)n;
70     memcpy(pp, &u, sizeof(u));
71   }else{
72     assert( 0 );
73   }
74   return TCL_OK;
75 }
76 
77 /*
78 ** Usage:    sqlite3_malloc  NBYTES
79 **
80 ** Raw test interface for sqlite3_malloc().
81 */
82 static int test_malloc(
83   void * clientData,
84   Tcl_Interp *interp,
85   int objc,
86   Tcl_Obj *CONST objv[]
87 ){
88   int nByte;
89   void *p;
90   char zOut[100];
91   if( objc!=2 ){
92     Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
93     return TCL_ERROR;
94   }
95   if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
96   p = sqlite3_malloc((unsigned)nByte);
97   pointerToText(p, zOut);
98   Tcl_AppendResult(interp, zOut, NULL);
99   return TCL_OK;
100 }
101 
102 /*
103 ** Usage:    sqlite3_realloc  PRIOR  NBYTES
104 **
105 ** Raw test interface for sqlite3_realloc().
106 */
107 static int test_realloc(
108   void * clientData,
109   Tcl_Interp *interp,
110   int objc,
111   Tcl_Obj *CONST objv[]
112 ){
113   int nByte;
114   void *pPrior, *p;
115   char zOut[100];
116   if( objc!=3 ){
117     Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
118     return TCL_ERROR;
119   }
120   if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
121   if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
122     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
123     return TCL_ERROR;
124   }
125   p = sqlite3_realloc(pPrior, (unsigned)nByte);
126   pointerToText(p, zOut);
127   Tcl_AppendResult(interp, zOut, NULL);
128   return TCL_OK;
129 }
130 
131 
132 /*
133 ** Usage:    sqlite3_free  PRIOR
134 **
135 ** Raw test interface for sqlite3_free().
136 */
137 static int test_free(
138   void * clientData,
139   Tcl_Interp *interp,
140   int objc,
141   Tcl_Obj *CONST objv[]
142 ){
143   void *pPrior;
144   if( objc!=2 ){
145     Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
146     return TCL_ERROR;
147   }
148   if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
149     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
150     return TCL_ERROR;
151   }
152   sqlite3_free(pPrior);
153   return TCL_OK;
154 }
155 
156 /*
157 ** These routines are in test_hexio.c
158 */
159 int sqlite3TestHexToBin(const char *, int, char *);
160 int sqlite3TestBinToHex(char*,int);
161 
162 /*
163 ** Usage:    memset  ADDRESS  SIZE  HEX
164 **
165 ** Set a chunk of memory (obtained from malloc, probably) to a
166 ** specified hex pattern.
167 */
168 static int test_memset(
169   void * clientData,
170   Tcl_Interp *interp,
171   int objc,
172   Tcl_Obj *CONST objv[]
173 ){
174   void *p;
175   int size, n, i;
176   char *zHex;
177   char *zOut;
178   char zBin[100];
179 
180   if( objc!=4 ){
181     Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
182     return TCL_ERROR;
183   }
184   if( textToPointer(Tcl_GetString(objv[1]), &p) ){
185     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
186     return TCL_ERROR;
187   }
188   if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
189     return TCL_ERROR;
190   }
191   if( size<=0 ){
192     Tcl_AppendResult(interp, "size must be positive", (char*)0);
193     return TCL_ERROR;
194   }
195   zHex = Tcl_GetStringFromObj(objv[3], &n);
196   if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
197   n = sqlite3TestHexToBin(zHex, n, zBin);
198   if( n==0 ){
199     Tcl_AppendResult(interp, "no data", (char*)0);
200     return TCL_ERROR;
201   }
202   zOut = p;
203   for(i=0; i<size; i++){
204     zOut[i] = zBin[i%n];
205   }
206   return TCL_OK;
207 }
208 
209 /*
210 ** Usage:    memget  ADDRESS  SIZE
211 **
212 ** Return memory as hexadecimal text.
213 */
214 static int test_memget(
215   void * clientData,
216   Tcl_Interp *interp,
217   int objc,
218   Tcl_Obj *CONST objv[]
219 ){
220   void *p;
221   int size, n;
222   char *zBin;
223   char zHex[100];
224 
225   if( objc!=3 ){
226     Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
227     return TCL_ERROR;
228   }
229   if( textToPointer(Tcl_GetString(objv[1]), &p) ){
230     Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
231     return TCL_ERROR;
232   }
233   if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
234     return TCL_ERROR;
235   }
236   if( size<=0 ){
237     Tcl_AppendResult(interp, "size must be positive", (char*)0);
238     return TCL_ERROR;
239   }
240   zBin = p;
241   while( size>0 ){
242     if( size>(sizeof(zHex)-1)/2 ){
243       n = (sizeof(zHex)-1)/2;
244     }else{
245       n = size;
246     }
247     memcpy(zHex, zBin, n);
248     zBin += n;
249     size -= n;
250     sqlite3TestBinToHex(zHex, n);
251     Tcl_AppendResult(interp, zHex, (char*)0);
252   }
253   return TCL_OK;
254 }
255 
256 /*
257 ** Usage:    sqlite3_memory_used
258 **
259 ** Raw test interface for sqlite3_memory_used().
260 */
261 static int test_memory_used(
262   void * clientData,
263   Tcl_Interp *interp,
264   int objc,
265   Tcl_Obj *CONST objv[]
266 ){
267   Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
268   return TCL_OK;
269 }
270 
271 /*
272 ** Usage:    sqlite3_memory_highwater ?RESETFLAG?
273 **
274 ** Raw test interface for sqlite3_memory_highwater().
275 */
276 static int test_memory_highwater(
277   void * clientData,
278   Tcl_Interp *interp,
279   int objc,
280   Tcl_Obj *CONST objv[]
281 ){
282   int resetFlag = 0;
283   if( objc!=1 && objc!=2 ){
284     Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
285     return TCL_ERROR;
286   }
287   if( objc==2 ){
288     if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
289   }
290   Tcl_SetObjResult(interp,
291      Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
292   return TCL_OK;
293 }
294 
295 /*
296 ** Usage:    sqlite3_memdebug_backtrace DEPTH
297 **
298 ** Set the depth of backtracing.  If SQLITE_MEMDEBUG is not defined
299 ** then this routine is a no-op.
300 */
301 static int test_memdebug_backtrace(
302   void * clientData,
303   Tcl_Interp *interp,
304   int objc,
305   Tcl_Obj *CONST objv[]
306 ){
307   int depth;
308   if( objc!=2 ){
309     Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
310     return TCL_ERROR;
311   }
312   if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
313 #ifdef SQLITE_MEMDEBUG
314   {
315     extern void sqlite3MemdebugBacktrace(int);
316     sqlite3MemdebugBacktrace(depth);
317   }
318 #endif
319   return TCL_OK;
320 }
321 
322 /*
323 ** Usage:    sqlite3_memdebug_dump  FILENAME
324 **
325 ** Write a summary of unfreed memory to FILENAME.
326 */
327 static int test_memdebug_dump(
328   void * clientData,
329   Tcl_Interp *interp,
330   int objc,
331   Tcl_Obj *CONST objv[]
332 ){
333   if( objc!=2 ){
334     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
335     return TCL_ERROR;
336   }
337 #if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
338      || defined(SQLITE_POW2_MEMORY_SIZE)
339   {
340     extern void sqlite3MemdebugDump(const char*);
341     sqlite3MemdebugDump(Tcl_GetString(objv[1]));
342   }
343 #endif
344   return TCL_OK;
345 }
346 
347 /*
348 ** Usage:    sqlite3_memdebug_malloc_count
349 **
350 ** Return the total number of times malloc() has been called.
351 */
352 static int test_memdebug_malloc_count(
353   void * clientData,
354   Tcl_Interp *interp,
355   int objc,
356   Tcl_Obj *CONST objv[]
357 ){
358   int nMalloc = -1;
359   if( objc!=1 ){
360     Tcl_WrongNumArgs(interp, 1, objv, "");
361     return TCL_ERROR;
362   }
363 #if defined(SQLITE_MEMDEBUG)
364   {
365     extern int sqlite3MemdebugMallocCount();
366     nMalloc = sqlite3MemdebugMallocCount();
367   }
368 #endif
369   Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
370   return TCL_OK;
371 }
372 
373 
374 /*
375 ** Usage:    sqlite3_memdebug_fail  COUNTER  ?OPTIONS?
376 **
377 ** where options are:
378 **
379 **     -repeat    <count>
380 **     -benigncnt <varname>
381 **
382 ** Arrange for a simulated malloc() failure after COUNTER successes.
383 ** If a repeat count is specified, the fault is repeated that many
384 ** times.
385 **
386 ** Each call to this routine overrides the prior counter value.
387 ** This routine returns the number of simulated failures that have
388 ** happened since the previous call to this routine.
389 **
390 ** To disable simulated failures, use a COUNTER of -1.
391 */
392 static int test_memdebug_fail(
393   void * clientData,
394   Tcl_Interp *interp,
395   int objc,
396   Tcl_Obj *CONST objv[]
397 ){
398   int ii;
399   int iFail;
400   int nRepeat = 1;
401   Tcl_Obj *pBenignCnt = 0;
402   int nBenign;
403   int nFail = 0;
404 
405   if( objc<2 ){
406     Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
407     return TCL_ERROR;
408   }
409   if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
410 
411   for(ii=2; ii<objc; ii+=2){
412     int nOption;
413     char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
414     char *zErr = 0;
415 
416     if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
417       if( ii==(objc-1) ){
418         zErr = "option requires an argument: ";
419       }else{
420         if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
421           return TCL_ERROR;
422         }
423       }
424     }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
425       if( ii==(objc-1) ){
426         zErr = "option requires an argument: ";
427       }else{
428         pBenignCnt = objv[ii+1];
429       }
430     }else{
431       zErr = "unknown option: ";
432     }
433 
434     if( zErr ){
435       Tcl_AppendResult(interp, zErr, zOption, 0);
436       return TCL_ERROR;
437     }
438   }
439 
440   sqlite3_test_control(-12345); /* Just to stress the test_control interface */
441   nBenign = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_BENIGN_FAILURES,
442                                  SQLITE_FAULTINJECTOR_MALLOC);
443   nFail = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_FAILURES,
444                                SQLITE_FAULTINJECTOR_MALLOC);
445   sqlite3_test_control(SQLITE_TESTCTRL_FAULT_CONFIG,
446                        SQLITE_FAULTINJECTOR_MALLOC, iFail, nRepeat);
447   if( pBenignCnt ){
448     Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
449   }
450   Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
451   return TCL_OK;
452 }
453 
454 /*
455 ** Usage:    sqlite3_memdebug_pending
456 **
457 ** Return the number of malloc() calls that will succeed before a
458 ** simulated failure occurs. A negative return value indicates that
459 ** no malloc() failure is scheduled.
460 */
461 static int test_memdebug_pending(
462   void * clientData,
463   Tcl_Interp *interp,
464   int objc,
465   Tcl_Obj *CONST objv[]
466 ){
467   int nPending;
468   if( objc!=1 ){
469     Tcl_WrongNumArgs(interp, 1, objv, "");
470     return TCL_ERROR;
471   }
472   nPending = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_PENDING,
473                                   SQLITE_FAULTINJECTOR_MALLOC);
474   Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
475   return TCL_OK;
476 }
477 
478 
479 /*
480 ** Usage:    sqlite3_memdebug_settitle TITLE
481 **
482 ** Set a title string stored with each allocation.  The TITLE is
483 ** typically the name of the test that was running when the
484 ** allocation occurred.  The TITLE is stored with the allocation
485 ** and can be used to figure out which tests are leaking memory.
486 **
487 ** Each title overwrite the previous.
488 */
489 static int test_memdebug_settitle(
490   void * clientData,
491   Tcl_Interp *interp,
492   int objc,
493   Tcl_Obj *CONST objv[]
494 ){
495   const char *zTitle;
496   if( objc!=2 ){
497     Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
498     return TCL_ERROR;
499   }
500   zTitle = Tcl_GetString(objv[1]);
501 #ifdef SQLITE_MEMDEBUG
502   {
503     extern int sqlite3MemdebugSettitle(const char*);
504     sqlite3MemdebugSettitle(zTitle);
505   }
506 #endif
507   return TCL_OK;
508 }
509 
510 #define MALLOC_LOG_FRAMES 10
511 static Tcl_HashTable aMallocLog;
512 static int mallocLogEnabled = 0;
513 
514 typedef struct MallocLog MallocLog;
515 struct MallocLog {
516   int nCall;
517   int nByte;
518 };
519 
520 static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
521   if( mallocLogEnabled ){
522     MallocLog *pLog;
523     Tcl_HashEntry *pEntry;
524     int isNew;
525 
526     int aKey[MALLOC_LOG_FRAMES];
527     int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
528 
529     memset(aKey, 0, nKey);
530     if( (sizeof(void*)*nFrame)<nKey ){
531       nKey = nFrame*sizeof(void*);
532     }
533     memcpy(aKey, aFrame, nKey);
534 
535     pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
536     if( isNew ){
537       pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
538       memset(pLog, 0, sizeof(MallocLog));
539       Tcl_SetHashValue(pEntry, (ClientData)pLog);
540     }else{
541       pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
542     }
543 
544     pLog->nCall++;
545     pLog->nByte += nByte;
546   }
547 }
548 
549 static void test_memdebug_log_clear(){
550   Tcl_HashSearch search;
551   Tcl_HashEntry *pEntry;
552   for(
553     pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
554     pEntry;
555     pEntry=Tcl_NextHashEntry(&search)
556   ){
557     MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
558     Tcl_Free((char *)pLog);
559   }
560   Tcl_DeleteHashTable(&aMallocLog);
561   Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
562 }
563 
564 static int test_memdebug_log(
565   void * clientData,
566   Tcl_Interp *interp,
567   int objc,
568   Tcl_Obj *CONST objv[]
569 ){
570   static int isInit = 0;
571   int iSub;
572 
573   static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
574   enum MB_enum {
575       MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
576   };
577 
578   if( !isInit ){
579 #ifdef SQLITE_MEMDEBUG
580     extern void sqlite3MemdebugBacktraceCallback(
581         void (*xBacktrace)(int, int, void **));
582     sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
583 #endif
584     Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
585     isInit = 1;
586   }
587 
588   if( objc<2 ){
589     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
590   }
591   if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
592     return TCL_ERROR;
593   }
594 
595   switch( (enum MB_enum)iSub ){
596     case MB_LOG_START:
597       mallocLogEnabled = 1;
598       break;
599     case MB_LOG_STOP:
600       mallocLogEnabled = 0;
601       break;
602     case MB_LOG_DUMP: {
603       Tcl_HashSearch search;
604       Tcl_HashEntry *pEntry;
605       Tcl_Obj *pRet = Tcl_NewObj();
606 
607       assert(sizeof(int)==sizeof(void*));
608 
609       for(
610         pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
611         pEntry;
612         pEntry=Tcl_NextHashEntry(&search)
613       ){
614         Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
615         MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
616         int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
617         int ii;
618 
619         apElem[0] = Tcl_NewIntObj(pLog->nCall);
620         apElem[1] = Tcl_NewIntObj(pLog->nByte);
621         for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
622           apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
623         }
624 
625         Tcl_ListObjAppendElement(interp, pRet,
626             Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
627         );
628       }
629 
630       Tcl_SetObjResult(interp, pRet);
631       break;
632     }
633     case MB_LOG_CLEAR: {
634       test_memdebug_log_clear();
635       break;
636     }
637 
638     case MB_LOG_SYNC: {
639 #ifdef SQLITE_MEMDEBUG
640       extern void sqlite3MemdebugSync();
641       test_memdebug_log_clear();
642       mallocLogEnabled = 1;
643       sqlite3MemdebugSync();
644 #endif
645       break;
646     }
647   }
648 
649   return TCL_OK;
650 }
651 
652 /*
653 ** Register commands with the TCL interpreter.
654 */
655 int Sqlitetest_malloc_Init(Tcl_Interp *interp){
656   static struct {
657      char *zName;
658      Tcl_ObjCmdProc *xProc;
659   } aObjCmd[] = {
660      { "sqlite3_malloc",             test_malloc                   },
661      { "sqlite3_realloc",            test_realloc                  },
662      { "sqlite3_free",               test_free                     },
663      { "memset",                     test_memset                   },
664      { "memget",                     test_memget                   },
665      { "sqlite3_memory_used",        test_memory_used              },
666      { "sqlite3_memory_highwater",   test_memory_highwater         },
667      { "sqlite3_memdebug_backtrace", test_memdebug_backtrace       },
668      { "sqlite3_memdebug_dump",      test_memdebug_dump            },
669      { "sqlite3_memdebug_fail",      test_memdebug_fail            },
670      { "sqlite3_memdebug_pending",   test_memdebug_pending         },
671      { "sqlite3_memdebug_settitle",  test_memdebug_settitle        },
672      { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count },
673      { "sqlite3_memdebug_log",       test_memdebug_log },
674   };
675   int i;
676   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
677     Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
678   }
679   return TCL_OK;
680 }
681