summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Filipenský <pfilipen@redhat.com>2022-06-16 16:23:22 +0200
committerStefan Metzmacher <metze@samba.org>2023-01-18 16:26:36 +0000
commitc5d5ebb60d46d7b4717d23ca8d2cf1b14c7ff7e5 (patch)
tree7fe2c0fddae60b50443309ca68ff52ab5c1f23a3
parent07251f562c6cd777394b3ffa29525a69f5ceffc4 (diff)
downloadsamba-c5d5ebb60d46d7b4717d23ca8d2cf1b14c7ff7e5.tar.gz
tevent: Call depth tracking
The change to lib/tevent/ABI/tevent-0.13.0.sigs will be reverted in the commit for the 0.14.0 release... Signed-off-by: Pavel Filipenský <pfilipen@redhat.com> Reviewed-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Volker Lendecke <vl@samba.org>
-rw-r--r--lib/tevent/ABI/tevent-0.13.0.sigs4
-rw-r--r--lib/tevent/tevent.c3
-rw-r--r--lib/tevent/tevent.h163
-rw-r--r--lib/tevent/tevent_debug.c38
-rw-r--r--lib/tevent/tevent_internal.h3
-rw-r--r--lib/tevent/tevent_queue.c2
-rw-r--r--lib/tevent/tevent_req.c10
7 files changed, 223 insertions, 0 deletions
diff --git a/lib/tevent/ABI/tevent-0.13.0.sigs b/lib/tevent/ABI/tevent-0.13.0.sigs
index 76c84af812c..f2a11902a80 100644
--- a/lib/tevent/ABI/tevent-0.13.0.sigs
+++ b/lib/tevent/ABI/tevent-0.13.0.sigs
@@ -129,6 +129,10 @@ tevent_set_trace_timer_callback: void (struct tevent_context *, tevent_trace_tim
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 *)
+tevent_thread_call_depth_activate: void (size_t *)
+tevent_thread_call_depth_deactivate: void (void)
+tevent_thread_call_depth_reset_from_req: void (struct tevent_req *)
+tevent_thread_call_depth_start: void (struct tevent_req *)
tevent_thread_proxy_create: struct tevent_thread_proxy *(struct tevent_context *)
tevent_thread_proxy_schedule: void (struct tevent_thread_proxy *, struct tevent_immediate **, tevent_immediate_handler_t, void *)
tevent_threaded_context_create: struct tevent_threaded_context *(TALLOC_CTX *, struct tevent_context *)
diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c
index 19f30ff7722..698e467c88c 100644
--- a/lib/tevent/tevent.c
+++ b/lib/tevent/tevent.c
@@ -823,6 +823,9 @@ int _tevent_loop_once(struct tevent_context *ev, const char *location)
ret = ev->ops->loop_once(ev, location);
tevent_trace_point_callback(ev, TEVENT_TRACE_AFTER_LOOP_ONCE);
+ /* New event (and request) will always start with call depth 0. */
+ tevent_thread_call_depth_set(0);
+
if (ev->nesting.level > 0) {
if (ev->nesting.hook_fn) {
int ret2;
diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h
index 350270d8e40..30391852885 100644
--- a/lib/tevent/tevent.h
+++ b/lib/tevent/tevent.h
@@ -1929,6 +1929,169 @@ pid_t tevent_cached_getpid(void);
/**
+ * @defgroup tevent_thread_call_depth The tevent call depth tracking functions
+ * @ingroup tevent
+ *
+ *
+ * The call depth tracking consists of two parts.
+ *
+ * Part 1 - storing the depth inside each tevent request.
+ *
+ * Each instance of 'struct tevent_req' internally stores the value of the
+ * current depth. If a new subrequest is created via tevent_req_create(), the
+ * newly created subrequest gets the value from the parent incremented by 1.
+ *
+ * Part 2 - updating external variable with the call depth of the currently
+ * processed tevent request.
+ *
+ * The intended use of call depth is for the trace indentation. This is done
+ * by registering the address of an external size_t variable via
+ * tevent_thread_call_depth_activate(). And the tracing code just reads it's
+ * value.
+ *
+ * The updates happen during:
+ *
+ * tevent_req_create()
+ * - external variable is set to the value of the newly created request (i.e.
+ * value of the parent incremented by 1)
+ *
+ * tevent_req_notify_callback()
+ * - external variable is set to the value of the parent tevent request, which
+ * is just about to be processed
+ *
+ * tevent_queue_immediate_trigger()
+ * - external variable is set to the value of the request coming from the queue
+ *
+ *
+ * While 'Part 1' maintains the call depth value inside each teven request
+ * precisely, the value of the external variable depends on the call flow and
+ * can be changed after return from a function call, so it no longer matches
+ * the value of the request being processed in the current function.
+ *
+ * @code
+ * struct tevent_req *foo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+ * {
+ * struct tevent_req *req, *subreq;
+ * struct foo_state *state;
+ *
+ * // External variable has value 'X', which is the value in parent code
+ * // It is ok, since tracing starts often only after tevent_req_create()
+ * req = tevent_req_create(mem_ctx, &state, struct foo_state);
+ *
+ * // External variable has now value 'X + 1'
+ * D_DEBUG("foo_send(): the external variable has the expected value\n");
+ *
+ * subreq = bar_send(state, ev, ...);
+ * tevent_req_set_callback(subreq, foo_done, req);
+ *
+ * // External variable has value 'X + 1 + n', where n > 0 and n is the
+ * // depth reached in bar_send().
+ * // We want to reset it via tevent_thread_call_depth_reset_from_req(),
+ * // since we want the following D_DEBUG() to have the right trace
+ * //indentation.
+ *
+ * tevent_thread_call_depth_reset_from_req(req);
+ * // External variable has again value 'X + 1' taken from req.
+ * D_DEBUG("foo_send(): the external variable has the expected value\n");
+ * return req;
+ * }
+ *
+ * static void foo_done(struct tevent_req *subreq)
+ * {
+ * struct tevent_req *req =
+ * tevent_req_callback_data(subreq,
+ * struct tevent_req);
+ * struct foo_state *state =
+ * tevent_req_data(req,
+ * struct foo_state);
+ *
+ * // external variable has value 'X + 1'
+ *
+ * D_DEBUG("foo_done(): the external variable has the expected value\n");
+ * status = bar_recv(subreq, state, ...)
+ * tevent_req_done(req);
+ * }
+ *
+ * NTSTATUS foo_recv(struct tevent_req *req)
+ * {
+ * struct foo_state *state = tevent_req_data( req, struct foo_state);
+ *
+ * // external variable has value 'X' (not 'X + 1')
+ * // which is ok, if we consider _recv() to be an access function
+ * // called from the parent context
+ *
+ * D_DEBUG("foo_recv(): external variable has the value from parent\n");
+ * return NT_STATUS_OK;
+ * }
+ * @endcode
+ *
+ * Interface has 3 parts:
+ *
+ * Part 1: activation/deactivation
+ *
+ * tevent_thread_call_depth_activate(), tevent_thread_call_depth_deactivate()
+ *
+ * Activating registers external size_t variable that will be maintained with
+ * the current call depth.
+ *
+ * Part 2: Mark the request (and its subrequests) to be tracked
+ *
+ * tevent_thread_call_depth_start(struct tevent_req *req)
+ *
+ * By default, all newly created requests have call depth set to 0.
+ * tevent_thread_call_depth_start() should be called shortly after
+ * tevent_req_create(). It sets the call depth to 1.
+ * Subrequest will have call depth 2 and so on.
+ *
+ * Part 3: reset the external variable using value from tevent request
+ *
+ * tevent_thread_call_depth_reset_from_req(struct tevent_req *req)
+ *
+ * If the call depth is used for trace indentation, it might be usefull to
+ * reset the external variable to the call depth of currently processed tevent
+ * request, since the ext. variable can be changed after return from a function
+ * call that has created subrequests.
+ *
+ * THREADING
+ *
+ * The state is thread specific, i.e. each thread can activate it and register
+ * its own external variable.
+ *
+ * @{
+ */
+
+/**
+ * Activate call depth tracking and register external variable that will
+ * be updated to the call epth of currenty processed tevent request.
+ *
+ * @param[in] ptr Address of external variable
+ */
+void tevent_thread_call_depth_activate(size_t *ptr);
+
+/**
+ * Deactivate call depth tracking. Can be used in the child process,
+ * after fork.
+ */
+void tevent_thread_call_depth_deactivate(void);
+
+/**
+ * This request will have call depth set to 1, its subrequest will get 2 and so
+ * on. All other requests will have call depth 0.
+ */
+void tevent_thread_call_depth_start(struct tevent_req *req);
+
+/**
+ * Set the external variable to the call depth of the request req.
+ *
+ * @param[in] req Request from which the call depth is assigned to ext.
+ * variable.
+ */
+void tevent_thread_call_depth_reset_from_req(struct tevent_req *req);
+
+/* @} */
+
+
+/**
* @defgroup tevent_queue The tevent queue functions
* @ingroup tevent
*
diff --git a/lib/tevent/tevent_debug.c b/lib/tevent/tevent_debug.c
index 6b8f2d5100b..6b04089e85b 100644
--- a/lib/tevent/tevent_debug.c
+++ b/lib/tevent/tevent_debug.c
@@ -292,3 +292,41 @@ void tevent_trace_queue_callback(struct tevent_context *ev,
ev->tracing.qe.callback(qe, tp, ev->tracing.qe.private_data);
}
}
+
+static __thread size_t *tevent_thread_call_depth_ptr = NULL;
+
+void tevent_thread_call_depth_activate(size_t *ptr)
+{
+ tevent_thread_call_depth_ptr = ptr;
+ *tevent_thread_call_depth_ptr = 0;
+}
+
+void tevent_thread_call_depth_deactivate(void)
+{
+ /* Reset the previous storage */
+ if (tevent_thread_call_depth_ptr != NULL) {
+ *tevent_thread_call_depth_ptr = 0;
+ }
+ tevent_thread_call_depth_ptr = NULL;
+}
+
+void tevent_thread_call_depth_start(struct tevent_req *req)
+{
+ if (tevent_thread_call_depth_ptr != NULL) {
+ *tevent_thread_call_depth_ptr = req->internal.call_depth = 1;
+ }
+}
+
+void tevent_thread_call_depth_reset_from_req(struct tevent_req *req)
+{
+ if (tevent_thread_call_depth_ptr != NULL) {
+ *tevent_thread_call_depth_ptr = req->internal.call_depth;
+ }
+}
+
+_PRIVATE_ void tevent_thread_call_depth_set(size_t depth)
+{
+ if (tevent_thread_call_depth_ptr != NULL) {
+ *tevent_thread_call_depth_ptr = depth;
+ }
+}
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index a5df1b14b26..e6853d148ee 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -169,6 +169,8 @@ struct tevent_req {
* @brief The place where profiling data is kept
*/
struct tevent_req_profile *profile;
+
+ size_t call_depth;
} internal;
};
@@ -497,6 +499,7 @@ void tevent_epoll_set_panic_fallback(struct tevent_context *ev,
bool replay));
#endif
+void tevent_thread_call_depth_set(size_t depth);
void tevent_trace_point_callback(struct tevent_context *ev,
enum tevent_trace_point);
diff --git a/lib/tevent/tevent_queue.c b/lib/tevent/tevent_queue.c
index dffe30fdcfb..647b8a00ed3 100644
--- a/lib/tevent/tevent_queue.c
+++ b/lib/tevent/tevent_queue.c
@@ -149,6 +149,8 @@ static void tevent_queue_immediate_trigger(struct tevent_context *ev,
tevent_trace_queue_callback(ev, q->list,
TEVENT_EVENT_TRACE_BEFORE_HANDLER);
+ /* Set the call depth of the request coming from the queue. */
+ tevent_thread_call_depth_set(q->list->req->internal.call_depth);
q->list->triggered = true;
q->list->trigger(q->list->req, q->list->private_data);
}
diff --git a/lib/tevent/tevent_req.c b/lib/tevent/tevent_req.c
index 7821d9ae734..e76863b2170 100644
--- a/lib/tevent/tevent_req.c
+++ b/lib/tevent/tevent_req.c
@@ -120,6 +120,13 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
}
*ppdata = data;
+
+ /* Initially, talloc_zero_size() sets internal.call_depth to 0 */
+ if (parent != NULL && parent->internal.call_depth > 0) {
+ req->internal.call_depth = parent->internal.call_depth + 1;
+ tevent_thread_call_depth_set(req->internal.call_depth);
+ }
+
return req;
}
@@ -138,6 +145,9 @@ void _tevent_req_notify_callback(struct tevent_req *req, const char *location)
return;
}
if (req->async.fn != NULL) {
+ /* Calling back the parent code, decrement the call depth. */
+ tevent_thread_call_depth_set(req->internal.call_depth > 0 ?
+ req->internal.call_depth - 1 : 0);
req->async.fn(req);
}
}