Fix the ".lint fkey-indexes" shell command so that it works with WITHOUT ROWID
[sqlite.git] / ext / lsm1 / lsm-test / lsmtest_mem.c
blob4c35e849f2504a73584bf0385f28cbc0daa9a966
2 #include <stdio.h>
3 #include <assert.h>
4 #include <string.h>
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
19 #else
20 # define backtrace(A,B) 1
21 # define backtrace_symbols_fd(A,B,C)
22 #endif
25 typedef struct TmBlockHdr TmBlockHdr;
26 typedef struct TmAgg TmAgg;
27 typedef struct TmGlobal TmGlobal;
29 struct TmGlobal {
30 /* Linked list of all currently outstanding allocations. And a table of
31 ** all allocations, past and present, indexed by backtrace() info. */
32 TmBlockHdr *pFirst;
33 #ifdef TM_BACKTRACE
34 TmAgg *aHash[10000];
35 #endif
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).
58 int nCountdown;
59 int bPersist;
60 int bEnable;
61 void (*xHook)(void *);
62 void *pHookCtx;
65 struct TmBlockHdr {
66 TmBlockHdr *pNext;
67 TmBlockHdr *pPrev;
68 int nByte;
69 #ifdef TM_BACKTRACE
70 TmAgg *pAgg;
71 #endif
72 u32 iForeGuard;
75 #ifdef TM_BACKTRACE
76 struct TmAgg {
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 */
84 #endif
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){
95 static int nErr = 0;
96 nErr++;
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));
116 tmEnterMutex(pTm);
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. */
122 lsmtest_oom_error();
123 pTm->xFree(pNew);
124 pTm->nCountdown = pTm->bPersist;
125 if( pTm->xHook ) pTm->xHook(pTm->pHookCtx);
126 pUser = 0;
127 }else{
128 if( pTm->bEnable && pTm->nCountdown ) pTm->nCountdown--;
130 pNew->iForeGuard = FOREGUARD;
131 pNew->nByte = nByte;
132 pNew->pNext = pTm->pFirst;
134 if( pTm->pFirst ){
135 pTm->pFirst->pPrev = pNew;
137 pTm->pFirst = pNew;
139 pUser = &((u8 *)pNew)[BLOCK_HDR_SIZE];
140 memset(pUser, 0x56, nByte);
141 memcpy(&pUser[nByte], &rearguard, 4);
143 #ifdef TM_BACKTRACE
145 TmAgg *pAgg;
146 int i;
147 u32 iHash = 0;
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;
160 if( !pAgg ){
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;
167 pAgg->nAlloc++;
168 pAgg->nByte += nByte;
169 pAgg->nOutAlloc++;
170 pAgg->nOutByte += nByte;
171 pNew->pAgg = pAgg;
173 #endif
176 tmLeaveMutex(pTm);
177 return pUser;
180 static void tmFree(TmGlobal *pTm, void *p){
181 if( p ){
182 TmBlockHdr *pHdr;
183 u8 *pUser = (u8 *)p;
185 tmEnterMutex(pTm);
186 pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
187 assert( pHdr->iForeGuard==FOREGUARD );
188 assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) );
190 if( pHdr->pPrev ){
191 assert( pHdr->pPrev->pNext==pHdr );
192 pHdr->pPrev->pNext = pHdr->pNext;
193 }else{
194 assert( pHdr==pTm->pFirst );
195 pTm->pFirst = pHdr->pNext;
197 if( pHdr->pNext ){
198 assert( pHdr->pNext->pPrev==pHdr );
199 pHdr->pNext->pPrev = pHdr->pPrev;
202 #ifdef TM_BACKTRACE
203 pHdr->pAgg->nOutAlloc--;
204 pHdr->pAgg->nOutByte -= pHdr->nByte;
205 #endif
207 tmLeaveMutex(pTm);
208 memset(pUser, 0x58, pHdr->nByte);
209 memset(pHdr, 0x57, sizeof(TmBlockHdr));
210 pTm->xFree(pHdr);
214 static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){
215 void *pNew;
217 pNew = tmMalloc(pTm, nByte);
218 if( pNew && p ){
219 TmBlockHdr *pHdr;
220 u8 *pUser = (u8 *)p;
221 pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
222 memcpy(pNew, p, MIN(nByte, pHdr->nByte));
223 tmFree(pTm, p);
225 return pNew;
228 static void tmMallocOom(
229 TmGlobal *pTm,
230 int nCountdown,
231 int bPersist,
232 void (*xHook)(void *),
233 void *pHookCtx
235 assert( nCountdown>=0 );
236 assert( bPersist==0 || bPersist==1 );
237 pTm->nCountdown = nCountdown;
238 pTm->bPersist = bPersist;
239 pTm->xHook = xHook;
240 pTm->pHookCtx = pHookCtx;
241 pTm->bEnable = 1;
244 static void tmMallocOomEnable(
245 TmGlobal *pTm,
246 int bEnable
248 pTm->bEnable = bEnable;
251 static void tmMallocCheck(
252 TmGlobal *pTm,
253 int *pnLeakAlloc,
254 int *pnLeakByte,
255 FILE *pFile
257 TmBlockHdr *pHdr;
258 int nLeak = 0;
259 int nByte = 0;
261 if( pTm==0 ) return;
263 for(pHdr=pTm->pFirst; pHdr; pHdr=pHdr->pNext){
264 nLeak++;
265 nByte += pHdr->nByte;
267 if( pnLeakAlloc ) *pnLeakAlloc = nLeak;
268 if( pnLeakByte ) *pnLeakByte = nByte;
270 #ifdef TM_BACKTRACE
271 if( pFile ){
272 int i;
273 fprintf(pFile, "LEAKS\n");
274 for(i=0; i<ArraySize(pTm->aHash); i++){
275 TmAgg *pAgg;
276 for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
277 if( pAgg->nOutAlloc ){
278 int j;
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++){
289 TmAgg *pAgg;
290 for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){
291 int j;
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");
298 #else
299 (void)pFile;
300 #endif
304 #include "lsm.h"
305 #include "stdlib.h"
307 typedef struct LsmMutex LsmMutex;
308 struct LsmMutex {
309 lsm_env *pEnv;
310 lsm_mutex *pMutex;
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;
323 pTm->xFree(p);
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){
340 TmGlobal *pGlobal;
341 LsmMutex *pMutex;
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));
351 pMutex->pEnv = pEnv;
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;
371 pEnv->pMemCtx = 0;
372 if( p ){
373 pEnv->xMalloc = p->xSaveMalloc;
374 pEnv->xRealloc = p->xSaveRealloc;
375 pEnv->xFree = p->xSaveFree;
376 p->xDelMutex(p);
377 tmLsmFree(p);
381 void testMallocCheck(
382 lsm_env *pEnv,
383 int *pnLeakAlloc,
384 int *pnLeakByte,
385 FILE *pFile
387 if( pEnv->pMemCtx==0 ){
388 *pnLeakAlloc = 0;
389 *pnLeakByte = 0;
390 }else{
391 tmMallocCheck((TmGlobal *)(pEnv->pMemCtx), pnLeakAlloc, pnLeakByte, pFile);
395 void testMallocOom(
396 lsm_env *pEnv,
397 int nCountdown,
398 int bPersist,
399 void (*xHook)(void *),
400 void *pHookCtx
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);