MemTracer.cpp

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

Generated on Fri Jun 16 15:39:08 2006 for blocxx by  doxygen 1.4.6