From f0b0183262fc75a4d15fdac1f0ddb6502e6af829 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Fri, 15 Dec 2023 15:53:02 -0500 Subject: [PATCH] Implement statedump request notifications Signed-off-by: Mathieu Desnoyers --- include/side/instrumentation-c-api.h | 24 ++++++++--- include/side/trace.h | 39 +++++++++++++---- src/side.c | 62 +++++++++++++++++++++++++++- src/tracer.c | 8 ++-- 4 files changed, 114 insertions(+), 19 deletions(-) diff --git a/include/side/instrumentation-c-api.h b/include/side/instrumentation-c-api.h index 00cd20b..99a5acc 100644 --- a/include/side/instrumentation-c-api.h +++ b/include/side/instrumentation-c-api.h @@ -1180,21 +1180,24 @@ if (side_unlikely(__atomic_load_n(&side_event_state__##_identifier.enabled, \ __ATOMIC_RELAXED))) -#define side_event_call(_identifier, _sav) \ +#define _side_event_call(_call, _identifier, _sav) \ { \ const struct side_arg side_sav[] = { _sav }; \ const struct side_arg_vec side_arg_vec = { \ .sav = SIDE_PTR_INIT(side_sav), \ .len = SIDE_ARRAY_SIZE(side_sav), \ }; \ - side_call(&(side_event_state__##_identifier).parent, &side_arg_vec); \ + _call(&(side_event_state__##_identifier).parent, &side_arg_vec); \ } +#define side_event_call(_identifier, _sav) \ + _side_event_call(side_call, _identifier, SIDE_PARAM(_sav)) + #define side_event(_identifier, _sav) \ side_event_cond(_identifier) \ side_event_call(_identifier, SIDE_PARAM(_sav)) -#define side_event_call_variadic(_identifier, _sav, _var_fields, _attr...) \ +#define _side_event_call_variadic(_call, _identifier, _sav, _var_fields, _attr...) \ { \ const struct side_arg side_sav[] = { _sav }; \ const struct side_arg_vec side_arg_vec = { \ @@ -1204,17 +1207,26 @@ const struct side_arg_dynamic_field side_fields[] = { _var_fields }; \ const struct side_arg_dynamic_struct var_struct = { \ .fields = SIDE_PTR_INIT(side_fields), \ - .attr = SIDE_PTR_INIT(SIDE_PARAM_SELECT_ARG1(_, ##_attr, side_attr_list())), \ + .attr = SIDE_PTR_INIT(_attr), \ .len = SIDE_ARRAY_SIZE(side_fields), \ - .nr_attr = SIDE_ARRAY_SIZE(SIDE_PARAM_SELECT_ARG1(_, ##_attr, side_attr_list())), \ + .nr_attr = SIDE_ARRAY_SIZE(_attr), \ }; \ - side_call_variadic(&(side_event_state__##_identifier.parent), &side_arg_vec, &var_struct); \ + _call(&(side_event_state__##_identifier.parent), &side_arg_vec, &var_struct); \ } +#define side_event_call_variadic(_identifier, _sav, _var_fields, _attr...) \ + _side_event_call_variadic(side_call_variadic, _identifier, SIDE_PARAM(_sav), SIDE_PARAM(_var_fields), SIDE_PARAM_SELECT_ARG1(_, ##_attr, side_attr_list())) + #define side_event_variadic(_identifier, _sav, _var, _attr...) \ side_event_cond(_identifier) \ side_event_call_variadic(_identifier, SIDE_PARAM(_sav), SIDE_PARAM(_var), SIDE_PARAM_SELECT_ARG1(_, ##_attr, side_attr_list())) +#define side_statedump_event_call(_identifier, _sav) \ + _side_event_call(side_statedump_call, _identifier, SIDE_PARAM(_sav)) + +#define side_statedump_event_call_variadic(_identifier, _sav, _var_fields, _attr...) \ + _side_event_call_variadic(side_statedump_call_variadic, _identifier, SIDE_PARAM(_sav), SIDE_PARAM(_var_fields), SIDE_PARAM_SELECT_ARG1(_, ##_attr, side_attr_list())) + #define _side_define_event(_linkage, _identifier, _provider, _event, _loglevel, _fields, _flags, _attr...) \ _linkage struct side_event_description __attribute__((section("side_event_description"))) \ _identifier; \ diff --git a/include/side/trace.h b/include/side/trace.h index 22d88c7..fa1fd5e 100644 --- a/include/side/trace.h +++ b/include/side/trace.h @@ -73,6 +73,8 @@ extern "C" { #endif struct side_callback; +struct side_tracer_handle; +struct side_statedump_request_handle; extern const char side_empty_callback[]; @@ -81,13 +83,6 @@ void side_call(const struct side_event_state *state, void side_call_variadic(const struct side_event_state *state, const struct side_arg_vec *side_arg_vec, const struct side_arg_dynamic_struct *var_struct); -void side_call_key(const struct side_event_state *state, - const struct side_arg_vec *side_arg_vec, - void *key); -void side_call_variadic_key(const struct side_event_state *state, - const struct side_arg_vec *side_arg_vec, - const struct side_arg_dynamic_struct *var_struct, - void *key); struct side_events_register_handle *side_events_register(struct side_event_description **events, uint32_t nr_events); @@ -132,6 +127,36 @@ struct side_tracer_handle *side_tracer_event_notification_register( void *priv); void side_tracer_event_notification_unregister(struct side_tracer_handle *handle); +/* + * The side_statedump_call APIs should be used for application/library + * state dump. + * The statedump callback dumps application state to tracers by invoking + * side_statedump_call APIs. + * The statedump callback is invoked with side library internal lock held. + * The statedump callback should not invoke functions which require the + * side library internal lock. + */ +void side_statedump_call(const struct side_event_state *state, + const struct side_arg_vec *side_arg_vec); +void side_statedump_call_variadic(const struct side_event_state *state, + const struct side_arg_vec *side_arg_vec, + const struct side_arg_dynamic_struct *var_struct); + +/* + * If side_statedump_request_notification_register is invoked from + * library constructors and side_statedump_request_notification_unregister + * from library destructors, make sure to: + * - invoke side_event_description_ptr_init before registration of the + * callback, + * - invoke side_event_description_ptr_exit after unregistration of the + * callback. + */ + +struct side_statedump_request_handle *side_statedump_request_notification_register(void (*statedump_cb)(void)); +void side_statedump_request_notification_unregister(struct side_statedump_request_handle *handle); + +void side_tracer_statedump_request(void *key); + /* * Explicit hooks to initialize/finalize the side instrumentation * library. Those are also library constructor/destructor. diff --git a/src/side.c b/src/side.c index df61236..045ffcf 100644 --- a/src/side.c +++ b/src/side.c @@ -38,6 +38,11 @@ struct side_tracer_handle { void *priv; }; +struct side_statedump_request_handle { + struct side_list_node node; + void (*cb)(void); +}; + struct side_callback { union { void (*call)(const struct side_event_description *desc, @@ -70,6 +75,7 @@ static pthread_mutex_t side_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static DEFINE_SIDE_LIST_HEAD(side_events_list); static DEFINE_SIDE_LIST_HEAD(side_tracer_list); +static DEFINE_SIDE_LIST_HEAD(side_statedump_list); /* * Callback filter key for state dump. @@ -117,7 +123,7 @@ void side_call(const struct side_event_state *event_state, const struct side_arg _side_call(event_state, side_arg_vec, NULL); } -void side_call_key(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec) +void side_statedump_call(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec) { _side_call(event_state, side_arg_vec, filter_key); } @@ -162,7 +168,7 @@ void side_call_variadic(const struct side_event_state *event_state, _side_call_variadic(event_state, side_arg_vec, var_struct, NULL); } -void side_call_variadic_key(const struct side_event_state *event_state, +void side_statedump_call_variadic(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec, const struct side_arg_dynamic_struct *var_struct) { @@ -480,6 +486,58 @@ void side_tracer_event_notification_unregister(struct side_tracer_handle *tracer free(tracer_handle); } +struct side_statedump_request_handle *side_statedump_request_notification_register(void (*statedump_cb)(void)) +{ + struct side_statedump_request_handle *handle; + + if (finalized) + return NULL; + if (!initialized) + side_init(); + /* + * The statedump request notification should not be registered + * from a notification callback. + */ + assert(filter_key == NULL); + handle = (struct side_statedump_request_handle *) + calloc(1, sizeof(struct side_statedump_request_handle)); + if (!handle) + return NULL; + pthread_mutex_lock(&side_lock); + handle->cb = statedump_cb; + side_list_insert_node_tail(&side_statedump_list, &handle->node); + /* Invoke callback for all tracers. */ + statedump_cb(); + pthread_mutex_unlock(&side_lock); + return handle; +} + +void side_statedump_request_notification_unregister(struct side_statedump_request_handle *handle) +{ + if (finalized) + return; + if (!initialized) + side_init(); + assert(filter_key == NULL); + pthread_mutex_lock(&side_lock); + side_list_remove_node(&handle->node); + pthread_mutex_unlock(&side_lock); + free(handle); +} + +void side_tracer_statedump_request(void *key) +{ + struct side_statedump_request_handle *handle; + + /* Invoke the state dump callback specifically for the tracer key. */ + filter_key = key; + pthread_mutex_lock(&side_lock); + side_list_for_each_entry(handle, &side_statedump_list, node) + handle->cb(); + pthread_mutex_unlock(&side_lock); + filter_key = NULL; +} + void side_init(void) { if (initialized) diff --git a/src/tracer.c b/src/tracer.c index 427f9e6..1b95afc 100644 --- a/src/tracer.c +++ b/src/tracer.c @@ -2120,21 +2120,21 @@ void tracer_event_notification(enum side_tracer_notification notif, event->nr_side_attr_type - _NR_SIDE_ATTR_TYPE); } if (event->flags & SIDE_EVENT_FLAG_VARIADIC) { - ret = side_tracer_callback_variadic_register(event, tracer_call_variadic, NULL, NULL); + ret = side_tracer_callback_variadic_register(event, tracer_call_variadic, NULL, tracer_handle); if (ret) abort(); } else { - ret = side_tracer_callback_register(event, tracer_call, NULL, NULL); + ret = side_tracer_callback_register(event, tracer_call, NULL, tracer_handle); if (ret) abort(); } } else { if (event->flags & SIDE_EVENT_FLAG_VARIADIC) { - ret = side_tracer_callback_variadic_unregister(event, tracer_call_variadic, NULL, NULL); + ret = side_tracer_callback_variadic_unregister(event, tracer_call_variadic, NULL, tracer_handle); if (ret) abort(); } else { - ret = side_tracer_callback_unregister(event, tracer_call, NULL, NULL); + ret = side_tracer_callback_unregister(event, tracer_call, NULL, tracer_handle); if (ret) abort(); } -- 2.34.1