Back to index

lightning-sunbird  0.9+nobinonly
libmalloc.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Kipp E.B. Hickman.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "libmalloc.h"
00038 #include <memory.h>
00039 #include <fcntl.h>
00040 #include <stdio.h>
00041 #include <unistd.h>
00042 #include <stdlib.h>
00043 #include <errno.h>
00044 #include <setjmp.h>
00045 #include <dlfcn.h>
00046 
00047 #ifdef NTO
00048 #include <sys/link.h>
00049 extern r_debug _r_debug;
00050 #else
00051 #include <link.h>
00052 #endif
00053 
00054 #ifdef NTO
00055 #define JB_BP 0x08
00056 #include <setjmp.h>
00057 #endif
00058 
00059 extern "C" {
00060 #ifdef NEED_WRAPPERS
00061   void* __wrap_malloc(size_t);
00062   void* __wrap_realloc(void*, size_t);
00063   void __wrap_free(void*);
00064   void* __wrap___builtin_new(size_t);
00065   void __wrap___builtin_delete(void*);
00066   void* __wrap___builtin_vec_new(size_t);
00067   void __wrap___builtin_vec_delete(void*);
00068   void* __wrap_PR_Malloc(size_t);
00069   void* __wrap_PR_Calloc(size_t, size_t);
00070   void* __wrap_PR_Realloc(void*, size_t);
00071   void __wrap_PR_Free(void*);
00072 #endif
00073 }
00074 
00075 static int gLogFD = -1;
00076 static u_long gFlags;
00077 static int gFillCount = 0;
00078 
00079 #define MARKER0             0xFFFFFFFF
00080 #define MARKER1             0xEEEEEEEE
00081 #define MARKER2_0    0xD8D8D8D8
00082 #define MARKER2_1    0xE8E8E8E8
00083 
00084 #define PATTERN_BYTE      0xFE
00085 #define FREE_PATTERN_BYTE 0xDE
00086 
00087 struct Header {
00088   u_long marker0;
00089   size_t rawSize;           // user requested size
00090   size_t size;                     // size rounded up
00091   u_long marker1;
00092 };
00093 
00094 struct Trailer {
00095   u_long marker2[2];
00096 };
00097 
00098 //----------------------------------------------------------------------
00099 
00100 #if defined(i386)
00101 static void CrawlStack(malloc_log_entry* me, jmp_buf jb)
00102 {
00103 #ifdef NTO
00104   u_long* bp = (u_long*) (jb[0].__savearea[JB_BP]);
00105 #else
00106   u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]);
00107 #endif
00108   u_long numpcs = 0;
00109   int skip = 2;
00110   while (numpcs < MAX_STACK_CRAWL) {
00111     u_long* nextbp = (u_long*) *bp++;
00112     u_long pc = *bp;
00113     if ((pc < 0x08000000) || (pc > 0x7fffffff) || (nextbp < bp)) {
00114       break;
00115     }
00116     if (--skip < 0) {
00117       me->pcs[numpcs++] = (char*) pc;
00118     }
00119     bp = nextbp;
00120   }
00121   me->numpcs = numpcs;
00122 }
00123 #endif
00124 
00125 //----------------------------------------------------------------------
00126 
00127 #if defined(linux) || defined(NTO)
00128 static void DumpAddressMap()
00129 {
00130   int mfd = open("malloc-map", O_CREAT|O_WRONLY|O_TRUNC, 0666);
00131   if (mfd >= 0) {
00132     malloc_map_entry mme;
00133     link_map* map = _r_debug.r_map;
00134     while (NULL != map) {
00135       if (0 != map->l_addr) {
00136        mme.nameLen = strlen(map->l_name);
00137        mme.address = map->l_addr;
00138        write(mfd, &mme, sizeof(mme));
00139        write(mfd, map->l_name, mme.nameLen);
00140 #if 0
00141        write(1, map->l_name, mme.nameLen);
00142        write(1, "\n", 1);
00143 #endif
00144       }
00145       map = map->l_next;
00146     }
00147     close(mfd);
00148   }
00149 }
00150 #endif
00151 
00152 //----------------------------------------------------------------------
00153 
00154 static int Verify(Header* h)
00155 {
00156   // Sanity check the header first
00157   if ((h->marker0 != MARKER0) ||
00158       (h->marker1 != MARKER1) ||
00159       (h->rawSize > h->size)) {
00160     DumpAddressMap();
00161     abort();
00162   }
00163 
00164   // Sanity check the trailer second
00165   Trailer* t = (Trailer*) ((char*)(h + 1) + h->size);
00166   if ((t->marker2[0] != MARKER2_0) ||
00167       (t->marker2[1] != MARKER2_1)) {
00168     DumpAddressMap();
00169     abort();
00170   }
00171 
00172   // Verify there were no overruns
00173   size_t fill = h->size - h->rawSize;
00174   if (0 != fill) {
00175     unsigned char* cp = ((unsigned char*)(h + 1)) + h->rawSize;
00176     unsigned char* end = cp + fill;
00177     while (cp < end) {
00178       unsigned char ch = *cp++;
00179       if (ch != PATTERN_BYTE) {
00180        DumpAddressMap();
00181        abort();
00182       }
00183     }
00184   }
00185   return 1;
00186 }
00187 
00188 static void
00189 Log(int aType, void* aAddr, size_t aSize, void* aOldAddr)
00190 {
00191   malloc_log_entry me;
00192 
00193   me.type = (u_long) aType;
00194   me.address = (u_long) aAddr;
00195   me.size = (u_long) aSize;
00196   me.oldaddress = (u_long) aOldAddr;
00197 
00198   jmp_buf jb;
00199   setjmp(jb);
00200   CrawlStack(&me, jb);
00201 
00202   write(gLogFD, &me, sizeof(me) - MAX_STACK_CRAWL*sizeof(char*) +
00203        me.numpcs*sizeof(char*));
00204 }
00205 
00206 static void*
00207 MallocHook(size_t aSize, u_long aLogType)
00208 {
00209   size_t roundedSize = aSize;
00210   roundedSize = ((roundedSize + 4 + gFillCount) >> 2) << 2;
00211 
00212   void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer));
00213 
00214   if (NULL != ptr) {
00215     Header* h = (Header*) ptr;
00216     h->rawSize = aSize;
00217     h->size = roundedSize;
00218     h->marker0 = MARKER0;
00219     h->marker1 = MARKER1;
00220 
00221     ptr = (void*) ((char*)(h+1));
00222 
00223     // Fill new memory with a pattern to help detect overruns and
00224     // usage of un-written memory
00225     memset(ptr, PATTERN_BYTE, roundedSize);
00226 
00227     Trailer* t = (Trailer*) ((char*)ptr + roundedSize);
00228     t->marker2[0] = MARKER2_0;
00229     t->marker2[1] = MARKER2_1;
00230 
00231     if (LIBMALLOC_LOG & gFlags) {
00232       Log(aLogType, ptr, aSize, 0);
00233     }
00234   }
00235 
00236   return ptr;
00237 }
00238 
00239 static void
00240 FreeHook(void* aAddr, u_long aLogType)
00241 {
00242   if (0 == aAddr) {
00243     return;
00244   }
00245   if (LIBMALLOC_LOG & gFlags) {
00246     Log(aLogType, aAddr, 0, 0);
00247   }
00248   Header* h = (Header*) ((char*)aAddr - sizeof(Header));
00249   if (Verify(h)) {
00250     // Munge the header so that a dup-free will fail the verify
00251     h->marker0 = 0xDEADBEEF;
00252     h->marker1 = 0xDEADBEEF;
00253 
00254     // Munge the body of the allocation so that if the user
00255     // still has a live reference they get messed up
00256     void* ptr = (void*) ((char*)(h+1));
00257     memset(ptr, FREE_PATTERN_BYTE, h->rawSize);
00258 
00259     if (0 == (LIBMALLOC_NOFREE & gFlags)) {
00260       REAL_FREE(h);
00261     }
00262   }
00263   else {
00264     if (0 == (LIBMALLOC_NOFREE & gFlags)) {
00265       REAL_FREE(aAddr);
00266     }
00267   }
00268 }
00269 
00270 static void*
00271 ReallocHook(void* aOldAddr, size_t aSize)
00272 {
00273   if (0 == aOldAddr) {
00274     return MallocHook(aSize, malloc_log_malloc);
00275   }
00276   Header* oldh = (Header*) ((char*)aOldAddr - sizeof(Header));
00277   if (!Verify(oldh)) {
00278     return REAL_REALLOC(aOldAddr, aSize);
00279   }
00280   size_t oldSize = oldh->rawSize;
00281 
00282   size_t roundedSize = aSize;
00283   roundedSize = ((roundedSize + 4) >> 2) << 2;
00284 
00285   void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer));
00286 
00287   if (NULL != ptr) {
00288     Header* h = (Header*) ptr;
00289     h->rawSize = aSize;
00290     h->size = roundedSize;
00291     h->marker0 = MARKER0;
00292     h->marker1 = MARKER1;
00293 
00294     ptr = (void*) ((char*)(h+1));
00295 
00296     Trailer* t = (Trailer*) ((char*)ptr + roundedSize);
00297     t->marker2[0] = MARKER2_0;
00298     t->marker2[1] = MARKER2_1;
00299 
00300     // Copy old memory into new memory (don't copy too much!)
00301     size_t copy = oldSize;
00302     if (copy > aSize) copy = aSize;
00303     memcpy(ptr, aOldAddr, copy);
00304 
00305     // Fill any uncopied memory with the overrun pattern
00306     size_t fill = roundedSize - copy;
00307     if (0 != fill) {
00308       memset((char*)ptr + copy, PATTERN_BYTE, fill);
00309     }
00310 
00311     if (0 == (LIBMALLOC_NOFREE & gFlags)) {
00312       REAL_FREE(oldh);
00313     }
00314     else {
00315       // Mark the old header so that a verify will fail if the caller
00316       // dup free's it.
00317       oldh->marker0 = 0xDEADBEEF;
00318       oldh->marker1 = 0xDEADBEEF;
00319 
00320       // Munge the body of the old allocation so that if the user
00321       // still has a live reference they get messed up
00322       void* optr = (void*) ((char*)(oldh+1));
00323       memset(optr, FREE_PATTERN_BYTE, oldh->rawSize);
00324     }
00325 
00326     if (LIBMALLOC_LOG & gFlags) {
00327       Log(malloc_log_realloc, ptr, aSize, aOldAddr);
00328     }
00329   }
00330   return ptr;
00331 }
00332 
00333 u_long
00334 SetMallocFlags(u_long aFlags)
00335 {
00336   u_long old = gFlags;
00337   gFlags = aFlags;
00338 
00339   if ((-1 == gLogFD) && ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags)) {
00340     gLogFD = open("malloc-log", O_CREAT|O_WRONLY|O_TRUNC, 0666);
00341     if (gLogFD < 0) {
00342       gFlags &= ~LIBMALLOC_LOG;
00343       printf("unable to create malloc-log: %d\n", errno);
00344     }
00345   }
00346   if ((gLogFD >= 0) && (0 == ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags))) {
00347     close(gLogFD);
00348     gLogFD = -1;
00349   }
00350 #ifndef NTO
00351   if (LIBMALLOC_CHECK & gFlags) {
00352     mallopt(M_CHECK_ACTION, 1);
00353   }
00354 #endif
00355 
00356   // Try to guarantee that the address map is always dumped
00357   atexit(DumpAddressMap);
00358 
00359   return old;
00360 }
00361 
00362 static int gFirstTime = 1;
00363 
00364 static void Init()
00365 {
00366   gFirstTime = 0;
00367   u_long flags = 0;
00368   char* s = getenv("LIBMALLOC_LOG");
00369   if (s) {
00370     flags = atoi(s);
00371     if (LIBMALLOC_LOG & flags) {
00372       char m1[] = "dbgmalloc: enabled memory logging\n";
00373       write(1, m1, sizeof(m1)-1);
00374     }
00375     if (LIBMALLOC_LOG_RC & flags) {
00376       char m2[] = "dbgmalloc: enabled refcnt logging\n";
00377       write(1, m2, sizeof(m2)-1);
00378     }
00379     if (LIBMALLOC_NOFREE & flags) {
00380       char m3[] = "dbgmalloc: disabled free\n";
00381       write(1, m3, sizeof(m3)-1);
00382     }
00383   }
00384   SetMallocFlags(flags);
00385   s = getenv("LIBMALLOC_FILL");
00386   if (s) {
00387     gFillCount = atoi(s);
00388     char m4[] = "dbgmalloc: adding extra memory fill ";
00389     write(1, m4, sizeof(m4)-1);
00390     write(1, s, strlen(s));
00391     write(1, "\n", 1);
00392   }
00393 }
00394 
00395 //----------------------------------------------------------------------
00396 
00397 #ifdef NEED_WRAPPERS
00398 void* __wrap_malloc(size_t aSize)
00399 {
00400   if (gFirstTime) {
00401     Init();
00402   }
00403   return MallocHook(aSize, malloc_log_malloc);
00404 }
00405 
00406 void* __wrap_realloc(void* aPtr, size_t aSize)
00407 {
00408   if (gFirstTime) {
00409     Init();
00410   }
00411   return ReallocHook(aPtr, aSize);
00412 }
00413 
00414 void __wrap_free(void* aPtr)
00415 {
00416   if (gFirstTime) {
00417     Init();
00418   }
00419   FreeHook(aPtr, malloc_log_free);
00420 }
00421 
00422 void* __wrap___builtin_new(size_t aSize)
00423 {
00424   if (gFirstTime) {
00425     Init();
00426   }
00427   return MallocHook(aSize, malloc_log_new);
00428 }
00429 
00430 void __wrap___builtin_delete(void* aPtr)
00431 {
00432   if (gFirstTime) {
00433     Init();
00434   }
00435   FreeHook(aPtr, malloc_log_delete);
00436 }
00437 
00438 void* __wrap___builtin_vec_new(size_t aSize)
00439 {
00440   if (gFirstTime) {
00441     Init();
00442   }
00443   return MallocHook(aSize, malloc_log_new);
00444 }
00445 
00446 void __wrap___builtin_vec_delete(void* aPtr)
00447 {
00448   if (gFirstTime) {
00449     Init();
00450   }
00451   FreeHook(aPtr, malloc_log_delete);
00452 }
00453 
00454 void* __wrap_PR_Malloc(size_t aSize)
00455 {
00456   if (gFirstTime) {
00457     Init();
00458   }
00459   return MallocHook(aSize, malloc_log_malloc);
00460 }
00461 
00462 void* __wrap_PR_Calloc(size_t aSize, size_t aBsize)
00463 {
00464   if (gFirstTime) {
00465     Init();
00466   }
00467   size_t size = aSize*aBsize;
00468   void* ptr = MallocHook(size, malloc_log_malloc);
00469   if (NULL != ptr) {
00470     memset(ptr, 0, size);
00471   }
00472   return ptr;
00473 }
00474 
00475 void* __wrap_PR_Realloc(void* aPtr, size_t aSize)
00476 {
00477   if (gFirstTime) {
00478     Init();
00479   }
00480   return ReallocHook(aPtr, aSize);
00481 }
00482 
00483 void __wrap_PR_Free(void* aPtr)
00484 {
00485   if (gFirstTime) {
00486     Init();
00487   }
00488   FreeHook(aPtr, malloc_log_free);
00489 }
00490 #endif
00491 
00492 //----------------------------------------
00493 
00494 // Strong symbols so that libc references are routed to us
00495 
00496 void* malloc(size_t aSize)
00497 {
00498   if (gFirstTime) {
00499     Init();
00500   }
00501   return MallocHook(aSize, malloc_log_malloc);
00502 }
00503 
00504 void* realloc(void* aPtr, size_t aSize)
00505 {
00506   if (gFirstTime) {
00507     Init();
00508   }
00509   return ReallocHook(aPtr, aSize);
00510 }
00511 
00512 void free(void* aPtr)
00513 {
00514   if (gFirstTime) {
00515     Init();
00516   }
00517   FreeHook(aPtr, malloc_log_free);
00518 }
00519 
00520 void* calloc(size_t aSize, size_t aBsize)
00521 {
00522   if (gFirstTime) {
00523     Init();
00524   }
00525   size_t size = aSize*aBsize;
00526   void* ptr = MallocHook(size, malloc_log_malloc);
00527   if (NULL != ptr) {
00528     memset(ptr, 0, size);
00529   }
00530   return ptr;
00531 }
00532 
00533 void cfree(void* ptr)
00534 {
00535   if (gFirstTime) {
00536     Init();
00537   }
00538   FreeHook(ptr, malloc_log_free);
00539 }
00540 
00541 void* memalign(size_t alignment, size_t size)
00542 {
00543   ::abort();
00544 }
00545 
00546 void* valloc(size_t size)
00547 {
00548   ::abort();
00549 }
00550 
00551 void* pvalloc(size_t size)
00552 {
00553   ::abort();
00554 }
00555 
00556 void* __builtin_new(size_t aSize)
00557 {
00558   if (gFirstTime) {
00559     Init();
00560   }
00561   return MallocHook(aSize, malloc_log_new);
00562 }
00563 
00564 void __builtin_delete(void* aPtr)
00565 {
00566   if (gFirstTime) {
00567     Init();
00568   }
00569   FreeHook(aPtr, malloc_log_delete);
00570 }
00571 
00572 void* __builtin_vec_new(size_t aSize)
00573 {
00574   if (gFirstTime) {
00575     Init();
00576   }
00577   return MallocHook(aSize, malloc_log_new);
00578 }
00579 
00580 void __builtin_vec_delete(void* aPtr)
00581 {
00582   if (gFirstTime) {
00583     Init();
00584   }
00585   FreeHook(aPtr, malloc_log_delete);
00586 }
00587 
00588 void
00589 __log_addref(void* p, int oldrc, int newrc)
00590 {
00591   if (gFirstTime) {
00592     Init();
00593   }
00594   if (LIBMALLOC_LOG_RC & gFlags) {
00595     Log(malloc_log_addref, p, size_t(oldrc), (void*)newrc);
00596   }
00597 }
00598 
00599 void
00600 __log_release(void* p, int oldrc, int newrc)
00601 {
00602   if (gFirstTime) {
00603     Init();
00604   }
00605   if (LIBMALLOC_LOG_RC & gFlags) {
00606     Log(malloc_log_release, p, size_t(oldrc), (void*)newrc);
00607   }
00608 }