summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPavel Březina <pbrezina@redhat.com>2021-06-01 14:10:05 +0200
committerAndreas Schneider <asn@cryptomilk.org>2021-07-07 05:07:30 +0000
commit683c44a79f09550a59e99606c9c80304666604a0 (patch)
tree756b76b685b54e1ab35530d361b2335048ae6d98 /lib
parentfc9dd8ce9f0ae2a7ae972035e56c4c4f9028e261 (diff)
downloadsamba-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.sigs12
-rw-r--r--lib/tevent/tests/test_tevent_trace.c1065
-rw-r--r--lib/tevent/tevent.c4
-rw-r--r--lib/tevent/tevent.h149
-rw-r--r--lib/tevent/tevent_debug.c139
-rw-r--r--lib/tevent/tevent_fd.c4
-rw-r--r--lib/tevent/tevent_immediate.c31
-rw-r--r--lib/tevent/tevent_internal.h43
-rw-r--r--lib/tevent/tevent_signal.c3
-rw-r--r--lib/tevent/tevent_timed.c15
-rw-r--r--lib/tevent/wscript6
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: