summaryrefslogtreecommitdiffstats
path: root/ext/lsm1/lsm-test/lsmtest_mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/lsm1/lsm-test/lsmtest_mem.c')
-rw-r--r--ext/lsm1/lsm-test/lsmtest_mem.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/ext/lsm1/lsm-test/lsmtest_mem.c b/ext/lsm1/lsm-test/lsmtest_mem.c
new file mode 100644
index 0000000..4c35e84
--- /dev/null
+++ b/ext/lsm1/lsm-test/lsmtest_mem.c
@@ -0,0 +1,409 @@
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))
+
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+typedef long long int i64;
+typedef unsigned long long int u64;
+
+#if defined(__GLIBC__) && defined(LSM_DEBUG_MEM)
+ extern int backtrace(void**,int);
+ extern void backtrace_symbols_fd(void*const*,int,int);
+# define TM_BACKTRACE 12
+#else
+# define backtrace(A,B) 1
+# define backtrace_symbols_fd(A,B,C)
+#endif
+
+
+typedef struct TmBlockHdr TmBlockHdr;
+typedef struct TmAgg TmAgg;
+typedef struct TmGlobal TmGlobal;
+
+struct TmGlobal {
+ /* Linked list of all currently outstanding allocations. And a table of
+ ** all allocations, past and present, indexed by backtrace() info. */
+ TmBlockHdr *pFirst;
+#ifdef TM_BACKTRACE
+ TmAgg *aHash[10000];
+#endif
+
+ /* Underlying malloc/realloc/free functions */
+ void *(*xMalloc)(int); /* underlying malloc(3) function */
+ void *(*xRealloc)(void *, int); /* underlying realloc(3) function */
+ void (*xFree)(void *); /* underlying free(3) function */
+
+ /* Mutex to protect pFirst and aHash */
+ void (*xEnterMutex)(TmGlobal*); /* Call this to enter the mutex */
+ void (*xLeaveMutex)(TmGlobal*); /* Call this to leave mutex */
+ void (*xDelMutex)(TmGlobal*); /* Call this to delete mutex */
+ void *pMutex; /* Mutex handle */
+
+ void *(*xSaveMalloc)(void *, size_t);
+ void *(*xSaveRealloc)(void *, void *, size_t);
+ void (*xSaveFree)(void *, void *);
+
+ /* OOM injection scheduling. If nCountdown is greater than zero when a
+ ** malloc attempt is made, it is decremented. If this means nCountdown
+ ** transitions from 1 to 0, then the allocation fails. If bPersist is true
+ ** when this happens, nCountdown is then incremented back to 1 (so that the
+ ** next attempt fails too).
+ */
+ int nCountdown;
+ int bPersist;
+ int bEnable;
+ void (*xHook)(void *);
+ void *pHookCtx;
+};
+
+struct TmBlockHdr {
+ TmBlockHdr *pNext;
+ TmBlockHdr *pPrev;
+ int nByte;
+#ifdef TM_BACKTRACE
+ TmAgg *pAgg;
+#endif
+ u32 iForeGuard;
+};
+
+#ifdef TM_BACKTRACE
+struct TmAgg {
+ int nAlloc; /* Number of allocations at this path */
+ int nByte; /* Total number of bytes allocated */
+ int nOutAlloc; /* Number of outstanding allocations */
+ int nOutByte; /* Number of outstanding bytes */
+ void *aFrame[TM_BACKTRACE]; /* backtrace() output */
+ TmAgg *pNext; /* Next object in hash-table collision */
+};
+#endif
+
+#define FOREGUARD 0x80F5E153
+#define REARGUARD 0xE4676B53
+static const u32 rearguard = REARGUARD;
+
+#define ROUND8(x) (((x)+7)&~7)
+
+#define BLOCK_HDR_SIZE (ROUND8( sizeof(TmBlockHdr) ))
+
+static void lsmtest_oom_error(void){
+ static int nErr = 0;
+ nErr++;
+}
+
+static void tmEnterMutex(TmGlobal *pTm){
+ pTm->xEnterMutex(pTm);
+}
+static void tmLeaveMutex(TmGlobal *pTm){
+ pTm->xLeaveMutex(pTm);
+}
+
+static void *tmMalloc(TmGlobal *pTm, int nByte){
+ TmBlockHdr *pNew; /* New allocation header block */
+ u8 *pUser; /* Return value */
+ int nReq; /* Total number of bytes requested */
+
+ assert( sizeof(rearguard)==4 );
+ nReq = BLOCK_HDR_SIZE + nByte + 4;
+ pNew = (TmBlockHdr *)pTm->xMalloc(nReq);
+ memset(pNew, 0, sizeof(TmBlockHdr));
+
+ tmEnterMutex(pTm);
+ assert( pTm->nCountdown>=0 );
+ assert( pTm->bPersist==0 || pTm->bPersist==1 );
+
+ if( pTm->bEnable && pTm->nCountdown==1 ){
+ /* Simulate an OOM error. */
+ lsmtest_oom_error();
+ pTm->xFree(pNew);
+ pTm->nCountdown = pTm->bPersist;
+ if( pTm->xHook ) pTm->xHook(pTm->pHookCtx);
+ pUser = 0;
+ }else{
+ if( pTm->bEnable && pTm->nCountdown ) pTm->nCountdown--;
+
+ pNew->iForeGuard = FOREGUARD;
+ pNew->nByte = nByte;
+ pNew->pNext = pTm->pFirst;
+
+ if( pTm->pFirst ){
+ pTm->pFirst->pPrev = pNew;
+ }
+ pTm->pFirst = pNew;
+
+ pUser = &((u8 *)pNew)[BLOCK_HDR_SIZE];
+ memset(pUser, 0x56, nByte);
+ memcpy(&pUser[nByte], &rearguard, 4);
+
+#ifdef TM_BACKTRACE
+ {
+ TmAgg *pAgg;
+ int i;
+ u32 iHash = 0;
+ void *aFrame[TM_BACKTRACE];
+ memset(aFrame, 0, sizeof(aFrame));
+ backtrace(aFrame, TM_BACKTRACE);
+
+ for(i=0; i<ArraySize(aFrame); i++){
+ iHash += (u64)(aFrame[i]) + (iHash<<3);
+ }
+ iHash = iHash % ArraySize(pTm->aHash);
+
+ for(pAgg=pTm->aHash[iHash]; pAgg; pAgg=pAgg->pNext){
+ if( memcmp(pAgg->aFrame, aFrame, sizeof(aFrame))==0 ) break;
+ }
+ if( !pAgg ){
+ pAgg = (TmAgg *)pTm->xMalloc(sizeof(TmAgg));
+ memset(pAgg, 0, sizeof(TmAgg));
+ memcpy(pAgg->aFrame, aFrame, sizeof(aFrame));
+ pAgg->pNext = pTm->aHash[iHash];
+ pTm->aHash[iHash] = pAgg;
+ }
+ pAgg->nAlloc++;
+ pAgg->nByte += nByte;
+ pAgg->nOutAlloc++;
+ pAgg->nOutByte += nByte;
+ pNew->pAgg = pAgg;
+ }
+#endif
+ }
+
+ tmLeaveMutex(pTm);
+ return pUser;
+}
+
+static void tmFree(TmGlobal *pTm, void *p){
+ if( p ){
+ TmBlockHdr *pHdr;
+ u8 *pUser = (u8 *)p;
+
+ tmEnterMutex(pTm);
+ pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
+ assert( pHdr->iForeGuard==FOREGUARD );
+ assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) );
+
+ if( pHdr->pPrev ){
+ assert( pHdr->pPrev->pNext==pHdr );
+ pHdr->pPrev->pNext = pHdr->pNext;
+ }else{
+ assert( pHdr==pTm->pFirst );
+ pTm->pFirst = pHdr->pNext;
+ }
+ if( pHdr->pNext ){
+ assert( pHdr->pNext->pPrev==pHdr );
+ pHdr->pNext->pPrev = pHdr->pPrev;
+ }
+
+#ifdef TM_BACKTRACE
+ pHdr->pAgg->nOutAlloc--;
+ pHdr->pAgg->nOutByte -= pHdr->nByte;
+#endif
+
+ tmLeaveMutex(pTm);
+ memset(pUser, 0x58, pHdr->nByte);
+ memset(pHdr, 0x57, sizeof(TmBlockHdr));
+ pTm->xFree(pHdr);
+ }
+}
+
+static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){
+ void *pNew;
+
+ pNew = tmMalloc(pTm, nByte);
+ if( pNew && p ){
+ TmBlockHdr *pHdr;
+ u8 *pUser = (u8 *)p;
+ pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
+ memcpy(pNew, p, MIN(nByte, pHdr->nByte));
+ tmFree(pTm, p);
+ }
+ return pNew;
+}
+
+static void tmMallocOom(
+ TmGlobal *pTm,
+ int nCountdown,
+ int bPersist,
+ void (*xHook)(void *),
+ void *pHookCtx
+){
+ assert( nCountdown>=0 );
+ assert( bPersist==0 || bPersist==1 );
+ pTm->nCountdown = nCountdown;
+ pTm->bPersist = bPersist;
+ pTm->xHook = xHook;
+ pTm->pHookCtx = pHookCtx;
+ pTm->bEnable = 1;
+}
+
+static void tmMallocOomEnable(
+ TmGlobal *pTm,
+ int bEnable
+){
+ pTm->bEnable = bEnable;
+}
+
+static void tmMallocCheck(
+ TmGlobal *pTm,
+ int *pnLeakAlloc,
+ int *pnLeakByte,
+ FILE *pFile
+){
+ TmBlockHdr *pHdr;
+ int nLeak = 0;
+ int nByte = 0;
+
+ if( pTm==0 ) return;
+
+ for(pHdr=pTm->pFirst; pHdr; pHdr=pHdr->pNext){
+ nLeak++;
+ nByte += pHdr->nByte;
+ }
+ if( pnLeakAlloc ) *pnLeakAlloc = nLeak;
+ if( pnLeakByte ) *pnLeakByte = nByte;
+
+#ifdef TM_BACKTRACE
+ if( pFile ){
+ int i;
+ fprintf(pFile, "LEAKS\n");
+ for(i=0; i<ArraySize(pTm->aHash); i++){
+ TmAgg *pAgg;
+ for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
+ if( pAgg->nOutAlloc ){
+ int j;
+ fprintf(pFile, "%d %d ", pAgg->nOutByte, pAgg->nOutAlloc);
+ for(j=0; j<TM_BACKTRACE; j++){
+ fprintf(pFile, "%p ", pAgg->aFrame[j]);
+ }
+ fprintf(pFile, "\n");
+ }
+ }
+ }
+ fprintf(pFile, "\nALLOCATIONS\n");
+ for(i=0; i<ArraySize(pTm->aHash); i++){
+ TmAgg *pAgg;
+ for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
+ int j;
+ fprintf(pFile, "%d %d ", pAgg->nByte, pAgg->nAlloc);
+ for(j=0; j<TM_BACKTRACE; j++) fprintf(pFile, "%p ", pAgg->aFrame[j]);
+ fprintf(pFile, "\n");
+ }
+ }
+ }
+#else
+ (void)pFile;
+#endif
+}
+
+
+#include "lsm.h"
+#include "stdlib.h"
+
+typedef struct LsmMutex LsmMutex;
+struct LsmMutex {
+ lsm_env *pEnv;
+ lsm_mutex *pMutex;
+};
+
+static void tmLsmMutexEnter(TmGlobal *pTm){
+ LsmMutex *p = (LsmMutex *)pTm->pMutex;
+ p->pEnv->xMutexEnter(p->pMutex);
+}
+static void tmLsmMutexLeave(TmGlobal *pTm){
+ LsmMutex *p = (LsmMutex *)(pTm->pMutex);
+ p->pEnv->xMutexLeave(p->pMutex);
+}
+static void tmLsmMutexDel(TmGlobal *pTm){
+ LsmMutex *p = (LsmMutex *)pTm->pMutex;
+ pTm->xFree(p);
+}
+static void *tmLsmMalloc(int n){ return malloc(n); }
+static void tmLsmFree(void *ptr){ free(ptr); }
+static void *tmLsmRealloc(void *ptr, int n){ return realloc(ptr, n); }
+
+static void *tmLsmEnvMalloc(lsm_env *p, size_t n){
+ return tmMalloc((TmGlobal *)(p->pMemCtx), n);
+}
+static void tmLsmEnvFree(lsm_env *p, void *ptr){
+ tmFree((TmGlobal *)(p->pMemCtx), ptr);
+}
+static void *tmLsmEnvRealloc(lsm_env *p, void *ptr, size_t n){
+ return tmRealloc((TmGlobal *)(p->pMemCtx), ptr, n);
+}
+
+void testMallocInstall(lsm_env *pEnv){
+ TmGlobal *pGlobal;
+ LsmMutex *pMutex;
+ assert( pEnv->pMemCtx==0 );
+
+ /* Allocate and populate a TmGlobal structure. */
+ pGlobal = (TmGlobal *)tmLsmMalloc(sizeof(TmGlobal));
+ memset(pGlobal, 0, sizeof(TmGlobal));
+ pGlobal->xMalloc = tmLsmMalloc;
+ pGlobal->xRealloc = tmLsmRealloc;
+ pGlobal->xFree = tmLsmFree;
+ pMutex = (LsmMutex *)pGlobal->xMalloc(sizeof(LsmMutex));
+ pMutex->pEnv = pEnv;
+ pEnv->xMutexStatic(pEnv, LSM_MUTEX_HEAP, &pMutex->pMutex);
+ pGlobal->xEnterMutex = tmLsmMutexEnter;
+ pGlobal->xLeaveMutex = tmLsmMutexLeave;
+ pGlobal->xDelMutex = tmLsmMutexDel;
+ pGlobal->pMutex = (void *)pMutex;
+
+ pGlobal->xSaveMalloc = pEnv->xMalloc;
+ pGlobal->xSaveRealloc = pEnv->xRealloc;
+ pGlobal->xSaveFree = pEnv->xFree;
+
+ /* Set up pEnv to the use the new TmGlobal */
+ pEnv->pMemCtx = (void *)pGlobal;
+ pEnv->xMalloc = tmLsmEnvMalloc;
+ pEnv->xRealloc = tmLsmEnvRealloc;
+ pEnv->xFree = tmLsmEnvFree;
+}
+
+void testMallocUninstall(lsm_env *pEnv){
+ TmGlobal *p = (TmGlobal *)pEnv->pMemCtx;
+ pEnv->pMemCtx = 0;
+ if( p ){
+ pEnv->xMalloc = p->xSaveMalloc;
+ pEnv->xRealloc = p->xSaveRealloc;
+ pEnv->xFree = p->xSaveFree;
+ p->xDelMutex(p);
+ tmLsmFree(p);
+ }
+}
+
+void testMallocCheck(
+ lsm_env *pEnv,
+ int *pnLeakAlloc,
+ int *pnLeakByte,
+ FILE *pFile
+){
+ if( pEnv->pMemCtx==0 ){
+ *pnLeakAlloc = 0;
+ *pnLeakByte = 0;
+ }else{
+ tmMallocCheck((TmGlobal *)(pEnv->pMemCtx), pnLeakAlloc, pnLeakByte, pFile);
+ }
+}
+
+void testMallocOom(
+ lsm_env *pEnv,
+ int nCountdown,
+ int bPersist,
+ void (*xHook)(void *),
+ void *pHookCtx
+){
+ TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx);
+ tmMallocOom(pTm, nCountdown, bPersist, xHook, pHookCtx);
+}
+
+void testMallocOomEnable(lsm_env *pEnv, int bEnable){
+ TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx);
+ tmMallocOomEnable(pTm, bEnable);
+}