From 011380dcb7a21625678451dd98c29dcce8ec529b Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Tue, 15 Nov 2011 02:01:35 +0100 Subject: [PATCH] [core] change memory profiler output, add minsize parameter to debug.profiler_dump action --- include/lighttpd/profiler.h | 3 +- src/main/lighttpd_worker.c | 3 +- src/main/profiler.c | 151 ++++++++++++++++++++++++++++++------ src/modules/mod_debug.c | 20 +++-- 4 files changed, 141 insertions(+), 36 deletions(-) diff --git a/include/lighttpd/profiler.h b/include/lighttpd/profiler.h index 7e8a93b..cea9999 100644 --- a/include/lighttpd/profiler.h +++ b/include/lighttpd/profiler.h @@ -17,7 +17,6 @@ struct liProfilerMem { LI_API void li_profiler_enable(gchar *output_path); /* enables the profiler */ LI_API void li_profiler_finish(); -LI_API void li_profiler_dump(); /* dumps memory statistics to stdout */ -LI_API void li_profiler_dump_table(); +LI_API void li_profiler_dump(gint minsize); /* dumps memory statistics to file specified in LI_PROFILE_MEM env var */ #endif diff --git a/src/main/lighttpd_worker.c b/src/main/lighttpd_worker.c index 25c3f57..0638468 100644 --- a/src/main/lighttpd_worker.c +++ b/src/main/lighttpd_worker.c @@ -55,8 +55,7 @@ int main(int argc, char *argv[]) { /*g_mem_set_vtable(glib_mem_profiler_table);*/ li_profiler_enable(profile_mem); atexit(li_profiler_finish); - atexit(li_profiler_dump); - /*atexit(profiler_dump_table);*/ + /*atexit(li_profiler_dump);*/ } } #endif diff --git a/src/main/profiler.c b/src/main/profiler.c index dbd222a..9f1fc08 100644 --- a/src/main/profiler.c +++ b/src/main/profiler.c @@ -21,17 +21,27 @@ #endif #define PROFILER_HASHTABLE_SIZE 65521 - +#define PROFILER_STACKFRAMES 36 typedef struct profiler_block profiler_block; struct profiler_block { gpointer addr; gsize size; profiler_block *next; - void *stackframes[12]; + void *stackframes[PROFILER_STACKFRAMES]; gint stackframes_num; }; +typedef struct profiler_stackframe profiler_stackframe; +struct profiler_stackframe { + gpointer addr; + gsize size; + guint blocks; + gchar *symbol; + profiler_stackframe *next; + profiler_stackframe *children; +}; + static void profiler_free(gpointer addr); @@ -80,7 +90,7 @@ static void profiler_hashtable_insert(gpointer addr, gsize size) { block->addr = addr; block->size = size; #ifdef HAVE_EXECINFO_H - block->stackframes_num = backtrace(block->stackframes, 12); + block->stackframes_num = backtrace(block->stackframes, PROFILER_STACKFRAMES); #endif block->next = profiler_hashtable[hash % PROFILER_HASHTABLE_SIZE]; @@ -276,14 +286,75 @@ void li_profiler_finish() { free(profiler_hashtable); } -void li_profiler_dump() { - profiler_block *block; - guint i; - gint len; + +static void profiler_dump_frame(guint level, profiler_stackframe *frame, gsize minsize) { gchar str[1024]; + gint len; + gboolean swapped; + profiler_stackframe *f; + + /* sort this tree level according to total allocated size. yes, bubblesort. */ + do { + profiler_stackframe *f1, *f2; + + swapped = FALSE; + + for (f1 = frame, f2 = frame->next; f1 != NULL && f2 != NULL; f1 = f2, f2 = f2->next) { + if (f2->size > f1->size) { + profiler_stackframe tmp = *f1; + + f1->addr = f2->addr; + f1->blocks = f2->blocks; + f1->size = f2->size; + f1->children = f2->children; + f1->symbol = f2->symbol; + + f2->addr = tmp.addr; + f2->blocks = tmp.blocks; + f2->size = tmp.size; + f2->children = tmp.children; + f2->symbol = tmp.symbol; + + swapped = TRUE; + } + } + } while (swapped); + + while (frame) { + if (frame->size >= minsize) { + /* indention */ + memset(str, ' ', level*4); + profiler_write(str, level*4); + len = sprintf(str, + "%"G_GSIZE_FORMAT" %s in %u blocks @ %p %s\n", + (frame->size > 1024) ? frame->size / 1024 : frame->size, + (frame->size > 1024) ? "kilobytes" : "bytes", + frame->blocks, + frame->addr, frame->symbol + ); + profiler_write(str, len); + } + + if (frame->children) + profiler_dump_frame(level+1, frame->children, minsize); + + f = frame->next; + free(frame->symbol); + free(frame); + frame = f; + } +} + +void li_profiler_dump(gint minsize) { + profiler_stackframe *tree_cur, *frame; + gchar **symbols; + gint i, j, len; + profiler_block *block; struct stat st; - gsize leaked_size = 0; - guint leaked_num = 0; + gchar str[1024]; + gsize total_size = 0; + guint total_blocks = 0; + profiler_stackframe *tree = calloc(1, sizeof(profiler_stackframe)); g_static_mutex_lock(&profiler_mutex); @@ -295,33 +366,63 @@ void li_profiler_dump() { for (i = 0; i < PROFILER_HASHTABLE_SIZE; i++) { for (block = profiler_hashtable[i]; block != NULL; block = block->next) { - leaked_num++; - leaked_size += block->size; - len = sprintf(str, "--------------- unfreed block of %"G_GSIZE_FORMAT" bytes @ %p ---------------\n", block->size, block->addr); - profiler_write(str, len); + total_size += block->size; + total_blocks++; + #ifdef HAVE_EXECINFO_H - backtrace_symbols_fd(block->stackframes, block->stackframes_num, profiler_output_fd); + /* resolve all symbols */ + symbols = backtrace_symbols(block->stackframes, block->stackframes_num); + + tree_cur = tree; + + for (j = block->stackframes_num-1; j >= 0; j--) { + for (frame = tree_cur->children; frame != NULL; frame = frame->next) { + if (block->stackframes[j] == frame->addr) { + frame->blocks++; + frame->size += block->size; + break; + } + } + + if (!frame) { + frame = malloc(sizeof(profiler_stackframe)); + frame->addr = block->stackframes[j]; + frame->blocks = 1; + frame->size = block->size; + frame->symbol = strdup(symbols[j]); + frame->children = NULL; + frame->next = tree_cur->children; + tree_cur->children = frame; + } + + tree_cur = frame; + } + + free(symbols); #endif + } } +#ifdef HAVE_EXECINFO_H + profiler_dump_frame(0, tree->children, minsize); +#endif + len = sprintf(str, - "--------------- memory profiler stats ---------------\n" - "leaked objects:\t\t%u\n" - "leaked bytes:\t\t%"G_GSIZE_FORMAT" %s\n", - leaked_num, - (leaked_size > 1024) ? leaked_size / 1024 : leaked_size, - (leaked_size > 1024) ? "kilobytes" : "bytes" + "--------------- memory profiler summary ---------------\n" + "total blocks: %u\n" + "total size: %"G_GSIZE_FORMAT" %s\n", + total_blocks, + (total_size > 1024) ? total_size / 1024 : total_size, + (total_size > 1024) ? "kilobytes" : "bytes" ); profiler_write(str, len); len = sprintf(str, "--------------- memory profiler dump end ---------------\n"); + + free(tree); + profiler_write(str, len); g_static_mutex_unlock(&profiler_mutex); - -} - -void li_profiler_dump_table() { - } diff --git a/src/modules/mod_debug.c b/src/modules/mod_debug.c index aee331d..b999783 100644 --- a/src/modules/mod_debug.c +++ b/src/modules/mod_debug.c @@ -28,7 +28,7 @@ * - more debug actions (for other stuff than connections) * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * License: * MIT, see COPYING file in the lighttpd 2 tree */ @@ -333,25 +333,31 @@ static liAction* debug_show_connections_create(liServer *srv, liWorker *wrk, liP #ifdef WITH_PROFILER static liHandlerResult debug_profiler_dump(liVRequest *vr, gpointer param, gpointer *context) { - UNUSED(vr); UNUSED(param); UNUSED(context); + gint minsize = GPOINTER_TO_INT(param); + + UNUSED(vr); UNUSED(context); if (!getenv("LIGHTY_PROFILE_MEM")) return LI_HANDLER_GO_ON; - li_profiler_dump(); + li_profiler_dump(minsize); return LI_HANDLER_GO_ON; } static liAction* debug_profiler_dump_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) { - UNUSED(wrk); UNUSED(userdata); + gpointer ptr; - if (val) { - ERROR(srv, "%s", "debug.profiler_dump doesn't expect any parameters"); + UNUSED(wrk); UNUSED(p); UNUSED(userdata); + + if (val && val->type != LI_VALUE_NUMBER) { + ERROR(srv, "%s", "debug.profiler_dump takes an optional integer (minsize) as parameter"); return NULL; } - return li_action_new_function(debug_profiler_dump, NULL, NULL, p); + ptr = GINT_TO_POINTER(val ? val->data.number : 10240); + + return li_action_new_function(debug_profiler_dump, NULL, NULL, ptr); } #endif