]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: data-stack - Add data_stack_grow event
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 16 Nov 2020 18:54:20 +0000 (20:54 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 4 May 2021 07:02:35 +0000 (07:02 +0000)
This is now always enabled, not just in debug builds.

src/lib/data-stack.c
src/lib/data-stack.h
src/lib/lib.c
src/lib/test-data-stack.c

index 201296e30b92e3665c982d027241fff68be68be1..a9eb91b44da5fbe9f7b0817ade1a593095f09091 100644 (file)
@@ -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) ||
index c0384e9c3a70a3320d5992ae4b03cb8ac00ee744..8c1316bfc76410bd8e123231cb3791b71bb8faaf 100644 (file)
@@ -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
index 25d5271cce54287b5bb6bc1c7b044fcc3b5d4644..5c8853fa98b90529a3f64c333d8265e11f9d3fa5 100644 (file)
@@ -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);
index c6f22a51cb6d36d82bc9084187a0afee903d5d1d..f437c2ddf5748ad600068a9596816750d47042cb 100644 (file)
@@ -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();