00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
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
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
00255 }
00256 }
00257
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 }
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