Logo Search packages:      
Sourcecode: netams version File versions  Download package

memory.c

/*************************************************************************
***   Authentication, authorization, accounting + firewalling package
***   Copyright 1998-2002 Anton Vinokurov <anton@netams.com>
***   Copyright 2002-2008 NeTAMS Development Team
***   This code is GPL v3
***   For latest version and more info, visit this project web page
***   located at http://www.netams.com
***
*************************************************************************/
/* $Id: memory.c,v 1.40 2009-08-01 09:23:55 anton Exp $ */

#include "netams.h"

//////////////////////////////////////////////////////////////////////////////////////////
// Memory management ///////////////////////////////////////////////////////////////////
static unsigned long long bytes_allocated=0;
static unsigned long times_allocated=0;
static unsigned long times_freed=0;
static unsigned times_freed_null=0;
pthread_mutex_t mem_lock = PTHREAD_MUTEX_INITIALIZER;

#if defined(DEBUG) && defined(MEMORY_DEBUG)
u_char AllocMemory(const char *file, unsigned line, void *ptr, size_t size);
u_char FreeMemory(const char *file, unsigned line, void *ptr);
typedef struct mem_unit {
      void *ptr;
      size_t size;
      pthread_t allocated_by;
      mem_unit *next;
} mem_unit;

mem_unit *mem_ready=NULL;
static mem_unit TMP;

//mem hash
#ifndef WIPE_OPENSSL
LHASH *mem_hash;

unsigned MEM_hash(mem_unit *m) {
      return (unsigned)m->ptr;
}
int MEM_cmp(mem_unit *m1, mem_unit *m2) {
      return (m1->ptr == m2->ptr)?0:1;
}
void MEM_cleanup(mem_unit *m) {
      free(m);
}

/* Create the type-safe wrapper functions for use in the LHASH internals */
static IMPLEMENT_LHASH_HASH_FN(MEM_hash, mem_unit *);
static IMPLEMENT_LHASH_COMP_FN(MEM_cmp, mem_unit *);
static IMPLEMENT_LHASH_DOALL_FN(MEM_cleanup, mem_unit *);
#else
GHashTable *mem_hash;

void MEM_cleanup(void *m) {
      free((mem_unit *)m);
}
#endif

//thread list for cShowDebugMem()
typedef struct thread_unit {
        pthread_t id;
      char *name;
        thread_unit *next;
        unsigned errors;
      unsigned long bytes_allocated;
      unsigned long times_allocated;
      unsigned long times_freed;
} thread_unit;
thread_unit *thread_root=NULL;
thread_unit *getThread(pthread_t id);
#else
void aMemoryDebugRelease() {}
int cShowDebugMemory(struct cli_def *cli, const char *cmd, char **argv, int argc) {
      cli_error(cli, "This command enabled only if compiled with -DDEBUG and -DMEMORY_DEBUG");
      return CLI_OK;
} 
#endif

#if defined(DEBUG) && defined(MEMORY_DEBUG)
#undef aMalloc
void *aMalloc(const char *file, unsigned line, size_t size){
#else
void *aMalloc(size_t size){
#endif
        netams_mutex_lock(&mem_lock);
        void *res;
      //we seriously depend that res are zeroed on allocation
        res=calloc(1,size);
        if (res==NULL) {
            aLog(D_CRIT, "malloc of %d bytes failed!\n", size);  //will never returns
                
            netams_mutex_unlock(&mem_lock);
                return NULL;
        }
            
      times_allocated++;
            bytes_allocated+=size;
#if defined(DEBUG) && defined(MEMORY_DEBUG)
      AllocMemory(file, line, res,size);
#endif
        netams_mutex_unlock(&mem_lock);
        return res;
}

#if defined(DEBUG) && defined(MEMORY_DEBUG)
#undef aFree
void aFree(const char *file, unsigned line, void *ptr){
#else
void aFree(void *ptr){
#endif
        netams_mutex_lock(&mem_lock);
        if (!ptr)
                times_freed_null++;
        else {
#if defined(DEBUG) && defined(MEMORY_DEBUG)
            if(!FreeMemory(file, line, ptr)) {
                  netams_mutex_unlock(&mem_lock);
                  return;
            }
#endif
                times_freed++;
                free(ptr);
        }
        netams_mutex_unlock(&mem_lock);
}

unsigned long long aGetBytesAllocated() { return bytes_allocated; }
unsigned long aGetTimesAllocated() { return times_allocated; }
unsigned long aGetTimesFreed() { return times_freed; }
unsigned aGetTimesFreedNull() { return times_freed_null; }
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
void aMemoryDebugInit() {
#if defined(DEBUG) && defined(MEMORY_DEBUG)
#ifndef WIPE_OPENSSL
      mem_hash=lh_new(LHASH_HASH_FN(MEM_hash), LHASH_COMP_FN(MEM_cmp));
#else
      mem_hash=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, MEM_cleanup);
#endif
      aLog(D_INFO, "Memory Debugging Initialized\n");
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////
#if defined(DEBUG) && defined(MEMORY_DEBUG)

void aMemoryDebugRelease() {
      // produce statistic
      thread_unit *t;

      aLog(D_INFO, "Memory Debugging Stopped\n");
      aLog(D_INFO, "memory usage summary:\ntotal allocated: %llu (%lu times), freed %lu times (%u null)\n", \
            aGetBytesAllocated(), aGetTimesAllocated(), aGetTimesFreed(), aGetTimesFreedNull());

      aLog(D_INFO, " Thread  |    Service   |bytes allocated|times allocated|times freed|errors\n");
      for(t=thread_root;t!=NULL;t=t->next){
                aLog(D_INFO, "%p %-11s %15lu %15lu %11lu %6u\n",t->id,t->name,t->bytes_allocated,t->times_allocated,t->times_freed,t->errors);
      }

#ifndef WIPE_OPENSSL
      /* So to run "MEM_cleanup" against all items in a hash table ... */
      lh_doall(mem_hash, LHASH_DOALL_FN(MEM_cleanup));
      lh_free(mem_hash);
#else
      g_hash_table_destroy(mem_hash);
#endif

      //clear ready
      mem_unit *ptr;
      while(mem_ready) {
            ptr=mem_ready;
            mem_ready=mem_ready->next;
            free(ptr);
      }

      //clear thread info
      while(thread_root) {
            t=thread_root;
            thread_root=thread_root->next;
            free(t->name);
            free(t);
      }
}

u_char AllocMemory(const char *file, unsigned line, void *ptr, size_t size) {
        pthread_t caller=pthread_self();
      mem_unit *m;      
      thread_unit *t;

      //get thread info
      t=getThread(caller);

#ifndef WIPE_OPENSSL
      TMP.ptr = ptr;
      m=(mem_unit*)lh_retrieve(mem_hash, &TMP);
#else
      m=(mem_unit*)g_hash_table_lookup(mem_hash, ptr);
#endif
      if(m) {
            aLog(D_ERR,"thread %p (%s) tries to use used pointer %p from %s:%u\n", \
                  caller, t->name?t->name:"", ptr, file, line);
            //rememer error in thread info
            t=getThread(m->allocated_by);
            t->errors++;
            t=getThread(caller);
            t->errors++;
            return 0;
      }

      //fill thread info
      t->bytes_allocated+=size;
      t->times_allocated++;

      //look for mem_unit
      if(mem_ready) {
            m=mem_ready;
            mem_ready=mem_ready->next;
      } else 
            m=(mem_unit*)calloc(1,sizeof(mem_unit));

      //fill mem_unit
      m->ptr=ptr;
      m->size=size;
      m->allocated_by=caller;
      
      //insert into hash
#ifndef WIPE_OPENSSL
      lh_insert(mem_hash, m);
#else
      g_hash_table_insert(mem_hash, ptr, m);
#endif

        aDebug(DEBUG_MEMORY, "%p - %lu bytes allocated from thread %p %s from %s:%u\n", \
            ptr, size, caller, t->name?t->name:"", file, line);

      return 1;
}

u_char FreeMemory(const char *file, unsigned line, void *ptr) {
      mem_unit *m;
      pthread_t caller=pthread_self();
      thread_unit *t;

#ifndef WIPE_OPENSSL
      TMP.ptr = ptr;
      m = (mem_unit*)lh_delete(mem_hash, &TMP);
#else
      m = (mem_unit*)g_hash_table_lookup(mem_hash, ptr);
      g_hash_table_steal(mem_hash, ptr);
#endif

      if(!m) {
            t=getThread(caller);
            aLog(D_ERR,"thread %p (%s) tries to free unused pointer %p from %s:%u\n",
                  caller, t->name?t->name:"", ptr, file, line);
            t->errors++;
            return 0;
      }
      
      size_t size=m->size;

      //fill thread info
        t=getThread(m->allocated_by);
        t->bytes_allocated-=size;
        t->times_freed++;

        aDebug(DEBUG_MEMORY, "%p - %lu bytes memory freed from thread %p %s from %s:%u\n", \
            ptr, size, caller, t->name?t->name:"", file, line);
      
      //put to ready
      m->next=mem_ready;
      mem_ready=m;
      
      return 1;
}
//////////////////////////////////////////////////////////////////////////////////////////
thread_unit *getThread(pthread_t id) {
      thread_unit *t=NULL;
      
      for(t=thread_root;t!=NULL;t=t->next)
            if(t->id==id) break;

      if(!t) {
            t=(thread_unit*)calloc(1,sizeof(thread_unit));
            t->id=id;
            t->next=thread_root;
            thread_root=t;
            t->name=NULL;
      }

      if(!t->name) {
            Service *s;
            Connection *conn;

            if(Services && (s=Services->getServiceByThr(id))){
                  asprintf(&t->name,"%s:%u",s->getName(),s->instance);
            } else if(Connections && (conn=Connections->getConnectionByThr(id))) {
                  asprintf(&t->name,"conn:%u",conn->id);
            }
      }
      return t;
}
//////////////////////////////////////////////////////////////////////////////////////////
//"show memory"
int cShowDebugMemory(struct cli_def *cli, const char *cmd, char **argv, int argc) {
      thread_unit *t;

      netams_mutex_lock(&mem_lock);

      cli_print(cli, " Thread  |    Service   |bytes allocated|times allocated|times freed|errors"); 
      for(t=thread_root;t!=NULL;t=t->next){
            cli_print(cli, "%u %-11s %15lu %15lu %11lu %6u",
                  (unsigned)t->id,t->name?t->name:"<undef>",t->bytes_allocated,t->times_allocated,t->times_freed,t->errors);
      }
      
        //gather memory hash statistic
/*        mem_unit *ptr;
        unsigned used=0;
        unsigned max_chain=0;
        unsigned tmp;
        unsigned long total=0;

        for(unsigned i=0;i<MEM_HASH_SIZE;i++) {
                if(!(ptr=mem_hash[i])) continue;
                used++;
                tmp=0;
                for(;ptr!=NULL;ptr=ptr->next) {
                        tmp++;
                }
                total+=tmp;
                if(max_chain<tmp) max_chain=tmp;
        }
      
        cli_print(cli, "\nMemory HASH: size=%u, %lu pointers hashed, %u nodes used, max chain=%u",
            MEM_HASH_SIZE+1,total,used,max_chain);
*/    
      netams_mutex_unlock(&mem_lock);
      return CLI_OK;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////

Generated by  Doxygen 1.6.0   Back to index