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);
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 */
*/
#include <side/trace.h>
+#include <string.h>
+
#include "tracer.h"
#include "rcu.h"
#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)
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)