6 #define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))
8 #define MIN(x,y) ((x)<(y) ? (x) : (y))
10 typedef unsigned int u32
;
11 typedef unsigned char u8
;
12 typedef long long int i64
;
13 typedef unsigned long long int u64
;
15 #if defined(__GLIBC__) && defined(LSM_DEBUG_MEM)
16 extern int backtrace(void**,int);
17 extern void backtrace_symbols_fd(void*const*,int,int);
18 # define TM_BACKTRACE 12
20 # define backtrace(A,B) 1
21 # define backtrace_symbols_fd(A,B,C)
25 typedef struct TmBlockHdr TmBlockHdr
;
26 typedef struct TmAgg TmAgg
;
27 typedef struct TmGlobal TmGlobal
;
30 /* Linked list of all currently outstanding allocations. And a table of
31 ** all allocations, past and present, indexed by backtrace() info. */
37 /* Underlying malloc/realloc/free functions */
38 void *(*xMalloc
)(int); /* underlying malloc(3) function */
39 void *(*xRealloc
)(void *, int); /* underlying realloc(3) function */
40 void (*xFree
)(void *); /* underlying free(3) function */
42 /* Mutex to protect pFirst and aHash */
43 void (*xEnterMutex
)(TmGlobal
*); /* Call this to enter the mutex */
44 void (*xLeaveMutex
)(TmGlobal
*); /* Call this to leave mutex */
45 void (*xDelMutex
)(TmGlobal
*); /* Call this to delete mutex */
46 void *pMutex
; /* Mutex handle */
48 void *(*xSaveMalloc
)(void *, size_t);
49 void *(*xSaveRealloc
)(void *, void *, size_t);
50 void (*xSaveFree
)(void *, void *);
52 /* OOM injection scheduling. If nCountdown is greater than zero when a
53 ** malloc attempt is made, it is decremented. If this means nCountdown
54 ** transitions from 1 to 0, then the allocation fails. If bPersist is true
55 ** when this happens, nCountdown is then incremented back to 1 (so that the
56 ** next attempt fails too).
61 void (*xHook
)(void *);
77 int nAlloc
; /* Number of allocations at this path */
78 int nByte
; /* Total number of bytes allocated */
79 int nOutAlloc
; /* Number of outstanding allocations */
80 int nOutByte
; /* Number of outstanding bytes */
81 void *aFrame
[TM_BACKTRACE
]; /* backtrace() output */
82 TmAgg
*pNext
; /* Next object in hash-table collision */
86 #define FOREGUARD 0x80F5E153
87 #define REARGUARD 0xE4676B53
88 static const u32 rearguard
= REARGUARD
;
90 #define ROUND8(x) (((x)+7)&~7)
92 #define BLOCK_HDR_SIZE (ROUND8( sizeof(TmBlockHdr) ))
94 static void lsmtest_oom_error(void){
99 static void tmEnterMutex(TmGlobal
*pTm
){
100 pTm
->xEnterMutex(pTm
);
102 static void tmLeaveMutex(TmGlobal
*pTm
){
103 pTm
->xLeaveMutex(pTm
);
106 static void *tmMalloc(TmGlobal
*pTm
, int nByte
){
107 TmBlockHdr
*pNew
; /* New allocation header block */
108 u8
*pUser
; /* Return value */
109 int nReq
; /* Total number of bytes requested */
111 assert( sizeof(rearguard
)==4 );
112 nReq
= BLOCK_HDR_SIZE
+ nByte
+ 4;
113 pNew
= (TmBlockHdr
*)pTm
->xMalloc(nReq
);
114 memset(pNew
, 0, sizeof(TmBlockHdr
));
117 assert( pTm
->nCountdown
>=0 );
118 assert( pTm
->bPersist
==0 || pTm
->bPersist
==1 );
120 if( pTm
->bEnable
&& pTm
->nCountdown
==1 ){
121 /* Simulate an OOM error. */
124 pTm
->nCountdown
= pTm
->bPersist
;
125 if( pTm
->xHook
) pTm
->xHook(pTm
->pHookCtx
);
128 if( pTm
->bEnable
&& pTm
->nCountdown
) pTm
->nCountdown
--;
130 pNew
->iForeGuard
= FOREGUARD
;
132 pNew
->pNext
= pTm
->pFirst
;
135 pTm
->pFirst
->pPrev
= pNew
;
139 pUser
= &((u8
*)pNew
)[BLOCK_HDR_SIZE
];
140 memset(pUser
, 0x56, nByte
);
141 memcpy(&pUser
[nByte
], &rearguard
, 4);
148 void *aFrame
[TM_BACKTRACE
];
149 memset(aFrame
, 0, sizeof(aFrame
));
150 backtrace(aFrame
, TM_BACKTRACE
);
152 for(i
=0; i
<ArraySize(aFrame
); i
++){
153 iHash
+= (u64
)(aFrame
[i
]) + (iHash
<<3);
155 iHash
= iHash
% ArraySize(pTm
->aHash
);
157 for(pAgg
=pTm
->aHash
[iHash
]; pAgg
; pAgg
=pAgg
->pNext
){
158 if( memcmp(pAgg
->aFrame
, aFrame
, sizeof(aFrame
))==0 ) break;
161 pAgg
= (TmAgg
*)pTm
->xMalloc(sizeof(TmAgg
));
162 memset(pAgg
, 0, sizeof(TmAgg
));
163 memcpy(pAgg
->aFrame
, aFrame
, sizeof(aFrame
));
164 pAgg
->pNext
= pTm
->aHash
[iHash
];
165 pTm
->aHash
[iHash
] = pAgg
;
168 pAgg
->nByte
+= nByte
;
170 pAgg
->nOutByte
+= nByte
;
180 static void tmFree(TmGlobal
*pTm
, void *p
){
186 pHdr
= (TmBlockHdr
*)(pUser
- BLOCK_HDR_SIZE
);
187 assert( pHdr
->iForeGuard
==FOREGUARD
);
188 assert( 0==memcmp(&pUser
[pHdr
->nByte
], &rearguard
, 4) );
191 assert( pHdr
->pPrev
->pNext
==pHdr
);
192 pHdr
->pPrev
->pNext
= pHdr
->pNext
;
194 assert( pHdr
==pTm
->pFirst
);
195 pTm
->pFirst
= pHdr
->pNext
;
198 assert( pHdr
->pNext
->pPrev
==pHdr
);
199 pHdr
->pNext
->pPrev
= pHdr
->pPrev
;
203 pHdr
->pAgg
->nOutAlloc
--;
204 pHdr
->pAgg
->nOutByte
-= pHdr
->nByte
;
208 memset(pUser
, 0x58, pHdr
->nByte
);
209 memset(pHdr
, 0x57, sizeof(TmBlockHdr
));
214 static void *tmRealloc(TmGlobal
*pTm
, void *p
, int nByte
){
217 pNew
= tmMalloc(pTm
, nByte
);
221 pHdr
= (TmBlockHdr
*)(pUser
- BLOCK_HDR_SIZE
);
222 memcpy(pNew
, p
, MIN(nByte
, pHdr
->nByte
));
228 static void tmMallocOom(
232 void (*xHook
)(void *),
235 assert( nCountdown
>=0 );
236 assert( bPersist
==0 || bPersist
==1 );
237 pTm
->nCountdown
= nCountdown
;
238 pTm
->bPersist
= bPersist
;
240 pTm
->pHookCtx
= pHookCtx
;
244 static void tmMallocOomEnable(
248 pTm
->bEnable
= bEnable
;
251 static void tmMallocCheck(
263 for(pHdr
=pTm
->pFirst
; pHdr
; pHdr
=pHdr
->pNext
){
265 nByte
+= pHdr
->nByte
;
267 if( pnLeakAlloc
) *pnLeakAlloc
= nLeak
;
268 if( pnLeakByte
) *pnLeakByte
= nByte
;
273 fprintf(pFile
, "LEAKS\n");
274 for(i
=0; i
<ArraySize(pTm
->aHash
); i
++){
276 for(pAgg
=pTm
->aHash
[i
]; pAgg
; pAgg
=pAgg
->pNext
){
277 if( pAgg
->nOutAlloc
){
279 fprintf(pFile
, "%d %d ", pAgg
->nOutByte
, pAgg
->nOutAlloc
);
280 for(j
=0; j
<TM_BACKTRACE
; j
++){
281 fprintf(pFile
, "%p ", pAgg
->aFrame
[j
]);
283 fprintf(pFile
, "\n");
287 fprintf(pFile
, "\nALLOCATIONS\n");
288 for(i
=0; i
<ArraySize(pTm
->aHash
); i
++){
290 for(pAgg
=pTm
->aHash
[i
]; pAgg
; pAgg
=pAgg
->pNext
){
292 fprintf(pFile
, "%d %d ", pAgg
->nByte
, pAgg
->nAlloc
);
293 for(j
=0; j
<TM_BACKTRACE
; j
++) fprintf(pFile
, "%p ", pAgg
->aFrame
[j
]);
294 fprintf(pFile
, "\n");
307 typedef struct LsmMutex LsmMutex
;
313 static void tmLsmMutexEnter(TmGlobal
*pTm
){
314 LsmMutex
*p
= (LsmMutex
*)pTm
->pMutex
;
315 p
->pEnv
->xMutexEnter(p
->pMutex
);
317 static void tmLsmMutexLeave(TmGlobal
*pTm
){
318 LsmMutex
*p
= (LsmMutex
*)(pTm
->pMutex
);
319 p
->pEnv
->xMutexLeave(p
->pMutex
);
321 static void tmLsmMutexDel(TmGlobal
*pTm
){
322 LsmMutex
*p
= (LsmMutex
*)pTm
->pMutex
;
325 static void *tmLsmMalloc(int n
){ return malloc(n
); }
326 static void tmLsmFree(void *ptr
){ free(ptr
); }
327 static void *tmLsmRealloc(void *ptr
, int n
){ return realloc(ptr
, n
); }
329 static void *tmLsmEnvMalloc(lsm_env
*p
, size_t n
){
330 return tmMalloc((TmGlobal
*)(p
->pMemCtx
), n
);
332 static void tmLsmEnvFree(lsm_env
*p
, void *ptr
){
333 tmFree((TmGlobal
*)(p
->pMemCtx
), ptr
);
335 static void *tmLsmEnvRealloc(lsm_env
*p
, void *ptr
, size_t n
){
336 return tmRealloc((TmGlobal
*)(p
->pMemCtx
), ptr
, n
);
339 void testMallocInstall(lsm_env
*pEnv
){
342 assert( pEnv
->pMemCtx
==0 );
344 /* Allocate and populate a TmGlobal structure. */
345 pGlobal
= (TmGlobal
*)tmLsmMalloc(sizeof(TmGlobal
));
346 memset(pGlobal
, 0, sizeof(TmGlobal
));
347 pGlobal
->xMalloc
= tmLsmMalloc
;
348 pGlobal
->xRealloc
= tmLsmRealloc
;
349 pGlobal
->xFree
= tmLsmFree
;
350 pMutex
= (LsmMutex
*)pGlobal
->xMalloc(sizeof(LsmMutex
));
352 pEnv
->xMutexStatic(pEnv
, LSM_MUTEX_HEAP
, &pMutex
->pMutex
);
353 pGlobal
->xEnterMutex
= tmLsmMutexEnter
;
354 pGlobal
->xLeaveMutex
= tmLsmMutexLeave
;
355 pGlobal
->xDelMutex
= tmLsmMutexDel
;
356 pGlobal
->pMutex
= (void *)pMutex
;
358 pGlobal
->xSaveMalloc
= pEnv
->xMalloc
;
359 pGlobal
->xSaveRealloc
= pEnv
->xRealloc
;
360 pGlobal
->xSaveFree
= pEnv
->xFree
;
362 /* Set up pEnv to the use the new TmGlobal */
363 pEnv
->pMemCtx
= (void *)pGlobal
;
364 pEnv
->xMalloc
= tmLsmEnvMalloc
;
365 pEnv
->xRealloc
= tmLsmEnvRealloc
;
366 pEnv
->xFree
= tmLsmEnvFree
;
369 void testMallocUninstall(lsm_env
*pEnv
){
370 TmGlobal
*p
= (TmGlobal
*)pEnv
->pMemCtx
;
373 pEnv
->xMalloc
= p
->xSaveMalloc
;
374 pEnv
->xRealloc
= p
->xSaveRealloc
;
375 pEnv
->xFree
= p
->xSaveFree
;
381 void testMallocCheck(
387 if( pEnv
->pMemCtx
==0 ){
391 tmMallocCheck((TmGlobal
*)(pEnv
->pMemCtx
), pnLeakAlloc
, pnLeakByte
, pFile
);
399 void (*xHook
)(void *),
402 TmGlobal
*pTm
= (TmGlobal
*)(pEnv
->pMemCtx
);
403 tmMallocOom(pTm
, nCountdown
, bPersist
, xHook
, pHookCtx
);
406 void testMallocOomEnable(lsm_env
*pEnv
, int bEnable
){
407 TmGlobal
*pTm
= (TmGlobal
*)(pEnv
->pMemCtx
);
408 tmMallocOomEnable(pTm
, bEnable
);