#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