Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

base/MemoryTracer.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)1996 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008   
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013   
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017   
00018   $Id: MemoryTracer.cpp 1048 2004-02-27 19:22:02Z jungd $
00019   $Revision: 1.4 $
00020   $Date: 2004-02-27 14:22:02 -0500 (Fri, 27 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <base/MemoryTracer>
00026 
00027 #include <string>
00028 #include <iostream>
00029 
00030 using std::string;
00031 
00032 using base::MemoryTracer;
00033 
00034 
00035 
00036 // if defined, will cause a menu to be displayed when a memory problem is
00037 //  detected (stopping the program)
00038 #define _INTERACTIVE_ 
00039 
00040 // if defined and not _INTERACTIVE_, then a memory problem will throw a
00041 //  std::bad_alloc exception (possibly from within new or delete)
00042 #define _THROWEXCEPTION_
00043 
00044 // if defined, calls to allocate, validate, release & invalidate will report arguments (on every call)
00045 //  (even more verbose that using _TRACEOUTPUT_ )
00046 //#define _MEMORYTRACEDEBUG_
00047 
00048 
00049 
00050 base::Byte MemoryTracer::prefill[MemoryTracer::Filling]; // Filler for integrity check
00051 base::Byte MemoryTracer::initialized = 0;
00052 bool MemoryTracer::cleanedUp = false;
00053 Int MemoryTracer::entry = 0;
00054 Int MemoryTracer::fullCheckCounter = 0;
00055 base::LInt MemoryTracer::allocCount = 0;
00056 base::LInt MemoryTracer::deallocCount = 0;
00057 base::LInt MemoryTracer::checkCount = 0;
00058 MemoryTracer::AllocList MemoryTracer::mlist; // the allocated memory list
00059 MemoryTracer::AllocList MemoryTracer::dlist; // list of memory allocated and then deallocated (so we can catch double delete's)
00060 base::Byte MemoryTracer::postfill[Filling]; // Filler for integrity check
00061 
00062 const int MemoryTracer::IsArray = 0xabcd;
00063 const int MemoryTracer::IsNotArray = 0xfcad;
00064 
00065 inline void MemoryTracer::Logerr(char* errstr) 
00066 {
00067   // can't use base's Logln because that uses iostreams (which need new/delete)
00068   char str[256];
00069   sprintf(str,"MemoryTracer Log: %s\n",errstr);
00070   fprintf(stderr,str);
00071 }
00072 
00073 
00074 // this is not meant to be an atomic lock, just something to help flag multiple thread entry
00075 inline void MemoryTracer::entrylock() 
00076 {
00077   if (entry != 0) {
00078     Logerr("Multiple thread entry detected!  MemoryTracer is not threadsafe - please restrict use to a single thread at a time.");
00079     handleError();
00080   }
00081   else
00082     entry++;
00083 }
00084 
00085 inline void MemoryTracer::exitunlock()
00086 {
00087   entry--;
00088 }
00089 
00090 //
00091 // Home grown doubly linked list
00092 //  (Unfortunately, MemoryTracer can't used STL because it must not call the global
00093 //   new or delete.  Using STL containers with malloc_alloc as their
00094 //   allocator should work - but, alas, it doesn't).
00095 
00096 
00097 void MemoryTracer::AllocList::push_front(AllocEntry* e)
00098 {
00099   if (head == 0) {
00100     head = tail = e;
00101     e->next = e->prev = 0;
00102   }
00103   else {
00104     AllocEntry* oldhead = head;
00105     head = e;
00106     head->next = oldhead;
00107     head->prev = 0;
00108     oldhead->prev = head;
00109   }
00110   len++;
00111 }
00112 
00113 
00114 void MemoryTracer::AllocList::push_back(AllocEntry* e)
00115 {
00116   if (head == 0) {
00117     head = tail = e;
00118     e->next = e->prev = 0;
00119   }
00120   else {
00121     AllocEntry* oldtail = tail;
00122     tail = e;
00123     tail->next = 0;
00124     tail->prev = oldtail;
00125     oldtail->next = tail;
00126   }
00127   len++;
00128 }
00129 
00130 MemoryTracer::AllocEntry* MemoryTracer::AllocList::pop_front()
00131 {
00132   AllocEntry* oldhead = head;
00133   if (head != 0) {
00134     head = head->next;
00135     if (head)
00136       head->prev = 0;
00137     else
00138       tail = 0;
00139     len--;
00140   }
00141   return oldhead;
00142 }
00143 
00144 
00145 MemoryTracer::AllocEntry* MemoryTracer::AllocList::pop_back()
00146 {
00147   AllocEntry* oldtail = tail;
00148   if (tail != 0) {
00149     tail = tail->prev;
00150     if (tail)
00151       tail->next = 0;
00152     else
00153       head = 0;
00154     len--;
00155   }
00156   return oldtail;
00157 }
00158 
00159 
00160 void MemoryTracer::AllocList::remove(AllocEntry* e)
00161 {
00162   if (e == tail) {
00163     pop_back();
00164   }
00165   else if (e == head) {
00166     pop_front();
00167   }
00168   else {
00169     e->next->prev = e->prev;
00170     e->prev->next = e->next;
00171     len--;
00172   }
00173 }
00174 
00175 
00176 MemoryTracer::AllocEntry* MemoryTracer::AllocList::find(void* address, AllocEntry* from) const
00177 { 
00178   Byte* addr = (Byte*)address;
00179   AllocEntry* m = (from==0)?head:from;
00180   while (m) {
00181     if (!m->isExternal()) {
00182       if (addr == m->address+Filling)
00183         return m;
00184     }
00185     else
00186       if (addr == m->address)
00187         return m;
00188     
00189     m = m->next;
00190   }
00191   return 0;
00192 }
00193 
00194 
00195 
00196   void MemoryTracer::menu() throw(std::bad_alloc)
00197   {
00198     char ans[32];
00199     bool quit=false;
00200     while (!quit) {
00201       printf("\n");
00202       printf("Choose: (a)llocated memory list        (n)amed allocated memory\n");
00203       printf("        (r)eciently deallocated memory (d)ivide by 0 crash (SIGFPE)\n");
00204       printf("         e(x)it   (t)hrow bad_alloc    (c)continue? ");
00205       scanf("%s",ans);
00206       switch (tolower(ans[0])) {
00207       case 'a': dump(); break;
00208       case 'n': dumpNamed(); break;
00209       case 'r': dumpDeallocated(); break;
00210       case 'd': causefault(); break;
00211       case 'x': exit(-1); break;
00212       case 't': throw std::bad_alloc(); break;
00213       case 'c': quit=true; break;
00214       default: ;
00215       }
00216     }
00217   }
00218 
00219 
00220   void MemoryTracer::handleError() throw(std::bad_alloc)
00221   {
00222 #ifdef _INTERACTIVE_
00223     menu();
00224 #else
00225 #ifdef _THROWEXCEPTION_
00226     throw std::bad_alloc();
00227 #endif
00228 #endif
00229   }
00230 
00231 
00232   // Does a dividie by zero - Debugger can catch this to provide a stack trace
00233   int MemoryTracer::causefault() 
00234   {
00235     int i=5, j=5;
00236     // try not to let compiler optimise this out of existance 
00237     j = i/(j-i); // Divide by 0 to get debugger's attention
00238     return j;
00239   }
00240   
00241   
00242   // Checks all currently allocated memory for overwrites
00243   bool MemoryTracer::checkAllMemory() throw(std::bad_alloc)
00244   {
00245     bool ok = true;
00246 #ifdef _TRACEMEMORY_
00247     checkCount++;
00248     AllocEntry* m = mlist.front();
00249     int size, i,j;
00250     char errstr[256];
00251     
00252     while (m) {
00253       if (!m->isOK()) {
00254         Logerr("Memory allocation record has been corrupted (AllocEntry).");
00255         bool prefillOK = true;
00256         for(Int i=0; i<Filling; i++) 
00257           if (m->prefill[i] != Filler) prefillOK = false;
00258         bool postfillOK = true;
00259         for(Int i=0; i<Filling; i++) 
00260           if (m->postfill[i] != Filler) postfillOK = false;
00261         sprintf(errstr,"Entry: prefill:%s postfill:%s size:%6d isMagic:%s",
00262                 prefillOK?"OK":"BAD", postfillOK?"OK":"BAD",m->size,m->isMagic()?"OK":"BAD");
00263         Logerr(errstr);
00264         m=0;
00265         ok=false;
00266       } 
00267       else {
00268         
00269         if (!m->isExternal()) {
00270           Byte* mem = (Byte*)m->address;
00271           size = m->size;
00272           for(i=0; (i<Filling) && ok; i++) {
00273             if ( *(mem+i) != Filler ) {
00274               sprintf(errstr,"Memory before allocation %x (%s) has been overwritten",(Int)mem+Filling,m->name);
00275               for(j=0; j<Filling; j++) {
00276                 sprintf(errstr,"addr(m-%d) = %d (should be %d)",Filling-j,(unsigned int)*(mem+i),Filler);
00277                 Logerr(errstr);
00278               }
00279               ok=false;
00280             }
00281             if ( *(mem+Filling+size+i) != Filler ) {
00282               sprintf(errstr,"Memory after allocation %x (%s) has been overwritten",(Int)mem+Filling,m->name);
00283               Logerr(errstr);
00284               for(j=0; j<Filling; j++) {
00285                 sprintf(errstr,"addr(m+size+%d) = %d (should be %d)",j,(Int)*(mem+size+Filling+i),Filler);
00286                 Logerr(errstr);
00287               }
00288               ok=false;
00289             }
00290             
00291           }
00292         } 
00293         
00294         m = m->next;
00295       }
00296       
00297     }
00298     
00299     if (!ok) handleError();
00300 #endif    
00301     return ok;
00302   }
00303   
00304 
00305   // allocate memory (returns 0 on error)
00306   void* MemoryTracer::allocate(int size, const string& name, bool isArray) throw(std::bad_alloc)
00307   {
00308     entrylock();
00309     if (!isOK()) {
00310       Logerr("memory allocation records have been corrupted (MemoryTracer).");
00311       exitunlock();
00312       return 0;
00313     }
00314 
00315     int i;
00316     AllocEntry* ne;
00317     if (size <= 0) {
00318       Logerr("passed size <= 0.");
00319       handleError();
00320       return 0;
00321     }
00322     void* mem = malloc(size + Filling*2);
00323     if (mem == 0) return 0;
00324 
00325 #if (_FULLCHECKFREQUENTY_ > 0)
00326     if ((++fullCheckCounter % _FULLCHECKFREQUENTY_) == 0) {
00327       fullCheckCounter=0;
00328       checkAllMemory();
00329     }
00330 #endif
00331 
00332     ne = (AllocEntry*)malloc(sizeof(AllocEntry));
00333     if (ne == 0) {
00334       free(mem);
00335       Logerr("no memory available to allocate (for entry).");
00336       return 0;
00337     }
00338     ne->fill();
00339 
00340     // fill 'Filling' bytes before and after returned memory area with 'Filler' byte 
00341     for(i=0; i< Filling; i++) {
00342       *(((Byte*)mem)+i) = Filler;
00343       *(((Byte*)mem)+size+Filling+i) = Filler;
00344     }
00345 
00346     ne->size = size;
00347     ne->name = 0;
00348     ne->setIsArray(isArray);
00349     
00350     if (name.size() > 0) {
00351       ne->name = (char*)malloc(name.size()+1);
00352       if (ne->name == 0) {
00353         Logerr("no memory available to allocate name.");
00354       }
00355       else
00356         strcpy(ne->name, name.c_str());
00357     }
00358 
00359     ne->setMagic();
00360     ne->address = (Byte*)mem;
00361 
00362     mlist.push_front(ne);
00363     
00364     allocCount++;
00365     exitunlock();
00366     void* addr = ((Byte*)mem)+Filling;
00367 #ifdef _MEMORYTRACEDEBUG_
00368     char logstr[80];
00369     sprintf(logstr, "allocated(size:%d, name:%s, isArray:%s) = %x", size, (name.size()>0)?name.c_str():"<unnamed>",isArray?"TRUE":"FALSE",Int(addr));
00370     Logerr(logstr);
00371 #endif
00372     return addr;
00373   }
00374   
00375 
00376 
00377   // registers an externally allocated region as valid (for Checks).
00378   bool MemoryTracer::validate(void* address, int size, const string& name) throw(std::bad_alloc)
00379   {
00380     entrylock();
00381     if (!isOK()) {
00382       Logerr("memory allocation records have been corrupted.");
00383       exitunlock();
00384       return false;
00385     }
00386 
00387     AllocEntry* ne;
00388     char errstr[256];
00389 
00390     if (size < 0) {
00391       Logerr("passed size < 0.");
00392       exitunlock();
00393       handleError();
00394       return false;
00395     }
00396     
00397 #if (_FULLCHECKFREQUENTY_ > 0)
00398     if ((++fullCheckCounter % _FULLCHECKFREQUENTY_) == 0) {
00399       fullCheckCounter=0;
00400       checkAllMemory();
00401     }
00402 #endif
00403     
00404     // first look to see if this address is already registered as valid,
00405     // if so, return - nothing more to do 
00406     AllocEntry* m = mlist.find(address);
00407     if (m) {
00408       if (!m->isExternal()) {
00409         if (address == m->address + Filling) {
00410           sprintf(errstr,"memory at address %x (%s) was allocated with allocate(), so cannot be validated with validate()\n",
00411                   Int(m->address+Filling),(m->name==0)?"<unnamed>":m->name);
00412           Logerr(errstr);
00413           exitunlock();
00414           return false;
00415         }
00416       }
00417       else
00418         if (address == m->address) {
00419           exitunlock();
00420           return true;
00421         }
00422     }
00423     
00424     ne = (AllocEntry*)malloc(sizeof(AllocEntry));
00425     if (ne == 0) {
00426       Logerr("no memory available to allocate (for entry).");
00427       exitunlock();
00428       return false;
00429     }
00430     
00431     ne->size = size;
00432     ne->name = 0;
00433 
00434     if (name.size() > 0) {
00435       ne->name = (char*)malloc(name.size()+1);
00436       if (ne->name == 0) {
00437         Logerr("no memory available to allocate (for name).");
00438       }
00439       else
00440         strcpy(ne->name, name.c_str());
00441       
00442     }
00443     ne->setMagic();
00444     ne->setExternal();
00445     ne->setIsArray(false);
00446     ne->address = (Byte*)address;
00447     
00448     mlist.push_front(ne);
00449 
00450     exitunlock();
00451 
00452 #ifdef _MEMORYTRACEDEBUG_
00453     char logstr[80];
00454     sprintf(logstr, "validated(address:%x, size:%d, name:%s)", Int(address), size, (name.size()>0)?name.c_str():"<unnamed>");
00455     Logerr(logstr);
00456 #endif
00457 
00458     return true;
00459   }
00460   
00461   
00462 
00463   // free allocated memory
00464   bool MemoryTracer::release(void* address, bool isArray, int size) throw(std::bad_alloc)
00465   {
00466     entrylock();
00467     if (!isOK()) {
00468       Logerr("memory allocation records have been corrupted.");
00469       exitunlock();
00470       return false;
00471     }
00472 
00473     if (cleanedUp) return true;
00474 
00475     Byte* addr = (Byte*)address;
00476     char errstr[256];
00477 
00478 #if (_FULLCHECKFREQUENTY_ > 0)
00479     if ((++fullCheckCounter % _FULLCHECKFREQUENTY_) == 0) {
00480       fullCheckCounter=0;
00481       checkAllMemory();
00482     }
00483 #endif
00484     
00485     AllocEntry* m = mlist.find(address);
00486     bool found = (m!=0);
00487     char name[128];
00488     strcpy(name, "<unknown>");
00489 
00490     if (found) {
00491       
00492       if (addr == m->address+Filling) {
00493         if ((size!=-1) && (size!=m->size)) {
00494           sprintf(errstr,"size given (%d) for region at %x doesn't match that allocated (%d)",
00495                   size,Int(address),m->size);
00496           Logerr(errstr);
00497         }
00498         if (m->isArray() != isArray) {
00499           if (m->isArray()) {
00500             sprintf(errstr,"region at %x was allocated as an array but not free'd as one",
00501                     Int(address));
00502             Logerr(errstr);
00503             handleError();
00504           }
00505           else {
00506             sprintf(errstr,"region at %x was free'd as an array but not allocated as one",
00507                     Int(address));
00508             Logerr(errstr);
00509             handleError();
00510           }
00511         }
00512         
00513 #ifdef _MEMORYTRACEDEBUG_
00514         if (m->name)
00515           strncpy(name, m->name, 127);
00516 #endif
00517         
00518         // remove from list 
00519         mlist.remove(m);
00520 
00521         free(m->address); // free memory
00522 
00523         dlist.push_front(m); // add to deallocated list
00524         if (dlist.size() > DList_Max) {
00525           AllocEntry* dm = dlist.pop_back();
00526           if (dm->name!=0) 
00527             free(dm->name);
00528           free(dm);
00529         }
00530       }
00531       else {
00532         if ((addr == m->address) && (m->isExternal())) {
00533           sprintf(errstr,"memory at address %x was not allocated with allocate(), but was validated with validate(). (will invalidate without freeing).",(Int)address);
00534           Logerr(errstr);
00535           exitunlock();
00536           invalidate(address,size);
00537           entrylock();
00538         }
00539       }
00540     }
00541     else { // not found
00542       sprintf(errstr,"no memory allocated at address %x.",(Int)address);
00543       Logerr(errstr);
00544 
00545       // check to see if the memory was previously deallocated
00546       AllocEntry* from = dlist.front();
00547       AllocEntry* dm;
00548       do {
00549         dm = dlist.find(address,from);
00550         if (dm) {
00551           Byte* addr = (dm->isExternal()?dm->address:dm->address+Filling);
00552           sprintf(errstr,"memory was previously allocated at address %x as %s (size %d) - but has since been deallocated.",
00553                   Int(addr),((dm->name==0)?"<unnamed>":dm->name),dm->size);
00554           Logerr(errstr);
00555           from = dm->next;
00556           if (from==0) from = dlist.back();
00557         }
00558       } while (dm);
00559 
00560       //dump();
00561       checkAllMemory();
00562       exitunlock();
00563       handleError();
00564       return found;
00565     }
00566 
00567     deallocCount++;
00568     exitunlock();
00569 
00570 #ifdef _MEMORYTRACEDEBUG_
00571     char logstr[80];
00572     sprintf(logstr, "released(address:%x, isArray:%s, size:%6d, name:%s)", Int(address), isArray?"TRUE":"FALSE", size, name);
00573     Logerr(logstr);
00574 #endif
00575 
00576     return found;
00577   }
00578   
00579 
00580 
00581   // invalidate externally allocated region, previously registered
00582   //   with validate()
00583   bool MemoryTracer::invalidate(void* address, int size) throw(std::bad_alloc)
00584   {
00585     entrylock();
00586     if (!isOK()) {
00587       Logerr("memory allocation records have been corrupted.");
00588       exitunlock();
00589       return false;
00590     }
00591 
00592     if (cleanedUp) return true;
00593 
00594     Byte* addr = (Byte*)address;
00595     char errstr[256];
00596     
00597 #if (_FULLCHECKFREQUENTY_ > 0)
00598     if ((++fullCheckCounter % _FULLCHECKFREQUENTY_) == 0) {
00599       fullCheckCounter=0;
00600       checkAllMemory();
00601     }
00602 #endif
00603 
00604     AllocEntry* m = mlist.find(address);
00605     bool found = (m!=0);
00606     char name[128];
00607     strcpy(name, "<unknown>");
00608 
00609     if (found) {
00610       
00611       if (addr == m->address) {
00612         if ((size!=-1) && (size!=m->size)) {
00613           sprintf(errstr,"size given (%d) doesn't match that validated (%d).",size,m->size);
00614           Logerr(errstr);
00615         }
00616 
00617         // remove from list 
00618 #ifdef _MEMORYTRACEDEBUG_
00619         if (m->name)
00620           strncpy(name, m->name, 127);
00621 #endif
00622         mlist.remove(m);
00623         if (m->name != 0) free(m->name);
00624         free(m);
00625       }
00626       else {
00627         if ((addr == m->address+Filling) && (!m->isExternal())) {
00628           sprintf(errstr,"memory at address %x was allocated with allocate(), not validated with validate().",(Int)address);
00629           Logerr(errstr);
00630         }
00631       }
00632       
00633     }
00634     else { // !found
00635       sprintf(errstr,"no memory validated at address %x.",(Int)address);
00636       Logerr(errstr);
00637       exitunlock();
00638       handleError();
00639       return found;
00640     }
00641 
00642     exitunlock();
00643 
00644 #ifdef _MEMORYTRACEDEBUG_
00645     char logstr[80];
00646     sprintf(logstr, "invalidated(address:%x, size:%6d, name:%s)", Int(address), size, name);
00647     Logerr(logstr);
00648 #endif
00649 
00650     return found;
00651   }
00652   
00653   
00654   // Check that addr & addr+typesize-1 are within an 
00655   //    allocated memory area
00656   void* MemoryTracer::checkAddrRange(void* address, long typesize) throw(std::bad_alloc)
00657   {
00658     checkAddr(address, address, true);
00659     if (typesize>0) checkAddr( ((char*)address)+typesize-1, address, false);
00660     return address;
00661   }
00662 
00663 
00664   // Check that addr is within an allocated memory area
00665   bool MemoryTracer::checkAddr(void* address, void* reportAddr, bool checkAllMemory) throw(std::bad_alloc)
00666   {
00667     bool found=false;
00668     Byte *addr = (Byte*)address;
00669     char errstr[256];
00670     AllocEntry* m = mlist.front();
00671     
00672     if (checkAllMemory) MemoryTracer::checkAllMemory();
00673 
00674     if (reportAddr == 0) reportAddr = address;
00675     
00676     while (m && !found) {
00677       if (!m->isExternal()) {
00678         
00679         if ((m->address <= addr) && (addr < m->address+Filling*2+m->size)) { // it's within greater area 
00680           
00681           if ((m->address+Filling <= addr) && (addr < m->address+Filling+m->size)) // within valid area? 
00682             found=true;
00683           else { // must be in one of filling areas 
00684             if ((m->address <= addr) && (addr < m->address+Filling)) {
00685               if (address == reportAddr) {
00686                 sprintf(errstr,"reference address %x is before memory area at %x (size %d) (%s).",
00687                         Int(addr), Int(m->address+Filling), m->size, m->name);
00688                 Logerr(errstr);
00689               }
00690               else {
00691                 sprintf(errstr,"reference to address %x may allow to access address %x which is before memory area at %x (size %d) (%s).",
00692                         Int(reportAddr), Int(addr), Int(m->address+Filling), m->size, m->name);
00693                 Logerr(errstr);
00694               }
00695             }
00696             else { // must be after then 
00697               if (address == reportAddr) {
00698                 sprintf(errstr,"reference address %x is after memory area at %x (size %d) (%s).",
00699                         Int(addr), Int(m->address+Filling), m->size, m->name);
00700                 Logerr(errstr);
00701               }
00702               else {
00703                 sprintf(errstr,"reference to address %x may allow to access address %x which is after memory area at %x (size %d) (%s)\n",
00704                         Int(reportAddr), Int(addr), Int(m->address+Filling), m->size, m->name);
00705                 Logerr(errstr);
00706               }
00707             }
00708             
00709           }
00710         }
00711       } else { // external 
00712         if ((m->address <= addr) && (addr < m->address+m->size))
00713           found = true;
00714       }
00715       
00716       m = m->next;
00717     }
00718     if (!found) {
00719       if (address == reportAddr) {
00720         sprintf(errstr,"reference to invalid memory address %x detected.",Int(addr));
00721         Logerr(errstr);
00722       }
00723       else {
00724         sprintf(errstr,"detected reference to address %x that may allow access to invalid address %x.",Int(reportAddr), Int(addr));
00725         Logerr(errstr);
00726       }
00727       handleError();
00728       return false;
00729   }
00730     
00731     return true;
00732   }
00733   
00734 
00735   char* MemoryTracer::getName(void* addr) 
00736   {
00737     AllocEntry* m = mlist.front();
00738     bool found=false;
00739     AllocEntry* entry = 0;
00740     while (m && !found) {
00741       if (!m->isExternal()) {
00742         
00743         if ((m->address <= addr) && (addr < m->address+Filling*2+m->size)) { // it's within greater area 
00744           
00745           if ((m->address+Filling <= addr) && (addr < m->address+Filling+m->size)) { // within valid area? 
00746             found=true;
00747             entry = m;
00748           }
00749         }
00750       } else { // external 
00751         if ((m->address <= addr) && (addr < m->address+m->size)) {
00752           found = true;
00753           entry=m;
00754         }
00755       }
00756       
00757       m = m->next;
00758     }
00759     
00760     if (found)
00761       return entry->name;
00762     else
00763       return 0;
00764   }
00765 
00766 
00767   long MemoryTracer::getOffset(void* addr) 
00768   {
00769     AllocEntry* m = mlist.front();
00770     bool found=false;
00771 
00772     long offset=-1;
00773     while (m && !found) {
00774       if (!m->isExternal()) {
00775         
00776         if ((m->address <= addr) && (addr < m->address+Filling*2+m->size)) { // it's within greater area 
00777           
00778           if ((m->address+Filling <= addr) && (addr < m->address+Filling+m->size)) { // within valid area? 
00779             found=true;
00780             offset = (Byte*)addr - (m->address+Filling);
00781           }
00782         }
00783       } else { // external 
00784         if ((m->address <= addr) && (addr < m->address+m->size)) {
00785           found = true;
00786           offset = (Byte*)addr - m->address;
00787         }
00788       }
00789       
00790       m = m->next;
00791     }
00792     
00793     return offset;
00794   }
00795 
00796 
00797   // free all allocated memory
00798   bool MemoryTracer::cleanup()
00799   {
00800 #ifdef _TRACEMEMORY_
00801     printf("(Cleanup: %ld allocations, %ld deallocations, %ld integrity checks performed)\n",allocCount,deallocCount,checkCount);
00802 
00803     char errstr[256];
00804     if (mlist.size() > 0) {
00805       dump();
00806       sprintf(errstr,"Freeing %d leaked memory allocations",mlist.size());
00807       Logerr(errstr);
00808       while (mlist.size() > 0) {
00809         AllocEntry* m = mlist.pop_front();
00810         if (m->name != 0) free(m->name);
00811         if (!m->isExternal()) free(m->address);
00812         free(m);
00813       }
00814     }
00815 
00816     while (dlist.size() > 0) {
00817       AllocEntry* m = dlist.pop_front();
00818       if (m->name != 0) free(m->name);
00819       free(m);
00820     }
00821 #endif    
00822     cleanedUp = true;
00823     return true;
00824   }
00825 
00826   // display allocated memory areas
00827   void MemoryTracer::dump() 
00828   {
00829 #ifdef _TRACEMEMORY_
00830     AllocEntry* m = mlist.front();
00831     
00832     checkAllMemory();
00833     
00834     printf("\nValid memory regions (%d):\n",mlist.size());
00835     printf("(%ld allocations, %ld deallocations, %ld integrity checks performed)\n",allocCount,deallocCount,checkCount);
00836     printf("----------------------------------------------------------------------------------\n");
00837     
00838     while(m) {
00839       if (m->isOK()) {
00840         printf("%52s: %10x (%9d) %s %s\n", (m->name==0)?"<unnamed>":m->name, Int(m->address+Filling), 
00841                m->size,(m->isArray()?"[]":"  "),m->isExternal()?"EXT":"");
00842         m = m->next;
00843       }
00844       else {
00845         Logerr("Memory allocation structure corrupted!");
00846         m=0;
00847       }
00848     }
00849     printf("----------------------------------------------------------------------------------\n\n");
00850 #endif
00851   }
00852 
00853 
00854   // display allocated memory areas reciently deallocated
00855   void MemoryTracer::dumpDeallocated() 
00856   {
00857 #ifdef _TRACEMEMORY_
00858     AllocEntry* m = dlist.front();
00859     
00860     printf("\nReciently deallocated memory regions (%d):\n",dlist.size());
00861     printf("(%ld allocations, %ld deallocations, %ld integrity checks performed)\n",allocCount,deallocCount,checkCount);
00862     printf("----------------------------------------------------------------------------------\n");
00863     
00864     while(m) {
00865       if (m->isOK()) {
00866         printf("%52s: %10x (%9d) %s %s\n", (m->name==0)?"<unnamed>":m->name, Int(m->address+Filling), 
00867                m->size,(m->isArray()?"[]":"  "),m->isExternal()?"EXT":"");
00868         m = m->next;
00869       }
00870       else {
00871         Logerr("Memory allocation structure corrupted!");
00872         m=0;
00873       }
00874     }
00875     printf("----------------------------------------------------------------------------------\n\n");
00876 #endif
00877   }
00878 
00879 
00880   // display named allocated memory areas only
00881   void MemoryTracer::dumpNamed() 
00882   {
00883 #ifdef _TRACEMEMORY_
00884     AllocEntry* m = mlist.front();
00885     
00886     checkAllMemory();
00887     
00888     printf("\nValid named memory regions (from %d total):\n",mlist.size());
00889     printf("(%ld allocations, %ld deallocations, %ld integrity checks performed)\n",allocCount,deallocCount,checkCount);
00890     printf("----------------------------------------------------------------------------------\n");
00891     
00892     int e=0;
00893     while(m) {
00894       if (m->isOK()) {
00895         if (m->name!=0)
00896           printf("%4d %52s: %10x (%9d) %s %s\n",e,m->name, (Int)(m->address+Filling),
00897                  m->size,(m->isArray()?"[]":"  "),m->isExternal()?"EXT":"");
00898         ++e; 
00899         m = m->next;
00900       }
00901       else {
00902         Logerr("Memory allocation structure corrupted!");
00903         m=0;
00904       }
00905     }
00906     printf("----------------------------------------------------------------------------------\n\n");
00907 #endif    
00908   }
00909 
00910 
00911 
00912 
00913 
00914 
00915 
00916 #include <list>
00917 
00918 namespace {
00919 
00920 using base::Byte;
00921 using base::Int;
00922 
00923 std::list<Byte*> delayedDeletes;
00924 Int count = 0;
00925 Int maxDepth = 0;
00926 static const Int maxCount = 5000;
00927 
00928 void stressTestAllocatorRecurse(Int depth)
00929 {
00930   Int size = (rand() & 0xff)+1;
00931   Byte* mem = new Byte[size];
00932   ++count;
00933 
00934   if (depth > maxDepth) 
00935     maxDepth = depth;
00936 
00937   Int r = (rand() & 0xc0) >> 6; // 0-2
00938   if (r && (count < maxCount))
00939     stressTestAllocatorRecurse(depth+1);
00940 
00941   Int d = (rand() & 0x80) >> 7; // 0 or 1
00942   if (d)
00943     delete[] mem;
00944   else
00945     delayedDeletes.push_back(mem);
00946 }
00947 
00948 } // namespace
00949 
00950 using std::list;
00951 
00952 void base::stressTestAllocator()
00953 {
00954   Logfln("Performing memory allocator test...");
00955   while (count < maxCount) {
00956     stressTestAllocatorRecurse(1);
00957     MemoryTracer::checkAllMemory();
00958 
00959     // delete half of the delayed delete's
00960     base::Int c = delayedDeletes.size()/2;
00961     std::list<Byte*>::iterator s = delayedDeletes.begin();
00962     std::list<Byte*>::iterator end = delayedDeletes.end();
00963     while (s != end) {
00964       if (--c > 0) 
00965         if (*s) delete[] (*s);
00966       (*s) = 0;
00967       ++s;
00968     }
00969     MemoryTracer::checkAllMemory();
00970   }
00971   
00972   // delete remaining delayed delete's
00973   std::list<Byte*>::const_iterator s = delayedDeletes.begin();
00974   std::list<Byte*>::const_iterator end = delayedDeletes.end();
00975   while (s != end) {
00976     if (*s) delete[] (*s);
00977     ++s;
00978   }
00979   delayedDeletes.clear();
00980   MemoryTracer::checkAllMemory();
00981 
00982   Logfln("Done. " << count << " allocations (" << maxDepth << " recursion depth)")
00983 }
00984 
00985 
00986 
00987 
00988 
00989 // global
00990 
00991 
00992 #ifdef _TRACEMEMORY_
00993 
00994 void* operator new(size_t size) throw (std::bad_alloc)
00995 {
00996   void* p = base::MemoryTracer::allocate(size,"");
00997 #ifdef _TRACEOUTPUT_
00998 #ifndef _NAMEDOUTPUTONLY_
00999   fprintf(stderr, "new(%d) = %x\n",size,Int(p));
01000 #endif
01001 #endif
01002   return p;
01003 }
01004 
01005 void* operator new(size_t size, const string& name) throw (std::bad_alloc)
01006 {
01007   void* p = base::MemoryTracer::allocate(size,name);
01008 #ifdef _TRACEOUTPUT_
01009   fprintf(stderr, "new(%d,%s) = %x\n",size,name.c_str(),Int(p));
01010 #endif
01011   return p;
01012 }
01013 
01014 void operator delete(void* p) throw()
01015 {
01016 #ifdef _TRACEOUTPUT_
01017   char* name = base::MemoryTracer::getName(p);
01018   if (name)
01019     fprintf(stderr, "delete[](%x,%s)\n", Int(p),name);
01020 #ifndef _NAMEDOUTPUTONLY_
01021   else
01022     fprintf(stderr, "delete[](%x)\n", Int(p));
01023 #endif
01024 #endif
01025   base::MemoryTracer::release(p);
01026 }
01027 
01028 
01029 void* operator new[](size_t size) throw (std::bad_alloc)
01030 {
01031   void* p = base::MemoryTracer::allocate(size,"",true);
01032 #ifdef _TRACEOUTPUT_
01033 #ifndef _NAMEDOUTPUTONLY_
01034   fprintf(stderr, "new[](%d) = %x\n",size,Int(p));
01035 #endif
01036 #endif
01037   return p;
01038 }
01039 
01040 void* operator new[](size_t size, const string& name) throw (std::bad_alloc)
01041 {
01042   void* p = base::MemoryTracer::allocate(size,name,true);
01043 #ifdef _TRACEOUTPUT_
01044   fprintf(stderr, "new[](%d,%s) = %x\n",size,name.c_str(),Int(p));
01045 #endif
01046   return p;
01047 }
01048 
01049 void operator delete[](void* p) throw()
01050 {
01051 #ifdef _TRACEOUTPUT_
01052   char* name = base::MemoryTracer::getName(p);
01053   if (name)
01054     fprintf(stderr, "delete[](%x,%s)\n", Int(p),name);
01055 #ifndef _NAMEDOUTPUTONLY_
01056   else
01057     fprintf(stderr, "delete[](%x)\n", Int(p));
01058 #endif
01059 #endif
01060   base::MemoryTracer::release(p,true);
01061 }
01062 
01063 #endif

Generated on Thu Jul 29 15:56:08 2004 for OpenSim by doxygen 1.3.6