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
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
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
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
00252 }
00253 }
00254
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 }
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