1 
2 #include <stdio.h>
3 #include <assert.h>
4 #include <string.h>
5 
6 #define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))
7 
8 #define MIN(x,y) ((x)<(y) ? (x) : (y))
9 
10 typedef unsigned int  u32;
11 typedef unsigned char u8;
12 typedef long long int i64;
13 typedef unsigned long long int u64;
14 
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
23 
24 
25 typedef struct TmBlockHdr TmBlockHdr;
26 typedef struct TmAgg TmAgg;
27 typedef struct TmGlobal TmGlobal;
28 
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
36 
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 */
41 
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 */
47 
48   void *(*xSaveMalloc)(void *, size_t);
49   void *(*xSaveRealloc)(void *, void *, size_t);
50   void (*xSaveFree)(void *, void *);
51 
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).
57   */
58   int nCountdown;
59   int bPersist;
60   int bEnable;
61   void (*xHook)(void *);
62   void *pHookCtx;
63 };
64 
65 struct TmBlockHdr {
66   TmBlockHdr *pNext;
67   TmBlockHdr *pPrev;
68   int nByte;
69 #ifdef TM_BACKTRACE
70   TmAgg *pAgg;
71 #endif
72   u32 iForeGuard;
73 };
74 
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 */
83 };
84 #endif
85 
86 #define FOREGUARD 0x80F5E153
87 #define REARGUARD 0xE4676B53
88 static const u32 rearguard = REARGUARD;
89 
90 #define ROUND8(x) (((x)+7)&~7)
91 
92 #define BLOCK_HDR_SIZE (ROUND8( sizeof(TmBlockHdr) ))
93 
lsmtest_oom_error(void)94 static void lsmtest_oom_error(void){
95   static int nErr = 0;
96   nErr++;
97 }
98 
tmEnterMutex(TmGlobal * pTm)99 static void tmEnterMutex(TmGlobal *pTm){
100   pTm->xEnterMutex(pTm);
101 }
tmLeaveMutex(TmGlobal * pTm)102 static void tmLeaveMutex(TmGlobal *pTm){
103   pTm->xLeaveMutex(pTm);
104 }
105 
tmMalloc(TmGlobal * pTm,int nByte)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 */
110 
111   assert( sizeof(rearguard)==4 );
112   nReq = BLOCK_HDR_SIZE + nByte + 4;
113   pNew = (TmBlockHdr *)pTm->xMalloc(nReq);
114   memset(pNew, 0, sizeof(TmBlockHdr));
115 
116   tmEnterMutex(pTm);
117   assert( pTm->nCountdown>=0 );
118   assert( pTm->bPersist==0 || pTm->bPersist==1 );
119 
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--;
129 
130     pNew->iForeGuard = FOREGUARD;
131     pNew->nByte = nByte;
132     pNew->pNext = pTm->pFirst;
133 
134     if( pTm->pFirst ){
135       pTm->pFirst->pPrev = pNew;
136     }
137     pTm->pFirst = pNew;
138 
139     pUser = &((u8 *)pNew)[BLOCK_HDR_SIZE];
140     memset(pUser, 0x56, nByte);
141     memcpy(&pUser[nByte], &rearguard, 4);
142 
143 #ifdef TM_BACKTRACE
144     {
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);
151 
152       for(i=0; i<ArraySize(aFrame); i++){
153         iHash += (u64)(aFrame[i]) + (iHash<<3);
154       }
155       iHash = iHash % ArraySize(pTm->aHash);
156 
157       for(pAgg=pTm->aHash[iHash]; pAgg; pAgg=pAgg->pNext){
158         if( memcmp(pAgg->aFrame, aFrame, sizeof(aFrame))==0 ) break;
159       }
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;
166       }
167       pAgg->nAlloc++;
168       pAgg->nByte += nByte;
169       pAgg->nOutAlloc++;
170       pAgg->nOutByte += nByte;
171       pNew->pAgg = pAgg;
172     }
173 #endif
174   }
175 
176   tmLeaveMutex(pTm);
177   return pUser;
178 }
179 
tmFree(TmGlobal * pTm,void * p)180 static void tmFree(TmGlobal *pTm, void *p){
181   if( p ){
182     TmBlockHdr *pHdr;
183     u8 *pUser = (u8 *)p;
184 
185     tmEnterMutex(pTm);
186     pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
187     assert( pHdr->iForeGuard==FOREGUARD );
188     assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) );
189 
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;
196     }
197     if( pHdr->pNext ){
198       assert( pHdr->pNext->pPrev==pHdr );
199       pHdr->pNext->pPrev = pHdr->pPrev;
200     }
201 
202 #ifdef TM_BACKTRACE
203     pHdr->pAgg->nOutAlloc--;
204     pHdr->pAgg->nOutByte -= pHdr->nByte;
205 #endif
206 
207     tmLeaveMutex(pTm);
208     memset(pUser, 0x58, pHdr->nByte);
209     memset(pHdr, 0x57, sizeof(TmBlockHdr));
210     pTm->xFree(pHdr);
211   }
212 }
213 
tmRealloc(TmGlobal * pTm,void * p,int nByte)214 static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){
215   void *pNew;
216 
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);
224   }
225   return pNew;
226 }
227 
tmMallocOom(TmGlobal * pTm,int nCountdown,int bPersist,void (* xHook)(void *),void * pHookCtx)228 static void tmMallocOom(
229   TmGlobal *pTm,
230   int nCountdown,
231   int bPersist,
232   void (*xHook)(void *),
233   void *pHookCtx
234 ){
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;
242 }
243 
tmMallocOomEnable(TmGlobal * pTm,int bEnable)244 static void tmMallocOomEnable(
245   TmGlobal *pTm,
246   int bEnable
247 ){
248   pTm->bEnable = bEnable;
249 }
250 
tmMallocCheck(TmGlobal * pTm,int * pnLeakAlloc,int * pnLeakByte,FILE * pFile)251 static void tmMallocCheck(
252   TmGlobal *pTm,
253   int *pnLeakAlloc,
254   int *pnLeakByte,
255   FILE *pFile
256 ){
257   TmBlockHdr *pHdr;
258   int nLeak = 0;
259   int nByte = 0;
260 
261   if( pTm==0 ) return;
262 
263   for(pHdr=pTm->pFirst; pHdr; pHdr=pHdr->pNext){
264     nLeak++;
265     nByte += pHdr->nByte;
266   }
267   if( pnLeakAlloc ) *pnLeakAlloc = nLeak;
268   if( pnLeakByte ) *pnLeakByte = nByte;
269 
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]);
282           }
283           fprintf(pFile, "\n");
284         }
285       }
286     }
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");
295       }
296     }
297   }
298 #else
299   (void)pFile;
300 #endif
301 }
302 
303 
304 #include "lsm.h"
305 #include "stdlib.h"
306 
307 typedef struct LsmMutex LsmMutex;
308 struct LsmMutex {
309   lsm_env *pEnv;
310   lsm_mutex *pMutex;
311 };
312 
tmLsmMutexEnter(TmGlobal * pTm)313 static void tmLsmMutexEnter(TmGlobal *pTm){
314   LsmMutex *p = (LsmMutex *)pTm->pMutex;
315   p->pEnv->xMutexEnter(p->pMutex);
316 }
tmLsmMutexLeave(TmGlobal * pTm)317 static void tmLsmMutexLeave(TmGlobal *pTm){
318   LsmMutex *p = (LsmMutex *)(pTm->pMutex);
319   p->pEnv->xMutexLeave(p->pMutex);
320 }
tmLsmMutexDel(TmGlobal * pTm)321 static void tmLsmMutexDel(TmGlobal *pTm){
322   LsmMutex *p = (LsmMutex *)pTm->pMutex;
323   pTm->xFree(p);
324 }
tmLsmMalloc(int n)325 static void *tmLsmMalloc(int n){ return malloc(n); }
tmLsmFree(void * ptr)326 static void tmLsmFree(void *ptr){ free(ptr); }
tmLsmRealloc(void * ptr,int n)327 static void *tmLsmRealloc(void *ptr, int n){ return realloc(ptr, n); }
328 
tmLsmEnvMalloc(lsm_env * p,size_t n)329 static void *tmLsmEnvMalloc(lsm_env *p, size_t n){
330   return tmMalloc((TmGlobal *)(p->pMemCtx), n);
331 }
tmLsmEnvFree(lsm_env * p,void * ptr)332 static void tmLsmEnvFree(lsm_env *p, void *ptr){
333   tmFree((TmGlobal *)(p->pMemCtx), ptr);
334 }
tmLsmEnvRealloc(lsm_env * p,void * ptr,size_t n)335 static void *tmLsmEnvRealloc(lsm_env *p, void *ptr, size_t n){
336   return tmRealloc((TmGlobal *)(p->pMemCtx), ptr, n);
337 }
338 
testMallocInstall(lsm_env * pEnv)339 void testMallocInstall(lsm_env *pEnv){
340   TmGlobal *pGlobal;
341   LsmMutex *pMutex;
342   assert( pEnv->pMemCtx==0 );
343 
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;
357 
358   pGlobal->xSaveMalloc = pEnv->xMalloc;
359   pGlobal->xSaveRealloc = pEnv->xRealloc;
360   pGlobal->xSaveFree = pEnv->xFree;
361 
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;
367 }
368 
testMallocUninstall(lsm_env * pEnv)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);
378   }
379 }
380 
testMallocCheck(lsm_env * pEnv,int * pnLeakAlloc,int * pnLeakByte,FILE * pFile)381 void testMallocCheck(
382   lsm_env *pEnv,
383   int *pnLeakAlloc,
384   int *pnLeakByte,
385   FILE *pFile
386 ){
387   if( pEnv->pMemCtx==0 ){
388     *pnLeakAlloc = 0;
389     *pnLeakByte = 0;
390   }else{
391     tmMallocCheck((TmGlobal *)(pEnv->pMemCtx), pnLeakAlloc, pnLeakByte, pFile);
392   }
393 }
394 
testMallocOom(lsm_env * pEnv,int nCountdown,int bPersist,void (* xHook)(void *),void * pHookCtx)395 void testMallocOom(
396   lsm_env *pEnv,
397   int nCountdown,
398   int bPersist,
399   void (*xHook)(void *),
400   void *pHookCtx
401 ){
402   TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx);
403   tmMallocOom(pTm, nCountdown, bPersist, xHook, pHookCtx);
404 }
405 
testMallocOomEnable(lsm_env * pEnv,int bEnable)406 void testMallocOomEnable(lsm_env *pEnv, int bEnable){
407   TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx);
408   tmMallocOomEnable(pTm, bEnable);
409 }
410