| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "sqliteInt.h" |
|
|
| |
| |
| |
| |
| #ifdef SQLITE_MEMDEBUG |
|
|
| |
| |
| |
| #ifdef __GLIBC__ |
| extern int backtrace(void**,int); |
| extern void backtrace_symbols_fd(void*const*,int,int); |
| #else |
| # define backtrace(A,B) 1 |
| # define backtrace_symbols_fd(A,B,C) |
| #endif |
| #include <stdio.h> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct MemBlockHdr { |
| i64 iSize; |
| struct MemBlockHdr *pNext, *pPrev; |
| char nBacktrace; |
| char nBacktraceSlots; |
| u8 nTitle; |
| u8 eType; |
| int iForeGuard; |
| }; |
|
|
| |
| |
| |
| #define FOREGUARD 0x80F5E153 |
| #define REARGUARD 0xE4676B53 |
|
|
| |
| |
| |
| #define NCSIZE 1000 |
|
|
| |
| |
| |
| |
| |
| |
| static struct { |
| |
| |
| |
| |
| sqlite3_mutex *mutex; |
|
|
| |
| |
| |
| struct MemBlockHdr *pFirst; |
| struct MemBlockHdr *pLast; |
| |
| |
| |
| |
| int nBacktrace; |
| void (*xBacktrace)(int, int, void **); |
|
|
| |
| |
| |
| int nTitle; |
| char zTitle[100]; |
|
|
| |
| |
| |
| |
| int disallow; |
|
|
| |
| |
| |
| |
| |
| |
| int nAlloc[NCSIZE]; |
| int nCurrent[NCSIZE]; |
| int mxCurrent[NCSIZE]; |
|
|
| } mem; |
|
|
|
|
| |
| |
| |
| static void adjustStats(int iSize, int increment){ |
| int i = ROUND8(iSize)/8; |
| if( i>NCSIZE-1 ){ |
| i = NCSIZE - 1; |
| } |
| if( increment>0 ){ |
| mem.nAlloc[i]++; |
| mem.nCurrent[i]++; |
| if( mem.nCurrent[i]>mem.mxCurrent[i] ){ |
| mem.mxCurrent[i] = mem.nCurrent[i]; |
| } |
| }else{ |
| mem.nCurrent[i]--; |
| assert( mem.nCurrent[i]>=0 ); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static struct MemBlockHdr *sqlite3MemsysGetHeader(const void *pAllocation){ |
| struct MemBlockHdr *p; |
| int *pInt; |
| u8 *pU8; |
| int nReserve; |
|
|
| p = (struct MemBlockHdr*)pAllocation; |
| p--; |
| assert( p->iForeGuard==(int)FOREGUARD ); |
| nReserve = ROUND8(p->iSize); |
| pInt = (int*)pAllocation; |
| pU8 = (u8*)pAllocation; |
| assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); |
| |
| |
| |
| |
| while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 ); |
| return p; |
| } |
|
|
| |
| |
| |
| static int sqlite3MemSize(void *p){ |
| struct MemBlockHdr *pHdr; |
| if( !p ){ |
| return 0; |
| } |
| pHdr = sqlite3MemsysGetHeader(p); |
| return (int)pHdr->iSize; |
| } |
|
|
| |
| |
| |
| static int sqlite3MemInit(void *NotUsed){ |
| UNUSED_PARAMETER(NotUsed); |
| assert( (sizeof(struct MemBlockHdr)&7) == 0 ); |
| if( !sqlite3GlobalConfig.bMemstat ){ |
| |
| |
| mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); |
| } |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static void sqlite3MemShutdown(void *NotUsed){ |
| UNUSED_PARAMETER(NotUsed); |
| mem.mutex = 0; |
| } |
|
|
| |
| |
| |
| static int sqlite3MemRoundup(int n){ |
| return ROUND8(n); |
| } |
|
|
| |
| |
| |
| |
| |
| static void randomFill(char *pBuf, int nByte){ |
| unsigned int x, y, r; |
| x = SQLITE_PTR_TO_INT(pBuf); |
| y = nByte | 1; |
| while( nByte >= 4 ){ |
| x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); |
| y = y*1103515245 + 12345; |
| r = x ^ y; |
| *(int*)pBuf = r; |
| pBuf += 4; |
| nByte -= 4; |
| } |
| while( nByte-- > 0 ){ |
| x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); |
| y = y*1103515245 + 12345; |
| r = x ^ y; |
| *(pBuf++) = r & 0xff; |
| } |
| } |
|
|
| |
| |
| |
| static void *sqlite3MemMalloc(int nByte){ |
| struct MemBlockHdr *pHdr; |
| void **pBt; |
| char *z; |
| int *pInt; |
| void *p = 0; |
| int totalSize; |
| int nReserve; |
| sqlite3_mutex_enter(mem.mutex); |
| assert( mem.disallow==0 ); |
| nReserve = ROUND8(nByte); |
| totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + |
| mem.nBacktrace*sizeof(void*) + mem.nTitle; |
| p = malloc(totalSize); |
| if( p ){ |
| z = p; |
| pBt = (void**)&z[mem.nTitle]; |
| pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; |
| pHdr->pNext = 0; |
| pHdr->pPrev = mem.pLast; |
| if( mem.pLast ){ |
| mem.pLast->pNext = pHdr; |
| }else{ |
| mem.pFirst = pHdr; |
| } |
| mem.pLast = pHdr; |
| pHdr->iForeGuard = FOREGUARD; |
| pHdr->eType = MEMTYPE_HEAP; |
| pHdr->nBacktraceSlots = mem.nBacktrace; |
| pHdr->nTitle = mem.nTitle; |
| if( mem.nBacktrace ){ |
| void *aAddr[40]; |
| pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; |
| memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); |
| assert(pBt[0]); |
| if( mem.xBacktrace ){ |
| mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); |
| } |
| }else{ |
| pHdr->nBacktrace = 0; |
| } |
| if( mem.nTitle ){ |
| memcpy(z, mem.zTitle, mem.nTitle); |
| } |
| pHdr->iSize = nByte; |
| adjustStats(nByte, +1); |
| pInt = (int*)&pHdr[1]; |
| pInt[nReserve/sizeof(int)] = REARGUARD; |
| randomFill((char*)pInt, nByte); |
| memset(((char*)pInt)+nByte, 0x65, nReserve-nByte); |
| p = (void*)pInt; |
| } |
| sqlite3_mutex_leave(mem.mutex); |
| return p; |
| } |
|
|
| |
| |
| |
| static void sqlite3MemFree(void *pPrior){ |
| struct MemBlockHdr *pHdr; |
| void **pBt; |
| char *z; |
| assert( sqlite3GlobalConfig.bMemstat || sqlite3GlobalConfig.bCoreMutex==0 |
| || mem.mutex!=0 ); |
| pHdr = sqlite3MemsysGetHeader(pPrior); |
| pBt = (void**)pHdr; |
| pBt -= pHdr->nBacktraceSlots; |
| sqlite3_mutex_enter(mem.mutex); |
| if( pHdr->pPrev ){ |
| assert( pHdr->pPrev->pNext==pHdr ); |
| pHdr->pPrev->pNext = pHdr->pNext; |
| }else{ |
| assert( mem.pFirst==pHdr ); |
| mem.pFirst = pHdr->pNext; |
| } |
| if( pHdr->pNext ){ |
| assert( pHdr->pNext->pPrev==pHdr ); |
| pHdr->pNext->pPrev = pHdr->pPrev; |
| }else{ |
| assert( mem.pLast==pHdr ); |
| mem.pLast = pHdr->pPrev; |
| } |
| z = (char*)pBt; |
| z -= pHdr->nTitle; |
| adjustStats((int)pHdr->iSize, -1); |
| randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + |
| (int)pHdr->iSize + sizeof(int) + pHdr->nTitle); |
| free(z); |
| sqlite3_mutex_leave(mem.mutex); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void *sqlite3MemRealloc(void *pPrior, int nByte){ |
| struct MemBlockHdr *pOldHdr; |
| void *pNew; |
| assert( mem.disallow==0 ); |
| assert( (nByte & 7)==0 ); |
| pOldHdr = sqlite3MemsysGetHeader(pPrior); |
| pNew = sqlite3MemMalloc(nByte); |
| if( pNew ){ |
| memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize)); |
| if( nByte>pOldHdr->iSize ){ |
| randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize); |
| } |
| sqlite3MemFree(pPrior); |
| } |
| return pNew; |
| } |
|
|
| |
| |
| |
| |
| void sqlite3MemSetDefault(void){ |
| static const sqlite3_mem_methods defaultMethods = { |
| sqlite3MemMalloc, |
| sqlite3MemFree, |
| sqlite3MemRealloc, |
| sqlite3MemSize, |
| sqlite3MemRoundup, |
| sqlite3MemInit, |
| sqlite3MemShutdown, |
| 0 |
| }; |
| sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); |
| } |
|
|
| |
| |
| |
| void sqlite3MemdebugSetType(void *p, u8 eType){ |
| if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ |
| struct MemBlockHdr *pHdr; |
| pHdr = sqlite3MemsysGetHeader(p); |
| assert( pHdr->iForeGuard==FOREGUARD ); |
| pHdr->eType = eType; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int sqlite3MemdebugHasType(const void *p, u8 eType){ |
| int rc = 1; |
| if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ |
| struct MemBlockHdr *pHdr; |
| pHdr = sqlite3MemsysGetHeader(p); |
| assert( pHdr->iForeGuard==FOREGUARD ); |
| if( (pHdr->eType&eType)==0 ){ |
| rc = 0; |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int sqlite3MemdebugNoType(const void *p, u8 eType){ |
| int rc = 1; |
| if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ |
| struct MemBlockHdr *pHdr; |
| pHdr = sqlite3MemsysGetHeader(p); |
| assert( pHdr->iForeGuard==FOREGUARD ); |
| if( (pHdr->eType&eType)!=0 ){ |
| rc = 0; |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| void sqlite3MemdebugBacktrace(int depth){ |
| if( depth<0 ){ depth = 0; } |
| if( depth>20 ){ depth = 20; } |
| depth = (depth+1)&0xfe; |
| mem.nBacktrace = depth; |
| } |
|
|
| void sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){ |
| mem.xBacktrace = xBacktrace; |
| } |
|
|
| |
| |
| |
| void sqlite3MemdebugSettitle(const char *zTitle){ |
| unsigned int n = sqlite3Strlen30(zTitle) + 1; |
| 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 = ROUND8(n); |
| sqlite3_mutex_leave(mem.mutex); |
| } |
|
|
| void sqlite3MemdebugSync(){ |
| struct MemBlockHdr *pHdr; |
| for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ |
| void **pBt = (void**)pHdr; |
| pBt -= pHdr->nBacktraceSlots; |
| mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); |
| } |
| } |
|
|
| |
| |
| |
| |
| void sqlite3MemdebugDump(const char *zFilename){ |
| FILE *out; |
| struct MemBlockHdr *pHdr; |
| void **pBt; |
| int i; |
| out = fopen(zFilename, "w"); |
| if( out==0 ){ |
| fprintf(stderr, "** Unable to output memory debug output log: %s **\n", |
| zFilename); |
| return; |
| } |
| for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ |
| char *z = (char*)pHdr; |
| z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle; |
| fprintf(out, "**** %lld bytes at %p from %s ****\n", |
| pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???"); |
| if( pHdr->nBacktrace ){ |
| fflush(out); |
| pBt = (void**)pHdr; |
| pBt -= pHdr->nBacktraceSlots; |
| backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out)); |
| fprintf(out, "\n"); |
| } |
| } |
| fprintf(out, "COUNTS:\n"); |
| for(i=0; i<NCSIZE-1; i++){ |
| if( mem.nAlloc[i] ){ |
| fprintf(out, " %5d: %10d %10d %10d\n", |
| i*8, mem.nAlloc[i], mem.nCurrent[i], mem.mxCurrent[i]); |
| } |
| } |
| if( mem.nAlloc[NCSIZE-1] ){ |
| fprintf(out, " %5d: %10d %10d %10d\n", |
| NCSIZE*8-8, mem.nAlloc[NCSIZE-1], |
| mem.nCurrent[NCSIZE-1], mem.mxCurrent[NCSIZE-1]); |
| } |
| fclose(out); |
| } |
|
|
| |
| |
| |
| int sqlite3MemdebugMallocCount(){ |
| int i; |
| int nTotal = 0; |
| for(i=0; i<NCSIZE; i++){ |
| nTotal += mem.nAlloc[i]; |
| } |
| return nTotal; |
| } |
|
|
|
|
| #endif |
|
|