[core] change memory profiler output, add minsize parameter to debug.profiler_dump action
parent
60fc8d421d
commit
011380dcb7
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue