Here are some patches. 0001 fires without following fixes aff6ba1 Fix request_finished memory leak with debugging turned on. 9b724b2 Fix evsig_dealloc memory leak with debugging turned on.
0002 is just a preparation for 0003 0003 fires with current codebase but, seems, it should not. I'm sorry, I can't produce a nice fix right now, so I'm just sharing the testcase at this moment. :) And the last question: should I send further patches to ML or via github pull request? -- WBRBW, Leonid Evdokimov xmpp:l...@darkk.net.ru && http://darkk.net.ru tel:+79816800702 && tel:+79050965222
From 7a616e13a318a0380a0f298a0779c996883cf4d1 Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov <l...@darkk.net.ru> Date: Tue, 18 Oct 2011 17:49:40 +0400 Subject: [PATCH 1/3] Test for commit aff6ba1 --- test/regress_dns.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 116 insertions(+), 0 deletions(-) diff --git a/test/regress_dns.c b/test/regress_dns.c index 01cb0db..19581ef 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -1625,6 +1625,119 @@ gaic_launch(struct event_base *base, struct evdns_base *dns_base) ++pending; } +static int allocated_chunks = 0; + +static void* +cnt_malloc(size_t sz) +{ + allocated_chunks += 1; + return malloc(sz); +} + +static void* +cnt_realloc(void *old, size_t sz) +{ + if (!old) + allocated_chunks += 1; + if (!sz) + allocated_chunks -= 1; + return realloc(old, sz); +} + +static void +cnt_free(void *ptr) +{ + allocated_chunks -= 1; + return free(ptr); +} + +struct testleak_env_t { + struct event_base* base; + struct evdns_base* dns_base; + struct evdns_request* req; + struct generic_dns_callback_result r; +}; + +static void* +testleak_setup(const struct testcase_t *testcase) +{ + struct testleak_env_t* env; + + allocated_chunks = 0; + event_set_mem_functions(cnt_malloc, cnt_realloc, cnt_free); + event_enable_debug_mode(); + + env = calloc(1, sizeof(struct testleak_env_t)); + env->base = event_base_new(); + env->dns_base = evdns_base_new(env->base, 0); + env->req = evdns_base_resolve_ipv4( + env->dns_base, "example.com", DNS_QUERY_NO_SEARCH, + generic_dns_callback, &env->r); + return env; +} + +static int +testleak_cleanup(const struct testcase_t *testcase, void *env_) +{ + int ok = 0; + struct testleak_env_t* env = env_; + /* FIXME: that's `1' because of event_debug_map_HT_GROW */ + tt_int_op(allocated_chunks, ==, 1); + ok = 1; +end: + if (env->dns_base) + evdns_base_free(env->dns_base, 0); + if (env->base) + event_base_free(env->base); + if (env) + free(env); + return ok; +} + +static struct testcase_setup_t testleak_funcs = { + testleak_setup, testleak_cleanup +}; + +static void +test_dbg_leak_cancel(void *env_) +{ + /* cancel, loop, free/dns, free/base */ + struct testleak_env_t* env = env_; + int send_err_shutdown = 1; + evdns_cancel_request(env->dns_base, env->req); + env->req = 0; + + /* `req` is freed in callback, that's why one loop is required. */ + event_base_loop(env->base, EVLOOP_NONBLOCK); + + /* send_err_shutdown means nothing as soon as our request is + * already canceled */ + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; + event_base_free(env->base); + env->base = 0; +} + +static void +test_dbg_leak_shutdown(void *env_) +{ + /* free/dns, loop, free/base */ + struct testleak_env_t* env = env_; + int send_err_shutdown = 1; + + /* `req` is freed both with `send_err_shutdown` and without it, + * the only difference is `evdns_callback` call */ + env->req = 0; + + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; + + /* `req` is freed in callback, that's why one loop is required */ + event_base_loop(env->base, EVLOOP_NONBLOCK); + event_base_free(env->base); + env->base = 0; +} + static void test_getaddrinfo_async_cancel_stress(void *ptr) { @@ -1702,6 +1815,9 @@ struct testcase_t dns_testcases[] = { { "getaddrinfo_cancel_stress", test_getaddrinfo_async_cancel_stress, TT_FORK, NULL, NULL }, + { "leak_shutdown", test_dbg_leak_shutdown, TT_FORK, &testleak_funcs, NULL }, + { "leak_cancel", test_dbg_leak_cancel, TT_FORK, &testleak_funcs, NULL }, + END_OF_TESTCASES }; -- 1.7.5.4
From 79bcd81586f2e1bd0bf8bb7fd11630b9a68c7ebb Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov <l...@darkk.net.ru> Date: Wed, 19 Oct 2011 17:44:17 +0400 Subject: [PATCH 2/3] More detailed message in case of libevent self-debugging failure. --- event.c | 24 ++++++++++++++++-------- 1 files changed, 16 insertions(+), 8 deletions(-) diff --git a/event.c b/event.c index 9064f89..cecba2b 100644 --- a/event.c +++ b/event.c @@ -250,8 +250,10 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, dent->added = 1; \ } else { \ event_errx(_EVENT_ERR_ABORT, \ - "%s: noting an add on a non-setup event %p", \ - __func__, (ev)); \ + "%s: noting an add on a non-setup event %p" \ + " (events: 0x%x, fd: %d, flags: 0x%x)", \ + __func__, (ev), (ev)->ev_events, \ + (ev)->ev_fd, (ev)->ev_flags); \ } \ EVLOCK_UNLOCK(_event_debug_map_lock, 0); \ } \ @@ -268,8 +270,10 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, dent->added = 0; \ } else { \ event_errx(_EVENT_ERR_ABORT, \ - "%s: noting a del on a non-setup event %p", \ - __func__, (ev)); \ + "%s: noting a del on a non-setup event %p" \ + " (events: 0x%x, fd: %d, flags: 0x%x)", \ + __func__, (ev), (ev)->ev_events, \ + (ev)->ev_fd, (ev)->ev_flags); \ } \ EVLOCK_UNLOCK(_event_debug_map_lock, 0); \ } \ @@ -284,8 +288,10 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, dent = HT_FIND(event_debug_map, &global_debug_map, &find); \ if (!dent) { \ event_errx(_EVENT_ERR_ABORT, \ - "%s called on a non-initialized event %p", \ - __func__, (ev)); \ + "%s called on a non-initialized event %p" \ + " (events: 0x%x, fd: %d, flags: 0x%x)", \ + __func__, (ev), (ev)->ev_events, \ + (ev)->ev_fd, (ev)->ev_flags); \ } \ EVLOCK_UNLOCK(_event_debug_map_lock, 0); \ } \ @@ -300,8 +306,10 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, dent = HT_FIND(event_debug_map, &global_debug_map, &find); \ if (dent && dent->added) { \ event_errx(_EVENT_ERR_ABORT, \ - "%s called on an already added event %p", \ - __func__, (ev)); \ + "%s called on an already added event %p" \ + " (events: 0x%x, fd: %d, flags: 0x%x)", \ + __func__, (ev), (ev)->ev_events, \ + (ev)->ev_fd, (ev)->ev_flags); \ } \ EVLOCK_UNLOCK(_event_debug_map_lock, 0); \ } \ -- 1.7.5.4
From db1e7c404be1e83c424b1f2c2d816647f7ff7c4e Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov <l...@darkk.net.ru> Date: Wed, 19 Oct 2011 17:46:08 +0400 Subject: [PATCH 3/3] Some failing tests for self-debugging during event_reinit. --- test/regress.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 51 insertions(+), 0 deletions(-) diff --git a/test/regress.c b/test/regress.c index f22f01f..5342e2a 100644 --- a/test/regress.c +++ b/test/regress.c @@ -883,6 +883,54 @@ test_fork(void) } static void +nop_signal_cb(evutil_socket_t fd, short event, void *arg) +{ +} + +static int mainloop_cfg_st = EVENT_BASE_FLAG_NOLOCK; +static int mainloop_cfg_mt = 0; + +static void +test_fork_dbg(void* ptr) +{ + struct basic_test_data* env = ptr; + int *mainloop_cfg = env->setup_data; + struct event_config* cfg; + struct event_base* base; + struct event sig_ev; + int pid, status, ok = 0; + + event_enable_debug_mode(); + if (mainloop_cfg) { + cfg = event_config_new(); + tt_ptr_op(cfg, !=, NULL); + event_config_set_flag(cfg, *mainloop_cfg); + base = event_base_new_with_config(cfg); + } else { + base = event_base_new(); + } + tt_ptr_op(base, !=, NULL); + evsignal_assign(&sig_ev, base, SIGUSR1, nop_signal_cb, NULL); + tt_int_op(evsignal_add(&sig_ev, NULL), ==, 0); + + if ((pid = fork()) == 0) { + tt_int_op(event_reinit(base), ==, 0); + tt_int_op(evsignal_del(&sig_ev), ==, 0); + tt_int_op(event_base_dispatch(base), ==, 1); + event_base_free(base); + exit(76); + } + sleep(1); + tt_int_op(waitpid(pid, &status, 0), !=, -1); + tt_int_op(WEXITSTATUS(status), ==, 76); + + ok = 1; +end: + if (!ok) + exit(1); +} + +static void signal_cb_sa(int sig) { test_ok = 2; @@ -2358,6 +2406,9 @@ struct testcase_t main_testcases[] = { #ifndef _WIN32 LEGACY(fork, TT_ISOLATED), + { "fork_dbg", test_fork_dbg, TT_FORK, &basic_setup, NULL }, + { "fork_dbg_st", test_fork_dbg, TT_FORK, &basic_setup, &mainloop_cfg_st }, + { "fork_dbg_mt", test_fork_dbg, TT_FORK, &basic_setup, &mainloop_cfg_mt }, #endif END_OF_TESTCASES }; -- 1.7.5.4