- fixed the slow memory leak problem.
[codemux.git] / debug.c
diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..6878994
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,177 @@
+#include <stdlib.h>
+#include "codemuxlib.h"
+#include "debug.h"
+#include "queue.h"
+
+#ifdef DEBUG_MEMORY_LEAK
+
+/* Maximum length of the location string containing function, file and
+   the line number of the memory allocation part in the original
+   source code. The location info is added in front of the pointer
+   when allocating memory and return p + MAX_LOCATION_STRLEN to the
+   user */
+#define MAX_LOCATION_STRLEN 512 
+
+typedef struct MemoryAllocInfo {
+  char *mi_locstr;             /* location string "__FUNCTION__:__LINE__" */
+  int  mi_count;               /* allocated memory counts */
+  LIST_ENTRY(MemoryAllocInfo) mi_hash;
+  LIST_ENTRY(MemoryAllocInfo) mi_all;                   
+} MemoryAllocInfo;
+
+#define MAX_BINS 257 /* a prime number near 256 */
+static LIST_HEAD(, MemoryAllocInfo) miBins[MAX_BINS];
+static LIST_HEAD(, MemoryAllocInfo) miHead = LIST_HEAD_INITIALIZER(miHead);
+
+/*-------------------------------------------------------------------------*/
+void 
+dbg_print_memtrace(void)
+{
+  MemoryAllocInfo *walk;
+
+  TRACE("memory alloc counts dump begins\n");
+  LIST_FOREACH(walk, &miHead, mi_all) {
+    TRACE("%-50s: %d\n", walk->mi_locstr, walk->mi_count);
+  }
+  TRACE("memory alloc counts dump ends\n");
+}
+/*-------------------------------------------------------------------------*/
+static void 
+increase_alloc_count(char* p, const char* func, 
+                    const char* file, const int line)
+{
+  int bin;
+  MemoryAllocInfo* walk;
+
+#define MAX_LINE_DIGIT 7 /* million lines of code per file is way too much */
+#define LOCSTR_LENGH  (strlen(func) + strlen(file) + MAX_LINE_DIGIT + 3)
+
+  if (LOCSTR_LENGH >= MAX_LOCATION_STRLEN) {
+    TRACE("over the length limit %s:%d\n", func, line);
+    FlushLogF(hdebugLog);
+    exit(-1);
+  }
+  snprintf(p, MAX_LOCATION_STRLEN, "%s:%s:%d", func, file, line);
+
+  bin = (int)(HashString(p, 0, FALSE, FALSE) % MAX_BINS);
+  LIST_FOREACH(walk, &miBins[bin], mi_hash) {
+    if (strcmp(walk->mi_locstr, p) == 0) { /* match */
+      walk->mi_count++;
+      return;
+    }
+  }
+  
+  /* allocate it if not found */
+  if ((walk = (MemoryAllocInfo *)calloc(1, sizeof(MemoryAllocInfo))) == NULL) {
+    TRACE("calloc failed\n");
+    FlushLogF(hdebugLog);
+    exit(-1);
+  }
+  if ((walk->mi_locstr = strdup(p)) == NULL) {
+    TRACE("calloc failed\n");
+    FlushLogF(hdebugLog);
+    exit(-1);
+  }
+  walk->mi_count++;
+  LIST_INSERT_HEAD(&miBins[bin], walk, mi_hash);
+  LIST_INSERT_HEAD(&miHead, walk, mi_all);
+}
+/*-------------------------------------------------------------------------*/
+static void
+decrease_alloc_count(char *p, const char* func, 
+                    const char* file, const int line)
+{ 
+  int bin = (int)(HashString(p, 0, FALSE, FALSE) % MAX_BINS);
+  MemoryAllocInfo* walk;
+
+  LIST_FOREACH(walk, &miBins[bin], mi_hash) {
+    if (strcmp(walk->mi_locstr, p) == 0) { /* match */
+      walk->mi_count--;
+      if (walk->mi_count == 0) {
+       LIST_REMOVE(walk, mi_hash);
+       LIST_REMOVE(walk, mi_all);
+       free(walk->mi_locstr);
+       free(walk);
+      }
+      return;
+    }
+  }
+
+  TRACE("decrease failed %s:%s:%d\n", func, file, line);
+  FlushLogF(hdebugLog);
+  exit(-1);
+}
+/*-------------------------------------------------------------------------*/
+void *
+dbgcalloc(size_t nmemb, size_t size, 
+         const char* func,  const char* file, const int line)
+{
+  int msize = nmemb * size + MAX_LOCATION_STRLEN;
+  char *p;
+
+  if ((p = (char *)calloc(1, msize)) != NULL) {
+    increase_alloc_count(p, func, file, line);
+    return (void *)(p + MAX_LOCATION_STRLEN);
+  }
+  return NULL;
+}
+/*-------------------------------------------------------------------------*/
+void *
+dbgmalloc(size_t size, const char* func, const char* file, const int line)
+{
+  int msize = size + MAX_LOCATION_STRLEN;
+  char *p;
+
+  if ((p = (char *)malloc(msize)) != NULL) {
+    increase_alloc_count(p, func, file, line);
+    return (void *)(p + MAX_LOCATION_STRLEN);
+  }
+  return NULL;
+}
+/*-------------------------------------------------------------------------*/
+void *
+dbgrealloc(void *ptr, size_t size, 
+          const char* func, const char* file, const int line)
+{
+  int msize = size + MAX_LOCATION_STRLEN;
+  char *p;
+
+  if (ptr != NULL) {
+    ptr -= MAX_LOCATION_STRLEN;
+    decrease_alloc_count(ptr, func, file, line);
+  }
+
+  if ((p = (char *)realloc(ptr, msize)) != NULL) {
+    increase_alloc_count(p, func, file, line);
+    return (void *)(p + MAX_LOCATION_STRLEN);
+  }
+  return NULL;
+}
+/*-------------------------------------------------------------------------*/
+char *
+dbgstrdup(const char *s, const char* func, const char* file, const int line)
+{
+  int msize = strlen(s) + 1 + MAX_LOCATION_STRLEN;
+  char *p;
+
+  if ((p = (char *)malloc(msize))) {
+    increase_alloc_count(p, func, file, line);
+    p += MAX_LOCATION_STRLEN;
+    strcpy(p, s);
+    return p;
+  }
+  return NULL;
+}
+/*-------------------------------------------------------------------------*/
+void 
+dbgfree(void *ptr, const char* func, const char* file, const int line)
+{
+  /* should free the original pointer */
+  char* chptr = (char *)ptr;
+
+  chptr -= MAX_LOCATION_STRLEN;
+  decrease_alloc_count(chptr, func, file, line);
+  free(chptr);
+}
+#endif
+