Greatly enhance the slab allocator.
authorMichael Poole <mdpoole@troilus.org>
Fri, 14 Dec 2007 04:47:10 +0000 (23:47 -0500)
committerMichael Poole <mdpoole@troilus.org>
Fri, 14 Dec 2007 04:47:10 +0000 (23:47 -0500)
configure.in: Look for mprotect() function.
src/Makefile.am: Add slab-read program.
src/slab-read.c: New helper program.
src/alloc-slab.c (SLAB_DEBUG_HEADER): New bitmask macro.
  (SLAB_DEBUG_LOG): Likewise.  Implement its functions.
  (SLAB_DEBUG_PERMS): Likewise.  Implement its functions.
  (SLAB_DEBUG): Update default.
  (slab_alloc): Call slab_unprotect() when pulling free_slab_head.  Call
    slab_log_alloc() on any slab allocation.
  (slab_unalloc): Call slab_log_free() on slab unallocation.  Call
    slab_unprotect() and slab_protect() when manipulating free slabs.
  (slab_malloc): Call slab_log_alloc() on large object allocation.
  (slab_free): Call slab_log_unmap() on large object free.

configure.in
src/Makefile.am
src/alloc-slab.c
src/slab-read.c [new file with mode: 0644]

index f9d3bf746ed144807e7edbd6c3a69235785ed333..31c82aa7ac180f06cf040c7dc06b77482454db0a 100644 (file)
@@ -83,7 +83,7 @@ AC_CHECK_MEMBER([struct addrinfo.ai_flags],
 #include <netdb.h>])
 
 dnl We have fallbacks in case these are missing, so just check for them.
-AC_CHECK_FUNCS(freeaddrinfo getaddrinfo gai_strerror getnameinfo getpagesize memcpy memset strdup strerror strsignal localtime_r setrlimit getopt getopt_long regcomp regexec regfree sysconf inet_aton epoll_create select gettimeofday times GetProcessTimes,,)
+AC_CHECK_FUNCS(freeaddrinfo getaddrinfo gai_strerror getnameinfo getpagesize memcpy memset strdup strerror strsignal localtime_r setrlimit getopt getopt_long regcomp regexec regfree sysconf inet_aton epoll_create select gettimeofday times GetProcessTimes mprotect,,)
 
 dnl Check for the fallbacks for functions missing above.
 if test $ac_cv_func_gettimeofday = no; then
index 155ccd60e3f76cd86edec271016f64484b01033f..76b4ca3d1806b9a39d189f443976757899a883b2 100644 (file)
@@ -1,7 +1,7 @@
 AM_CPPFLAGS = @RX_INCLUDES@
 LIBS = @LIBS@ @RX_LIBS@
 
-noinst_PROGRAMS = srvx
+noinst_PROGRAMS = srvx slab-read
 EXTRA_PROGRAMS = checkdb globtest
 noinst_DATA = \
        chanserv.help \
@@ -87,3 +87,4 @@ srvx_SOURCES = \
 
 checkdb_SOURCES = checkdb.c common.h compat.c compat.h dict-splay.c dict.h recdb.c recdb.h saxdb.c saxdb.h tools.c conf.h log.h modcmd.h saxdb.h timeq.h
 globtest_SOURCES = common.h compat.c compat.h dict-splay.c dict.h globtest.c tools.c
+slab_read_SOURCES = slab-read.c
index 43c857549c30715732e5ab0b843f3e523c701f7c..ac6731a30c84775db22b6c1e9f702ce78a1cd79d 100644 (file)
 # error The slab allocator requires that your system have the mmap() system call.
 #endif
 
+#define SLAB_DEBUG_HEADER 1
+#define SLAB_DEBUG_LOG    2
+#define SLAB_DEBUG_PERMS  4
+
 #if !defined(SLAB_DEBUG)
-# define SLAB_DEBUG 1
+# define SLAB_DEBUG 0
 #endif
 
 #if !defined(SLAB_RESERVE)
@@ -37,7 +41,7 @@
 # define MAX_SLAB_FREE 1024
 #endif
 
-#if SLAB_DEBUG
+#if SLAB_DEBUG & SLAB_DEBUG_HEADER
 
 #define ALLOC_MAGIC 0x1a
 #define FREE_MAGIC  0xcf
@@ -131,6 +135,92 @@ unsigned long slab_alloc_size;
 # define MAP_ANON 0
 #endif
 
+#if SLAB_DEBUG & SLAB_DEBUG_LOG
+
+FILE *slab_log;
+
+struct slab_log_entry
+{
+    struct timeval tv;
+    void *slab;
+    ssize_t size;
+};
+
+static void
+close_slab_log(void)
+{
+    fclose(slab_log);
+}
+
+static void
+slab_log_alloc(void *slab, size_t size)
+{
+    struct slab_log_entry sle;
+
+    gettimeofday(&sle.tv, NULL);
+    sle.slab = slab;
+    sle.size = (ssize_t)size;
+
+    if (!slab_log)
+    {
+        const char *fname;
+        fname = getenv("SLAB_LOG_FILE");
+        if (!fname)
+            fname = "slab.log";
+        slab_log = fopen(fname, "w");
+        atexit(close_slab_log);
+    }
+
+    fwrite(&sle, sizeof(sle), 1, slab_log);
+}
+
+static void
+slab_log_free(void *slab, size_t size)
+{
+    struct slab_log_entry sle;
+
+    gettimeofday(&sle.tv, NULL);
+    sle.slab = slab;
+    sle.size = -(ssize_t)size;
+    fwrite(&sle, sizeof(sle), 1, slab_log);
+}
+
+static void
+slab_log_unmap(void *slab)
+{
+    struct slab_log_entry sle;
+
+    gettimeofday(&sle.tv, NULL);
+    sle.slab = slab;
+    sle.size = 0;
+    fwrite(&sle, sizeof(sle), 1, slab_log);
+}
+
+#else
+# define slab_log_alloc(SLAB, SIZE)
+# define slab_log_free(SLAB, SIZE)
+# define slab_log_unmap(SLAB)
+#endif
+
+#if (SLAB_DEBUG & SLAB_DEBUG_PERMS) && defined(HAVE_MPROTECT)
+
+static void
+slab_protect(struct slab *slab)
+{
+    mprotect(slab, (char*)(slab + 1) - (char*)slab->base, PROT_NONE);
+}
+
+static void
+slab_unprotect(struct slab *slab)
+{
+    mprotect(slab, (char*)(slab + 1) - (char*)slab->base, PROT_READ | PROT_WRITE);
+}
+
+#else
+# define slab_protect(SLAB) (void)(SLAB)
+# define slab_unprotect(SLAB) (void)(SLAB)
+#endif
+
 static size_t
 slab_pagesize(void)
 {
@@ -205,6 +295,7 @@ slab_alloc(struct slabset *sset)
         /* Allocate new slab. */
         if (free_slab_head) {
             slab = free_slab_head;
+            slab_unprotect(slab);
             if (!(free_slab_head = slab->next))
                 free_slab_tail = NULL;
         } else {
@@ -213,6 +304,7 @@ slab_alloc(struct slabset *sset)
             slab->base = item;
             slab_count++;
         }
+        slab_log_alloc(slab, sset->size);
 
         /* Populate free list. */
         step = (sset->size + SLAB_ALIGN - 1) & ~(SLAB_ALIGN - 1);
@@ -293,6 +385,8 @@ slab_unalloc(void *ptr, size_t size)
         assert(!slab->next || slab == slab->next->prev);
         assert(!slab->prev || slab == slab->prev->next);
     } else if (!slab->used) {
+        slab_log_free(slab, size);
+
         /* Unlink slab from its parent. */
         slab->parent->nslabs--;
         if (slab->prev)
@@ -320,8 +414,11 @@ slab_unalloc(void *ptr, size_t size)
             free_slab_tail = tslab;
             if (!free_slab_head)
                 free_slab_head = tslab;
-            else
+            else {
+                slab_unprotect(tslab->prev);
                 tslab->prev->next = tslab;
+                slab_protect(tslab->prev);
+            }
             free_slab_count++;
             slab_count++;
         }
@@ -331,11 +428,14 @@ slab_unalloc(void *ptr, size_t size)
         slab->parent = NULL;
         slab->next = NULL;
         slab->prev = free_slab_tail;
-        free_slab_tail = slab;
-        if (free_slab_head)
+        if (slab->prev) {
+            slab_unprotect(slab->prev);
             slab->prev->next = slab;
-        else
+            slab_protect(slab->prev);
+        } else
             free_slab_head = slab;
+        slab_protect(slab);
+        free_slab_tail = slab;
         free_slab_count++;
 
 #if MAX_SLAB_FREE >= 0
@@ -345,13 +445,17 @@ slab_unalloc(void *ptr, size_t size)
             struct slab *tslab;
 
             tslab = free_slab_tail;
+            slab_unprotect(tslab);
             free_slab_tail = tslab->prev;
-            if (tslab->prev)
+            if (tslab->prev) {
+                slab_unprotect(tslab->prev);
                 tslab->prev->next = NULL;
-            else
+                slab_protect(tslab->prev);
+            } else
                 free_slab_head = NULL;
             free_slab_count--;
             slab_count--;
+            slab_log_unmap(slab);
             munmap(slab->base, slab_pagesize());
         }
 #endif
@@ -375,8 +479,9 @@ slab_malloc(const char *file, unsigned int line, size_t size)
         res = slab_map(slab_round_up(real));
         big_alloc_count++;
         big_alloc_size += size;
+        slab_log_alloc(res, size);
     }
-#if SLAB_DEBUG
+#if SLAB_DEBUG & SLAB_DEBUG_HEADER
     res->file_id = get_file_id(file);
     res->size = size;
     res->line = line;
@@ -400,7 +505,7 @@ slab_realloc(const char *file, unsigned int line, void *ptr, size_t size)
 
     verify(ptr);
     orig = (alloc_header_t*)ptr - 1;
-#if SLAB_DEBUG
+#if SLAB_DEBUG & SLAB_DEBUG_HEADER
     osize = orig->size;
 #else
     osize = *orig;
@@ -435,7 +540,7 @@ slab_free(const char *file, unsigned int line, void *ptr)
         return;
     verify(ptr);
     hdr = (alloc_header_t*)ptr - 1;
-#if SLAB_DEBUG
+#if SLAB_DEBUG & SLAB_DEBUG_HEADER
     hdr->file_id = get_file_id(file);
     hdr->line = line;
     hdr->magic = FREE_MAGIC;
@@ -454,6 +559,7 @@ slab_free(const char *file, unsigned int line, void *ptr)
         munmap(hdr, slab_round_up(real));
         big_alloc_count--;
         big_alloc_size -= user;
+        slab_log_unmap(hdr);
     }
 }
 
@@ -469,7 +575,7 @@ verify(const void *ptr)
         return;
 
     hdr = (alloc_header_t*)ptr - 1;
-#if SLAB_DEBUG
+#if SLAB_DEBUG & SLAB_DEBUG_HEADER
     real = hdr->size + sizeof(*hdr);
     assert(hdr->file_id < file_ids_used);
     assert(hdr->magic == ALLOC_MAGIC);
diff --git a/src/slab-read.c b/src/slab-read.c
new file mode 100644 (file)
index 0000000..485aa8b
--- /dev/null
@@ -0,0 +1,57 @@
+#include "compat.h"
+
+struct slab_log_entry
+{
+    struct timeval tv;
+    void *slab;
+    ssize_t size;
+};
+
+static void
+read_log_file(const char *name)
+{
+    struct slab_log_entry sle;
+    FILE *log;
+
+    log = fopen(name, "r");
+    if (!log)
+    {
+        fprintf(stderr, "Unable to open %s: %s\n", name, strerror(errno));
+        return;
+    }
+
+    while (fread(&sle, sizeof(sle), 1, log) == 1)
+    {
+        fprintf(stdout, "%ld.%06ld %p ", (long)sle.tv.tv_sec, (long)sle.tv.tv_usec, sle.slab);
+        if (sle.size > 0)
+        {
+            fprintf(stdout, "-> %zd\n", sle.size);
+        }
+        else if (sle.size < 0)
+        {
+            fprintf(stdout, "<- %zd\n", -sle.size);
+        }
+        else /* slze.size == 0 */
+        {
+            fprintf(stdout, "unmap\n");
+        }
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    int ii;
+
+    if (argc < 2)
+    {
+        fprintf(stderr, "Usage: %s <logfile ...>\n", argv[0]);
+        return 1;
+    }
+
+    for (ii = 1; ii < argc; ++ii)
+    {
+        read_log_file(argv[ii]);
+    }
+    return 0;
+}