#include "gb-include.h" #include "Mem.h" #include // setrlimit() #include // setrlimit() #include "Threads.h" #include "SafeBuf.h" #include "PingServer.h" //#include "MemPoolVar.h" //#include "malloc.h" //#include "Stats.h" // put me back //#define _EFENCE_ // uncomment this for _EFENCE_ to do underflow checks instead of the // default overflow checks //#define _CHECKUNDERFLOW_ // only Mem.cpp can call ::malloc, everyone else must call mmalloc() so // we can keep tabs on memory usage. in Mem.h we #define this to be coreme() #undef malloc #undef calloc #undef realloc // from malloc.c (dlmalloc) //void *dlmalloc(size_t); //void dlfree(void*); //void* dlcalloc(size_t, size_t); //void* dlrealloc(void*, size_t); #include "malloc.c" #define sysmalloc ::malloc #define syscalloc ::calloc #define sysrealloc ::realloc #define sysfree ::free /* // try using dlmalloc to see how it cores #define sysmalloc dlmalloc #define syscalloc dlcalloc #define sysrealloc dlrealloc #define sysfree dlfree */ // allocate an extra space before and after the allocated memory to try // to catch sequential buffer underruns and overruns. if the write is way // beyond this padding radius, chances are it will seg fault right then and // there because it will hit a different PAGE, to be more sure we could // make UNDERPAD and OVERPAD PAGE bytes, although the overrun could still write // to another allocated area of memory and we can never catch it. #ifdef _EFENCE_ #define UNDERPAD 0 #define OVERPAD 0 #else #define UNDERPAD 4 #define OVERPAD 4 #endif #define MAGICCHAR 0xda class Mem g_mem; extern bool g_isYippy; bool freeCacheMem(); #ifdef _EFENCE_ static void *getElecMem ( long size ) ; static void freeElecMem ( void *p ) ; #endif /* static long s_mutexLockAvail = 1; // usually we can use the UdpServer ptr as the pid, or if the main process, // then just use 0 void mutexLock ( ) { //log("gb: mutex lock"); loop: //log("gb: mutex lock loop 1"); // wait for the lock if already taken while ( s_mutexLockAvail != 1 ) sched_yield(); //log("gb: mutex lock loop 2"); // . it now *seems* to be available, i.e. equal to 1 so try to get it // . similar to atomic.h in kernel source // Atomically decrements @s_mutexLockAvail by 1 // and returns true if the result is zero, or false for all // other cases. Note that the guaranteed // useful range of an atomic_t is only 24 bits. unsigned char c; __asm__ __volatile__( "lock;" "decl %0; sete %1" :"=m" (s_mutexLockAvail), "=qm" (c) :"m" (s_mutexLockAvail) : "memory"); //log("gb c=%li mutexAvail=%li",c,s_mutexLockAvail); // if c is 0, we got the lock, otherwise, keep trying if ( c != 1 ) { //log("gb: failed to get lock. retrying."); goto loop; } // log("gb: got mutex lock"); s_mutexLockAvail = 0; } void mutexUnlock ( ) { //if ( s_mutexLockAvail != 0 ) { // log("gb: mutex unlock HEY lock=%li",s_mutexLockAvail); // //char *xx = NULL; *xx = 0; //} // a single instruction is atomic __asm__ __volatile__( //"lock;" "movl $1,%0;" :"=m" (s_mutexLockAvail) :"m" (s_mutexLockAvail) : "memory"); if ( s_mutexLockAvail != 1 ) logf(LOG_INFO,"gb: mutex unlock lock=%li",s_mutexLockAvail); } */ // if we alloc too much in one call, pthread_create() fails for some reason //#define MAXMEMPERCALL (256*1024*1024-1) //[mwells@lenny c]$ echo "inuse on" > .psrc //[mwells@lenny c]$ ./slowleak //** Insure messages will be written to insra ** //[mwells@lenny c]$ tca -X // the thread lock //static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; // make it big for production machines //#define DMEMTABLESIZE (1024*602) // there should not be too many mallocs any more #define DMEMTABLESIZE (1024*302) //#define DMEMTABLESIZE (1024*202) // and small for local machine //#define DMEMTABLESIZE (1024*50) // a table used in debug to find mem leaks static void **s_mptrs ; static long *s_sizes ; static char *s_labels; static char *s_isnew; static long s_n = 0; static bool s_initialized = 0; // our own memory manager //static MemPoolVar s_pool; void operator delete ( void *ptr ) { // now just call this g_mem.gbfree ( (char *)ptr , -1 , NULL ); } void operator delete [] ( void *ptr ) { // now just call this g_mem.gbfree ( ((char *)ptr-4) , -1 , NULL ); } #define MINMEM 6000000 //#define MINMEM 0 // caution -- put {}'s around the "new" //#define new(X) new X; g_mem.addMem(X,sizeof(*X),"new"); void Mem::addnew ( void *ptr , long size , const char *note ) { // 1 --> isnew addMem ( ptr , size , note , 1 ); } void Mem::delnew ( void *ptr , long size , const char *note ) { // we don't need to use mdelete() if checking for leaks is enabled // because the size of the allocated mem is in the hash table under // s_sizes[]. and the delete() operator is overriden below to // catch this. return; /* // don't let electric fence zap us //if ( size == 0 && ptr==(void *)0x7fffffff) return; if ( size == 0 ) return; // watch out for bad sizes if ( size < 0 ) { log(LOG_LOGIC,"mem: delete(%li): Bad size.", size ); return; } // debug if ( size > MINMEM ) log(LOG_INFO,"mem: delete(%li): %s.",size,note); // count it if ( size > 0 ) { m_used -= size; m_numAllocated--; } */ } // this must be defined for newer libc++ //int bad_alloc ( ) { return 1; }; // . global override of new and delete operators // . seems like constructor and destructor are still called // . just use to check if enough memory // . before this just called mmalloc which sometimes returned NULL which // would cause us to throw an unhandled signal. So for now I don't // call mmalloc since it is limited in the mem it can use and would often // return NULL and set g_errno to ENOMEM void * operator new (size_t size) throw (std::bad_alloc) { // don't let electric fence zap us if ( size == 0 ) return (void *)0x7fffffff; // . fail randomly // . good for testing if we can handle out of memory gracefully //static long s_mcount = 0; //s_mcount++; //if ( s_mcount > 57 && (rand() % 1000) < 2 ) { if ( g_conf.m_testMem && (rand() % 100) < 2 ) { g_errno = ENOMEM; log("mem: new-fake(%i): %s",size, mstrerror(g_errno)); throw std::bad_alloc(); // return NULL; } } //char unlock = true; //if ( ! g_stats.m_gotLock || g_threads.amThread() ) mutexLock(); //else unlock = false; // don't go over max if ( g_mem.m_used + size >= g_mem.m_maxMem && g_mem.m_maxMem > 1000000 ) { log("mem: new(%i): Out of memory.", size ); //if ( unlock ) mutexUnlock(); throw std::bad_alloc(); //throw 1; } #ifdef _EFENCE_ void *mem = getElecMem(size); #else //void *mem = dlmalloc ( size ); void *mem = sysmalloc ( size ); #endif long memLoop = 0; newmemloop: //void *mem = s_pool.malloc ( size ); if ( ! mem && size > 0 ) { g_errno = errno; log("mem: new(%i): %s",size, mstrerror(g_errno)); //if ( unlock ) mutexUnlock(); throw std::bad_alloc(); //throw 1; //return NULL; } if ( (unsigned long)mem < 0x00010000 ) { #ifdef _EFENCE_ void *remem = getElecMem(size); #else void *remem = sysmalloc(size); #endif log ( LOG_WARN, "mem: Caught low memory allocation at %08lx, " "reallocated to %08lx", (unsigned long)mem, (unsigned long)remem ); #ifdef _EFENCE_ freeElecMem (mem); #else sysfree(mem); #endif mem = remem; if ( memLoop > 100 ) { log ( LOG_WARN, "mem: Attempted to reallocate low " "memory allocation 100 times, " "aborting and returning ENOMEM." ); g_errno = ENOMEM; //if ( unlock ) mutexUnlock(); throw std::bad_alloc(); } goto newmemloop; } g_mem.addMem ( mem , size , "TMPMEM" , 1 ); //if ( unlock ) mutexUnlock(); return mem; } //WARNING: Use this construct only when your datatype has a destructor! //the compiler checks to see if a destructor is defined, if it is it //will add 4 bytes to your requested size and put the number of objects //in those bytes and returns a mem ptr at the malloced address + 4. //this can screw up the subsequent call to addmem because the size and //ptrs are off. void * operator new [] (size_t size) throw (std::bad_alloc) { // don't let electric fence zap us if ( size == 0 ) return (void *)0x7fffffff; // . fail randomly // . good for testing if we can handle out of memory gracefully //static long s_count = 0; //s_count++; //if ( s_count > 3000 && (rand() % 100) < 2 ) { // g_errno = ENOMEM; // log("mem: new-fake(%i): %s",size, mstrerror(g_errno)); // throw bad_alloc(); // // return NULL; } //} // don't go over max if ( g_mem.m_used + size >= g_mem.m_maxMem && g_mem.m_maxMem > 1000000 ) { log("mem: new(%i): Out of memory.", size ); throw bad_alloc(); //throw 1; } #ifdef _EFENCE_ void *mem = getElecMem(size); #else //void *mem = dlmalloc ( size ); void *mem = sysmalloc ( size ); #endif long memLoop = 0; newmemloop: //void *mem = s_pool.malloc ( size ); if ( ! mem && size > 0 ) { g_errno = errno; log("mem: new(%i): %s",size, mstrerror(g_errno)); //if ( unlock ) mutexUnlock(); throw std::bad_alloc(); //throw 1; //return NULL; } if ( (unsigned long)mem < 0x00010000 ) { #ifdef _EFENCE_ void *remem = getElecMem(size); #else void *remem = sysmalloc(size); #endif log ( LOG_WARN, "mem: Caught low memory allocation at %08lx, " "reallocated to %08lx", (long)mem, (long)remem ); #ifdef _EFENCE_ freeElecMem (mem); #else sysfree(mem); #endif mem = remem; if ( memLoop > 100 ) { log ( LOG_WARN, "mem: Attempted to reallocate low " "memory allocation 100 times, " "aborting and returning ENOMEM." ); g_errno = ENOMEM; //if ( unlock ) mutexUnlock(); throw std::bad_alloc(); } goto newmemloop; } //offset by 4 because that is metadata describing the number of objs //in the array g_mem.addMem ( (char*)mem+4 , size-4, "TMPMEM" , 1 ); //if ( unlock ) mutexUnlock(); return mem; } Mem::Mem() { m_used = 0; // assume large max until this gets set for real m_maxMem = 50000000; m_numAllocated = 0; m_numTotalAllocated = 0; m_maxAlloc = 0; m_maxAllocBy = ""; m_maxAlloced = 0; m_memtablesize = DMEMTABLESIZE; m_stackStart = NULL; // shared mem used m_sharedUsed = 0LL; } Mem::~Mem() { if ( getUsedMem() == 0 ) return; //log(LOG_INIT,"mem: Memory allocated now: %li.\n", getUsedMem() ); // this is now called from main.cpp::allExit() because it freezes // up in printMem()'s call to sysmalloc() for some reason // printMem(); } //long Mem::getUsedMem () { return 0; }; //return mallinfo().usmblks; }; long long Mem::getAvailMem () { return 0; }; //long long Mem::getMaxAlloced () { return 0; }; long long Mem::getMaxMem () { return g_conf.m_maxMem; } long Mem::getNumChunks () { return 0; }; // process id of the main process pid_t s_pid = (pid_t) -1; void Mem::setPid() { s_pid = getpid(); if(s_pid == -1 ) { log("monitor: bad s_pid"); char *xx=NULL;*xx=0; } } pid_t Mem::getPid() { return s_pid; } bool Mem::init ( long long maxMem ) { // set main process pid s_pid = getpid(); // . don't swap our memory out, man... // . damn, linux 2.4.17 seems to crash the kernel sometimes w/ this //if ( mlockall( MCL_CURRENT | MCL_FUTURE ) == -1 ) { // log("Mem::init: mlockall: %s" , strerror(errno) ); // errno = 0; //} m_maxMem = maxMem; // set it //struct rlimit lim; //lim.rlim_max = maxMem; //setrlimit ( RLIMIT_AS , &lim ); // ulimit -v // note log(LOG_INIT,"mem: Max memory usage set to %lli bytes.", maxMem); // warning msg if ( g_conf.m_detectMemLeaks ) log(LOG_INIT,"mem: Memory leak checking is enabled."); #ifdef _EFENCE_ log(LOG_INIT,"mem: using electric fence!!!!!!!"); #endif // init or own malloc stuff in malloc.c (from doug leay) //if ( mdw_init_sbrk ( maxMem ) ) return true; // bitch //return log("Mem::init: failed to malloc %li bytes", maxMem); return true; } //bool Mem::reserveMem ( long long bytesToReserve ) { // TODO: use sbrk()? // char *s = (char *) malloc ( bytesToReserve ); // if ( s ) { free ( s ); return true; } // TODO: try smaller blocks // return false; //} // this is called by C++ classes' constructors to register mem void Mem::addMem ( void *mem , long size , const char *note , char isnew ) { //validate(); // sanity check if ( g_inSigHandler ) { log(LOG_LOGIC,"mem: In sig handler."); char *xx = NULL; *xx = 0; } // debug msg (mdw) //char bb[100]; //bb[0]=0; //if ( strcmp(note,"UdpSlot")== 0 ) { // unsigned char c = (*(unsigned char *)mem) & 0x3f; // sprintf(bb," msgType=0x%lx",(long)c); //} if ( g_conf.m_logDebugMem ) log("mem: add %08lx %li bytes (%lli) (%s)", (long)mem,size,m_used,note); //if ( strcmp(note,"RdbList") == 0 ) // log("mem: freelist%08lx %libytes (%s)",(long)mem,size,note); // check for breech after every call to alloc or free in order to // more easily isolate breeching code.. this slows things down a lot // though. if ( g_conf.m_logDebugMem ) printBreeches(1); // copy the magic character, iff not a new() call if ( size == 0 ) { char *xx = NULL; *xx = 0; } // don't add 0 bytes //if ( size == 0 ) return; // sanity check if ( size < 0 ) { log("mem: addMem: Negative size."); return; } // sanity check -- for machines with > 4GB ram? if ( (unsigned long)mem + (unsigned long)size < (unsigned long)mem ) { log(LOG_LOGIC,"mem: Kernel returned mem at %08lx of size %li " "which would wrap. Bad kernel.",(long)mem,(long)size); char *xx = NULL; *xx = 0; } if ( ! isnew ) { for ( long i = 0 ; i < UNDERPAD ; i++ ) ((char *)mem)[0-i-1] = MAGICCHAR; for ( long i = 0 ; i < OVERPAD ; i++ ) ((char *)mem)[0+size+i] = MAGICCHAR; } // hey! if ( s_pid == -1 && m_numTotalAllocated >1000 ) { char *xx=NULL;*xx=0;} // threads can't be here! if ( s_pid != -1 && getpid() != s_pid ) { log("mem: addMem: Called from thread."); sleep(50000); //char *p = NULL; //*p = 1; char *xx = NULL; *xx = 0; } // if no label! if ( ! note[0] ) log(LOG_LOGIC,"mem: addmem: NO note."); // lock for threads //pthread_mutex_lock ( &s_lock ); // return NULL if we'd go over our limit //if ( getUsedMem() + size > s_maxMem ) { // log("Mem::addMem: max mem limit breeched"); // sleep(50000); // return; //} // clear mem ptrs if this is our first call if ( ! s_initialized ) { s_mptrs = (void **)sysmalloc ( m_memtablesize*sizeof(void *)); s_sizes = (long *)sysmalloc ( m_memtablesize*sizeof(long )); s_labels = (char *)sysmalloc ( m_memtablesize*16 ); s_isnew = (char *)sysmalloc ( m_memtablesize ); if ( ! s_mptrs || ! s_sizes || ! s_labels || ! s_isnew ) { if ( s_mptrs ) sysfree ( s_mptrs ); if ( s_sizes ) sysfree ( s_sizes ); if ( s_labels ) sysfree ( s_labels ); if ( s_isnew ) sysfree ( s_labels ); log("mem: addMem: Init failed. Disabling checks."); g_conf.m_detectMemLeaks = false; return; } s_initialized = true; memset ( s_mptrs , 0 , 4 * m_memtablesize ); } // try to add ptr/size/note to leak-detecting table if ( (long)s_n > (long)m_memtablesize ) { log("mem: addMem: No room in table for %s size=%li.", note,size); // unlock for threads //pthread_mutex_unlock ( &s_lock ); return; } // hash into table unsigned long u = (unsigned long)mem * (unsigned long)0x4bf60ade; unsigned long h = u % (unsigned long)m_memtablesize; // chain to an empty bucket long count = (long)m_memtablesize; while ( s_mptrs[h] ) { // if an occupied bucket as our same ptr then chances are // we freed without calling rmMem() and a new addMem() got it if ( s_mptrs[h] == mem ) { // if we are being called from addnew(), the // overloaded "operator new" function above should // have stored a temp ptr in here... allow that, it // is used in case an engineer forgets to call // mnew() after calling new() so gigablast would never // realize that the memory was allocated. if ( s_sizes[h] == size && s_labels[h*16+0] == 'T' && s_labels[h*16+1] == 'M' && s_labels[h*16+2] == 'P' && s_labels[h*16+3] == 'M' && s_labels[h*16+4] == 'E' && s_labels[h*16+5] == 'M' ) goto skipMe; log("mem: addMem: Mem already added. " "rmMem not called?"); char *xx = NULL; *xx = 0; //sleep(50000); } h++; if ( h == m_memtablesize ) h = 0; if ( --count == 0 ) { log("mem: addMem: Mem table is full."); printMem(); char *xx = NULL; *xx = 0; //sleep(50000); } } // add to debug table s_mptrs [ h ] = mem; s_sizes [ h ] = size; s_isnew [ h ] = isnew; //log("adding %li size=%li to [%li] #%li (%s)", //(long)mem,size,h,s_n,note); s_n++; // debug if ( size > MINMEM && g_conf.m_logDebugMemUsage ) log(LOG_INFO,"mem: addMem(%li): %s. ptr=0x%lx used=%lli", size,note,(long)mem,m_used); // now update used mem // we do this here now since we always call addMem() now m_used += size; m_numAllocated++; m_numTotalAllocated++; if ( size > m_maxAlloc ) { m_maxAlloc = size; m_maxAllocBy = note; } if ( m_used > m_maxAlloced ) m_maxAlloced = m_used; skipMe: long len = gbstrlen(note); if ( len > 15 ) len = 15; char *here = &s_labels [ h * 16 ]; memcpy ( here , note , len ); // make sure NULL terminated here[len] = '\0'; // unlock for threads //pthread_mutex_unlock ( &s_lock ); //validate(); } #define PRINT_TOP 40 class MemEntry { public: long m_hash; char *m_label; long m_allocated; long m_numAllocs; }; // print out the mem table // but combine allocs with the same label // sort by mem allocated bool Mem::printMemBreakdownTable ( SafeBuf* sb, char *lightblue, char *darkblue) { char *ss = ""; // make sure the admin viewing this table knows that there will be // frees in here that are delayed if electric fence is enabled. #ifdef _EFENCE_ ss = " *DELAYED FREES ENABLED*"; #endif sb->safePrintf ( "" "
" "" "\n" "" "" "" "" "" , lightblue, darkblue , ss ); long n = m_numAllocated * 2; MemEntry *e = (MemEntry *)mcalloc ( sizeof(MemEntry) * n , "Mem" ); if ( ! e ) { log("admin: Could not alloc %li bytes for mem table.", (long)sizeof(MemEntry)*n); return false; } // hash em up, combine allocs of like label together for this hash for ( long i = 0 ; i < (long)m_memtablesize ; i++ ) { // skip empty buckets if ( ! s_mptrs[i] ) continue; // get label ptr, use as a hash char *label = &s_labels[i*16]; long h = hash32n ( label ); if ( h == 0 ) h = 1; // accumulate the size long b = (unsigned long)h % n; // . chain till we find it or hit empty // . use the label as an indicator if bucket is full or empty while ( e[b].m_hash && e[b].m_hash != h ) if ( ++b >= n ) b = 0; // add it in e[b].m_hash = h; e[b].m_label = label; e[b].m_allocated += s_sizes[i]; e[b].m_numAllocs++; } // get the top 20 users of mem MemEntry *winners [ PRINT_TOP ]; long i = 0; long count = 0; for ( ; i < n && count < PRINT_TOP ; i++ ) // if non-empty, add to winners array if ( e[i].m_hash ) winners [ count++ ] = &e[i]; // compute new min long min = 0x7fffffff; long mini = -1000; for ( long j = 0 ; j < count ; j++ ) { if ( winners[j]->m_allocated > min ) continue; min = winners[j]->m_allocated; mini = j; } // now the rest must compete for ( ; i < n ; i++ ) { // if empty skip if ( ! e[i].m_hash ) continue; //if ( e[i].m_allocated > 120 && e[i].m_allocated < 2760 ) // log("hey %li", e[i].m_allocated); // skip if not a winner if ( e[i].m_allocated <= min ) continue; // replace the lowest winner winners[mini] = &e[i]; // compute new min min = 0x7fffffff; for ( long j = 0 ; j < count ; j++ ) { if ( winners[j]->m_allocated > min ) continue; min = winners[j]->m_allocated; mini = j; } } // now sort them bool flag = true; while ( flag ) { flag = false; for ( long i = 1 ; i < count ; i++ ) { // no need to swap? if ( winners[i-1]->m_allocated >= winners[i]->m_allocated ) continue; // swap flag = true; MemEntry *tmp = winners[i-1]; winners[i-1] = winners[i]; winners[i ] = tmp; } } // now print into buffer for ( long i = 0 ; i < count ; i++ ) sb->safePrintf ( "" "" "" "" "\n", winners[i]->m_label, winners[i]->m_numAllocs, winners[i]->m_allocated); sb->safePrintf ( "
" "
Mem Breakdown%s
allocatornum allocsallocated
%s%li%li
\n"); // don't forget to release this mem mfree ( e , (long)sizeof(MemEntry) * n , "Mem" ); return true; } // Relabels memory in table. Returns true on success, false on failure. // Purpose is for times when UdpSlot's buffer is not owned and freed by someone // else. Now we can verify that passed memory is freed. bool Mem::lblMem( void *mem, long size, const char *note ) { // seems to be a bad bug in this... return true; bool val = false; // Make sure we're not relabeling a NULL or dummy memory address, // if so, error then exit if( !mem ){ //log( "mem: lblMem: Mem addr (0x%08X) invalid/NULL, not " // "relabeling.", mem ); return val; } else if( (unsigned long)mem == 0x7fffffff ) { //log( "mem: lblMem: Mem addr (0x%08X) is dummy address, not " // "relabeling.", mem ); return val; } unsigned long u = (unsigned long)mem * (unsigned long)0x4bf60ade; unsigned long h = u % (unsigned long)m_memtablesize; // chain to bucket while( s_mptrs[h] ) { if( s_mptrs[h] == mem ) { if( s_sizes[h] != size ) { val = false; log( "mem: lblMem: Mem addr (0x%08X) exists, " "size is %li off.", (unsigned int)mem, s_sizes[h]-size ); break; } long len = gbstrlen(note); if ( len > 15 ) len = 15; char *here = &s_labels [ h * 16 ]; memcpy ( here , note , len ); // make sure NULL terminated here[len] = '\0'; val = true; break; } h++; if ( h == m_memtablesize ) h = 0; } if( !val ) log( "mem: lblMem: Mem addr (0x%08X) not found.", (unsigned int)mem ); return val; } // this is called by C++ classes' destructors to unregister mem bool Mem::rmMem ( void *mem , long size , const char *note ) { //validate(); // sanity check if ( g_inSigHandler ) { log(LOG_LOGIC,"mem: In sig handler 2."); char *xx = NULL; *xx = 0; } // debug msg (mdw) if ( g_conf.m_logDebugMem ) log("mem: free %08lx %libytes (%s)",(long)mem,size,note); //if ( strcmp(note,"RdbList") == 0 ) // log("mem: freelist%08lx %libytes (%s)",(long)mem,size,note); // check for breech after every call to alloc or free in order to // more easily isolate breeching code.. this slows things down a lot // though. if ( g_conf.m_logDebugMem ) printBreeches(1); // don't free 0 bytes if ( size == 0 ) return true; // hey! if ( s_pid == -1 && m_numTotalAllocated >1000 ) { char *xx=NULL;*xx=0;} // threads can't be here! if ( s_pid != -1 && getpid() != s_pid ) { log("mem: rmMem: Called from thread."); sleep(50000); // throw a bogus sig so we crash char *xx=NULL;*xx=0; //sigval_t svt; //svt.sival_int = 1; // fd; //sigqueue ( s_pid, GB_SIGRTMIN+1 , svt ) ; //return true; } // lock for threads //pthread_mutex_lock ( &s_lock ); // . hash by first hashing "mem" to mix it up some // . balance the mallocs/frees // . hash into table unsigned long u = (unsigned long)mem * (unsigned long)0x4bf60ade; unsigned long h = u % (unsigned long)m_memtablesize; // . chain to an empty bucket // . CAUTION: loops forever if no empty bucket while ( s_mptrs[h] && s_mptrs[h] != mem ) { h++; if ( h == m_memtablesize ) h = 0; } // if not found, bitch if ( ! s_mptrs[h] ) { log("mem: rmMem: Unbalanced free. " "note=%s size=%li.",note,size); // . return false for now to prevent coring // . NOTE: but if entry was not added to table because there // was no room, we really need to be decrementing m_used // and m_numAllocated here // . no, we should core otherwise it can result in some // pretty hard to track down bugs later. //return false; #ifndef _VALGRIND_ char *xx = NULL; *xx = 0; #endif //sleep(50000); // unlock for threads //pthread_mutex_unlock ( &s_lock ); return false; } // are we from the "new" operator bool isnew = s_isnew[h]; // set our size if ( size == -1 ) size = s_sizes[h]; // must be legit now if ( size <= 0 ) { char *xx=NULL;*xx=0; } // . bitch is sizes don't match // . delete operator does not provide a size now (it's -1) if ( s_sizes[h] != size ) { log( "mem: rmMem: Freeing %li should be %li. (%s)", size,s_sizes[h],note); if ( g_isYippy ) { size = s_sizes[h]; goto keepgoing; } #ifndef _VALGRIND_ char *xx = NULL; *xx = 0; #endif //sleep(50000); // unlock for threads //pthread_mutex_unlock ( &s_lock ); return false; } keepgoing: // debug if ( size > MINMEM && g_conf.m_logDebugMemUsage ) log(LOG_INFO,"mem: rmMem (%li): ptr=0x%lx %s.",size,(long)mem,note); // // we do this here now since we always call rmMem() now // // decrement freed mem m_used -= size; // new/delete does not have padding because the "new" // function can't support it right now //if ( ! isnew ) m_used -= (UNDERPAD + OVERPAD); m_numAllocated--; // check for breeches, if we don't do it here, we won't be able // to check this guy for breeches later, cuz he's getting // removed if ( ! isnew ) printBreech ( h , 1 ); // empty our bucket, and point to next bucket after us s_mptrs[h++] = NULL; // dec the count s_n--; // wrap if we need to if ( h >= m_memtablesize ) h = 0; // var decl. unsigned long k; // shit after us may has to be rehashed in case it chained over us while ( s_mptrs[h] ) { // get mem ptr in bucket #h unsigned long mem = (unsigned long)s_mptrs[h]; // find the most wanted bucket for this mem ptr u = (unsigned long)mem * (unsigned long)0x4bf60ade; k= u % (unsigned long)m_memtablesize; // if it's in it, continue if ( k == h ) { h++; continue; } // otherwise, move it back to fill the gap s_mptrs[h] = NULL; // dec count //s_n--; // if slot #k is full, chain for ( ; s_mptrs[k] ; ) if ( ++k >= m_memtablesize ) k = 0; // re-add it to table s_mptrs[k] = (void *)mem; s_sizes[k] = s_sizes[h]; s_isnew[k] = s_isnew[h]; memcpy(&s_labels[k*16],&s_labels[h*16],16); // try next bucket now h++; // wrap if we need to if ( h >= m_memtablesize ) h = 0; } //validate(); // unlock for threads //pthread_mutex_unlock ( &s_lock ); return true; } long Mem::validate ( ) { if ( ! s_mptrs ) return 1; // stock up "p" and compute total bytes alloced long long total = 0; long count = 0; for ( long i = 0 ; i < (long)m_memtablesize ; i++ ) { // skip empty buckets if ( ! s_mptrs[i] ) continue; total += s_sizes[i]; count++; } // see if it matches if ( total != m_used ) { char *xx=NULL;*xx=0; } if ( count != m_numAllocated ) { char *xx=NULL;*xx=0; } return 1; } long Mem::getMemSlot ( void *mem ) { // hash into table unsigned long u = (unsigned long)mem * (unsigned long)0x4bf60ade; unsigned long h = u % (unsigned long)m_memtablesize; // . chain to an empty bucket // . CAUTION: loops forever if no empty bucket while ( s_mptrs[h] && s_mptrs[h] != mem ) { h++; if ( h == m_memtablesize ) h = 0; } // if not found, return -1 if ( ! s_mptrs[h] ) return -1; return h; } int Mem::printBreech ( long i , char core ) { // skip if empty if ( ! s_mptrs ) return 0; if ( ! s_mptrs[i] ) return 0; // skip if isnew is true, no padding there if ( s_isnew[i] ) return 0; // if no label! if ( ! s_labels[i*16] ) log(LOG_LOGIC,"mem: NO label found."); // do not test "Stack" allocated in Threads.cpp because it // uses mprotect() which messes up the magic chars if ( s_labels[i*16+0] == 'T' && s_labels[i*16+1] == 'h' && !strcmp(&s_labels[i*16 ],"ThreadStack" ) ) return 0; char flag = 0; // check for underruns char *mem = (char *)s_mptrs[i]; char *bp = NULL; for ( long j = 0 ; j < UNDERPAD ; j++ ) { if ( (unsigned char)mem[0-j-1] == MAGICCHAR ) continue; log(LOG_LOGIC,"mem: underrun at %lx loff=%li size=%li " "i=%li note=%s", (long)mem,0-j-1,(long)s_sizes[i],i,&s_labels[i*16]); // mark it for freed mem re-use check below if ( ! bp ) bp = &mem[0-j-1]; // now scan the whole hash table and find the mem buffer // just before that! but only do this once if ( flag == 1 ) continue; unsigned long min = 0; long mink = -1; for ( long k = 0 ; k < (long)m_memtablesize ; k++ ) { // skip empties if ( ! s_mptrs[k] ) continue; // do not look at mem after us if ( (unsigned long)s_mptrs[k] >= (unsigned long)mem ) continue; // get min diff if ( mink != -1 && (unsigned long)s_mptrs[k] < min ) continue; // new winner min = (unsigned long)s_mptrs[k]; mink = k; } // now report it if ( mink == -1 ) continue; log("mem: possible breeching buffer=%s dist=%li", &s_labels[mink*16], (unsigned long)mem- ((unsigned long)s_mptrs[mink]+(unsigned long)s_sizes[mink])); flag = 1; } // check for overruns long size = s_sizes[i]; for ( long j = 0 ; j < OVERPAD ; j++ ) { if ( (unsigned char)mem[size+j] == MAGICCHAR ) continue; log(LOG_LOGIC,"mem: overrun at %lx roff=%li note=%s", (long)mem,j,&s_labels[i*16]); // mark it for freed mem re-use check below if ( ! bp ) bp = &mem[size+j]; // now scan the whole hash table and find the mem buffer // just before that! but only do this once if ( flag == 1 ) continue; unsigned long min = 0; long mink = -1; for ( long k = 0 ; k < (long)m_memtablesize ; k++ ) { // skip empties if ( ! s_mptrs[k] ) continue; // do not look at mem before us if ( (unsigned long)s_mptrs[k] <= (unsigned long)mem ) continue; // get min diff if ( mink != -1 && (unsigned long)s_mptrs[k] > min ) continue; // new winner min = (unsigned long)s_mptrs[k]; mink = k; } // now report it if ( mink == -1 ) continue; log("mem: possible breeching buffer=%s dist=%li", &s_labels[mink*16], (long)s_mptrs[mink]-((long)mem+s_sizes[i])); flag = 1; } // return now if no breach if ( flag == 0 ) return 1; // need this if ( ! bp ) { char *xx=NULL;*xx=0; } /* // // check for freed memory re-use // FreeInfo *fi = s_cursor; FreeInfo *end = s_cursorEnd; if ( ! s_looped ) end = s_cursor; for ( ; ; ) { // decrement fi--; // wrap? if ( fi < s_cursorStart ) { // do not wrap if did not loop though! if ( ! s_looped ) break; // otherwise, wrap back to top fi = s_cursorEnd - 1; } // see if contains an overwritten magic char if ( bp >= (char *)fi->m_ptr && bp < (char *)fi->m_ptr + fi->m_size ) { log("mem: reused freed buffer note=%s", fi->m_note); break; } // all done? if ( fi == s_cursor ) break; } */ if ( flag && core ) { char *xx = NULL; *xx = 0; } return 1; } // check all allocated memory for buffer under/overruns int Mem::printBreeches ( char core ) { if ( ! s_mptrs ) return 0; // do not bother if no padding at all if ( (long)UNDERPAD == 0 && (long)OVERPAD == 0 ) return 0; // loop through the whole mem table for ( long i = 0 ; i < (long)m_memtablesize ; i++ ) // only check if non-empty if ( s_mptrs[i] ) printBreech ( i , core ); return 0; } int Mem::printMem ( ) { // has anyone breeched their buffer? printBreeches ( 0 ) ; // print table entries sorted by most mem first long *p = (long *)sysmalloc ( m_memtablesize * 4 ); if ( ! p ) return 0; // stock up "p" and compute total bytes alloced long long total = 0; long np = 0; for ( long i = 0 ; i < (long)m_memtablesize ; i++ ) { // skip empty buckets if ( ! s_mptrs[i] ) continue; total += s_sizes[i]; p[np++] = i; } // . sort p by size // . skip this because it blocks for like 30 seconds bool flag ; goto skipsort; flag = 1; while ( flag ) { flag = 0; for ( long i = 1 ; i < np ; i++ ) { long a = p[i-1]; long b = p[i ]; if ( s_sizes[a] <= s_sizes[b] ) continue; // switch these 2 p[i ] = a; p[i-1] = b; flag = 1; } } skipsort: // print out table sorted by sizes for ( long i = 0 ; i < np ; i++ ) { long a = p[i]; log(LOG_INFO,"mem: %05li) %li %lx %s", i,s_sizes[a] , (long)s_mptrs[a] , &s_labels[a*16] ); } sysfree ( p ); log(LOG_INFO,"mem: # current objects allocated now = %li", np ); log(LOG_INFO,"mem: totalMem alloced now = %lli", total ); //log("mem: max alloced at one time = %li", (long)(m_maxAlloced)); log(LOG_INFO,"mem: Memory allocated now: %lli.\n", getUsedMem() ); log(LOG_INFO,"mem: Num allocs %li.\n", m_numAllocated ); return 1; } void *Mem::gbmalloc ( int size , const char *note ) { // don't let electric fence zap us if ( size == 0 ) return (void *)0x7fffffff; // random oom testing //static long s_mcount = 0; //s_mcount++; if ( g_conf.m_testMem && (rand() % 100) < 2 ) { //if ( s_mcount > 1055 && (rand() % 1000) < 2 ) { g_errno = ENOMEM; log("mem: malloc-fake(%i,%s): %s",size,note, mstrerror(g_errno)); return NULL; } retry: // don't go over max if ( m_used + size + UNDERPAD + OVERPAD >= m_maxMem ) { // try to free temp mem. returns true if it freed some. if ( freeCacheMem() ) goto retry; g_errno = ENOMEM; log("mem: malloc(%i): Out of memory", size ); return NULL; } if ( size < 0 ) { g_errno = EBADENGINEER; log("mem: malloc(%i): Bad value.", size ); char *xx = NULL; *xx = 0; return NULL; } // to find bug that cores on malloc do this //printBreeches(true); //g_errno=ENOMEM;return (void *)log("Mem::malloc: reached mem limit");} #ifdef _EFENCE_ void *mem = getElecMem(size+UNDERPAD+OVERPAD); #else //void *mem = dlmalloc ( size ); void *mem = (void *)sysmalloc ( size + UNDERPAD + OVERPAD ); #endif // initialization debug //char *pend = (char *)mem + UNDERPAD + size; //for ( char *p = (char *)mem + UNDERPAD ; p < pend ; p++ ) // *p = (char )(rand() % 256); // test mem fragmentation email //static long s_count = 0; //s_count++; //if ( s_count > 1500 && (rand() % 100) < 2 ) { // log("mem: malloc-system(%i,%s): %s",size,note, // mstrerror(g_errno)); // mem = NULL; //} // special log //if ( size > 1000000 ) // log("allocated %i. (%s) current=%lli",size,note,m_used); //void *mem = s_pool.malloc ( size ); long memLoop = 0; mallocmemloop: if ( ! mem && size > 0 ) { // try to free temp mem. returns true if it freed some. if ( freeCacheMem() ) goto retry; g_errno = errno; static long long s_lastTime; static long s_missed = 0; long long now = gettimeofdayInMillisecondsLocal(); long long avail = (long long)m_maxMem - (long long)m_used; if ( now - s_lastTime >= 1000LL ) { log("mem: system malloc(%i) availShouldBe=%lli: " "%s (%s) (ooms suppressed since " "last log msg = %li)", size+UNDERPAD+OVERPAD,avail, mstrerror(g_errno), note, s_missed); s_lastTime = now; s_missed = 0; } else s_missed++; // to debug oom issues: //char *xx=NULL;*xx=0; // send an email alert if this happens! it is a sign of // "memory fragmentation" //static bool s_sentEmail = false; // stop sending these now... seems to be problematic. says // 160MB is avail and can't alloc 20MB... static bool s_sentEmail = true; // assume only 90% is really available because of // inefficient mallocing avail = (long long)((float)avail * 0.80); // but if it is within about 15MB of what is theoretically // available, don't send an email, because there is always some // minor fragmentation if ( ! s_sentEmail && avail > size ) { s_sentEmail = true; char msgbuf[1024]; Host *h = g_hostdb.m_myHost; snprintf(msgbuf, 1024, "Possible memory fragmentation " "on host #%li %s", h->m_hostId,h->m_note); log(LOG_WARN, "query: %s",msgbuf); g_pingServer.sendEmail(NULL, msgbuf,true,true); } return NULL; } if ( (unsigned long)mem < 0x00010000 ) { #ifdef _EFENCE_ void *remem = getElecMem(size); #else void *remem = sysmalloc(size); #endif log ( LOG_WARN, "mem: Caught low memory allocation at %08lx, " "reallocated to %08lx", (unsigned long)mem, (unsigned long)remem ); #ifdef _EFENCE_ freeElecMem (mem); #else sysfree(mem); #endif mem = remem; memLoop++; if ( memLoop > 100 ) { log ( LOG_WARN, "mem: Attempted to reallocate low " "memory allocation 100 times, " "aborting and returning NOMEM." ); g_errno = ENOMEM; return NULL; } goto mallocmemloop; } addMem ( (char *)mem + UNDERPAD , size , note , 0 ); return (char *)mem + UNDERPAD; } void *Mem::gbcalloc ( int size , const char *note ) { void *mem = gbmalloc ( size , note ); // init it if ( mem ) memset ( mem , 0, size ); return mem; } void *Mem::gbrealloc ( void *ptr , int oldSize , int newSize , const char *note ) { // return dummy values since realloc() returns NULL if failed if ( oldSize == 0 && newSize == 0 ) return (void *)0x7fffffff; // do nothing if size is same if ( oldSize == newSize ) return ptr; // crazy? if ( newSize < 0 ) { char *xx=NULL;*xx=0; } // if newSize is 0... if ( newSize == 0 ) { //mfree ( ptr , oldSize , note ); gbfree ( ptr , oldSize , note ); return (void *)0x7fffffff; } // don't do more than 128M at a time, it hurts pthread_create //if ( newSize > MAXMEMPERCALL ) { // g_errno = ENOMEM; // log("Mem::realloc(%i): can only alloc %li bytes per call", // newSize,MAXMEMPERCALL); // return NULL; //} retry: // don't go over max if ( m_used + newSize - oldSize >= m_maxMem ) { // try to free temp mem. returns true if it freed some. if ( freeCacheMem() ) goto retry; g_errno = ENOMEM; log("mem: realloc(%i,%i): Out of memory.",oldSize,newSize); return NULL; } // if oldSize is 0, use our malloc() instead if ( oldSize == 0 ) return gbmalloc ( newSize , note ); char *mem; #ifdef _EFENCE_ mem = (char *)mmalloc ( newSize , note ); if ( ! mem ) return NULL; // copy over to it memcpy ( mem , ptr , oldSize ); // free the old mfree ( ptr , oldSize , note ); // done return mem; #endif // assume it will be successful. we can't call rmMem() after // calling sysrealloc() because it will mess up our MAGICCHAR buf rmMem ( ptr , oldSize , note ); // . do the actual realloc // . CAUTION: don't pass in 0x7fffffff in as "ptr" // . this was causing problems mem = (char *)sysrealloc ( (char *)ptr - UNDERPAD , newSize + UNDERPAD + OVERPAD); // remove old guy on sucess if ( mem ) { addMem ( (char *)mem + UNDERPAD , newSize , note , 0 ); char *returnMem = mem + UNDERPAD; // set magic char bytes for mem for ( long i = 0 ; i < UNDERPAD ; i++ ) returnMem[0-i-1] = MAGICCHAR; for ( long i = 0 ; i < OVERPAD ; i++ ) returnMem[0+newSize+i] = MAGICCHAR; return returnMem; } // ok, just try using malloc then! mem = (char *)mmalloc ( newSize , note ); // bail on error if ( ! mem ) { // restore the original buf we tried to grow addMem ( ptr , oldSize , note , 0 ); errno = g_errno = ENOMEM; return NULL; } // copy over to it memcpy ( mem , ptr , oldSize ); // free the old mfree ( ptr , oldSize , note ); // log a note log(LOG_INFO,"mem: had to use malloc/memcpy instead of " "realloc."); // mmalloc() and mfree() should have taken care of it return mem; } char *Mem::dup ( const void *data , long dataSize , const char *note ) { // keep it simple char *mem = (char *)mmalloc ( dataSize , note ); if ( mem ) memcpy ( mem , data , dataSize ); return mem; } void Mem::gbfree ( void *ptr , int size , const char *note ) { // don't let electric fence zap us //if ( size == 0 && ptr==(void *)0x7fffffff) return; if ( size == 0 ) return; // huh? if ( ! ptr ) return; // . get how much it was from the mem table // . this is used for alloc/free wrappers for zlib because it does // not give us a size to free when it calls our mfree(), so we use -1 long slot = g_mem.getMemSlot ( ptr ); if ( slot < 0 ) { log(LOG_LOGIC,"mem: could not find slot (note=%s)",note); // return for now so procog does not core all the time! return; //char *xx = NULL; *xx = 0; } #ifdef _EFENCE_ // this does a delayed free so do not call rmMem() just yet freeElecMem ((char *)ptr - UNDERPAD ); #else bool isnew = s_isnew[slot]; // if this returns false it was an unbalanced free if ( ! rmMem ( ptr , size , note ) ) return; if ( isnew ) sysfree ( (char *)ptr ); else sysfree ( (char *)ptr - UNDERPAD ); #endif } long getLowestLitBitLL ( unsigned long long bits ) { // count how many bits we have to shift so that the first bit is 0 long shift = 0; while ( (bits & (1LL< src // . bit #0 is the least significant bit on this little endian machine // . TODO: should we speed this up? long membitcmp ( void *dst , long dstBits , // bit offset into "dst" void *src , long srcBits , // bit offset into "src" long nb ) { // # bits to compare char *s; char *d; char smask; char dmask; for ( long b = nb - 1 ; b >= 0 ; b-- ) { s =(char *)src + ((b + srcBits) >> 3 ); d =(char *)dst + ((b + dstBits) >> 3 ); smask = 0x01 << ((b + srcBits) & 0x07); dmask = 0x01 << ((b + dstBits) & 0x07); if ( *s & smask ) { if ( ! (*d & dmask) ) return -1; } else { if ( *d & dmask ) return 1; } } return 0; } // . returns # of bits in common // . bit #0 is the least significant bit on this little endian machine // . TODO: should we speed this up? long membitcmp2 ( void *dst , long dstBits , // bit offset into "dst" void *src , long srcBits , // bit offset into "src" long nb ) { // # bits to compare char *s; char *d; char smask; char dmask; long nc = 0; for ( long b = nb - 1 ; b >= 0 ; b-- ) { s =(char *)src + ((b + srcBits) >> 3 ); d =(char *)dst + ((b + dstBits) >> 3 ); smask = 0x01 << ((b + srcBits) & 0x07); dmask = 0x01 << ((b + dstBits) & 0x07); if ( *s & smask ) { if ( ! (*d & dmask) ) return nc; } else { if ( *d & dmask ) return nc; } nc++; } return nc; } // . bit #0 is the least significant bit on this little endian machine // . TODO: should we speed this up? // . we start copying at LOW bit void membitcpy1 ( void *dst , long dstBits , // bit offset into "dst" void *src , long srcBits , // bit offset into "src" long nb ) { // # bits to copy // debug msg //log("nb=%li",nb); // if src and dst overlap, it matters if b moves up or down char *s; char *d; char smask; char dmask; for ( long b = 0 ; b < nb ; b++ ) { s =(char *)src + ((b + srcBits) >> 3 ); d =(char *)dst + ((b + dstBits) >> 3 ); smask = 0x01 << ((b + srcBits) & 0x07); dmask = 0x01 << ((b + dstBits) & 0x07); if ( *s & smask ) *d |= dmask; else *d &= ~dmask; } } // like above, but we start copying at HIGH bit so you can // shift your recs without interference void membitcpy2 ( void *dst , long dstBits , // bit offset into "dst" void *src , long srcBits , // bit offset into "src" long nb ) { // # bits to copy // if src and dst overlap, it matters if b moves up or down char *s; char *d; char smask; char dmask; for ( long b = nb - 1 ; b >= 0 ; b-- ) { s =(char *)src + ((b + srcBits) >> 3 ); d =(char *)dst + ((b + dstBits) >> 3 ); smask = 0x01 << ((b + srcBits) & 0x07); dmask = 0x01 << ((b + dstBits) & 0x07); if ( *s & smask ) *d |= dmask; else *d &= ~dmask; } } long Mem::printBits ( void *src, long srcBits , long nb ) { char *s; char smask; fprintf(stdout,"low %li bits = ",nb); for ( long b = 0 ; b < nb ; b++ ) { s =(char *)src + ((b + srcBits) >> 3 ); smask = 0x01 << ((b + srcBits) & 0x07); if ( *s & smask ) fprintf(stdout,"1"); else fprintf(stdout,"0"); } fprintf(stdout,"\n"); return 0; } // ass = async signal safe, dumb ass void memset_ass ( register void *dest , register const char c , long len ) { register char *end = (char *)dest + len; // JAB: so... the optimizer should take care of the extra // register declaration for d, below... see note below. register char *d = (char *)dest; // JAB: gcc-3.4 did not like the cast in the previous version // while ( dest < end ) *((char *)dest)++ = c; while ( d < end ) { *d++ = c; } } void memset_nice( register void *dest , register const char c , long len , long niceness ) { char *end = (char *)dest + len; // JAB: so... the optimizer should take care of the extra // register declaration for d, below... see note below. register char *d = (char *)dest; // JAB: gcc-3.4 did not like the cast in the previous version // while ( dest < end ) *((char *)dest)++ = c; loop: register char *seg = d + 5000; if ( seg > end ) seg = end; QUICKPOLL ( niceness ); while ( d < seg ) { *d++ = c; } QUICKPOLL ( niceness ); // do more? if ( d < end ) goto loop; } // . TODO: avoid byteCopy by copying remnant bytes // . ass = async signal safe, dumb ass // . NOTE: src/dest should not overlap in this version of memcpy void memcpy_ass ( register void *dest2, register const void *src2, long len ) { // for now keep it simple!! len--; while ( len >= 0 ) { ((char *)dest2)[len] = ((char *)src2)[len]; len--; } /* // debug test //memcpy ( dest2 , src2 , len ); //return; // the end for the fast copy by word with partially unrolled loop register long *dest = (long *)dest2; register long *src = (long *)src2 ; register long *end = dest + (len >> 2); long *oldEnd = end; //long long start = gettimeofdayInMilliseconds(); //fprintf(stderr,"ln=%li,dest=%li,src=%li\n",len,(long)dest,(long)src); if ((len&0x03)!=0 || ((long)dest&0x03)!=0 || ((long)src&0x03)!=0 ) goto byteCopy; // truncate n so we can unroll this loop end = (long *) ( (long)end & 0x07 ); while ( dest < end ) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; dest[3] = src[3]; dest[4] = src[4]; dest[5] = src[5]; dest[6] = src[6]; dest[7] = src[7]; dest += 8; src += 8; } // copy remaining 7 or less longs while ( dest < oldEnd ) { *dest = *src; dest++; src++; } //fprintf(stderr,"t=%li\n",(long)(gettimeofdayInMilliseconds()-start)); return; byteCopy: len--; while ( len >= 0 ) { dest2[len] = src2[len]; len--; } */ } // Check the current stack usage long Mem::checkStackSize() { if ( !m_stackStart ) return 0; char final; char *stackEnd = &final; long size = m_stackStart - stackEnd; log("gb: stack size is %li",size); return size; } // Set the stack's start point (called in main.cpp) void Mem::setStackPointer( char *ptr ) { m_stackStart = ptr; } char g_a[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; #include "Msg20.h" bool freeCacheMem() { // returns true if it did free some stuff //if ( resetMsg20Cache() ) { // log("mem: freed cache mem."); // return true; //} return false; } #define MAXBEST 50 // scan all allocated memory, assume each byte is starting a character ptr, // and find such ptrs closes to "target" long Mem::findPtr ( void *target ) { if ( ! s_mptrs ) return 0; long maxDelta = 0x7fffffff; long topDelta[MAXBEST]; long topOff [MAXBEST]; long topi [MAXBEST]; long nt = 0; long minnt = 0; long i; // loop through the whole mem table for ( i = 0 ; i < (long)m_memtablesize ; i++ ) { // only check if non-empty if ( ! s_mptrs[i] ) continue; // get size to scan char *p = (char *)s_mptrs[i]; long size = s_sizes[i]; char *pend = p + size; long bestDelta = 0x7fffffff; long bestOff = 0x7fffffff; char *note = &s_labels[i*16]; // skip thread stack if ( strcmp(note,"ThreadStack") == 0 ) continue; // scan that for ( ; p +4 < pend ; p++ ) { // get ptr it might have char *pp = (char *)(*(long *)p); // delta from target long delta = (unsigned long)pp - (unsigned long)target; // make positive if ( delta < 0 ) delta *= -1; // is it a min? //if ( delta > 100 ) continue; // get top 10 if ( delta < bestDelta ) { bestDelta = delta; bestOff = (long)p - (long)(s_mptrs[i]); } } // bail if not good enough if ( bestDelta >= maxDelta ) continue; if ( bestDelta > 50000 ) continue; // add to top list if ( nt < MAXBEST ) { topDelta[nt] = bestDelta; topOff [nt] = bestOff; topi [nt] = i; nt++; } else { topDelta[minnt] = bestDelta; topOff [minnt] = bestOff; topi [minnt] = i; } // compute minnt minnt = 0; for ( long j = 1 ; j < nt ; j++ ) if ( topDelta[j] > topDelta[minnt] ) minnt = j; } // print out top MAXBEST. "note" is the note attached to the allocated // memory the suspicious write ptr is in for ( long j = 0 ; j < nt ; j++ ) { // get it long bi = topi[j]; char *note = (char *)&s_labels[bi*16]; if ( ! note ) note = "unknown"; long *x = (long *)((char *)s_mptrs[bi] + topOff[j]); log("mem: topdelta=%li bytes away from corrupted mem. note=%s " "memblock=%li and memory of ptr is %li bytes into that " "memblock. and ptr is pointing to 0x%lx(%lu)", topDelta[j],note,bi,topOff[j], *x,*x); } return 0; } //#include /* for PAGESIZE */ #define PAGESIZE ((unsigned long)(8*1024)) void *getElecMem ( long size ) { // a page above OR a page below // let's go below this time since that seems to be the problem #ifdef _CHECKUNDERFLOW_ // how much to alloc // . assume sysmalloc returs one byte above a page, so we need // PAGESIZE-1 bytes to move p up to page boundary, another // PAGESIZE bytes for protected page, then the actual mem, // THEN possibly another PAGESIZE-1 bytes to hit the next page // boundary for protecting the "freed" mem below, but can get // by with (PAGESIZE-(size%PAGESIZE)) more long need = size + 8 + PAGESIZE + PAGESIZE ; // want to end on a page boundary too! need += (PAGESIZE-(size%PAGESIZE)); // get that char *realMem = (char *)sysmalloc ( need ); if ( ! realMem ) return NULL; // set it all to 0x11 memset ( realMem , 0x11 , need ); // use this char *realMemEnd = realMem + need; // parser char *p = realMem; // align p DOWN to nearest 8k boundary long remainder = (uint32_t)realMem % PAGESIZE; // complement remainder = PAGESIZE - remainder; // and add to ptr to be aligned on 8k boundary p += remainder; // save that char *protMem = p; // skip that p += PAGESIZE; // save this char *returnMem = p; // store the ptrs *(char **)(returnMem- 4) = realMem; *(char **)(returnMem- 8) = realMemEnd; // protect that after we wrote our ptr if ( mprotect ( protMem , PAGESIZE , PROT_NONE) < 0 ) log("mem: mprotect failed: %s",mstrerror(errno)); // advance over user data p += size; // now when we free this it should all be protected, so make sure // we have enough room on top long leftover = PAGESIZE - ((uint32_t)p % PAGESIZE); // skip that p += leftover; // inefficient? if ( realMemEnd - p > (long)PAGESIZE ) { char *xx=NULL;*xx=0;} // ensure we do not breach if ( p > realMemEnd ) { char *xx=NULL;*xx=0; } // test it, this should core //protmem[0] = 32; // return that for them return returnMem; #else // how much to alloc long need = size + 8 + PAGESIZE + PAGESIZE + PAGESIZE; // get that char *realMem = (char *)sysmalloc ( need ); if ( ! realMem ) return NULL; // set it all to 0x11 memset ( realMem , 0x11 , need ); // use this char *realMemEnd = realMem + need; // get the end of it char *end = realMemEnd; // back down from what we need end -= PAGESIZE; // get remainder from that long remainder = (uint32_t)end % PAGESIZE; // back down to that char *protMem = end - remainder; // get return mem char *returnMem = protMem - size; // back beyond that long leftover = (uint32_t)returnMem % PAGESIZE; // back up char *p = returnMem - leftover; // we are now on a page boundary, so we can protect this mem // after we "free" it below if ( p < realMem ) { char *xx=NULL;*xx=0; } // store mem ptrs before protecting *(char **)(returnMem- 4) = realMem; *(char **)(returnMem- 8) = realMemEnd; // sanity if ( returnMem - 8 < realMem ) { char *xx=NULL;*xx=0; } // protect that after we wrote our ptr if ( mprotect ( protMem , PAGESIZE , PROT_NONE) < 0 ) log("mem: mprotect failed: %s",mstrerror(errno)); // test it, this should core //protmem[0] = 32; // return that for them return returnMem; #endif } // stuff for detecting if a class frees memory and then re-uses it after // freeing it... class FreeInfo { public: char *m_fakeMem; long m_fakeSize; char *m_note; char *m_realMem; long m_realSize; char *m_protMem; long m_protSize; }; static FreeInfo s_freeBuf[4000]; static FreeInfo *s_cursor = &s_freeBuf[0]; static FreeInfo *s_cursorEnd = &s_freeBuf[4000]; static FreeInfo *s_cursorStart = &s_freeBuf[0]; static bool s_looped = false; static FreeInfo *s_freeCursor = &s_freeBuf[0]; static long long s_totalInRing = 0LL; // . now we must unprotect before freeing // . let's do delayed freeing because i think the nasty bug that is // corrupting malloc's space is overruning a freed buffer perhaps? void freeElecMem ( void *fakeMem ) { // cast it char *cp = (char *)fakeMem; // get mem info from the hash table long h = g_mem.getMemSlot ( cp ); if ( h < 0 ) { log("mem: unbalanced free ptr"); char *xx=NULL;*xx=0; } char *label = &s_labels[((unsigned long)h)*16]; long fakeSize = s_sizes[h]; #ifdef _CHECKUNDERFLOW_ char *oldProtMem = cp - PAGESIZE; #else char *oldProtMem = cp + fakeSize; #endif // unprotect it if ( mprotect ( oldProtMem , PAGESIZE, PROT_READ|PROT_WRITE) < 0 ) log("mem: munprotect failed: %s",mstrerror(errno)); // now original memptr is right before "p" and we can // read it now that we are unprotected char *realMem = *(char **)(cp-4); // set real mem end (no!?) char *realMemEnd = *(char **)(cp-8); // set it all to 0x99 memset ( realMem , 0x99 , realMemEnd - realMem ); // ok, back up to page boundary before us char *protMem = realMem + (PAGESIZE - (((unsigned long)realMem) % PAGESIZE)); // get end point char *protEnd = realMemEnd - ((unsigned long)realMemEnd % PAGESIZE); // sanity if ( protMem < realMem ) { char *xx=NULL;*xx=0; } if ( protMem - realMem > (long)PAGESIZE ) { char *xx=NULL;*xx=0; } // before adding it into the ring, protect it if ( mprotect ( protMem , protEnd-protMem, PROT_NONE) < 0 ) log("mem: mprotect2 failed: %s",mstrerror(errno)); // add our freed memory to the freed ring if ( s_cursor > s_cursorEnd ) { char *xx=NULL;*xx=0; } // to avoid losing free info by eating our own tail if ( s_cursor == s_freeCursor && s_looped ) { // free it g_mem.rmMem ( s_freeCursor->m_fakeMem, s_freeCursor->m_fakeSize, s_freeCursor->m_note ); // unprotect it if ( mprotect (s_freeCursor->m_protMem, s_freeCursor->m_protSize, PROT_READ|PROT_WRITE) < 0 ) log("mem: munprotect2 failed: %s",mstrerror(errno)); // get the original mem and nuke sysfree ( s_freeCursor->m_realMem ); // dec count. use fake mem size s_totalInRing -= s_freeCursor->m_realSize; // wrap it if we need to if ( ++s_freeCursor == s_cursorEnd ) s_freeCursor = s_cursorStart; } s_cursor->m_fakeMem = cp; s_cursor->m_fakeSize = fakeSize; s_cursor->m_note = label; s_cursor->m_realSize = realMemEnd - realMem; s_cursor->m_realMem = realMem; s_cursor->m_protMem = protMem; s_cursor->m_protSize = protEnd - protMem; // keep tabs on how much unfreed mem we need to free s_totalInRing += s_cursor->m_realSize; if ( ++s_cursor == s_cursorEnd ) { s_looped = true; s_cursor = s_cursorStart; } // now free begining at s_freeCursor for ( ; s_freeCursor != s_cursor && s_totalInRing > 150000000 ; ) { // free it g_mem.rmMem ( s_freeCursor->m_fakeMem, s_freeCursor->m_fakeSize, s_freeCursor->m_note ); // unprotect it if ( mprotect (s_freeCursor->m_protMem, s_freeCursor->m_protSize, PROT_READ|PROT_WRITE) < 0 ) log("mem: munprotect2 failed: %s",mstrerror(errno)); // get the original mem and nuke sysfree ( s_freeCursor->m_realMem ); // dec count. use fake mem size s_totalInRing -= s_freeCursor->m_realSize; // wrap it if we need to if ( ++s_freeCursor == s_cursorEnd ) s_freeCursor = s_cursorStart; } } // only Mem.cpp can call ::malloc, everyone else must call mmalloc() so // we can keep tabs on memory usage. #define malloc coreme #define calloc coreme #define realloc coreme