From 11ba875075db4905ec73226e3c09e8ad1b7965d4 Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Thu, 13 Dec 2007 23:47:10 -0500 Subject: [PATCH] Greatly enhance the slab allocator. 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 | 2 +- src/Makefile.am | 3 +- src/alloc-slab.c | 130 ++++++++++++++++++++++++++++++++++++++++++----- src/slab-read.c | 57 +++++++++++++++++++++ 4 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 src/slab-read.c diff --git a/configure.in b/configure.in index f9d3bf7..31c82aa 100644 --- a/configure.in +++ b/configure.in @@ -83,7 +83,7 @@ AC_CHECK_MEMBER([struct addrinfo.ai_flags], #include ]) 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 diff --git a/src/Makefile.am b/src/Makefile.am index 155ccd6..76b4ca3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/alloc-slab.c b/src/alloc-slab.c index 43c8575..ac6731a 100644 --- a/src/alloc-slab.c +++ b/src/alloc-slab.c @@ -25,8 +25,12 @@ # 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 index 0000000..485aa8b --- /dev/null +++ b/src/slab-read.c @@ -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 \n", argv[0]); + return 1; + } + + for (ii = 1; ii < argc; ++ii) + { + read_log_file(argv[ii]); + } + return 0; +} -- 2.20.1