diff options
author | drh <drh@noemail.net> | 2007-08-23 02:47:53 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2007-08-23 02:47:53 +0000 |
commit | 4a50aac5645b7dffed2c27e552675fa4be3a9368 (patch) | |
tree | 8a197bbd8fca21cf558ae84e510efd8168fe72c2 /src | |
parent | ed138fb3bc8e55a6ae93669899dce79e5e26c65a (diff) | |
download | sqlite-4a50aac5645b7dffed2c27e552675fa4be3a9368.tar.gz sqlite-4a50aac5645b7dffed2c27e552675fa4be3a9368.zip |
Improvements to memory leak detection. The --backtrace=NNN option is now
recognized by tester.tcl. Memory leak summaries are automatically written
to the file ./memleak.txt and each leak is tagged with the test in which
it occurred. The quick.test script runs on Linux with no errors and
no leaks. (CVS 4273)
FossilOrigin-Name: 21f6b31097692171c6493e6ca6de6acbd62dc595
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 6 | ||||
-rw-r--r-- | src/func.c | 5 | ||||
-rw-r--r-- | src/loadext.c | 1 | ||||
-rw-r--r-- | src/mem2.c | 58 | ||||
-rw-r--r-- | src/os_unix.c | 6 | ||||
-rw-r--r-- | src/sqlite.h.in | 19 | ||||
-rw-r--r-- | src/sqliteInt.h | 4 | ||||
-rw-r--r-- | src/test1.c | 14 | ||||
-rw-r--r-- | src/test6.c | 11 | ||||
-rw-r--r-- | src/test8.c | 4 | ||||
-rw-r--r-- | src/test_malloc.c | 56 | ||||
-rw-r--r-- | src/vdbeaux.c | 6 | ||||
-rw-r--r-- | src/vtab.c | 6 |
13 files changed, 147 insertions, 49 deletions
diff --git a/src/btree.c b/src/btree.c index f92e32cc5..bb740a74f 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.409 2007/08/22 11:41:18 drh Exp $ +** $Id: btree.c,v 1.410 2007/08/23 02:47:53 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -1136,12 +1136,16 @@ int sqlite3BtreeOpen( */ if( (flags & BTREE_PRIVATE)==0 && isMemdb==0 + && (pSqlite==0 || (pSqlite->flags &SQLITE_Vtab)==0) && zFilename && zFilename[0] && sqlite3SharedCacheEnabled ){ char *zFullPathname = (char *)sqlite3_malloc(pVfs->mxPathname); sqlite3_mutex *mutexShared; p->sharable = 1; + if( pSqlite ){ + pSqlite->flags |= SQLITE_SharedCache; + } if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; diff --git a/src/func.c b/src/func.c index 224f88f08..7b981df30 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.168 2007/08/21 19:33:56 drh Exp $ +** $Id: func.c,v 1.169 2007/08/23 02:47:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -807,14 +807,17 @@ static void replaceFunc( if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){ zOut[j++] = zStr[i]; }else{ + u8 *zOld; nOut += nRep - nPattern; if( nOut>=SQLITE_MAX_LENGTH ){ sqlite3_result_error_toobig(context); sqlite3_free(zOut); return; } + zOld = zOut; zOut = sqlite3_realloc(zOut, (int)nOut); if( zOut==0 ){ + sqlite3_free(zOld); return; } memcpy(&zOut[j], zRep, nRep); diff --git a/src/loadext.c b/src/loadext.c index a4c57c2c6..7d2340cf7 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -454,6 +454,7 @@ int sqlite3AutoLoadExtensions(sqlite3 *db){ "automatic extension loading failed: %s", zErrmsg); go = 0; rc = SQLITE_ERROR; + sqlite3_free(zErrmsg); } } return rc; diff --git a/src/mem2.c b/src/mem2.c index d9653ee81..4b3c4f58f 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -12,7 +12,7 @@ ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem2.c,v 1.7 2007/08/22 22:04:37 drh Exp $ +** $Id: mem2.c,v 1.8 2007/08/23 02:47:53 drh Exp $ */ /* @@ -59,9 +59,9 @@ /* ** Each memory allocation looks like this: ** -** ---------------------------------------------------------------- -** | backtrace pointers | MemBlockHdr | allocation | EndGuard | -** ---------------------------------------------------------------- +** ------------------------------------------------------------------------ +** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard | +** ------------------------------------------------------------------------ ** ** The application code sees only a pointer to the allocation. We have ** to back up from the allocation pointer to find the MemBlockHdr. The @@ -72,8 +72,9 @@ struct MemBlockHdr { struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ unsigned int iSize; /* Size of this allocation */ - unsigned short nBacktrace; /* Number of backtraces on this alloc */ - unsigned short nBacktraceSlots; /* Available backtrace slots */ + unsigned char nBacktrace; /* Number of backtraces on this alloc */ + unsigned char nBacktraceSlots; /* Available backtrace slots */ + unsigned short nTitle; /* Bytes of title; includes '\0' */ unsigned int iForeGuard; /* Guard word for sanity */ }; @@ -125,6 +126,12 @@ static struct { int nBacktrace; /* + ** Title text to insert in front of each block + */ + int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */ + char zTitle[100]; /* The title text */ + + /* ** These values are used to simulate malloc failures. When ** iFail is 1, simulate a malloc failures and reset the value ** to iReset. @@ -252,6 +259,7 @@ static void sqlite3MemsysFailed(void){ void *sqlite3_malloc(int nByte){ struct MemBlockHdr *pHdr; void **pBt; + char *z; unsigned int *pInt; void *p; unsigned int totalSize; @@ -269,7 +277,7 @@ void *sqlite3_malloc(int nByte){ } nByte = (nByte+3)&~3; totalSize = nByte + sizeof(*pHdr) + sizeof(unsigned int) + - mem.nBacktrace*sizeof(void*); + mem.nBacktrace*sizeof(void*) + mem.nTitle; if( mem.iFail>0 ){ if( mem.iFail==1 ){ p = 0; @@ -290,7 +298,8 @@ void *sqlite3_malloc(int nByte){ } } if( p ){ - pBt = p; + z = p; + pBt = (void**)&z[mem.nTitle]; pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; pHdr->pNext = 0; pHdr->pPrev = mem.pLast; @@ -302,6 +311,7 @@ void *sqlite3_malloc(int nByte){ mem.pLast = pHdr; pHdr->iForeGuard = FOREGUARD; pHdr->nBacktraceSlots = mem.nBacktrace; + pHdr->nTitle = mem.nTitle; if( mem.nBacktrace ){ void *aAddr[40]; pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; @@ -309,6 +319,9 @@ void *sqlite3_malloc(int nByte){ }else{ pHdr->nBacktrace = 0; } + if( mem.nTitle ){ + memcpy(z, mem.zTitle, mem.nTitle); + } pHdr->iSize = nByte; pInt = (unsigned int *)&pHdr[1]; pInt[nByte/sizeof(unsigned int)] = REARGUARD; @@ -329,6 +342,7 @@ void *sqlite3_malloc(int nByte){ void sqlite3_free(void *pPrior){ struct MemBlockHdr *pHdr; void **pBt; + char *z; if( pPrior==0 ){ return; } @@ -352,9 +366,11 @@ void sqlite3_free(void *pPrior){ assert( mem.pLast==pHdr ); mem.pLast = pHdr->pPrev; } - memset(pBt, 0x2b, sizeof(void*)*pHdr->nBacktrace + sizeof(*pHdr) + - pHdr->iSize + sizeof(unsigned int)); - free(pBt); + z = (char*)pBt; + z -= pHdr->nTitle; + memset(z, 0x2b, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + + pHdr->iSize + sizeof(unsigned int) + pHdr->nTitle); + free(z); sqlite3_mutex_leave(mem.mutex); } @@ -403,6 +419,22 @@ void sqlite3_memdebug_backtrace(int depth){ } /* +** Set the title string for subsequent allocations. +*/ +void sqlite3_memdebug_settitle(const char *zTitle){ + int n = strlen(zTitle) + 1; + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); + } + sqlite3_mutex_enter(mem.mutex); + if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; + memcpy(mem.zTitle, zTitle, n); + mem.zTitle[n] = 0; + mem.nTitle = (n+3)&~3; + sqlite3_mutex_leave(mem.mutex); +} + +/* ** Open the file indicated and write a log of all unfreed memory ** allocations into that log. */ @@ -417,7 +449,9 @@ void sqlite3_memdebug_dump(const char *zFilename){ return; } for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ - fprintf(out, "**** %d bytes at %p ****\n", pHdr->iSize, &pHdr[1]); + char *z = (char*)pHdr; + z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle; + fprintf(out, "**** %d bytes at %p from %s ****\n", pHdr->iSize,&pHdr[1],z); if( pHdr->nBacktrace ){ fflush(out); pBt = (void**)pHdr; diff --git a/src/os_unix.c b/src/os_unix.c index 75cf36e8d..eb7a66b64 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -2657,6 +2657,10 @@ static int unixRandomness(void *pNotUsed, int nBuf, char *zBuf){ /* ** Sleep for a little while. Return the amount of time slept. ** The argument is the number of microseconds we want to sleep. +** The return value is the number of microseconds of sleep actually +** requested from the underlying operating system, a number which +** might be greater than or equal to the argument, but not less +** than the argument. */ static int unixSleep(void *pNotUsed, int microseconds){ #if defined(HAVE_USLEEP) && HAVE_USLEEP @@ -2665,7 +2669,7 @@ static int unixSleep(void *pNotUsed, int microseconds){ #else int seconds = (microseconds+999999)/1000000; sleep(seconds); - return seconds; + return seconds*1000000; #endif } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6465245ca..b6b56686e 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.237 2007/08/22 20:18:22 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.238 2007/08/23 02:47:53 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1389,8 +1389,11 @@ int sqlite3_open_v2( ** (overwriting the previous values). Note that calls to [sqlite3_errcode()], ** [sqlite3_errmsg()], and [sqlite3_errmsg16()] themselves do not affect the ** results of future invocations. Calls to API routines that do not return -** an error code (examples: [sqlite3_data_count()] or [sqlite3_mprintf()]) do -** not change the error code returned by this routine. +** an error code (example: [sqlite3_data_count()]) do not +** change the error code returned by this routine. Interfaces that are +** not associated with a specific database connection (examples: +** [sqlite3_mprintf()] or [sqlite3_enable_shared_cache()] do not change +** the return code. ** ** Assuming no other intervening sqlite3_* API calls are made, the error ** code returned by this function is associated with the same error as @@ -1708,6 +1711,10 @@ int sqlite3_column_count(sqlite3_stmt *pStmt); ** [sqlite3_stmt | prepared statement] is destroyed by [sqlite3_finalize()] ** or until the next call sqlite3_column_name() or sqlite3_column_name16() ** on the same column. +** +** If sqlite3_malloc() fails during the processing of either routine +** (for example during a conversion from UTF-8 to UTF-16) then a +** NULL pointer is returned. */ const char *sqlite3_column_name(sqlite3_stmt*, int N); const void *sqlite3_column_name16(sqlite3_stmt*, int N); @@ -2040,6 +2047,12 @@ int sqlite3_data_count(sqlite3_stmt *pStmt); ** and blobs is freed automatically. Do <b>not</b> pass the pointers returned ** [sqlite3_column_blob()], [sqlite_column_text()], etc. into ** [sqlite3_free()]. +** +** If a memory allocation error occurs during the evaluation of any +** of these routines, a default value is returned. The default value +** is either the integer 0, the floating point number 0.0, or a NULL +** pointer. Subsequent calls to [sqlite3_errcode()] will return +** [SQLITE_NOMEM]. */ const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); int sqlite3_column_bytes(sqlite3_stmt*, int iCol); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4b6e01dec..29da21f5a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.597 2007/08/22 11:41:18 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.598 2007/08/23 02:47:53 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -493,6 +493,8 @@ struct sqlite3 { #define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ +#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ +#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */ /* ** Possible values for the sqlite.magic field. diff --git a/src/test1.c b/src/test1.c index 11ffb1e23..198744c2b 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.270 2007/08/22 20:18:22 drh Exp $ +** $Id: test1.c,v 1.271 2007/08/23 02:47:53 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -186,18 +186,6 @@ static int getStmtPointer( } /* -** Decode a pointer to an sqlite3_stmt object. -*/ -static int getFilePointer( - Tcl_Interp *interp, - const char *zArg, - sqlite3_file **ppFile -){ - *ppFile = (sqlite3_file*)sqlite3TextToPtr(zArg); - return TCL_OK; -} - -/* ** Generate a text representation of a pointer that can be understood ** by the getDbPointer and getVmPointer routines above. ** diff --git a/src/test6.c b/src/test6.c index 0abf2353a..93befb881 100644 --- a/src/test6.c +++ b/src/test6.c @@ -320,8 +320,6 @@ static int cfRead( sqlite_int64 iOfst ){ CrashFile *pCrash = (CrashFile *)pFile; - sqlite3_int64 iSize; - WriteBuffer *pWrite; /* Check the file-size to see if this is a short-read */ if( pCrash->iSize<(iOfst+iAmt) ){ @@ -346,9 +344,9 @@ static int cfWrite( pCrash->iSize = iAmt+iOfst; } while( pCrash->iSize>pCrash->nData ){ - char *zNew; + u8 *zNew; int nNew = (pCrash->nData*2) + 4096; - zNew = (char *)sqlite3_realloc(pCrash->zData, nNew); + zNew = sqlite3_realloc(pCrash->zData, nNew); if( !zNew ){ return SQLITE_NOMEM; } @@ -480,7 +478,7 @@ static int cfOpen( sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData; int rc; CrashFile *pWrapper = (CrashFile *)pFile; - sqlite3_file *pReal = &pWrapper[1]; + sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1]; memset(pWrapper, 0, sizeof(CrashFile)); rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags); @@ -495,7 +493,7 @@ static int cfOpen( } if( rc==SQLITE_OK ){ pWrapper->nData = (4096 + pWrapper->iSize); - pWrapper->zData = (char *)sqlite3_malloc(pWrapper->nData); + pWrapper->zData = sqlite3_malloc(pWrapper->nData); if( pWrapper->zData ){ memset(pWrapper->zData, 0, pWrapper->nData); rc = sqlite3OsRead(pReal, pWrapper->zData, pWrapper->iSize, 0); @@ -570,7 +568,6 @@ static int crashParamsObjCmd( int iDelay; const char *zCrashFile; int nCrashFile; - static struct crashAppData appData; static sqlite3_vfs crashVfs = { 1, /* iVersion */ diff --git a/src/test8.c b/src/test8.c index 28a49a75f..8e7d50d54 100644 --- a/src/test8.c +++ b/src/test8.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test8.c,v 1.52 2007/08/21 10:44:16 drh Exp $ +** $Id: test8.c,v 1.53 2007/08/23 02:47:53 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -852,7 +852,7 @@ int echoUpdate( "%s %Q=?%d", zSep, pVtab->aCol[i-2], i), 1); zSep = ","; } - string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 0); + string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 1); } /* If apData[0] is an integer and nData==1 then do a DELETE */ diff --git a/src/test_malloc.c b/src/test_malloc.c index 478c6c5f8..395288229 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -13,7 +13,7 @@ ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.3 2007/08/22 22:04:37 drh Exp $ +** $Id: test_malloc.c,v 1.4 2007/08/23 02:47:53 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -27,7 +27,16 @@ static void pointerToText(void *p, char *z){ static const char zHex[] = "0123456789abcdef"; int i, k; - sqlite3_uint64 n = (sqlite3_uint64)p; + unsigned int u; + sqlite3_uint64 n; + if( sizeof(n)==sizeof(p) ){ + memcpy(&n, &p, sizeof(p)); + }else if( sizeof(u)==sizeof(p) ){ + memcpy(&u, &p, sizeof(u)); + n = u; + }else{ + assert( 0 ); + } for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){ z[k] = zHex[n&0xf]; n >>= 4; @@ -46,6 +55,7 @@ static int hexToInt(int h){ static int textToPointer(const char *z, void **pp){ sqlite3_uint64 n = 0; int i; + unsigned int u; for(i=0; i<sizeof(void*)*2 && z[0]; i++){ int v; v = hexToInt(*z++); @@ -53,7 +63,14 @@ static int textToPointer(const char *z, void **pp){ n = n*16 + v; } if( *z!=0 ) return TCL_ERROR; - *pp = (void*)n; + if( sizeof(n)==sizeof(*pp) ){ + memcpy(pp, &n, sizeof(n)); + }else if( sizeof(u)==sizeof(*pp) ){ + u = (unsigned int)n; + memcpy(pp, &u, sizeof(u)); + }else{ + assert( 0 ); + } return TCL_OK; } @@ -290,6 +307,38 @@ static int test_memdebug_pending( /* +** Usage: sqlite3_memdebug_settitle TITLE +** +** Set a title string stored with each allocation. The TITLE is +** typically the name of the test that was running when the +** allocation occurred. The TITLE is stored with the allocation +** and can be used to figure out which tests are leaking memory. +** +** Each title overwrite the previous. +*/ +static int test_memdebug_settitle( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *zTitle; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "TITLE"); + return TCL_ERROR; + } + zTitle = Tcl_GetString(objv[1]); +#ifdef SQLITE_MEMDEBUG + { + extern int sqlite3_memdebug_settitle(const char*); + sqlite3_memdebug_settitle(zTitle); + } +#endif + return TCL_OK; +} + + +/* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ @@ -306,6 +355,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_memdebug_dump", test_memdebug_dump }, { "sqlite3_memdebug_fail", test_memdebug_fail }, { "sqlite3_memdebug_pending", test_memdebug_pending }, + { "sqlite3_memdebug_settitle", test_memdebug_settitle }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 85edcba11..ff93ac529 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1007,6 +1007,8 @@ static void Cleanup(Vdbe *p){ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ Mem *pColName; int n; + sqlite3 *db = p->db; + releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); sqlite3_free(p->aColName); n = nResColumn*COLNAME_N; @@ -1014,7 +1016,9 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ p->aColName = pColName = (Mem*)sqlite3DbMallocZero(p->db, sizeof(Mem)*n ); if( p->aColName==0 ) return; while( n-- > 0 ){ - (pColName++)->flags = MEM_Null; + pColName->flags = MEM_Null; + pColName->db = db; + pColName++; } } diff --git a/src/vtab.c b/src/vtab.c index ac54516fd..09793aef7 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.52 2007/08/22 02:56:44 drh Exp $ +** $Id: vtab.c,v 1.53 2007/08/23 02:47:53 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -167,12 +167,10 @@ void sqlite3VtabBeginParse( Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ -#if 0 /* FIX ME */ - if( sqlite3ThreadDataReadOnly()->useSharedData ){ + if( pParse->db->flags & SQLITE_SharedCache ){ sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode"); return; } -#endif sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); pTable = pParse->pNewTable; |