Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Helpers/MemDebug.cpp

    rf2f4ae rfc6053  
    66 */
    77
     8#ifndef NDBEGUG
     9#ifndef NO_MEMDEBUG
     10
    811#include <iostream>
    912#include <cstdlib>
     
    1114#include <boost/thread.hpp>
    1215
     16#ifdef __GNUC__
     17#include <execinfo.h>
     18#include <cxxabi.h>
     19#endif
     20
    1321using namespace std;
    1422
    15 #ifndef NDBEGUG
    16 #ifndef NO_MEMDEBUG
     23// we need our own low level mutexex, since we cannot assure the time of construction and destruction
     24// otherwise
     25#if defined(unix) || defined(__unix)
     26
     27#include <pthread.h>
     28#include <cassert>
     29#define mutex_t    pthread_mutex_t
     30#define mutex_init PTHREAD_MUTEX_INITIALIZER
     31#define mutex_lock(mtx) \
     32  do{\
     33    int res = pthread_mutex_lock(&(mtx));\
     34    assert(!res && "Could not lock mutex!");\
     35  }while(0)
     36
     37#define mutex_unlock(mtx) \
     38  do{\
     39    int res = pthread_mutex_unlock(&(mtx));\
     40    assert(!res && "Could not unlock mutex!");\
     41  }while(0)
     42
     43#else
     44# error "No thread structure defined for this plattform..."
     45#endif
    1746
    1847namespace Memory {
     
    3463      char file[length+1];
    3564      int line;
     65#ifdef __GNUC__  // function tracking only works with GCC
     66      // function names can get looooong
     67      enum {length2 = 256};
     68      char function[length2+1];
     69#endif
    3670      size_t nbytes;
    3771      bool isUsed;
     
    4478  };
    4579
    46   boost::mutex memorylock;
     80
     81  mutex_t memorylock = mutex_init;
    4782
    4883  // start and end of the doubly-linked list
     
    96131    for(entry_t *pos=begin;pos;pos=pos->next){
    97132      cout << "\nChunk of " << pos->info.nbytes << " bytes" << " still available" << endl;
     133#ifdef __GNUC__
     134      cout << "Chunk reserved at: " << pos->info.function
     135           << " (" << pos->info.file << ":" << pos->info.line  << ")" << endl;
     136#else
    98137      cout << "Chunk reserved at: " << pos->info.file << ":" << pos->info.line << endl;
    99     }
     138#endif
     139    }
     140  }
     141
     142  // Adds an entry to the linked list
     143  void addEntry(entry_t *entry){
     144    // check if the entry is already in the list
     145    if(!entry->isIgnored)
     146      return;
     147
     148    mutex_lock(Memory::memorylock);
     149
     150    entry->next=0;            // the created block is last in the list
     151    entry->prev=Memory::end;  // the created block is last in the list
     152    if(!Memory::begin){
     153      // the list was empty... start a new one
     154      Memory::begin=entry;
     155    }
     156    else {
     157      // other blocks present... we can add to the last one
     158      Memory::end->next=entry;
     159    }
     160    Memory::end=entry;
     161
     162    // update some global info
     163    Memory::state  += entry->info.nbytes;
     164    if(Memory::state>Memory::max){
     165        Memory::max = Memory::state;
     166    }
     167    ++Memory::allocs;
     168    // done with the list... it is safe to unlock now
     169    mutex_unlock(Memory::memorylock);
     170    entry->isIgnored = false;
    100171  }
    101172
     
    105176      return;
    106177
     178    mutex_lock(memorylock);
    107179    if(entry->prev){
    108180      entry->prev->next = entry->next;
     
    120192      end = entry->prev;
    121193    }
     194    Memory::state  -= entry->info.nbytes;
     195    mutex_unlock(memorylock);
    122196    entry->isIgnored = true;
    123     Memory::state  -= entry->info.nbytes;
     197
    124198  }
    125199
     
    130204    deleteEntry(entry);
    131205  }
    132 }
    133 
    134 void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
    135 
    136   // we need to lock, so that no one changes the linked list while we are here
    137   boost::mutex::scoped_lock guard(Memory::memorylock);
     206
     207#ifdef __GNUC__
     208  // this function let's us find the caller's name
     209  char* getCaller(){
     210    // stack looks like this:
     211    // getCaller();
     212    // operator new();
     213    // function_we_are_looking_for(); <-
     214    const size_t max_depth = 3;
     215    void* stack_addrs[max_depth];
     216    size_t stack_depth;
     217    char **stack_strings=0;
     218    const char *func_name=0;
     219    const char *toplevel = "Global scope";
     220    char *retval=0;
     221
     222    // get the backtrace, depth three
     223    stack_depth   = backtrace(stack_addrs,max_depth);
     224    stack_strings = backtrace_symbols(stack_addrs, stack_depth);
     225    // used later for demangling
     226    // reserved here, so we can free it unconditionally
     227    char *dm_function = static_cast<char*>(malloc(entry_t::info_t::length2));
     228    if(!dm_function){
     229      // malloc failed... we are out of luck
     230      throw std::bad_alloc();
     231    }
     232
     233    // see if we found our function name
     234    if(stack_depth==max_depth){
     235      // find the mangled function name
     236      char *begin = stack_strings[max_depth-1];
     237      // function name starts with a (
     238      while(*begin && *begin!='(') ++begin;
     239      char *end=begin;
     240      while(*end && *end!='+') ++end;
     241
     242      // see if we found our function name
     243      if(*begin && *end){
     244        *begin++ = 0;
     245        *end = 0;
     246        // use the C++ demangler
     247
     248        size_t sz = entry_t::info_t::length2;
     249        int status;
     250        char *func_ret = abi::__cxa_demangle(begin, dm_function, &sz, &status);
     251        if(func_ret){
     252          // abi might have realloced...
     253          dm_function = func_ret;
     254          func_name = dm_function;
     255        }
     256        else{
     257          // demangling failed... get the function name without demangling
     258          func_name = begin;
     259        }
     260      }
     261      else{
     262        // function name not found... get the whole line
     263        func_name = stack_strings[max_depth-1];
     264      }
     265
     266    }
     267    else{
     268      func_name = toplevel;
     269    }
     270
     271    // now we copy the desired function name
     272    if((retval = static_cast<char*>(malloc(strlen(func_name)+1)))){
     273      // we know that the string will fit, so strcpy is safe here
     274      strcpy(retval,func_name);
     275    }
     276    else{
     277      free(stack_strings); // malloc()ed by backtrace_symbols
     278      free(dm_function);
     279      // uh-uh ... seems we are out of luck for allocations now
     280      throw std::bad_alloc();
     281    }
     282    free(dm_function);
     283    free(stack_strings); // malloc()ed by backtrace_symbols
     284    return retval;
     285  }
     286#endif
     287}
     288
     289#ifdef __GNUC__
     290
     291void *operator new(size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
    138292
    139293  // to avoid allocations of 0 bytes if someone screws up
     
    153307  }
    154308
    155   // we got the space, so update the global info
    156   Memory::state += nbytes;
    157   if(Memory::state>Memory::max){
    158     Memory::max = Memory::state;
    159   }
    160   Memory::allocs++;
     309  // build the entry in front of the space
     310  Memory::entry_t *entry = (Memory::entry_t*) res;
     311  memset(res,0,entrySpace);
     312  entry->info.nbytes = nbytes;
     313  entry->info.isUsed = true;
     314  strncpy(entry->info.file,file,Memory::entry_t::info_t::length);
     315  entry->info.file[Memory::entry_t::info_t::length] = '\0';
     316  entry->info.line=line;
     317  strncpy(entry->info.function,func,Memory::entry_t::info_t::length2);
     318  entry->info.function[Memory::entry_t::info_t::length2] = '\0';
     319  // the space starts behind the info
     320  entry->info.location = (char*)res + entrySpace;
     321
     322  // mark the block as not in the list (will be changed by addEntry)
     323  entry->isIgnored = true;
     324  Memory::addEntry(entry);
     325
     326  // get the checksum...
     327  entry->checksum = Memory::calcChecksum(&entry->info);
     328
     329  // ok, space is prepared... the user can have it.
     330  // the rest (constructor, deleting when something is thrown etc)
     331  // is handled automatically
     332  return entry->info.location;
     333}
     334
     335#else
     336
     337void *operator new(size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
     338
     339  // to avoid allocations of 0 bytes if someone screws up
     340  // allocation with 0 byte size are undefined behavior, so we are
     341  // free to handle it this way
     342  if(!nbytes) {
     343    nbytes = 1;
     344  }
     345
     346  // get the size of the entry, including alignment
     347  static const size_t entrySpace = Memory::doAlign(sizeof(Memory::entry_t));
     348
     349  void *res;
     350  if(!(res=malloc(entrySpace + nbytes))){
     351    // new must throw, when space is low
     352    throw std::bad_alloc();
     353  }
    161354
    162355  // build the entry in front of the space
     
    171364  entry->info.location = (char*)res + entrySpace;
    172365
    173   // add the entry at the end of the list
    174   entry->next=0;            // the created block is last in the list
    175   entry->prev=Memory::end;  // the created block is last in the list
    176   if(!Memory::begin){
    177     // the list was empty... start a new one
    178     Memory::begin=entry;
    179   }
    180   else {
    181     // other blocks present... we can add to the last one
    182     Memory::end->next=entry;
    183   }
    184   Memory::end=entry;
     366  // mark the block as not in the list (will be changed by addEntry)
     367  entry->isIgnored = true;
     368  Memory::addEntry(entry);
    185369
    186370  // get the checksum...
     
    196380}
    197381
     382#endif
     383
    198384void *operator new(size_t nbytes) throw(std::bad_alloc) {
    199385  // Just forward to the other operator, when we do not know from
    200386  // where the allocation came
     387#ifdef __GNUC__
     388  // this might throw bad_alloc
     389  char *caller = Memory::getCaller();
     390  void* retval = 0;
     391
     392  // if this throws, we have to clean up the caller anyway
     393  try{
     394    retval = operator new(nbytes,"Unknown",0,caller);
     395  }
     396  catch(...)
     397  {
     398    free(caller); // malloc()ed by Memory::getCaller();
     399    throw;
     400  }
     401  free(caller); // malloc()ed by Memory::getCaller();
     402  return retval;
     403#else
    201404  return operator new(nbytes,"Unknown",0);
    202 }
     405#endif
     406}
     407
     408#ifdef __GNUC__
     409
     410void *operator new[] (size_t nbytes,const char* file, int line, const char* func) throw(std::bad_alloc) {
     411  // The difference between new and new[] is just for compiler bookkeeping.
     412  return operator new(nbytes,file,line,func);
     413}
     414
     415#else
    203416
    204417void *operator new[] (size_t nbytes,const char* file, int line) throw(std::bad_alloc) {
     
    207420}
    208421
     422#endif
     423
    209424void *operator new[] (size_t nbytes) throw(std::bad_alloc) {
    210425  // Forward again
     426#ifdef __GNUC__
     427  // this might throw bad_alloc
     428    char *caller = Memory::getCaller();
     429    void *retval=0;
     430
     431    // if this throws, we have to clean up the caller anyway
     432    try{
     433      retval = operator new[] (nbytes,"Unknown",0,caller);
     434    }
     435    catch(...)
     436    {
     437      free(caller); // malloc()ed by Memory::getCaller();
     438      throw;
     439    }
     440    free(caller); // malloc()ed by Memory::getCaller();
     441    return retval;
     442#else
    211443  return operator new[] (nbytes,"Unknown",0);
     444#endif
    212445}
    213446
     
    217450    return;
    218451  }
    219 
    220   // we need to lock, so the linked list does not changed while we are in here
    221   boost::mutex::scoped_lock guard(Memory::memorylock);
    222452
    223453  // get the size for the entry, including alignment
Note: See TracChangeset for help on using the changeset viewer.