From: Mathieu Desnoyers Date: Fri, 28 Oct 2022 01:31:42 +0000 (-0400) Subject: callback register/unregister X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=a3f36db71deaf74d9bae872ce1ec411145db7351;p=libside.git callback register/unregister Signed-off-by: Mathieu Desnoyers --- diff --git a/include/side/trace.h b/include/side/trace.h index 51acc70..32de0c5 100644 --- a/include/side/trace.h +++ b/include/side/trace.h @@ -143,6 +143,14 @@ enum side_visitor_status { SIDE_VISITOR_STATUS_ERROR = -1, }; +enum side_error { + SIDE_ERROR_OK = 0, + SIDE_ERROR_INVAL = 1, + SIDE_ERROR_EXIST = 2, + SIDE_ERROR_NOMEM = 3, + SIDE_ERROR_NOENT = 4, +}; + typedef enum side_visitor_status (*side_visitor)( const struct side_tracer_visitor_ctx *tracer_ctx, void *app_ctx); @@ -1101,12 +1109,35 @@ struct side_tracer_dynamic_vla_visitor_ctx { extern uint32_t side_event_enable_##_identifier; \ extern struct side_event_description _identifier +extern const struct side_callback side_empty_callback; + void side_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc); void side_call_variadic(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc, const struct side_arg_dynamic_event_struct *var_struct); -extern const struct side_callback side_empty_callback; +int side_tracer_callback_register(struct side_event_description *desc, + void (*call)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + void *priv), + void *priv); +int side_tracer_callback_variadic_register(struct side_event_description *desc, + void (*call_variadic)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + const struct side_arg_dynamic_event_struct *var_struct, + void *priv), + void *priv); +int side_tracer_callback_unregister(struct side_event_description *desc, + void (*call)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + void *priv), + void *priv); +int side_tracer_callback_variadic_unregister(struct side_event_description *desc, + void (*call_variadic)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + const struct side_arg_dynamic_event_struct *var_struct, + void *priv), + void *priv); #endif /* _SIDE_TRACE_H */ diff --git a/src/side.c b/src/side.c index 0715c74..14788d7 100644 --- a/src/side.c +++ b/src/side.c @@ -4,6 +4,8 @@ */ #include +#include + #include "tracer.h" #include "rcu.h" @@ -11,20 +13,26 @@ #define SIDE_EVENT_ENABLED_KERNEL_MASK 0xFF000000 #define SIDE_EVENT_ENABLED_KERNEL_USER_EVENT_MASK 0x80000000 -/* Allow 2^24 tracers to be registered on an event. */ +/* Allow 2^24 tracer callbacks to be registered on an event. */ #define SIDE_EVENT_ENABLED_USER_MASK 0x00FFFFFF -struct side_rcu_gp_state rcu_gp; +static struct side_rcu_gp_state rcu_gp; /* * Lazy initialization for early use within library constructors. */ static bool initialized; +static pthread_mutex_t side_lock = PTHREAD_MUTEX_INITIALIZER; + static void side_init(void) __attribute__((constructor)); +/* + * The empty callback has a NULL function callback pointer, which stops + * iteration on the array of callbacks immediately. + */ const struct side_callback side_empty_callback; void side_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc) @@ -77,6 +85,159 @@ void side_call_variadic(const struct side_event_description *desc, side_rcu_read_end(&rcu_gp, rcu_period); } +static +const struct side_callback *side_tracer_callback_lookup( + const struct side_event_description *desc, + void (*call)(), void *priv) +{ + const struct side_callback *cb; + + for (cb = desc->callbacks; cb->u.call != NULL; cb++) { + if (cb->u.call == call && cb->priv == priv) + return cb; + } + return NULL; +} + +static +int _side_tracer_callback_register(struct side_event_description *desc, + void (*call)(), void *priv) +{ + struct side_callback *old_cb, *new_cb; + int ret = SIDE_ERROR_OK; + uint32_t old_nr_cb; + + if (!call) + return SIDE_ERROR_INVAL; + pthread_mutex_lock(&side_lock); + old_nr_cb = *desc->enabled & SIDE_EVENT_ENABLED_USER_MASK; + if (old_nr_cb == SIDE_EVENT_ENABLED_USER_MASK) { + ret = SIDE_ERROR_INVAL; + goto unlock; + } + /* Reject duplicate (call, priv) tuples. */ + if (side_tracer_callback_lookup(desc, call, priv)) { + ret = SIDE_ERROR_EXIST; + goto unlock; + } + old_cb = (struct side_callback *) desc->callbacks; + /* old_nr_cb + 1 (new cb) + 1 (NULL) */ + new_cb = (struct side_callback *) calloc(old_nr_cb + 2, sizeof(struct side_callback)); + if (!new_cb) { + ret = SIDE_ERROR_NOMEM; + goto unlock; + } + memcpy(new_cb, old_cb, old_nr_cb); + if (desc->flags & SIDE_EVENT_FLAG_VARIADIC) + new_cb[old_nr_cb].u.call_variadic = call; + else + new_cb[old_nr_cb].u.call = call; + new_cb[old_nr_cb].priv = priv; + side_rcu_assign_pointer(desc->callbacks, new_cb); + side_rcu_wait_grace_period(&rcu_gp); + if (old_nr_cb) + free(old_cb); + /* Increment concurrently with kernel setting the top bits. */ + (void) __atomic_add_fetch(desc->enabled, 1, __ATOMIC_RELAXED); +unlock: + pthread_mutex_unlock(&side_lock); + return ret; +} + +int side_tracer_callback_register(struct side_event_description *desc, + void (*call)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + void *priv), + void *priv) +{ + if (desc->flags & SIDE_EVENT_FLAG_VARIADIC) + return SIDE_ERROR_INVAL; + return _side_tracer_callback_register(desc, call, priv); +} + +int side_tracer_callback_variadic_register(struct side_event_description *desc, + void (*call_variadic)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + const struct side_arg_dynamic_event_struct *var_struct, + void *priv), + void *priv) +{ + if (!(desc->flags & SIDE_EVENT_FLAG_VARIADIC)) + return SIDE_ERROR_INVAL; + return _side_tracer_callback_register(desc, call_variadic, priv); +} + +int _side_tracer_callback_unregister(struct side_event_description *desc, + void (*call)(), void *priv) +{ + struct side_callback *old_cb, *new_cb; + const struct side_callback *cb_pos; + uint32_t pos_idx; + int ret = SIDE_ERROR_OK; + uint32_t old_nr_cb; + + if (!call) + return SIDE_ERROR_INVAL; + pthread_mutex_lock(&side_lock); + old_nr_cb = *desc->enabled & SIDE_EVENT_ENABLED_USER_MASK; + if (old_nr_cb == 0) { + ret = SIDE_ERROR_INVAL; + goto unlock; + } + old_cb = (struct side_callback *) desc->callbacks; + cb_pos = side_tracer_callback_lookup(desc, call, priv); + if (!cb_pos) { + ret = SIDE_ERROR_NOENT; + goto unlock; + } + if (old_nr_cb == 1) { + new_cb = (struct side_callback *) &side_empty_callback; + } else { + pos_idx = cb_pos - desc->callbacks; + /* Remove entry at pos_idx. */ + /* old_nr_cb - 1 (removed cb) + 1 (NULL) */ + new_cb = (struct side_callback *) calloc(old_nr_cb, sizeof(struct side_callback)); + if (!new_cb) { + ret = SIDE_ERROR_NOMEM; + goto unlock; + } + memcpy(new_cb, old_cb, pos_idx); + memcpy(&new_cb[pos_idx], &old_cb[pos_idx + 1], old_nr_cb - pos_idx - 1); + } + side_rcu_assign_pointer(desc->callbacks, new_cb); + side_rcu_wait_grace_period(&rcu_gp); + free(old_cb); + /* Decrement concurrently with kernel setting the top bits. */ + (void) __atomic_add_fetch(desc->enabled, -1, __ATOMIC_RELAXED); +unlock: + pthread_mutex_unlock(&side_lock); + return ret; +} + +int side_tracer_callback_unregister(struct side_event_description *desc, + void (*call)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + void *priv), + void *priv) +{ + if (desc->flags & SIDE_EVENT_FLAG_VARIADIC) + return SIDE_ERROR_INVAL; + return _side_tracer_callback_unregister(desc, call, priv); +} + +int side_tracer_callback_variadic_unregister(struct side_event_description *desc, + void (*call_variadic)(const struct side_event_description *desc, + const struct side_arg_vec_description *sav_desc, + const struct side_arg_dynamic_event_struct *var_struct, + void *priv), + void *priv) +{ + if (!(desc->flags & SIDE_EVENT_FLAG_VARIADIC)) + return SIDE_ERROR_INVAL; + return _side_tracer_callback_unregister(desc, call_variadic, priv); +} + +static void side_init(void) { if (initialized)