add Imager's memory debugger
authorTony Cook <tony@develop=help.com>
Wed, 5 Jul 2006 09:11:57 +0000 (09:11 +0000)
committerTony Cook <tony@develop=help.com>
Wed, 5 Jul 2006 09:11:57 +0000 (09:11 +0000)
Array.xs
Makefile.PL
alloc.c [new file with mode: 0644]
alloc.h [new file with mode: 0644]
queue.c

index 4f67a59..0c2beb8 100644 (file)
--- a/Array.xs
+++ b/Array.xs
@@ -4,6 +4,7 @@
 #include <string.h> /* for memmove() mostly */
 #include <errno.h> /* errno values */
 #include "queue.h"
+#include "alloc.h"
 
 /* this typedef lets the standard T_PTROBJ typemap handle the
 conversion between perl class and C type and back again */
@@ -110,7 +111,7 @@ pq_remove_items(pq, filter, ...)
           }
        }
        if (removed_entries)
-          free(removed_entries);
+          myfree(removed_entries);
 
 void
 pq_adjust_priority(pq, id, filter, delta)
@@ -165,7 +166,7 @@ pq_peek_items(pq, filter, ...)
            rv = newRV_noinc((SV *)av);
            PUSHs(sv_2mortal(rv));
          }
-          free(ret_items);
+          myfree(ret_items);
        }
 
 void
index 06d1b93..0109297 100644 (file)
@@ -4,7 +4,7 @@ my %opts =
   (
    NAME => 'POE::XS::Queue::Array',
    VERSION_FROM => 'Array.pm',
-   OBJECT => 'Array.o queue.o',
+   OBJECT => 'Array.o queue.o alloc.o',
    PREREQ_PM => {
                 'POE'    => 0.29,
                },
diff --git a/alloc.c b/alloc.c
new file mode 100644 (file)
index 0000000..9a7dfb6
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,317 @@
+#include "alloc.h"
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+
+/* cheap version of Imager's logging */
+char const *last_file; int last_line;
+
+static void do_log(int level, char const *fmt, ...);
+static
+void
+bndcheck(int idx);
+void
+bndcheck_all(void);
+
+#define mm_log(x) ((last_file = __FILE__), (last_line = __LINE__), do_log x )
+
+#ifdef MEM_DEBUG
+
+#define MAXMAL 102400
+#define MAXDESC 65
+
+#define UNDRRNVAL 10
+#define OVERRNVAL 10
+
+#define PADBYTE 0xaa
+
+static int malloc_need_init = 1;
+
+void
+malloc_state(void);
+
+typedef struct {
+  void* ptr;
+  size_t size;
+  char comm[MAXDESC];
+} malloc_entry;
+
+malloc_entry malloc_pointers[MAXMAL];
+
+
+
+/* Utility functions */
+
+
+static
+void
+malloc_init(void) {
+  int i;
+  for(i=0; i<MAXMAL; i++) malloc_pointers[i].ptr = NULL;
+  malloc_need_init = 0;
+  atexit(malloc_state);
+}
+
+
+static
+int 
+find_ptr(void *p) {
+  int i;
+  for(i=0;i<MAXMAL;i++)
+    if (malloc_pointers[i].ptr == p)
+      return i;
+  return -1;
+}
+
+
+/* Takes a pointer to real start of array,
+ * sets the entries in the table, returns
+ * the offset corrected pointer */
+
+static
+void *
+set_entry(int i, char *buf, size_t size, char const *file, int line) {
+  memset( buf, PADBYTE, UNDRRNVAL );
+  memset( &buf[UNDRRNVAL+size], PADBYTE, OVERRNVAL );
+  buf += UNDRRNVAL;
+  malloc_pointers[i].ptr  = buf;
+  malloc_pointers[i].size = size;
+  sprintf(malloc_pointers[i].comm,"%s (%d)", file, line);
+  return buf;
+}
+
+void
+malloc_state(void) {
+  int i, total = 0;
+
+  mm_log((0,"malloc_state()\n"));
+  bndcheck_all();
+  for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr != NULL) {
+    mm_log((0,"%d: %d (0x%x) : %s\n", i, malloc_pointers[i].size, malloc_pointers[i].ptr, malloc_pointers[i].comm));
+    total += malloc_pointers[i].size;
+  }
+  if (total == 0) mm_log((0,"No memory currently used!\n"));
+                   else mm_log((0,"total: %d\n",total));
+}
+
+
+
+void*
+mymalloc_file_line(size_t size, char const* file, int line) {
+  char *buf;
+  int i;
+  if (malloc_need_init) malloc_init();
+  
+  /* bndcheck_all(); Uncomment for LOTS OF THRASHING */
+  
+  if ( (i = find_ptr(NULL)) < 0 ) {
+    mm_log((0,"more than %d segments allocated at %s (%d)\n", MAXMAL, file, line));
+    exit(3);
+  }
+
+  if ( (buf = malloc(size+UNDRRNVAL+OVERRNVAL)) == NULL ) {
+    mm_log((1,"Unable to allocate %i for %s (%i)\n", size, file, line));
+    exit(3);
+  }
+  
+  buf = set_entry(i, buf, size, file, line);
+  mm_log((1,"mymalloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, size, buf, file, line));
+  return buf;
+}
+
+void *
+(mymalloc)(int size) {
+  return mymalloc_file_line(size, "unknown", 0);
+}
+
+void*
+myrealloc_file_line(void *ptr, size_t newsize, char const * file, int line) {
+  char *buf;
+  int i;
+
+  if (malloc_need_init) malloc_init();
+  /* bndcheck_all(); ACTIVATE FOR LOTS OF THRASHING */
+  
+  if (!ptr) {
+    mm_log((1, "realloc called with ptr = NULL, sending request to malloc\n"));
+    return mymalloc_file_line(newsize, file, line);
+  }
+  
+  if (!newsize) {
+    mm_log((1, "newsize = 0, sending request to free\n"));
+    myfree_file_line(ptr, file, line);
+    return NULL;
+  }
+
+  if ( (i = find_ptr(ptr)) == -1) {
+    mm_log((0, "Unable to find %p in realloc for %s (%i)\n", ptr, file, line));
+    exit(3);
+  }
+  
+  if ( (buf = realloc(((char *)ptr)-UNDRRNVAL, UNDRRNVAL+OVERRNVAL+newsize)) == NULL ) {
+    mm_log((1,"Unable to reallocate %i bytes at %p for %s (%i)\n", newsize, ptr, file, line));
+    exit(3); 
+  }
+  
+  buf = set_entry(i, buf, newsize, file, line);
+  mm_log((1,"realloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, newsize, buf, file, line));
+  return buf;
+}
+
+void *
+(myrealloc)(void *ptr, size_t newsize) {
+  return myrealloc_file_line(ptr, newsize, "unknown", 0);
+}
+
+static
+void
+bndcheck(int idx) {
+  int i;
+  size_t s = malloc_pointers[idx].size;
+  unsigned char *pp = malloc_pointers[idx].ptr;
+  if (!pp) {
+    mm_log((1, "bndcheck: No pointer in slot %d\n", idx));
+    return;
+  }
+  
+  for(i=0;i<UNDRRNVAL;i++)
+     if (pp[-(1+i)] != PADBYTE)
+     mm_log((1,"bndcheck: UNDERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s ));
+  
+     for(i=0;i<OVERRNVAL;i++)
+    if (pp[s+i] != PADBYTE)
+      mm_log((1,"bndcheck: OVERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s ));
+}
+
+void
+bndcheck_all(void) {
+  int idx;
+  mm_log((1, "bndcheck_all()\n"));
+  for(idx=0; idx<MAXMAL; idx++)
+    if (malloc_pointers[idx].ptr)
+      bndcheck(idx);
+}
+
+void
+myfree_file_line(void *p, char const *file, int line) {
+  char  *pp = p;
+  int match = 0;
+  int i;
+  
+  for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr == p) {
+    mm_log((1,"myfree_file_line: pointer %i (%s) freed at %s (%i)\n", i, malloc_pointers[i].comm, file, line));
+    bndcheck(i);
+    malloc_pointers[i].ptr = NULL;
+    match++;
+  }
+
+  mm_log((1, "myfree_file_line: freeing address %p (real %p) (%s:%d)\n", pp, pp-UNDRRNVAL, file, line));
+  
+  if (match != 1) {
+    mm_log((1, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line));
+    fprintf(stderr, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line);
+               exit(255);
+  }
+  
+  
+  free(pp-UNDRRNVAL);
+}
+
+void
+(myfree)(void *block) {
+  myfree_file_line(block, "unknown", 0);
+}
+
+#else 
+
+#define malloc_comm(a,b) (mymalloc(a))
+
+void
+malloc_state() {
+}
+
+void*
+mymalloc(size_t size) {
+  void *buf;
+
+  if (size < 0) {
+    fprintf(stderr, "Attempt to allocate size %d\n", size);
+    exit(3);
+  }
+
+  if ( (buf = malloc(size)) == NULL ) {
+    mm_log((1, "mymalloc: unable to malloc %d\n", size));
+    fprintf(stderr,"Unable to malloc %d.\n", size); exit(3);
+  }
+  mm_log((1, "mymalloc(size %d) -> %p\n", size, buf));
+  return buf;
+}
+
+void *
+mymalloc_file_line(size_t size, char *file, int line) {
+  return mymalloc(size);
+}
+
+void
+myfree(void *p) {
+  mm_log((1, "myfree(p %p)\n", p));
+  free(p);
+}
+
+void
+myfree_file_line(void *p, char *file, int line) {
+  myfree(p);
+}
+
+void *
+myrealloc(void *block, size_t size) {
+  void *result;
+
+  mm_log((1, "myrealloc(block %p, size %u)\n", block, size));
+  if ((result = realloc(block, size)) == NULL) {
+    mm_log((1, "myrealloc: out of memory\n"));
+    fprintf(stderr, "Out of memory.\n");
+    exit(3);
+  }
+  return result;
+}
+
+void *
+myrealloc_file_line(void *block, size_t newsize, char *file, int size) {
+  return myrealloc(block, newsize);
+}
+
+#endif /* IMAGER_MALLOC_DEBUG */
+
+FILE *log_file;
+
+void
+setup_log(void) {
+  char *log_env = getenv("MEM_DEBUG");
+  if (!log_env)
+    return;
+
+  if (strcmp(log_env, "STDERR") == 0) {
+    log_file = stderr;
+    return;
+  }
+  log_file = fopen(log_env, "w+");
+  if (!log_file) {
+    fprintf(stderr, "Could not open log %s: %s\n", log_env, strerror(errno));
+    exit(3);
+  }
+}
+
+static void
+do_log(int level, char const *fmt, ...) {
+  if (!log_file) setup_log();
+  
+  if (log_file) {
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(log_file, "[%s:%d] %d:", last_file, last_line, level);
+    
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+  }
+}
diff --git a/alloc.h b/alloc.h
new file mode 100644 (file)
index 0000000..72e03d0
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,30 @@
+/* Imager's memory allocation debugging code */
+#ifndef XSQUEUE_ALLOC_H
+#define XSQUEUE_ALLOC_H
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#define MEM_DEBUG
+
+#ifdef MEM_DEBUG
+
+extern void *mymalloc_file_line(size_t size, char const *file, int line);
+extern void myfree_file_line(void *block, char const *file, int line);
+extern void *myrealloc_file_line(void *block, size_t new_size, char const *file, int line);
+
+#define mymalloc(size) (mymalloc_file_line((size), __FILE__, __LINE__))
+#define myfree(block) (myfree_file_line((block), __FILE__, __LINE__))
+#define myrealloc(block, size) (myrealloc_file_line((block), (size), __FILE__, __LINE__))
+
+extern void bndcheck_all(void);
+
+#else
+
+extern void *mymalloc(size_t size);
+extern void myfree(void *block);
+extern void *myrealloc(void *block, size_t new_size);
+
+#endif
+
+#endif
diff --git a/queue.c b/queue.c
index 09ed72e..070ff4b 100644 (file)
--- a/queue.c
+++ b/queue.c
@@ -3,6 +3,7 @@
 #include "XSUB.h"\r
 \r
 #include "queue.h"\r
+#include "alloc.h"\r
 \r
 /*#define DEBUG(x) x*/\r
 #define DEBUG(x)\r
@@ -64,7 +65,7 @@ No parameters.  returns the new queue object.
 */\r
 poe_queue *\r
 pq_create(void) {\r
-  poe_queue *pq = malloc(sizeof(poe_queue));\r
+  poe_queue *pq = mymalloc(sizeof(poe_queue));\r
   \r
   if (pq == NULL)\r
     croak("Out of memory");\r
@@ -73,7 +74,8 @@ pq_create(void) {
   pq->alloc = PQ_START_SIZE;\r
   pq->queue_seq = 0;\r
   pq->ids = newHV();\r
-  pq->entries = calloc(sizeof(pq_entry), PQ_START_SIZE);\r
+  pq->entries = mymalloc(sizeof(pq_entry) * PQ_START_SIZE);\r
+  memset(pq->entries, 0, sizeof(pq_entry) * PQ_START_SIZE);\r
   if (pq->entries == NULL)\r
     croak("Out of memory");\r
 \r
@@ -101,9 +103,9 @@ pq_delete(poe_queue *pq) {
   SvREFCNT_dec((SV *)pq->ids);\r
   pq->ids = NULL;\r
   if (pq->entries)\r
-    free(pq->entries);\r
+    myfree(pq->entries);\r
   pq->entries = NULL;\r
-  free(pq);\r
+  myfree(pq);\r
 }\r
 \r
 /*\r
@@ -247,7 +249,7 @@ pq_realloc(poe_queue *pq, int at_end) {
   }\r
   else {\r
     int new_alloc = pq->alloc * 3 / 2;\r
-    pq->entries = realloc(pq->entries, sizeof(pq_entry) * new_alloc);\r
+    pq->entries = myrealloc(pq->entries, sizeof(pq_entry) * new_alloc);\r
     pq->alloc = new_alloc;\r
 \r
     if (!pq->entries)\r
@@ -489,7 +491,7 @@ pq_remove_items(poe_queue *pq, SV *filter, int max_count, pq_entry **entries) {
   if (pq->start == pq->end)\r
     return 0;\r
 \r
-  *entries = malloc(sizeof(pq_entry) * (pq->end - pq->start));\r
+  *entries = mymalloc(sizeof(pq_entry) * (pq->end - pq->start));\r
   if (!*entries)\r
     croak("Out of memory");\r
   \r
@@ -641,14 +643,14 @@ pq_peek_items(poe_queue *pq, SV *filter, int max_count, pq_entry **items) {
   if (pq->end == pq->start)\r
     return 0;\r
 \r
-  *items = malloc(sizeof(pq_entry) * (pq->end - pq->start));\r
+  *items = mymalloc(sizeof(pq_entry) * (pq->end - pq->start));\r
   for (i = pq->start; i < pq->end; ++i) {\r
     if (pq_test_filter(pq->entries + i, filter)) {\r
       (*items)[count++] = pq->entries[i];\r
     }\r
   }\r
   if (!count) {\r
-    free(*items);\r
+    myfree(*items);\r
     *items = NULL;\r
   }\r
 \r