blocxx

MemTracer.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #define BLOCXX_MEMTRACER_CPP_INCLUDE_GUARD_
00040 #include "blocxx/BLOCXX_config.h"
00041 #ifdef BLOCXX_DEBUG_MEMORY
00042 #include "blocxx/MemTracer.hpp"
00043 #include "blocxx/Mutex.hpp"
00044 #include <map>
00045 #include <cstdio>
00046 #include <cstdlib>
00047 #include <cassert>
00048 #include <errno.h>
00049 
00050 #ifdef BLOCXX_HAVE_UNISTD_H
00051 #include <unistd.h>
00052 #endif
00053 
00054 #define BLOCXX_MEM_SIG 0xaaaaaaaa
00055 #define BLOCXX_FREE_MEM_SIG 0xbbbbbbbb
00056 
00057 namespace BLOCXX_NAMESPACE
00058 {
00059 
00060 // need to use our own allocator to avoid a deadlock with the standard allocator locking with a non-recursive mutex.
00061 template <typename T>
00062 class MemTracerAllocator
00063 {
00064 public:
00065    typedef std::size_t size_type;
00066    typedef std::ptrdiff_t difference_type;
00067    typedef T value_type;
00068    typedef value_type* pointer;
00069    typedef const value_type* const_pointer;
00070    typedef value_type& reference;
00071    typedef const value_type& const_reference;
00072    template <class U> struct rebind 
00073    { 
00074       typedef MemTracerAllocator<U> other; 
00075    };
00076 
00077    MemTracerAllocator() throw() {}
00078    template <class U> MemTracerAllocator(const MemTracerAllocator<U>& other) throw() {}
00079    pointer address(reference r) const { return &r; }
00080    const_pointer address(const_reference r) const { return &r; }
00081    pointer allocate(size_type n, const_pointer hint = 0)
00082    {
00083       return static_cast<pointer>(::malloc(n));
00084    }
00085    void deallocate(pointer p, size_type n)
00086    {
00087       ::free(p);
00088    }
00089    size_type max_size() const throw() { return static_cast<size_type>(-1); }
00090    void construct(pointer p, const_reference val) { new(p) value_type(val); }
00091    void destroy(pointer p) { p->~value_type(); }
00092    
00093 };
00094 
00095 
00096 
00097 static const char* const noFile = "<no file>";
00098 class MemTracer
00099 {
00100 public:
00101    class Entry
00102    {
00103    public:
00104       Entry (char const * file, int line, size_t sz)
00105       : m_file(file), m_line(line), m_size(sz), m_isDeleted(false) {}
00106       Entry()
00107       : m_file(NULL), m_line(-1), m_size(0), m_isDeleted(false) {}
00108       char const* getFile() const { return m_file; }
00109       int getLine() const { return m_line; }
00110       size_t getSize() const { return m_size; }
00111       void setDeleted() { m_isDeleted = true; }
00112       bool isDeleted() { return m_isDeleted; }
00113    private:
00114       char const* m_file;
00115       int m_line;
00116       size_t m_size;
00117       bool m_isDeleted;
00118    };
00119 private:
00120    class Lock
00121    {
00122    public:
00123       Lock(MemTracer & tracer) : m_tracer(tracer) { m_tracer.lock (); }
00124       ~Lock()
00125       {
00126          try
00127          {
00128             m_tracer.unlock ();
00129          }
00130          catch (...)
00131          {
00132             // don't let exceptions escape
00133          }
00134       }
00135    private:
00136       MemTracer& m_tracer;
00137    };
00138    typedef MemTracerAllocator<std::pair<void* const, Entry> > alloc_t;
00139    typedef std::map<void*, Entry, std::less<void*>, alloc_t > map_t;
00140    typedef map_t::iterator iterator;
00141    friend class Lock;
00142 public:
00143    MemTracer();
00144    ~MemTracer();
00145    void add(void* p, char const* file, int line, size_t sz);
00146    void* remove(void * p);
00147    void dump();
00148    Entry getEntry(void* idx);
00149    void printEntry(void* p);
00150    void checkMap();
00151 private:
00152    void lock() { m_lockCount++; }
00153    void unlock() { m_lockCount--; }
00154 private:
00155    map_t m_map;
00156    int m_lockCount;
00157 };
00158 
00159 static Mutex* memguard = NULL;
00160 static MemTracer* MemoryTracer = 0;
00161 static bool _shuttingDown = false;
00162 static bool noFree = false;
00163 static bool aggressive = false;
00164 static bool disabled = false;
00166 void
00167 myAtExitFunction()
00168 {
00169    _shuttingDown = true;
00170    if (MemoryTracer != 0)
00171    {
00172       fprintf(stderr, "*******************************************************************************\n");
00173       fprintf(stderr, "*   D U M P I N G   M E M O R Y\n");
00174       fprintf(stderr, "*******************************************************************************\n");
00175       MemoryTracer->dump();
00176       fprintf(stderr, "-------------------------------------------------------------------------------\n");
00177       fprintf(stderr, "-   D O N E   D U M P I N G   M E M O R Y\n");
00178       fprintf(stderr, "-------------------------------------------------------------------------------\n");
00179    }
00180    else
00181    {
00182       fprintf(stderr, BLOCXX_PACKAGE_NAME": MemoryTracer object does not exist\n");
00183    }
00184 }
00185 static bool owInternal = false;
00186 static bool initialized = false;
00187 void
00188 processEnv()
00189 {
00190    if (!initialized)
00191    {
00192       if (getenv("BLOCXX_MEM_DISABLE") && getenv("BLOCXX_MEM_DISABLE")[0] == '1')
00193       {
00194          disabled = true;
00195       }
00196       if (getenv("BLOCXX_MEM_NOFREE") && getenv("BLOCXX_MEM_NOFREE")[0] == '1')
00197       {
00198          noFree = true;
00199       }
00200       if (getenv("BLOCXX_MEM_AGGRESSIVE") && getenv("BLOCXX_MEM_AGGRESSIVE")[0] == '1')
00201       {
00202          aggressive = true;
00203          fprintf(stderr, "MemTracer running in aggressive mode.\n");
00204       }
00205       initialized = true;
00206    }
00207 }
00209 void
00210 allocMemTracer()
00211 {
00212    owInternal = true;
00213    processEnv();
00214    if (!disabled)
00215    {
00216       if (memguard == 0)
00217       {
00218          memguard = new Mutex;
00219          memguard->acquire();
00220       }
00221       if (MemoryTracer == 0)
00222       {
00223          atexit(myAtExitFunction);
00224          MemoryTracer = new MemTracer;
00225       }
00226    }
00227    owInternal = false;
00228 }
00230 void DumpMemory()
00231 {
00232    if (MemoryTracer != 0)
00233    {
00234       MemoryTracer->dump();
00235    }
00236    else
00237    {
00238       fprintf(stderr, BLOCXX_PACKAGE_NAME": MemoryTracer object does not exist\n");
00239    }
00240 }
00242 MemTracer::MemTracer() : m_lockCount (0)
00243 {
00244 }
00246 MemTracer::~MemTracer()
00247 {
00248    try
00249    {
00250       dump();
00251    }
00252    catch (...)
00253    {
00254       // don't let exceptions escape
00255    }
00256 }
00257 //static int delCount = 0;
00259 static void*
00260 checkSigs(void* p, size_t sz)
00261 {
00262    assert(sz);
00263    assert(p);
00264    unsigned long* plong = (unsigned long*)((char*)p - 4);
00265    if (*plong != BLOCXX_MEM_SIG)
00266    {
00267       fprintf(stderr, "UNDERRUN: Beginning boundary problem.  "
00268          "Sig is %x\n", (unsigned int)*plong);
00269       MemoryTracer->printEntry(p);
00270       assert(0);
00271    }
00272    plong = (unsigned long*)((char*)p + sz);
00273    if (*plong != BLOCXX_MEM_SIG)
00274    {
00275       fprintf(stderr, "OVERRUN: Ending boundary problem.  "
00276          "Sig is %x\n", (unsigned int)*plong);
00277       MemoryTracer->printEntry(p);
00278       fflush(stderr);
00279       assert(0);
00280    }
00281    return (void*)((char*)p - 4);
00282 }
00284 static void*
00285 checkAndSwitchSigs(void* p, size_t sz)
00286 {
00287    assert(sz);
00288    assert(p);
00289    unsigned long* plong = (unsigned long*)((char*)p - 4);
00290    if (*plong != BLOCXX_MEM_SIG)
00291    {
00292       fprintf(stderr, "UNDERRUN: Beginning boundary problem.  "
00293          "Sig is %x\n", (unsigned int)*plong);
00294       assert(0);
00295    }
00296    *plong = BLOCXX_FREE_MEM_SIG;
00297    plong = (unsigned long*)((char*)p + sz);
00298    if (*plong != BLOCXX_MEM_SIG)
00299    {
00300       fprintf(stderr, "OVERRUN: Ending boundary problem.  "
00301          "Sig is %x\n", (unsigned int)*plong);
00302       assert(0);
00303    }
00304    *plong = BLOCXX_FREE_MEM_SIG;
00305    return (void*)((char*)p - 4);
00306 }
00308 void
00309 MemTracer::checkMap()
00310 {
00311    for (iterator it = m_map.begin(); it != m_map.end(); ++it)
00312    {
00313       if (!it->second.isDeleted())
00314       {
00315          checkSigs(it->first, it->second.getSize());
00316       }
00317    }
00318 }
00320 void
00321 MemTracer::add(void* p, char const* file, int line, size_t sz)
00322 {
00323    const char* pfile = noFile;
00324    if (file)
00325    {
00326       pfile = strdup(file);
00327    }
00328    m_map[p] = Entry(pfile, line, sz);
00329 }
00331 void*
00332 MemTracer::remove(void* p)
00333 {
00334    iterator it = m_map.find(p);
00335    if (it != m_map.end())
00336    {
00337       if (noFree)
00338       {
00339          if (it->second.isDeleted())
00340          {
00341             fprintf(stderr, "DOUBLE DELETE (NOFREE): Attempting to double "
00342                "delete memory at: %p\n", p);
00343             assert(0);
00344          }
00345       }
00346       void* ptrToFree = checkAndSwitchSigs(p, it->second.getSize());
00347       void* pfile = (void*) it->second.getFile();
00348       if (noFree)
00349       {
00350          it->second.setDeleted();
00351       }
00352       else
00353       {
00354          m_map.erase(it);
00355          if (pfile != noFile)
00356          {
00357             free(pfile);
00358          }
00359       }
00360       return ptrToFree;
00361    }
00362    fprintf(stderr, "Attempting to delete memory not in map: %p\n", p);
00363    if (!noFree)
00364    {
00365       fprintf(stderr, "Trying to check beginning signature...\n");
00366       unsigned long* plong = (unsigned long*)((char*)p - 4);
00367       if (*plong == BLOCXX_MEM_SIG)
00368       {
00369          fprintf(stderr, "MemTracer is broken\n");
00370          assert(0);
00371       }
00372       if (*plong == BLOCXX_FREE_MEM_SIG)
00373       {
00374          fprintf(stderr, "DOUBLE DELETE: This memory was previously freed by MemTracer, "
00375             "probably double delete\n");
00376          assert(0);
00377       }
00378       fprintf(stderr, "No signature detected.\n");
00379    }
00380    fprintf(stderr, "UNKNOWN ADDRESS\n");
00381    assert(0);
00382    return p;
00383 }
00385 void
00386 MemTracer::printEntry(void* p)
00387 {
00388    Entry entry = getEntry(p);
00389    fprintf(stderr, "\tFILE: %s", entry.getFile());
00390    fprintf(stderr, "\tLINE: %d", entry.getLine());
00391    fprintf(stderr, "\tSIZE: %lu", static_cast<unsigned long>(entry.getSize()));
00392    fprintf(stderr, "\tADDR: %p\n", p);
00393 }
00395 MemTracer::Entry
00396 MemTracer::getEntry(void* idx)
00397 {
00398    memguard->acquire();
00399    iterator it = m_map.find(idx);
00400    MemTracer::Entry rval;
00401    if (it != m_map.end())
00402    {
00403       rval = it->second;
00404    }
00405    memguard->release();
00406    return rval;
00407 }
00409 void
00410 MemTracer::dump()
00411 {
00412    memguard->acquire();
00413    if (m_map.size() != 0)
00414    {
00415       fprintf(stderr, "**** %lu MEMORY LEAK(S) DETECTED\n", static_cast<unsigned long>(m_map.size()));
00416       size_t total = 0;
00417       for (iterator it = m_map.begin(); it != m_map.end (); ++it)
00418       {
00419          if (!it->second.isDeleted())
00420          {
00421             fprintf(stderr, "\tFILE: %s", it->second.getFile());
00422             fprintf(stderr, "\tLINE: %d", it->second.getLine());
00423             fprintf(stderr, "\tSIZE: %lu", static_cast<unsigned long>(it->second.getSize()));
00424             fprintf(stderr, "\tADDR: %p\n", it->first);
00425             total += it->second.getSize();
00426          }
00427       }
00428       fprintf(stderr, "***** END MEMORY LEAKS - TOTAL MEMORY LEAKED = %lu\n", static_cast<unsigned long>(total));
00429    }
00430    memguard->release();
00431 }
00433 static void
00434 writeSigs(void *& p, size_t size)
00435 {
00436    unsigned long* plong = (unsigned long*)p;
00437    *plong = BLOCXX_MEM_SIG;
00438    plong = (unsigned long*)((char*)p + size + 4);
00439    *plong = BLOCXX_MEM_SIG;
00440    p = (void*)((char*)p + 4);
00441 }
00442 static int internalNewCount = 0;
00444 static void*
00445 doNew(size_t size, char const* file, int line)
00446 {
00447    if (memguard)
00448    {
00449       memguard->acquire();
00450    }
00451    if (owInternal || disabled)
00452    {
00453       ++internalNewCount;
00454       if (internalNewCount > 2 && !disabled)
00455       {
00456          fprintf(stderr, "INTERNAL NEW called more than twice!  "
00457             "Possible bug in MemTracer.\n");
00458          assert(0);
00459       }
00460       void* rval =  malloc(size);
00461       if (memguard)
00462       {
00463          memguard->release();
00464       }
00465       --internalNewCount;
00466       return rval;
00467    }
00468    allocMemTracer();
00469    if (disabled)
00470    {
00471       return malloc(size);
00472    }
00473    if (aggressive)
00474    {
00475       MemoryTracer->checkMap();
00476    }
00477    void* p = malloc(size + 8);
00478    if (!p)
00479    {
00480       memguard->release();
00481       perror("malloc failed.");
00482       exit(errno);
00483    }
00484    writeSigs(p, size);
00485    owInternal = true;
00486    assert (MemoryTracer);
00487    MemoryTracer->add(p, file, line, size);
00488    owInternal = false;
00489    memguard->release();
00490    return p;
00491 }
00493 static void
00494 doDelete(void* p)
00495 {
00496    if (p)
00497    {
00498       if (memguard)
00499       {
00500          memguard->acquire();
00501       }
00502       if (owInternal || disabled)
00503       {
00504          if (!disabled)
00505          {
00506             fprintf(stderr, "INTERNAL DELETE: %p\n", p);
00507          }
00508          free(p);
00509          if (memguard)
00510          {
00511             memguard->release();
00512          }
00513          return;
00514       }
00515       if (aggressive)
00516       {
00517          MemoryTracer->checkMap();
00518       }
00519       owInternal = true;
00520       if (MemoryTracer != 0)
00521       {
00522          p = MemoryTracer->remove((void*)((char*)p));
00523       }
00524       else
00525       {
00526          printf("** MemTracer can't remove delete from map: ADDR: %p\n", p);
00527       }
00528       if (!noFree)
00529       {
00530          free(p);
00531       }
00532       owInternal = false;
00533       memguard->release();
00534    }
00535    if (_shuttingDown)
00536    {
00537       memguard->release();
00538       fprintf(stderr, "delete called\n");
00539    }
00540 }
00541 
00542 } // end namespace BLOCXX_NAMESPACE
00543 
00545 void*
00546 operator new[](std::size_t size, char const* file, int line) throw(std::bad_alloc)
00547 {
00548    return BLOCXX_NAMESPACE::doNew(size, file, line);
00549 }
00551 void*
00552 operator new(std::size_t size, char const* file, int line) throw(std::bad_alloc)
00553 {
00554    return BLOCXX_NAMESPACE::doNew(size, file, line);
00555 }
00557 void*
00558 operator new[](std::size_t size) throw(std::bad_alloc)
00559 {
00560    return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
00561 }
00563 void*
00564 operator new(std::size_t size) throw(std::bad_alloc)
00565 {
00566    return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
00567 }
00568 void
00569 operator delete(void* p)
00570 {
00571    BLOCXX_NAMESPACE::doDelete(p);
00572 }
00573 void
00574 operator delete[](void* p)
00575 {
00576    BLOCXX_NAMESPACE::doDelete(p);
00577 }
00578 
00579 
00580 #endif   // BLOCXX_DEBUG_MEMORY
00581 
00582