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