blocxx
|
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