diff options
author | Pavel Březina <pbrezina@redhat.com> | 2021-06-01 14:10:05 +0200 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2021-07-07 05:07:30 +0000 |
commit | 683c44a79f09550a59e99606c9c80304666604a0 (patch) | |
tree | 756b76b685b54e1ab35530d361b2335048ae6d98 /lib | |
parent | fc9dd8ce9f0ae2a7ae972035e56c4c4f9028e261 (diff) | |
download | samba-683c44a79f09550a59e99606c9c80304666604a0.tar.gz |
tevent: add event trace api
Adds new tracing API to trace fd, timer, signal and immediate events
on specific trace points: attach, before handler and dettach.
This can be used in combination with the event tag to keep track
of the currently executed event for purpose of debugging.
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Pavel Březina <pbrezina@redhat.com>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/tevent/ABI/tevent-0.10.2.sigs | 12 | ||||
-rw-r--r-- | lib/tevent/tests/test_tevent_trace.c | 1065 | ||||
-rw-r--r-- | lib/tevent/tevent.c | 4 | ||||
-rw-r--r-- | lib/tevent/tevent.h | 149 | ||||
-rw-r--r-- | lib/tevent/tevent_debug.c | 139 | ||||
-rw-r--r-- | lib/tevent/tevent_fd.c | 4 | ||||
-rw-r--r-- | lib/tevent/tevent_immediate.c | 31 | ||||
-rw-r--r-- | lib/tevent/tevent_internal.h | 43 | ||||
-rw-r--r-- | lib/tevent/tevent_signal.c | 3 | ||||
-rw-r--r-- | lib/tevent/tevent_timed.c | 15 | ||||
-rw-r--r-- | lib/tevent/wscript | 6 |
11 files changed, 1461 insertions, 10 deletions
diff --git a/lib/tevent/ABI/tevent-0.10.2.sigs b/lib/tevent/ABI/tevent-0.10.2.sigs index 238b69f0bd4..e64007e0516 100644 --- a/lib/tevent/ABI/tevent-0.10.2.sigs +++ b/lib/tevent/ABI/tevent-0.10.2.sigs @@ -60,6 +60,10 @@ tevent_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t) tevent_fd_set_flags: void (struct tevent_fd *, uint16_t) tevent_fd_set_tag: void (struct tevent_fd *, uint64_t) tevent_get_trace_callback: void (struct tevent_context *, tevent_trace_callback_t *, void *) +tevent_get_trace_fd_callback: void (struct tevent_context *, tevent_trace_fd_callback_t *, void *) +tevent_get_trace_immediate_callback: void (struct tevent_context *, tevent_trace_immediate_callback_t *, void *) +tevent_get_trace_signal_callback: void (struct tevent_context *, tevent_trace_signal_callback_t *, void *) +tevent_get_trace_timer_callback: void (struct tevent_context *, tevent_trace_timer_callback_t *, void *) tevent_immediate_get_tag: uint64_t (const struct tevent_immediate *) tevent_immediate_set_tag: void (struct tevent_immediate *, uint64_t) tevent_loop_allow_nesting: void (struct tevent_context *) @@ -112,6 +116,10 @@ tevent_set_debug: int (struct tevent_context *, void (*)(void *, enum tevent_deb tevent_set_debug_stderr: int (struct tevent_context *) tevent_set_default_backend: void (const char *) tevent_set_trace_callback: void (struct tevent_context *, tevent_trace_callback_t, void *) +tevent_set_trace_fd_callback: void (struct tevent_context *, tevent_trace_fd_callback_t, void *) +tevent_set_trace_immediate_callback: void (struct tevent_context *, tevent_trace_immediate_callback_t, void *) +tevent_set_trace_signal_callback: void (struct tevent_context *, tevent_trace_signal_callback_t, void *) +tevent_set_trace_timer_callback: void (struct tevent_context *, tevent_trace_timer_callback_t, void *) tevent_signal_get_tag: uint64_t (const struct tevent_signal *) tevent_signal_set_tag: void (struct tevent_signal *, uint64_t) tevent_signal_support: bool (struct tevent_context *) @@ -128,7 +136,11 @@ tevent_timeval_is_zero: bool (const struct timeval *) tevent_timeval_set: struct timeval (uint32_t, uint32_t) tevent_timeval_until: struct timeval (const struct timeval *, const struct timeval *) tevent_timeval_zero: struct timeval (void) +tevent_trace_fd_callback: void (struct tevent_context *, struct tevent_fd *, enum tevent_event_trace_point) +tevent_trace_immediate_callback: void (struct tevent_context *, struct tevent_immediate *, enum tevent_event_trace_point) tevent_trace_point_callback: void (struct tevent_context *, enum tevent_trace_point) +tevent_trace_signal_callback: void (struct tevent_context *, struct tevent_signal *, enum tevent_event_trace_point) +tevent_trace_timer_callback: void (struct tevent_context *, struct tevent_timer *, enum tevent_event_trace_point) tevent_update_timer: void (struct tevent_timer *, struct timeval) tevent_wakeup_recv: bool (struct tevent_req *) tevent_wakeup_send: struct tevent_req *(TALLOC_CTX *, struct tevent_context *, struct timeval) diff --git a/lib/tevent/tests/test_tevent_trace.c b/lib/tevent/tests/test_tevent_trace.c new file mode 100644 index 00000000000..e9d26f9ceba --- /dev/null +++ b/lib/tevent/tests/test_tevent_trace.c @@ -0,0 +1,1065 @@ +/* + * Unix SMB/CIFS implementation. + * + * testing of some tevent_req aspects + * + * Copyright (C) Pavel Březina <pbrezina@redhat.com> 2021 + * + * ** NOTE! The following LGPL license applies to the tevent + * ** library. This does NOT imply that all of Samba is released + * ** under the LGPL + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <setjmp.h> +#include <stdlib.h> +#include <stdint.h> +#include <signal.h> +#include <unistd.h> + +#include <talloc.h> +#include <tevent.h> +#include <cmocka.h> + +struct test_ctx { + struct tevent_context *ev; + + bool handler_skipped; + bool reattach_reset; + + uint64_t (*get_tag)(const void *event); + void (*set_tag)(void *event, uint64_t tag); + uint64_t current_tag; + + bool attach; + bool before_handler; + bool handler_called; + bool detach; +}; + +static void fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_fd_get_tag(fde); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + return; +} + +static void timer_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_timer_get_tag(te); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + return; +} + +static void signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_signal_get_tag(se); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + return; +} + +static void immediate_handler(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_immediate_get_tag(im); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + return; +} + +static void immediate_handler_reschedule(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_immediate_get_tag(im); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + + assert_false(tctx->reattach_reset); + tctx->reattach_reset = true; + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_false(tctx->reattach_reset); + assert_false(tctx->handler_skipped); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + assert_int_not_equal(tag, tctx->current_tag); + tag = tevent_immediate_get_tag(im); + assert_int_equal(tag, tctx->current_tag); + + tctx->handler_skipped = true; + tctx->reattach_reset = true; + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_false(tctx->reattach_reset); + assert_false(tctx->handler_skipped); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + assert_int_not_equal(tag, tctx->current_tag); + tag = tevent_immediate_get_tag(im); + assert_int_equal(tag, tctx->current_tag); +} + +static void fd_handler_free(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_fd_get_tag(fde); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + TALLOC_FREE(fde); + assert_true(tctx->detach); + return; +} + +static void timer_handler_free(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_timer_get_tag(te); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + TALLOC_FREE(te); + assert_true(tctx->detach); + return; +} + +static void signal_handler_free(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_signal_get_tag(se); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + TALLOC_FREE(se); + assert_true(tctx->detach); + return; +} + +static void immediate_handler_free(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tevent_immediate_get_tag(im); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->handler_called = true; + assert_int_equal(tag, tctx->current_tag); + TALLOC_FREE(im); + assert_true(tctx->detach); + return; +} + +static void trace_event_cb(void *event, + enum tevent_event_trace_point point, + void *private_data) +{ + struct test_ctx *tctx = (struct test_ctx *)private_data; + uint64_t tag = tctx->get_tag(event); + + switch (point) { + case TEVENT_EVENT_TRACE_ATTACH: + if (tctx->reattach_reset) { + assert_true(tctx->attach); + assert_true(tctx->detach); + tctx->attach = false; + tctx->before_handler = false; + tctx->handler_called = false; + tctx->detach = false; + tctx->handler_skipped = false; + tctx->reattach_reset = false; + } + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->attach = true; + assert_int_equal(tag, tctx->current_tag); + tag = ++tctx->current_tag; + tctx->set_tag(event, tag); + break; + case TEVENT_EVENT_TRACE_BEFORE_HANDLER: + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + tctx->before_handler = true; + assert_int_equal(tag, tctx->current_tag); + break; + case TEVENT_EVENT_TRACE_DETACH: + assert_true(tctx->attach); + if (tctx->handler_skipped) { + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + } else { + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + } + assert_false(tctx->detach); + tctx->detach = true; + assert_int_equal(tag, tctx->current_tag); + break; + } +} + +static int test_setup(void **state) +{ + struct test_ctx *tctx; + + tctx = talloc_zero(NULL, struct test_ctx); + assert_non_null(tctx); + + tctx->ev = tevent_context_init(tctx); + assert_non_null(tctx->ev); + + *state = tctx; + return 0; +} + +static int test_teardown(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + tctx->get_tag = NULL; + tctx->set_tag = NULL; + tevent_set_trace_fd_callback(tctx->ev, NULL, NULL); + tevent_set_trace_timer_callback(tctx->ev, NULL, NULL); + tevent_set_trace_signal_callback(tctx->ev, NULL, NULL); + tevent_set_trace_immediate_callback(tctx->ev, NULL, NULL); + TALLOC_FREE(tctx); + return 0; +} + +static uint64_t fd_get_tag(const void *_event) +{ + const struct tevent_fd *event = + (const struct tevent_fd *)_event; + + return tevent_fd_get_tag(event); +} + +static void fd_set_tag(void *_event, uint64_t tag) +{ + struct tevent_fd *event = + (struct tevent_fd *)_event; + + tevent_fd_set_tag(event, tag); +} + +static uint64_t timer_get_tag(const void *_event) +{ + const struct tevent_timer *event = + (const struct tevent_timer *)_event; + + return tevent_timer_get_tag(event); +} + +static void timer_set_tag(void *_event, uint64_t tag) +{ + struct tevent_timer *event = + (struct tevent_timer *)_event; + + tevent_timer_set_tag(event, tag); +} + +static uint64_t signal_get_tag(const void *_event) +{ + const struct tevent_signal *event = + (const struct tevent_signal *)_event; + + return tevent_signal_get_tag(event); +} + +static void signal_set_tag(void *_event, uint64_t tag) +{ + struct tevent_signal *event = + (struct tevent_signal *)_event; + + tevent_signal_set_tag(event, tag); +} + +static uint64_t immediate_get_tag(const void *_event) +{ + const struct tevent_immediate *event = + (const struct tevent_immediate *)_event; + + return tevent_immediate_get_tag(event); +} + +static void immediate_set_tag(void *_event, uint64_t tag) +{ + struct tevent_immediate *event = + (struct tevent_immediate *)_event; + + tevent_immediate_set_tag(event, tag); +} + +static void test_trace_event_fd__loop(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_fd *fde; + + tctx->get_tag = fd_get_tag; + tctx->set_tag = fd_set_tag; + tevent_set_trace_fd_callback(tctx->ev, (tevent_trace_fd_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + fde = tevent_add_fd(tctx->ev, tctx, 0, TEVENT_FD_WRITE, fd_handler, tctx); + assert_non_null(fde); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + TEVENT_FD_WRITEABLE(fde); + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_false(tctx->detach); + + TALLOC_FREE(fde); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_fd__reset(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_fd *fde; + + tctx->get_tag = fd_get_tag; + tctx->set_tag = fd_set_tag; + tevent_set_trace_fd_callback(tctx->ev, (tevent_trace_fd_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + fde = tevent_add_fd(tctx->ev, tctx, 0, TEVENT_FD_WRITE, fd_handler, tctx); + assert_non_null(fde); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + tevent_re_initialise(tctx->ev); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_fd__free(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_fd *fde; + + tctx->get_tag = fd_get_tag; + tctx->set_tag = fd_set_tag; + tevent_set_trace_fd_callback(tctx->ev, (tevent_trace_fd_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + fde = tevent_add_fd(tctx->ev, tctx, 0, TEVENT_FD_WRITE, fd_handler, tctx); + assert_non_null(fde); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + TALLOC_FREE(fde); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_fd__free_in_handler(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_fd *fde; + + tctx->get_tag = fd_get_tag; + tctx->set_tag = fd_set_tag; + tevent_set_trace_fd_callback(tctx->ev, (tevent_trace_fd_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + fde = tevent_add_fd(tctx->ev, tctx, 0, TEVENT_FD_WRITE, fd_handler_free, tctx); + assert_non_null(fde); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + TEVENT_FD_WRITEABLE(fde); + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_timer__loop(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_timer *te; + struct timeval next; + + tctx->get_tag = timer_get_tag; + tctx->set_tag = timer_set_tag; + tevent_set_trace_timer_callback(tctx->ev, (tevent_trace_timer_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + next = tevent_timeval_current(); + te = tevent_add_timer(tctx->ev, tctx, next, timer_handler, tctx); + assert_non_null(te); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + /* timer events are self destructing after calling the handler */ + assert_true(tctx->detach); +} + +static void test_trace_event_timer__reset(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_timer *te; + struct timeval next; + + tctx->get_tag = timer_get_tag; + tctx->set_tag = timer_set_tag; + tevent_set_trace_timer_callback(tctx->ev, (tevent_trace_timer_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + next = tevent_timeval_current(); + te = tevent_add_timer(tctx->ev, tctx, next, timer_handler, tctx); + assert_non_null(te); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + assert_true(tctx->attach); + + assert_false(tctx->reattach_reset); + tctx->handler_skipped = true; + tctx->reattach_reset = true; + next = tevent_timeval_current(); + tevent_update_timer(te, next); + assert_false(tctx->reattach_reset); + assert_false(tctx->handler_skipped); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + tevent_re_initialise(tctx->ev); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_timer__free(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_timer *te; + struct timeval next; + + tctx->get_tag = timer_get_tag; + tctx->set_tag = timer_set_tag; + tevent_set_trace_timer_callback(tctx->ev, (tevent_trace_timer_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + next = tevent_timeval_current(); + te = tevent_add_timer(tctx->ev, tctx, next, timer_handler, tctx); + assert_non_null(te); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + assert_true(tctx->attach); + + tctx->handler_skipped = true; + TALLOC_FREE(te); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_timer__free_in_handler(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_timer *te; + struct timeval next; + + tctx->get_tag = timer_get_tag; + tctx->set_tag = timer_set_tag; + tevent_set_trace_timer_callback(tctx->ev, (tevent_trace_timer_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + next = tevent_timeval_current(); + te = tevent_add_timer(tctx->ev, tctx, next, timer_handler_free, tctx); + assert_non_null(te); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_signal__loop(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_signal *se; + + tctx->get_tag = signal_get_tag; + tctx->set_tag = signal_set_tag; + tevent_set_trace_signal_callback(tctx->ev, (tevent_trace_signal_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + se = tevent_add_signal(tctx->ev, tctx, SIGUSR1, 0, signal_handler, tctx); + assert_non_null(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + kill(getpid(), SIGUSR1); + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_false(tctx->detach); + + TALLOC_FREE(se); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_signal__reset(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_signal *se; + + tctx->get_tag = signal_get_tag; + tctx->set_tag = signal_set_tag; + tevent_set_trace_signal_callback(tctx->ev, (tevent_trace_signal_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + se = tevent_add_signal(tctx->ev, tctx, SIGUSR1, 0, signal_handler, tctx); + assert_non_null(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + TALLOC_FREE(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_signal__free(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_signal *se; + + tctx->get_tag = signal_get_tag; + tctx->set_tag = signal_set_tag; + tevent_set_trace_signal_callback(tctx->ev, (tevent_trace_signal_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + se = tevent_add_signal(tctx->ev, tctx, SIGUSR1, 0, signal_handler, tctx); + assert_non_null(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + TALLOC_FREE(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_signal__free_in_handler(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_signal *se; + + tctx->get_tag = signal_get_tag; + tctx->set_tag = signal_set_tag; + tevent_set_trace_signal_callback(tctx->ev, (tevent_trace_signal_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + se = tevent_add_signal(tctx->ev, tctx, SIGUSR1, 0, signal_handler_free, tctx); + assert_non_null(se); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + kill(getpid(), SIGUSR1); + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_immediate__loop(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_immediate *im; + + tctx->get_tag = immediate_get_tag; + tctx->set_tag = immediate_set_tag; + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + im = tevent_create_immediate(tctx); + assert_non_null(im); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + /* immediate events are self detaching */ + assert_true(tctx->detach); +} + +static void test_trace_event_immediate__reset(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_immediate *im; + + tctx->get_tag = immediate_get_tag; + tctx->set_tag = immediate_set_tag; + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + im = tevent_create_immediate(tctx); + assert_non_null(im); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + tevent_re_initialise(tctx->ev); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_immediate__free(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_immediate *im; + + tctx->get_tag = immediate_get_tag; + tctx->set_tag = immediate_set_tag; + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + im = tevent_create_immediate(tctx); + assert_non_null(im); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tctx->handler_skipped = true; + TALLOC_FREE(im); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_immediate__free_in_handler(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_immediate *im; + + tctx->get_tag = immediate_get_tag; + tctx->set_tag = immediate_set_tag; + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + im = tevent_create_immediate(tctx); + assert_non_null(im); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_schedule_immediate(im, tctx->ev, immediate_handler_free, tctx); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + assert_true(tctx->detach); +} + +static void test_trace_event_immediate__reschedule(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + struct tevent_immediate *im; + + tctx->get_tag = immediate_get_tag; + tctx->set_tag = immediate_set_tag; + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + im = tevent_create_immediate(tctx); + assert_non_null(im); + + assert_false(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_schedule_immediate(im, tctx->ev, immediate_handler, tctx); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + assert_false(tctx->reattach_reset); + tctx->handler_skipped = true; + tctx->reattach_reset = true; + tevent_schedule_immediate(im, tctx->ev, immediate_handler_reschedule, tctx); + assert_false(tctx->reattach_reset); + assert_false(tctx->handler_skipped); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_false(tctx->reattach_reset); + assert_true(tctx->attach); + assert_false(tctx->before_handler); + assert_false(tctx->handler_called); + assert_false(tctx->detach); + + tevent_loop_once(tctx->ev); + assert_true(tctx->attach); + assert_true(tctx->before_handler); + assert_true(tctx->handler_called); + /* immediate events are self detaching */ + assert_true(tctx->detach); +} + +static void test_get_set_trace_fd_callback(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + tevent_trace_fd_callback_t cb; + void *pvt; + + tevent_get_trace_fd_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); + + tevent_set_trace_fd_callback(tctx->ev, (tevent_trace_fd_callback_t)trace_event_cb, tctx); + tevent_get_trace_fd_callback(tctx->ev, &cb, &pvt); + assert_ptr_equal(cb, trace_event_cb); + assert_ptr_equal(pvt, tctx); + + tevent_set_trace_fd_callback(tctx->ev, NULL, NULL); + tevent_get_trace_fd_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); +} + +static void test_get_set_trace_timer_callback(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + tevent_trace_timer_callback_t cb; + void *pvt; + + tevent_get_trace_timer_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); + + tevent_set_trace_timer_callback(tctx->ev, (tevent_trace_timer_callback_t)trace_event_cb, tctx); + tevent_get_trace_timer_callback(tctx->ev, &cb, &pvt); + assert_ptr_equal(cb, trace_event_cb); + assert_ptr_equal(pvt, tctx); + + tevent_set_trace_timer_callback(tctx->ev, NULL, NULL); + tevent_get_trace_timer_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); +} + +static void test_get_set_trace_signal_callback(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + tevent_trace_signal_callback_t cb; + void *pvt; + + tevent_get_trace_signal_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); + + tevent_set_trace_signal_callback(tctx->ev, (tevent_trace_signal_callback_t)trace_event_cb, tctx); + tevent_get_trace_signal_callback(tctx->ev, &cb, &pvt); + assert_ptr_equal(cb, trace_event_cb); + assert_ptr_equal(pvt, tctx); + + tevent_set_trace_signal_callback(tctx->ev, NULL, NULL); + tevent_get_trace_signal_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); +} + +static void test_get_set_trace_immediate_callback(void **state) +{ + struct test_ctx *tctx = (struct test_ctx *)(*state); + tevent_trace_immediate_callback_t cb; + void *pvt; + + tevent_get_trace_immediate_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); + + tevent_set_trace_immediate_callback(tctx->ev, (tevent_trace_immediate_callback_t)trace_event_cb, tctx); + tevent_get_trace_immediate_callback(tctx->ev, &cb, &pvt); + assert_ptr_equal(cb, trace_event_cb); + assert_ptr_equal(pvt, tctx); + + tevent_set_trace_immediate_callback(tctx->ev, NULL, NULL); + tevent_get_trace_immediate_callback(tctx->ev, &cb, &pvt); + assert_null(cb); + assert_null(pvt); +} + +int main(int argc, char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_trace_event_fd__loop, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_fd__reset, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_fd__free, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_fd__free_in_handler, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_timer__loop, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_timer__reset, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_timer__free, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_timer__free_in_handler, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_signal__loop, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_signal__reset, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_signal__free, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_signal__free_in_handler, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_immediate__loop, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_immediate__reset, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_immediate__free, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_immediate__free_in_handler, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_trace_event_immediate__reschedule, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_get_set_trace_fd_callback, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_get_set_trace_timer_callback, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_get_set_trace_signal_callback, test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_get_set_trace_immediate_callback, test_setup, test_teardown), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c index a94bb440b9b..81268a1bd7a 100644 --- a/lib/tevent/tevent.c +++ b/lib/tevent/tevent.c @@ -377,6 +377,7 @@ int tevent_common_context_destructor(struct tevent_context *ev) for (fd = ev->fd_events; fd; fd = fn) { fn = fd->next; + tevent_trace_fd_callback(fd->event_ctx, fd, TEVENT_EVENT_TRACE_DETACH); fd->wrapper = NULL; fd->event_ctx = NULL; DLIST_REMOVE(ev->fd_events, fd); @@ -385,6 +386,7 @@ int tevent_common_context_destructor(struct tevent_context *ev) ev->last_zero_timer = NULL; for (te = ev->timer_events; te; te = tn) { tn = te->next; + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_DETACH); te->wrapper = NULL; te->event_ctx = NULL; DLIST_REMOVE(ev->timer_events, te); @@ -392,6 +394,7 @@ int tevent_common_context_destructor(struct tevent_context *ev) for (ie = ev->immediate_events; ie; ie = in) { in = ie->next; + tevent_trace_immediate_callback(ie->event_ctx, ie, TEVENT_EVENT_TRACE_DETACH); ie->wrapper = NULL; ie->event_ctx = NULL; ie->cancel_fn = NULL; @@ -400,6 +403,7 @@ int tevent_common_context_destructor(struct tevent_context *ev) for (se = ev->signal_events; se; se = sn) { sn = se->next; + tevent_trace_signal_callback(se->event_ctx, se, TEVENT_EVENT_TRACE_DETACH); se->wrapper = NULL; se->event_ctx = NULL; DLIST_REMOVE(ev->signal_events, se); diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h index 5cfd1a1e9fa..aaa5a068c55 100644 --- a/lib/tevent/tevent.h +++ b/lib/tevent/tevent.h @@ -691,6 +691,155 @@ void tevent_get_trace_callback(struct tevent_context *ev, tevent_trace_callback_t *cb, void *private_data); +enum tevent_event_trace_point { + /** + * Corresponds to a trace point just before the event is added. + */ + TEVENT_EVENT_TRACE_ATTACH, + + /** + * Corresponds to a trace point just before the event is removed. + */ + TEVENT_EVENT_TRACE_DETACH, + + /** + * Corresponds to a trace point just before the event handler is called. + */ + TEVENT_EVENT_TRACE_BEFORE_HANDLER, +}; + +typedef void (*tevent_trace_fd_callback_t)(struct tevent_fd *fde, + enum tevent_event_trace_point, + void *private_data); + +typedef void (*tevent_trace_signal_callback_t)(struct tevent_signal *se, + enum tevent_event_trace_point, + void *private_data); + +typedef void (*tevent_trace_timer_callback_t)(struct tevent_timer *te, + enum tevent_event_trace_point, + void *private_data); + +typedef void (*tevent_trace_immediate_callback_t)(struct tevent_immediate *im, + enum tevent_event_trace_point, + void *private_data); + +/** + * Register a callback to be called at certain trace points of fd event. + * + * @param[in] ev Event context + * @param[in] cb Trace callback + * @param[in] private_data Data to be passed to callback + * + * @note The callback will be called at trace points defined by + * tevent_event_trace_point. Call with NULL to reset. + */ +void tevent_set_trace_fd_callback(struct tevent_context *ev, + tevent_trace_fd_callback_t cb, + void *private_data); + +/** + * Retrieve the current trace callback of file descriptor event. + * + * @param[in] ev Event context + * @param[out] cb Registered trace callback + * @param[out] p_private_data Registered data to be passed to callback + * + * @note This can be used to allow one component that wants to + * register a callback to respect the callback that another component + * has already registered. + */ +void tevent_get_trace_fd_callback(struct tevent_context *ev, + tevent_trace_fd_callback_t *cb, + void *p_private_data); + +/** + * Register a callback to be called at certain trace points of signal event. + * + * @param[in] ev Event context + * @param[in] cb Trace callback + * @param[in] private_data Data to be passed to callback + * + * @note The callback will be called at trace points defined by + * tevent_event_trace_point. Call with NULL to reset. + */ +void tevent_set_trace_signal_callback(struct tevent_context *ev, + tevent_trace_signal_callback_t cb, + void *private_data); + +/** + * Retrieve the current trace callback of signal event. + * + * @param[in] ev Event context + * @param[out] cb Registered trace callback + * @param[out] p_private_data Registered data to be passed to callback + * + * @note This can be used to allow one component that wants to + * register a callback to respect the callback that another component + * has already registered. + */ +void tevent_get_trace_signal_callback(struct tevent_context *ev, + tevent_trace_signal_callback_t *cb, + void *p_private_data); + +/** + * Register a callback to be called at certain trace points of timer event. + * + * @param[in] ev Event context + * @param[in] cb Trace callback + * @param[in] private_data Data to be passed to callback + * + * @note The callback will be called at trace points defined by + * tevent_event_trace_point. Call with NULL to reset. + */ +void tevent_set_trace_timer_callback(struct tevent_context *ev, + tevent_trace_timer_callback_t cb, + void *private_data); + +/** + * Retrieve the current trace callback of timer event. + * + * @param[in] ev Event context + * @param[out] cb Registered trace callback + * @param[out] p_private_data Registered data to be passed to callback + * + * @note This can be used to allow one component that wants to + * register a callback to respect the callback that another component + * has already registered. + */ +void tevent_get_trace_timer_callback(struct tevent_context *ev, + tevent_trace_timer_callback_t *cb, + void *p_private_data); + +/** + * Register a callback to be called at certain trace points of immediate event. + * + * @param[in] ev Event context + * @param[in] cb Trace callback + * @param[in] private_data Data to be passed to callback + * + * @note The callback will be called at trace points defined by + * tevent_event_trace_point. Call with NULL to reset. + */ +void tevent_set_trace_immediate_callback(struct tevent_context *ev, + tevent_trace_immediate_callback_t cb, + void *private_data); + +/** + * Retrieve the current trace callback of immediate event. + * + * @param[in] ev Event context + * @param[out] cb Registered trace callback + * @param[out] p_private_data Registered data to be passed to callback + * + * @note This can be used to allow one component that wants to + * register a callback to respect the callback that another component + * has already registered. + */ +void tevent_get_trace_immediate_callback(struct tevent_context *ev, + tevent_trace_immediate_callback_t *cb, + void *p_private_data); + /** * @} */ diff --git a/lib/tevent/tevent_debug.c b/lib/tevent/tevent_debug.c index 0a57639076e..44f76f789ca 100644 --- a/lib/tevent/tevent_debug.c +++ b/lib/tevent/tevent_debug.c @@ -114,22 +114,149 @@ void tevent_set_trace_callback(struct tevent_context *ev, return; } - ev->tracing.callback = cb; - ev->tracing.private_data = private_data; + ev->tracing.point.callback = cb; + ev->tracing.point.private_data = private_data; } void tevent_get_trace_callback(struct tevent_context *ev, tevent_trace_callback_t *cb, void *private_data) { - *cb = ev->tracing.callback; - *(void**)private_data = ev->tracing.private_data; + *cb = ev->tracing.point.callback; + *(void**)private_data = ev->tracing.point.private_data; } void tevent_trace_point_callback(struct tevent_context *ev, enum tevent_trace_point tp) { - if (ev->tracing.callback != NULL) { - ev->tracing.callback(tp, ev->tracing.private_data); + if (ev->tracing.point.callback != NULL) { + ev->tracing.point.callback(tp, ev->tracing.point.private_data); + } +} + +void tevent_set_trace_fd_callback(struct tevent_context *ev, + tevent_trace_fd_callback_t cb, + void *private_data) +{ + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_trace_fd_callback() on wrapper"); + return; + } + + ev->tracing.fde.callback = cb; + ev->tracing.fde.private_data = private_data; +} + +void tevent_get_trace_fd_callback(struct tevent_context *ev, + tevent_trace_fd_callback_t *cb, + void *p_private_data) +{ + *cb = ev->tracing.fde.callback; + *(void**)p_private_data = ev->tracing.fde.private_data; +} + +void tevent_trace_fd_callback(struct tevent_context *ev, + struct tevent_fd *fde, + enum tevent_event_trace_point tp) +{ + if (ev->tracing.fde.callback != NULL) { + ev->tracing.fde.callback(fde, tp, ev->tracing.fde.private_data); + } +} + +void tevent_set_trace_signal_callback(struct tevent_context *ev, + tevent_trace_signal_callback_t cb, + void *private_data) +{ + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_trace_signal_callback() " + "on wrapper"); + return; + } + + ev->tracing.se.callback = cb; + ev->tracing.se.private_data = private_data; +} + +void tevent_get_trace_signal_callback(struct tevent_context *ev, + tevent_trace_signal_callback_t *cb, + void *p_private_data) +{ + *cb = ev->tracing.se.callback; + *(void**)p_private_data = ev->tracing.se.private_data; +} + +void tevent_trace_signal_callback(struct tevent_context *ev, + struct tevent_signal *se, + enum tevent_event_trace_point tp) +{ + if (ev->tracing.se.callback != NULL) { + ev->tracing.se.callback(se, tp, ev->tracing.se.private_data); + } +} + +void tevent_set_trace_timer_callback(struct tevent_context *ev, + tevent_trace_timer_callback_t cb, + void *private_data) +{ + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_trace_timer_callback() " + "on wrapper"); + return; + } + + ev->tracing.te.callback = cb; + ev->tracing.te.private_data = private_data; +} + +void tevent_get_trace_timer_callback(struct tevent_context *ev, + tevent_trace_timer_callback_t *cb, + void *p_private_data) +{ + *cb = ev->tracing.te.callback; + *(void**)p_private_data = ev->tracing.te.private_data; +} + +void tevent_trace_timer_callback(struct tevent_context *ev, + struct tevent_timer *te, + enum tevent_event_trace_point tp) +{ + if (ev->tracing.te.callback != NULL) { + ev->tracing.te.callback(te, tp, ev->tracing.te.private_data); + } +} + +void tevent_set_trace_immediate_callback(struct tevent_context *ev, + tevent_trace_immediate_callback_t cb, + void *private_data) +{ + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_trace_immediate_callback() " + "on wrapper"); + return; + } + + ev->tracing.im.callback = cb; + ev->tracing.im.private_data = private_data; +} + +void tevent_get_trace_immediate_callback(struct tevent_context *ev, + tevent_trace_immediate_callback_t *cb, + void *p_private_data) +{ + *cb = ev->tracing.im.callback; + *(void**)p_private_data = ev->tracing.im.private_data; +} + +void tevent_trace_immediate_callback(struct tevent_context *ev, + struct tevent_immediate *im, + enum tevent_event_trace_point tp) +{ + if (ev->tracing.im.callback != NULL) { + ev->tracing.im.callback(im, tp, ev->tracing.im.private_data); } } diff --git a/lib/tevent/tevent_fd.c b/lib/tevent/tevent_fd.c index cd8b9741281..0e55cf1871d 100644 --- a/lib/tevent/tevent_fd.c +++ b/lib/tevent/tevent_fd.c @@ -38,6 +38,7 @@ int tevent_common_fd_destructor(struct tevent_fd *fde) fde->destroyed = true; if (fde->event_ctx) { + tevent_trace_fd_callback(fde->event_ctx, fde, TEVENT_EVENT_TRACE_DETACH); DLIST_REMOVE(fde->event_ctx->fd_events, fde); } @@ -85,10 +86,12 @@ struct tevent_fd *tevent_common_add_fd(struct tevent_context *ev, TALLOC_CTX *me .location = location, }; + tevent_trace_fd_callback(fde->event_ctx, fde, TEVENT_EVENT_TRACE_ATTACH); DLIST_ADD(ev->fd_events, fde); talloc_set_destructor(fde, tevent_common_fd_destructor); + return fde; } uint16_t tevent_common_fd_get_flags(struct tevent_fd *fde) @@ -135,6 +138,7 @@ int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags, fde->handler_name, fde->location); } + tevent_trace_fd_callback(fde->event_ctx, fde, TEVENT_EVENT_TRACE_BEFORE_HANDLER); fde->handler(handler_ev, fde, flags, fde->private_data); if (fde->wrapper != NULL) { fde->wrapper->ops->after_fd_handler( diff --git a/lib/tevent/tevent_immediate.c b/lib/tevent/tevent_immediate.c index cff0104e42d..eb607c49ab9 100644 --- a/lib/tevent/tevent_immediate.c +++ b/lib/tevent/tevent_immediate.c @@ -33,12 +33,23 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im) { const char *create_location = im->create_location; bool busy = im->busy; + uint64_t tag = im->tag; + struct tevent_context *detach_ev_ctx = NULL; if (im->destroyed) { tevent_abort(im->event_ctx, "tevent_immediate use after free"); return; } + if (im->detach_ev_ctx != NULL) { + detach_ev_ctx = im->detach_ev_ctx; + im->detach_ev_ctx = NULL; + tevent_trace_immediate_callback(detach_ev_ctx, + im, + TEVENT_EVENT_TRACE_DETACH); + return; + } + if (!im->event_ctx) { return; } @@ -54,11 +65,20 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im) im->cancel_fn(im); } + if (busy && im->handler_name == NULL) { + detach_ev_ctx = im->event_ctx; + } else { + tevent_trace_immediate_callback(im->event_ctx, + im, + TEVENT_EVENT_TRACE_DETACH); + } DLIST_REMOVE(im->event_ctx->immediate_events, im); *im = (struct tevent_immediate) { .create_location = create_location, .busy = busy, + .tag = tag, + .detach_ev_ctx = detach_ev_ctx, }; if (!busy) { @@ -122,6 +142,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im, .tag = tag, }; + tevent_trace_immediate_callback(im->event_ctx, im, TEVENT_EVENT_TRACE_ATTACH); DLIST_ADD_END(ev->immediate_events, im); talloc_set_destructor(im, tevent_common_immediate_destructor); @@ -165,6 +186,7 @@ int tevent_common_invoke_immediate_handler(struct tevent_immediate *im, cur.handler_name, cur.schedule_location); } + tevent_trace_immediate_callback(cur.event_ctx, im, TEVENT_EVENT_TRACE_BEFORE_HANDLER); cur.handler(handler_ev, im, cur.private_data); if (cur.wrapper != NULL) { cur.wrapper->ops->after_immediate_handler( @@ -178,6 +200,15 @@ int tevent_common_invoke_immediate_handler(struct tevent_immediate *im, } im->busy = false; + /* The event was removed in tevent_common_immediate_cancel(). */ + if (im->detach_ev_ctx != NULL) { + struct tevent_context *detach_ev_ctx = im->detach_ev_ctx; + im->detach_ev_ctx = NULL; + tevent_trace_immediate_callback(detach_ev_ctx, + im, + TEVENT_EVENT_TRACE_DETACH); + } + if (im->destroyed) { talloc_set_destructor(im, NULL); TALLOC_FREE(im); diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h index 78e86c7fa77..2256555943a 100644 --- a/lib/tevent/tevent_internal.h +++ b/lib/tevent/tevent_internal.h @@ -233,6 +233,7 @@ struct tevent_immediate { struct tevent_wrapper_glue *wrapper; bool busy; bool destroyed; + struct tevent_context *detach_ev_ctx; tevent_immediate_handler_t handler; /* this is private for the specific handler */ void *private_data; @@ -341,8 +342,30 @@ struct tevent_context { } nesting; struct { - tevent_trace_callback_t callback; - void *private_data; + struct { + tevent_trace_callback_t callback; + void *private_data; + } point; + + struct { + tevent_trace_fd_callback_t callback; + void *private_data; + } fde; + + struct { + tevent_trace_signal_callback_t callback; + void *private_data; + } se; + + struct { + tevent_trace_timer_callback_t callback; + void *private_data; + } te; + + struct { + tevent_trace_immediate_callback_t callback; + void *private_data; + } im; } tracing; struct { @@ -477,3 +500,19 @@ bool tevent_port_init(void); void tevent_trace_point_callback(struct tevent_context *ev, enum tevent_trace_point); + +void tevent_trace_fd_callback(struct tevent_context *ev, + struct tevent_fd *fde, + enum tevent_event_trace_point); + +void tevent_trace_signal_callback(struct tevent_context *ev, + struct tevent_signal *se, + enum tevent_event_trace_point); + +void tevent_trace_timer_callback(struct tevent_context *ev, + struct tevent_timer *te, + enum tevent_event_trace_point); + +void tevent_trace_immediate_callback(struct tevent_context *ev, + struct tevent_immediate *im, + enum tevent_event_trace_point); diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c index b55940c788e..eab36541eed 100644 --- a/lib/tevent/tevent_signal.c +++ b/lib/tevent/tevent_signal.c @@ -194,6 +194,7 @@ static int tevent_signal_destructor(struct tevent_signal *se) TALLOC_FREE(se->additional_data); if (se->event_ctx != NULL) { + tevent_trace_signal_callback(se->event_ctx, se, TEVENT_EVENT_TRACE_DETACH); DLIST_REMOVE(se->event_ctx->signal_events, se); } @@ -327,6 +328,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, sigemptyset(&set); sigaddset(&set, signum); sigprocmask(SIG_BLOCK, &set, &oldset); + tevent_trace_signal_callback(se->event_ctx, se, TEVENT_EVENT_TRACE_ATTACH); DLIST_ADD(sig_state->sig_handlers[signum], sl); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -367,6 +369,7 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se, se->handler_name, se->location); } + tevent_trace_signal_callback(se->event_ctx, se, TEVENT_EVENT_TRACE_BEFORE_HANDLER); se->handler(handler_ev, se, signum, count, siginfo, se->private_data); if (se->wrapper != NULL) { se->wrapper->ops->after_signal_handler( diff --git a/lib/tevent/tevent_timed.c b/lib/tevent/tevent_timed.c index b0458aaceaf..67e39ed0dd7 100644 --- a/lib/tevent/tevent_timed.c +++ b/lib/tevent/tevent_timed.c @@ -1,4 +1,4 @@ -/* +/* Unix SMB/CIFS implementation. common events code for timed events @@ -32,7 +32,7 @@ #include "tevent_util.h" /** - compare two timeval structures. + compare two timeval structures. Return -1 if tv1 < tv2 Return 0 if tv1 == tv2 Return 1 if tv1 > tv2 @@ -151,6 +151,8 @@ static int tevent_common_timed_destructor(struct tevent_timer *te) if (te->event_ctx->last_zero_timer == te) { te->event_ctx->last_zero_timer = DLIST_PREV(te); } + + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_DETACH); DLIST_REMOVE(te->event_ctx->timer_events, te); te->event_ctx = NULL; @@ -216,6 +218,7 @@ static void tevent_common_insert_timer(struct tevent_context *ev, prev_te = cur_te; } + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_ATTACH); DLIST_ADD_AFTER(ev->timer_events, te, prev_te); } @@ -255,6 +258,7 @@ static struct tevent_timer *tevent_common_add_timer_internal( talloc_set_destructor(te, tevent_common_timed_destructor); + tevent_debug(ev, TEVENT_DEBUG_TRACE, "Added timed event \"%s\": %p\n", handler_name, te); @@ -306,6 +310,7 @@ void tevent_update_timer(struct tevent_timer *te, struct timeval next_event) if (ev->last_zero_timer == te) { te->event_ctx->last_zero_timer = DLIST_PREV(te); } + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_DETACH); DLIST_REMOVE(ev->timer_events, te); te->next_event = next_event; @@ -367,6 +372,7 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te, te->handler_name, te->location); } + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_BEFORE_HANDLER); te->handler(handler_ev, te, current_time, te->private_data); if (te->wrapper != NULL) { te->wrapper->ops->after_timer_handler( @@ -386,6 +392,11 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te, "Ending timer event %p \"%s\"\n", te, te->handler_name); + /* The callback was already called when freed from the handler. */ + if (!te->destroyed) { + tevent_trace_timer_callback(te->event_ctx, te, TEVENT_EVENT_TRACE_DETACH); + } + te->wrapper = NULL; te->event_ctx = NULL; talloc_set_destructor(te, NULL); diff --git a/lib/tevent/wscript b/lib/tevent/wscript index 5c318972124..da5d10acdc4 100644 --- a/lib/tevent/wscript +++ b/lib/tevent/wscript @@ -140,6 +140,11 @@ def build(bld): deps='cmocka tevent', install=False) + bld.SAMBA_BINARY('test_tevent_trace', + source='tests/test_tevent_trace.c', + deps='cmocka tevent', + install=False) + def test(ctx): '''test tevent''' print("The tevent testsuite is part of smbtorture in samba4") @@ -152,6 +157,7 @@ def test(ctx): unit_test_ret = 0 unit_tests = [ 'test_tevent_tag', + 'test_tevent_trace', ] for unit_test in unit_tests: |