From: Timo Sirainen Date: Mon, 16 Nov 2020 18:54:20 +0000 (+0200) Subject: lib: data-stack - Add data_stack_grow event X-Git-Tag: 2.3.16~243 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0e3652c4ea5b34fe1d643e98964a47c38540a3ca;p=thirdparty%2Fdovecot%2Fcore.git lib: data-stack - Add data_stack_grow event This is now always enabled, not just in debug builds. --- diff --git a/src/lib/data-stack.c b/src/lib/data-stack.c index 201296e30b..a9eb91b44d 100644 --- a/src/lib/data-stack.c +++ b/src/lib/data-stack.c @@ -3,6 +3,8 @@ /* @UNSAFE: whole file */ #include "lib.h" +#include "backtrace-string.h" +#include "str.h" #include "data-stack.h" @@ -85,6 +87,9 @@ static struct stack_frame_block *current_frame_block; always NULL. */ static struct stack_block *current_block; +static struct event *event_datastack = NULL; +static bool event_datastack_deinitialized = FALSE; + static struct stack_block *last_buffer_block; static size_t last_buffer_size; #ifdef DEBUG @@ -383,12 +388,56 @@ static struct stack_block *mem_block_alloc(size_t min_size) return block; } +static void data_stack_send_grow_event(size_t last_alloc_size) +{ + if (event_datastack_deinitialized) { + /* already in the deinitialization code - + don't send more events */ + return; + } + if (event_datastack == NULL) + event_datastack = event_create(NULL); + event_set_name(event_datastack, "data_stack_grow"); + if (!event_want_debug(event_datastack)) + return; + + const char *backtrace; + if (backtrace_get(&backtrace) == 0) + event_add_str(event_datastack, "backtrace", backtrace); + event_add_int(event_datastack, "alloc_size", data_stack_get_alloc_size()); + event_add_int(event_datastack, "used_size", data_stack_get_used_size()); + event_add_int(event_datastack, "last_alloc_size", last_alloc_size); + event_add_int(event_datastack, "last_block_size", current_block->size); +#ifdef DEBUG + event_add_int(event_datastack, "frame_alloc_bytes", + current_frame_block->alloc_bytes[frame_pos]); + event_add_int(event_datastack, "frame_alloc_count", + current_frame_block->alloc_count[frame_pos]); +#endif + event_add_str(event_datastack, "frame_marker", + current_frame_block->marker[frame_pos]); + + string_t *str = t_str_new(128); + str_printfa(str, "total_used=%zu, total_alloc=%zu, last_alloc_size=%zu", + data_stack_get_used_size(), + data_stack_get_alloc_size(), + last_alloc_size); +#ifdef DEBUG + str_printfa(str, ", frame_bytes=%llu, frame_alloc_count=%u", + current_frame_block->alloc_bytes[frame_pos], + current_frame_block->alloc_count[frame_pos]); +#endif + e_debug(event_datastack, "Growing data stack by %zu for '%s' (%s)", + current_block->size, current_frame_block->marker[frame_pos], + str_c(str)); +} + static void *t_malloc_real(size_t size, bool permanent) { void *ret; size_t alloc_size; -#ifdef DEBUG bool warn = FALSE; +#ifdef DEBUG int old_errno = errno; #endif @@ -420,9 +469,7 @@ static void *t_malloc_real(size_t size, bool permanent) /* current block is full, allocate a new one */ block = mem_block_alloc(alloc_size); -#ifdef DEBUG warn = TRUE; -#endif /* The newly allocated block will replace the current_block, i.e. current_block always points to the last element in @@ -440,17 +487,12 @@ static void *t_malloc_real(size_t size, bool permanent) if (permanent) current_block->left -= alloc_size; -#ifdef DEBUG - if (warn && getenv("DEBUG_SILENT") == NULL) { - /* warn after allocation, so if i_debug() wants to + if (warn) { + /* warn after allocation, so if e_debug() wants to allocate more memory we don't go to infinite loop */ - i_debug("Growing data stack by %zu as " - "'%s' reaches %llu bytes from %u allocations.", - current_block->size, - current_frame_block->marker[frame_pos], - current_frame_block->alloc_bytes[frame_pos], - current_frame_block->alloc_count[frame_pos]); + data_stack_send_grow_event(alloc_size); } +#ifdef DEBUG memcpy(ret, &size, sizeof(size)); ret = PTR_OFFSET(ret, MEM_ALIGN(sizeof(size))); /* make sure the sentry contains CLEAR_CHRs. it might not if we @@ -689,6 +731,12 @@ void data_stack_init(void) root_frame_id = t_push("data_stack_init"); } +void data_stack_deinit_event(void) +{ + event_unref(&event_datastack); + event_datastack_deinitialized = TRUE; +} + void data_stack_deinit(void) { if (!t_pop(&root_frame_id) || diff --git a/src/lib/data-stack.h b/src/lib/data-stack.h index c0384e9c3a..8c1316bfc7 100644 --- a/src/lib/data-stack.h +++ b/src/lib/data-stack.h @@ -157,6 +157,7 @@ size_t data_stack_get_alloc_size(void); size_t data_stack_get_used_size(void); void data_stack_init(void); +void data_stack_deinit_event(void); void data_stack_deinit(void); #endif diff --git a/src/lib/lib.c b/src/lib/lib.c index 25d5271cce..5c8853fa98 100644 --- a/src/lib/lib.c +++ b/src/lib/lib.c @@ -160,6 +160,7 @@ void lib_deinit(void) hostpid_deinit(); var_expand_extensions_deinit(); event_filter_deinit(); + data_stack_deinit_event(); lib_event_deinit(); restrict_access_deinit(); i_close_fd(&dev_null_fd); diff --git a/src/lib/test-data-stack.c b/src/lib/test-data-stack.c index c6f22a51cb..f437c2ddf5 100644 --- a/src/lib/test-data-stack.c +++ b/src/lib/test-data-stack.c @@ -1,8 +1,70 @@ /* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ #include "test-lib.h" +#include "lib-event-private.h" +#include "event-filter.h" #include "data-stack.h" +static int ds_grow_event_count = 0; + +static bool +test_ds_grow_event_callback(struct event *event, + enum event_callback_type type, + struct failure_context *ctx, + const char *fmt ATTR_UNUSED, + va_list args ATTR_UNUSED) +{ + const struct event_field *field; + + if (type != EVENT_CALLBACK_TYPE_SEND) + return TRUE; + + ds_grow_event_count++; + test_assert(ctx->type == LOG_TYPE_DEBUG); + + field = event_find_field_nonrecursive(event, "alloc_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * (5 + 100)); + field = event_find_field_nonrecursive(event, "used_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * (5 + 100)); + field = event_find_field_nonrecursive(event, "last_alloc_size"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX && + field->value.intmax >= 1024 * 100); + field = event_find_field_nonrecursive(event, "frame_marker"); + test_assert(field != NULL && + field->value_type == EVENT_FIELD_VALUE_TYPE_STR && + strstr(field->value.str, "test-data-stack.c") != NULL); + return TRUE; +} + +static void test_ds_grow_event(void) +{ + const char *error; + + test_begin("data-stack grow event"); + event_register_callback(test_ds_grow_event_callback); + + i_assert(event_get_global_debug_log_filter() == NULL); + struct event_filter *filter = event_filter_create(); + test_assert(event_filter_parse("event=data_stack_grow", filter, &error) == 0); + event_set_global_debug_log_filter(filter); + event_filter_unref(&filter); + + T_BEGIN { + (void)t_malloc0(1024*5); + test_assert(ds_grow_event_count == 0); + (void)t_malloc0(1024*100); + test_assert(ds_grow_event_count == 1); + } T_END; + event_unset_global_debug_log_filter(); + event_unregister_callback(test_ds_grow_event_callback); + test_end(); +} + static void test_ds_get_bytes_available(void) { test_begin("data-stack t_get_bytes_available()"); @@ -235,6 +297,7 @@ static void test_ds_pass_str(void) void test_data_stack(void) { + test_ds_grow_event(); test_ds_get_bytes_available(); test_ds_buffers(); test_ds_realloc();