the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
5.3 KiB
231 lines
5.3 KiB
|
|
/* lighty memory profiler |
|
* counts how many times malloc/realloc/free have been called and the amounts of bytes allocated/freed |
|
* TODO: move hashtable to utils.c, optimize hashtable? implementation is very basic |
|
*/ |
|
|
|
|
|
#include "base.h" |
|
#include "profiler.h" |
|
|
|
#define PROFILER_HASHTABLE_SIZE 1024 |
|
|
|
|
|
static profiler_mem stats_mem = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
|
static GStaticMutex profiler_mutex = G_STATIC_MUTEX_INIT; |
|
static gboolean profiler_enabled = FALSE; |
|
|
|
|
|
struct profiler_entry { |
|
gpointer addr; |
|
gsize len; |
|
struct profiler_entry *next; |
|
}; |
|
typedef struct profiler_entry profiler_entry; |
|
|
|
static struct { |
|
profiler_entry **nodes; |
|
} profiler_hashtable; |
|
|
|
static void profiler_hashtable_init() { |
|
profiler_hashtable.nodes = calloc(1, sizeof(profiler_entry*) * PROFILER_HASHTABLE_SIZE); |
|
} |
|
|
|
static profiler_entry *profiler_hashtable_find(gpointer addr) { |
|
guint h = (gsize)addr % PROFILER_HASHTABLE_SIZE; |
|
|
|
for (profiler_entry *e = profiler_hashtable.nodes[h]; e != NULL; e = e->next) { |
|
if (e->addr == addr) |
|
return e; |
|
} |
|
assert(NULL); |
|
return NULL; |
|
} |
|
|
|
static void profiler_hashtable_insert(gpointer addr, gsize len) { |
|
profiler_entry *e = malloc(sizeof(profiler_entry)); |
|
|
|
e->addr = addr; |
|
e->len = len; |
|
e->next = NULL; |
|
|
|
guint h = (gsize)addr % PROFILER_HASHTABLE_SIZE; |
|
|
|
if (profiler_hashtable.nodes[h] == NULL) { |
|
profiler_hashtable.nodes[h] = e; |
|
return; |
|
} |
|
|
|
for (profiler_entry *ec = profiler_hashtable.nodes[h];; ec = ec->next) { |
|
if (ec->next == NULL) { |
|
ec->next = e; |
|
return; |
|
} |
|
} |
|
} |
|
|
|
static void profiler_hashtable_remove(gpointer addr) { |
|
guint h = (gsize)addr % PROFILER_HASHTABLE_SIZE; |
|
profiler_entry *prev = profiler_hashtable.nodes[h]; |
|
|
|
if (!prev) |
|
return; |
|
|
|
if (prev->addr == addr) { |
|
if (prev->next) |
|
profiler_hashtable.nodes[h] = prev->next; |
|
else |
|
profiler_hashtable.nodes[h] = NULL; |
|
free(prev); |
|
return; |
|
} |
|
|
|
for (profiler_entry *e = prev->next; e != NULL; e = e->next) { |
|
if (e->addr == addr) { |
|
prev->next = e->next; |
|
free(e); |
|
return; |
|
} |
|
prev = e; |
|
} |
|
} |
|
|
|
static gpointer profiler_try_malloc(gsize n_bytes) { |
|
/* we alloc sizeof(gsize) bytes more to hold n_bytes */ |
|
gsize *p; |
|
|
|
p = malloc(n_bytes); |
|
|
|
if (p) { |
|
g_static_mutex_lock(&profiler_mutex); |
|
profiler_hashtable_insert(p, n_bytes); |
|
stats_mem.alloc_times++; |
|
stats_mem.alloc_bytes += n_bytes; |
|
stats_mem.inuse_bytes += n_bytes; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
} |
|
|
|
return p; |
|
} |
|
|
|
static gpointer profiler_malloc(gsize n_bytes) { |
|
gpointer p = profiler_try_malloc(n_bytes); |
|
|
|
assert(p); |
|
|
|
return p; |
|
} |
|
|
|
static gpointer profiler_try_realloc(gpointer mem, gsize n_bytes) { |
|
gsize l; |
|
gsize *p = mem; |
|
|
|
if (!mem) { |
|
p = malloc(n_bytes); |
|
g_static_mutex_lock(&profiler_mutex); |
|
stats_mem.alloc_times++; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
l = 0; |
|
} |
|
else { |
|
p = realloc(p, n_bytes); |
|
g_static_mutex_lock(&profiler_mutex); |
|
profiler_entry *e = profiler_hashtable_find(mem); |
|
l = e->len; |
|
profiler_hashtable_remove(mem); |
|
g_static_mutex_unlock(&profiler_mutex); |
|
} |
|
|
|
if (p) { |
|
g_static_mutex_lock(&profiler_mutex); |
|
profiler_hashtable_insert(p, n_bytes); |
|
stats_mem.realloc_times++; |
|
stats_mem.realloc_bytes += n_bytes; |
|
stats_mem.inuse_bytes += n_bytes - l; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
} |
|
|
|
return p; |
|
} |
|
|
|
static gpointer profiler_realloc(gpointer mem, gsize n_bytes) { |
|
gpointer p = profiler_try_realloc(mem, n_bytes); |
|
|
|
assert(p); |
|
|
|
return p; |
|
} |
|
|
|
static gpointer profiler_calloc(gsize n_blocks, gsize n_bytes) { |
|
/* we alloc sizeof(gsize) bytes more to hold n_blocks*n_bytes */ |
|
gsize *p; |
|
|
|
gsize l = n_blocks * n_bytes; |
|
|
|
p = calloc(1, l); |
|
|
|
if (p) { |
|
g_static_mutex_lock(&profiler_mutex); |
|
profiler_hashtable_insert(p, l); |
|
stats_mem.calloc_times++; |
|
stats_mem.calloc_bytes += l; |
|
stats_mem.inuse_bytes += l; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
} |
|
|
|
assert(p); |
|
|
|
return p; |
|
} |
|
|
|
static void profiler_free(gpointer mem) { |
|
gsize *p = mem; |
|
|
|
assert(p); |
|
profiler_entry *e = profiler_hashtable_find(mem); |
|
g_static_mutex_lock(&profiler_mutex); |
|
stats_mem.free_times++; |
|
stats_mem.free_bytes += e->len; |
|
stats_mem.inuse_bytes -= e->len; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
free(p); |
|
profiler_hashtable_remove(mem); |
|
} |
|
|
|
/* public functions */ |
|
void profiler_enable() { |
|
GMemVTable t; |
|
|
|
if (profiler_enabled) |
|
return; |
|
|
|
profiler_enabled = TRUE; |
|
|
|
profiler_hashtable_init(); |
|
|
|
t.malloc = profiler_malloc; |
|
t.realloc = profiler_realloc; |
|
t.free = profiler_free; |
|
|
|
t.calloc = profiler_calloc; |
|
t.try_malloc = profiler_try_malloc; |
|
t.try_realloc = profiler_try_realloc; |
|
|
|
g_mem_set_vtable(&t); |
|
} |
|
|
|
void profiler_dump() { |
|
if (!profiler_enabled) |
|
return; |
|
|
|
g_static_mutex_lock(&profiler_mutex); |
|
profiler_mem s = stats_mem; |
|
g_static_mutex_unlock(&profiler_mutex); |
|
|
|
g_print("--- memory profiler stats ---\n"); |
|
g_print("malloc(): called %" G_GUINT64_FORMAT " times, %" G_GUINT64_FORMAT " bytes total\n", s.alloc_times, s.alloc_bytes); |
|
g_print("calloc(): called %" G_GUINT64_FORMAT " times, %" G_GUINT64_FORMAT " bytes total\n", s.calloc_times, s.calloc_bytes); |
|
g_print("realloc(): called %" G_GUINT64_FORMAT " times, %" G_GUINT64_FORMAT " bytes total\n", s.realloc_times, s.realloc_bytes); |
|
g_print("free(): called %" G_GUINT64_FORMAT " times, %" G_GUINT64_FORMAT " bytes total\n", s.free_times, s.free_bytes); |
|
g_print("memory remaining: %" G_GUINT64_FORMAT " bytes, %" G_GUINT64_FORMAT " calls to free()\n", s.inuse_bytes, s.alloc_times + s.calloc_times - s.free_times); |
|
}
|
|
|