aboutsummaryrefslogtreecommitdiff
path: root/src/chattymalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/chattymalloc.c')
-rw-r--r--src/chattymalloc.c268
1 files changed, 191 insertions, 77 deletions
diff --git a/src/chattymalloc.c b/src/chattymalloc.c
index 8614a27..bde18d4 100644
--- a/src/chattymalloc.c
+++ b/src/chattymalloc.c
@@ -1,108 +1,221 @@
+/*
+Copyright 2018-2020 Florian Fischer <florian.fl.fischer@fau.de>
+
+This file is part of allocbench.
+
+allocbench is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+allocbench is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with allocbench. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <unistd.h>
+#include "chattymalloc.h"
+
+#define unlikely(x) __builtin_expect((x),0)
+
+#define GROWTH_THRESHOLD 4096
+#define GROWTH_ENTRIES 100000
+
+// flag to stop recursion during bootstrap
+static int initializing = 0;
+
+// memory to bootstrap malloc
static char tmpbuff[4096];
static unsigned long tmppos = 0;
static unsigned long tmpallocs = 0;
-static int out = -1;
+// global log file descriptor
+static int out_fd = -1;
+// memory mapping of our output file
+static volatile trace_t* out[2] = {NULL, NULL};
+// next free index into the mapped buffer
+static volatile uint64_t next_entry = 0;
+// current size of our log file / mapping
+static volatile uint64_t total_entries = 0;
+
+// pthread mutex and cond to protect growth of the buffer
+static pthread_cond_t growing;
+static pthread_mutex_t growth_mutex;
+
+__thread pid_t tid = 0;
/*=========================================================
* intercepted functions
*/
-static void* (*myfn_malloc)(size_t size);
-static void (*myfn_free)(void* ptr);
-static void* (*myfn_calloc)(size_t nmemb, size_t size);
-static void* (*myfn_realloc)(void* ptr, size_t size);
-static void* (*myfn_memalign)(size_t alignment, size_t size);
-static int (*myfn_posix_memalign)(void** memptr, size_t alignment, size_t size);
-static void* (*myfn_valloc)(size_t size);
-static void* (*myfn_pvalloc)(size_t size);
-static void* (*myfn_aligned_alloc)(size_t alignment, size_t size);
-static int (*myfn_malloc_stats)();
+static void* (*next_malloc)(size_t size);
+static void (*next_free)(void* ptr);
+static void* (*next_calloc)(size_t nmemb, size_t size);
+static void* (*next_realloc)(void* ptr, size_t size);
+static void* (*next_memalign)(size_t alignment, size_t size);
+static int (*next_posix_memalign)(void** memptr, size_t alignment, size_t size);
+static void* (*next_valloc)(size_t size);
+static void* (*next_pvalloc)(size_t size);
+static void* (*next_aligned_alloc)(size_t alignment, size_t size);
+static int (*next_malloc_stats)();
+
+static void
+grow_trace()
+{
+ pthread_mutex_lock(&growth_mutex);
+
+ size_t old_buf_idx;
+ if (unlikely(total_entries == 0))
+ old_buf_idx = 0;
+ else
+ old_buf_idx = ((total_entries) / GROWTH_ENTRIES) % 2;
+ size_t new_buf_size = (total_entries + GROWTH_ENTRIES) * sizeof(trace_t);
+
+ /* remap old buffer
+ * hopefully no thread uses the old buffer anymore!
+ */
+ if (out[old_buf_idx] == NULL) {
+ out[old_buf_idx] = (trace_t*) mmap(NULL, new_buf_size, PROT_WRITE,
+ MAP_FILE | MAP_SHARED, out_fd, 0);
+ if (out[old_buf_idx] == MAP_FAILED) {
+ perror("mapping new buf failed");
+ exit(1);
+ }
+ } else {
+ size_t old_buf_size = (total_entries - GROWTH_ENTRIES) * sizeof(trace_t);
+ out[old_buf_idx] = (trace_t*) mremap((void*) out[old_buf_idx], old_buf_size,
+ new_buf_size, MREMAP_MAYMOVE);
+ if (out[old_buf_idx] == MAP_FAILED) {
+ perror("remapping old buf failed");
+ exit(1);
+ }
+ }
+
+
+ if(ftruncate(out_fd, new_buf_size) != 0) {
+ perror("extending file failed");
+ exit(1);
+ }
+
+ total_entries += GROWTH_ENTRIES;
+ pthread_cond_broadcast(&growing);
+ pthread_mutex_unlock(&growth_mutex);
+}
static void
-write_output(const char* fmt, ...)
+write_trace(char func, void* ptr, size_t size, size_t var_arg)
{
- static int prevent_recursion = 0;
+ if (unlikely(tid == 0)) {
+ tid = gettid();
+ }
- if (!prevent_recursion) {
- prevent_recursion = 1;
+ uint64_t idx = __atomic_fetch_add (&next_entry, 1, __ATOMIC_SEQ_CST);
+ if (idx == total_entries - GROWTH_THRESHOLD) {
+ grow_trace();
+ // wait for growth completion
+ } else if (idx >= total_entries) {
+ pthread_mutex_lock(&growth_mutex);
+ while(idx >= total_entries)
+ pthread_cond_wait(&growing, &growth_mutex);
+ pthread_mutex_unlock(&growth_mutex);
+ }
- /* lockf(out, F_LOCK, 0); */
+ volatile trace_t* trace = &out[(idx / GROWTH_ENTRIES) % 2][idx];
- va_list args;
- va_start(args, fmt);
- vdprintf(out, fmt, args);
- va_end(args);
+ trace->tid = tid;
+ trace->func = func;
+ trace->ptr = ptr;
+ trace->size = size;
+ trace->var_arg = var_arg;
+}
- /* lockf(out, F_ULOCK, 0); */
- prevent_recursion = 0;
+static void
+trim_trace()
+{
+ uint64_t cur_size = next_entry * sizeof(trace_t);
+ if(ftruncate(out_fd, cur_size) != 0) {
+ perror("trimming file failed");
}
+ close(out_fd);
}
static void __attribute__((constructor))
init()
{
+ initializing = 1;
char* fname = getenv("CHATTYMALLOC_FILE");
if (fname == NULL)
- fname = "chattymalloc.txt";
+ fname = "chattymalloc.trace";
- out = open(fname,
- O_WRONLY | O_TRUNC | O_CREAT,
+ out_fd = open(fname,
+ O_RDWR | O_TRUNC | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (out == -1) {
+ if (out_fd == -1) {
perror("opening output file");
exit(1);
}
-
- myfn_malloc = dlsym(RTLD_NEXT, "malloc");
- myfn_free = dlsym(RTLD_NEXT, "free");
- myfn_calloc = dlsym(RTLD_NEXT, "calloc");
- myfn_realloc = dlsym(RTLD_NEXT, "realloc");
- myfn_memalign = dlsym(RTLD_NEXT, "memalign");
- myfn_posix_memalign = dlsym(RTLD_NEXT, "posix_memalign");
- myfn_valloc = dlsym(RTLD_NEXT, "valloc");
- myfn_pvalloc = dlsym(RTLD_NEXT, "pvalloc");
- myfn_aligned_alloc = dlsym(RTLD_NEXT, "aligned_alloc");
- myfn_malloc_stats = dlsym(RTLD_NEXT, "malloc_stats");
-
- if (!myfn_malloc || !myfn_free || !myfn_calloc || !myfn_realloc ||
- !myfn_memalign) {
+
+ pthread_cond_init(&growing, NULL);
+ pthread_mutex_init(&growth_mutex, NULL);
+
+ // init trace buffer
+ grow_trace();
+
+ next_malloc = dlsym(RTLD_NEXT, "malloc");
+ next_free = dlsym(RTLD_NEXT, "free");
+ next_calloc = dlsym(RTLD_NEXT, "calloc");
+ next_realloc = dlsym(RTLD_NEXT, "realloc");
+ next_memalign = dlsym(RTLD_NEXT, "memalign");
+ next_posix_memalign = dlsym(RTLD_NEXT, "posix_memalign");
+ next_valloc = dlsym(RTLD_NEXT, "valloc");
+ next_pvalloc = dlsym(RTLD_NEXT, "pvalloc");
+ next_aligned_alloc = dlsym(RTLD_NEXT, "aligned_alloc");
+ next_malloc_stats = dlsym(RTLD_NEXT, "malloc_stats");
+
+ if (!next_malloc || !next_free || !next_calloc || !next_realloc ||
+ !next_memalign) {
fprintf(stderr, "Can't load core functions with `dlsym`: %s\n", dlerror());
exit(1);
}
- if (!myfn_posix_memalign)
+ if (!next_posix_memalign)
fprintf(stderr, "Can't load posix_memalign with `dlsym`: %s\n", dlerror());
- if (!myfn_valloc)
+ if (!next_valloc)
fprintf(stderr, "Can't load valloc with `dlsym`: %s\n", dlerror());
- if (!myfn_pvalloc)
+ if (!next_pvalloc)
fprintf(stderr, "Can't load pvalloc with `dlsym`: %s\n", dlerror());
- if (!myfn_aligned_alloc)
+ if (!next_aligned_alloc)
fprintf(stderr, "Can't load aligned_alloc with `dlsym`: %s\n", dlerror());
- if (!myfn_malloc_stats)
+ if (!next_malloc_stats)
fprintf(stderr, "Can't load malloc_stats with `dlsym`: %s\n", dlerror());
+
+ atexit(trim_trace);
+ initializing = 0;
}
void*
malloc(size_t size)
{
- static int initializing = 0;
- if (myfn_malloc == NULL) {
+ if (next_malloc == NULL) {
if (!initializing) {
- initializing = 1;
init();
- initializing = 0;
} else {
void* retptr = tmpbuff + tmppos;
@@ -121,8 +234,8 @@ malloc(size_t size)
}
}
- void* ptr = myfn_malloc(size);
- write_output("m %zu %p\n", size, ptr);
+ void* ptr = next_malloc(size);
+ write_trace(MALLOC, ptr, size, 0);
return ptr;
}
@@ -130,18 +243,18 @@ void
free(void* ptr)
{
// something wrong if we call free before one of the allocators!
- if (myfn_malloc == NULL)
+ if (next_malloc == NULL)
init();
if (!(ptr >= (void*)tmpbuff && ptr <= (void*)(tmpbuff + tmppos))) {
- write_output("f %p\n", ptr);
- myfn_free(ptr);
+ write_trace(FREE, ptr, 0, 0);
+ next_free(ptr);
}
}
void*
realloc(void* ptr, size_t size)
{
- if (myfn_realloc == NULL) {
+ if (next_realloc == NULL) {
void* nptr = malloc(size);
if (nptr && ptr) {
memmove(nptr, ptr, size);
@@ -150,98 +263,99 @@ realloc(void* ptr, size_t size)
return nptr;
}
- void* nptr = myfn_realloc(ptr, size);
- write_output("r %p %zu %p\n", ptr, size, nptr);
+ void* nptr = next_realloc(ptr, size);
+ write_trace(REALLOC, nptr, size, (size_t)ptr);
return nptr;
}
+
void*
calloc(size_t nmemb, size_t size)
{
- if (myfn_calloc == NULL) {
+ if (next_calloc == NULL) {
void* ptr = malloc(nmemb * size);
if (ptr)
memset(ptr, 0, nmemb * size);
return ptr;
}
- void* ptr = myfn_calloc(nmemb, size);
- write_output("c %zu %zu %p\n", nmemb, size, ptr);
+ void* ptr = next_calloc(nmemb, size);
+ write_trace(CALLOC, ptr, size, nmemb);
return ptr;
}
void*
memalign(size_t alignment, size_t size)
{
- if (myfn_memalign == NULL) {
+ if (next_memalign == NULL) {
fprintf(stderr, "called memalign before or during init\n");
exit(1);
}
- void* ptr = myfn_memalign(alignment, size);
- write_output("ma %zu %zu %p\n", alignment, size, ptr);
+ void* ptr = next_memalign(alignment, size);
+ write_trace(MEMALIGN, ptr, size, alignment);
return ptr;
}
int
posix_memalign(void** memptr, size_t alignment, size_t size)
{
- if (myfn_posix_memalign == NULL) {
+ if (next_posix_memalign == NULL) {
fprintf(stderr, "called posix_memalign before or during init\n");
exit(1);
}
- int ret = myfn_posix_memalign(memptr, alignment, size);
- write_output("p_ma %p %zu %zu %d\n", *memptr, alignment, size, ret);
+ int ret = next_posix_memalign(memptr, alignment, size);
+ write_trace(POSIX_MEMALIGN, *memptr, size, alignment);
return ret;
}
void*
valloc(size_t size)
{
- if (myfn_valloc == NULL) {
+ if (next_valloc == NULL) {
fprintf(stderr, "called valloc before or during init");
exit(1);
}
- void* ptr = myfn_valloc(size);
- write_output("v %zu %p\n", size, ptr);
+ void* ptr = next_valloc(size);
+ write_trace(VALLOC, ptr, size, 0);
return ptr;
}
void*
pvalloc(size_t size)
{
- if (myfn_pvalloc == NULL) {
+ if (next_pvalloc == NULL) {
fprintf(stderr, "called pvalloc before or during init\n");
exit(1);
}
- void* ptr = myfn_pvalloc(size);
- write_output("pv %zu %p\n", size, ptr);
+ void* ptr = next_pvalloc(size);
+ write_trace(PVALLOC, ptr, size, 0);
return ptr;
}
void*
aligned_alloc(size_t alignment, size_t size)
{
- if (myfn_aligned_alloc == NULL) {
+ if (next_aligned_alloc == NULL) {
fprintf(stderr, "called aligned_alloc before or during init\n");
exit(1);
}
- void* ptr = myfn_pvalloc(size);
- write_output("a_m %zu %zu %p\n", alignment, size, ptr);
+ void* ptr = next_aligned_alloc(alignment, size);
+ write_trace(ALIGNED_ALLOC, ptr, size, alignment);
return ptr;
}
int
malloc_stats()
{
- if (myfn_malloc_stats == NULL) {
+ if (next_malloc_stats == NULL) {
fprintf(stderr, "called malloc_stats before or during init\n");
exit(1);
}
fprintf(stderr, "chattymalloc by muhq\n");
- return myfn_malloc_stats();
+ return next_malloc_stats();
}