X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Faction-executor.c;h=14ac8103b1fe0c7738314c7ac962ca538234cb29;hp=d1369375d6f70d71244059040f64a1a6456db438;hb=da2475084f73e371756539a5da696d6ec5e187ce;hpb=0db0f8e075e3790a2707736d2a4d086b123f3160 diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c index d1369375d..14ac8103b 100644 --- a/src/bin/lttng-sessiond/action-executor.c +++ b/src/bin/lttng-sessiond/action-executor.c @@ -12,16 +12,20 @@ #include "notification-thread-internal.h" #include "session.h" #include "thread.h" +#include #include #include #include -#include +#include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -32,8 +36,50 @@ #define THREAD_NAME "Action Executor" #define MAX_QUEUED_WORK_COUNT 8192 +/* + * A work item is composed of a dynamic array of sub-items which + * represent a flattened, and augmented, version of a trigger's actions. + * + * We cannot rely solely on the trigger's actions since each action can have an + * execution context we need to comply with. + * + * The notion of execution context is required since for some actions the + * associated object are referenced by name and not by id. This can lead to + * a number of ambiguities when executing an action work item. + * + * For example, let's take a simple trigger such as: + * - condition: ust event a + * - action: start session S + * + * At time T, session S exists. + * At T + 1, the event A is hit. + * At T + 2, the tracer event notification is received and the work item is + * queued. Here session S have an id of 1. + * At T + 3, the session S is destroyed and a new session S is created, with a + * resulting id of 200. + * At T +4, the work item is popped from the queue and begin execution and will + * start session S with an id of 200 instead of the session S id 1 that was + * present at the queuing phase. + * + * The context to be respected is the one when the work item is queued. If the + * execution context is not the same at the moment of execution, we skip the + * execution of that sub-item. + * + * It is the same policy in regards to the validity of the associated + * trigger object at the moment of execution, if the trigger is found to be + * unregistered, the execution is skipped. + */ + struct action_work_item { uint64_t id; + + /* + * The actions to be executed with their respective execution context. + * See struct `action_work_subitem`. + */ + struct lttng_dynamic_array subitems; + + /* Execution context data */ struct lttng_trigger *trigger; struct lttng_evaluation *evaluation; struct notification_client_list *client_list; @@ -41,6 +87,14 @@ struct action_work_item { struct cds_list_head list_node; }; +struct action_work_subitem { + struct lttng_action *action; + struct { + /* Used by actions targeting a session. */ + LTTNG_OPTIONAL(uint64_t) session_id; + } context; +}; + struct action_executor { struct lttng_thread *thread; struct notification_thread_handle *notification_thread_handle; @@ -60,29 +114,33 @@ struct action_executor { */ typedef int (*action_executor_handler)(struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *action); + struct action_work_subitem *item); static int action_executor_notify_handler(struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); -static int action_executor_start_session_handler(struct action_executor *executor, + struct action_work_subitem *); +static int action_executor_start_session_handler( + struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); -static int action_executor_stop_session_handler(struct action_executor *executor, + struct action_work_subitem *); +static int action_executor_stop_session_handler( + struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); -static int action_executor_rotate_session_handler(struct action_executor *executor, + struct action_work_subitem *); +static int action_executor_rotate_session_handler( + struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); -static int action_executor_snapshot_session_handler(struct action_executor *executor, + struct action_work_subitem *); +static int action_executor_snapshot_session_handler( + struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); -static int action_executor_group_handler(struct action_executor *executor, + struct action_work_subitem *); +static int action_executor_list_handler(struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); + struct action_work_subitem *); static int action_executor_generic_handler(struct action_executor *executor, const struct action_work_item *, - const struct lttng_action *); + struct action_work_subitem *); static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler, @@ -90,17 +148,22 @@ static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler, [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler, [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = action_executor_snapshot_session_handler, - [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler, + [LTTNG_ACTION_TYPE_LIST] = action_executor_list_handler, }; -static const char *action_type_names[] = { - [LTTNG_ACTION_TYPE_NOTIFY] = "Notify", - [LTTNG_ACTION_TYPE_START_SESSION] = "Start session", - [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session", - [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session", - [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session", - [LTTNG_ACTION_TYPE_GROUP] = "Group", -}; +/* Forward declaration */ +static int add_action_to_subitem_array(struct lttng_action *action, + struct lttng_dynamic_array *subitems); + +static int populate_subitem_array_from_trigger(struct lttng_trigger *trigger, + struct lttng_dynamic_array *subitems); + +static void action_work_subitem_destructor(void *element) +{ + struct action_work_subitem *subitem = element; + + lttng_action_put(subitem->action); +} static const char *get_action_name(const struct lttng_action *action) { @@ -108,7 +171,7 @@ static const char *get_action_name(const struct lttng_action *action) assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN); - return action_type_names[action_type]; + return lttng_action_type_string(action_type); } /* Check if this trigger allowed to interect with a given session. */ @@ -137,6 +200,26 @@ static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger, return is_allowed; } +static const char *get_trigger_name(const struct lttng_trigger *trigger) +{ + const char *trigger_name; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + switch (trigger_status) { + case LTTNG_TRIGGER_STATUS_OK: + break; + case LTTNG_TRIGGER_STATUS_UNSET: + trigger_name = "(anonymous)"; + break; + default: + trigger_name = "(failed to get name)"; + break; + } + + return trigger_name; +} + static int client_handle_transmission_status( struct notification_client *client, enum client_transmission_status status, @@ -181,26 +264,28 @@ end: static int action_executor_notify_handler(struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { return notification_client_list_send_evaluation(work_item->client_list, - lttng_trigger_get_const_condition(work_item->trigger), + work_item->trigger, work_item->evaluation, - lttng_trigger_get_credentials(work_item->trigger), - LTTNG_OPTIONAL_GET_PTR(work_item->object_creds), - client_handle_transmission_status, - executor); + work_item->object_creds.is_set ? + &(work_item->object_creds.value) : + NULL, + client_handle_transmission_status, executor); } -static int action_executor_start_session_handler(struct action_executor *executor, +static int action_executor_start_session_handler( + struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { int ret = 0; const char *session_name; enum lttng_action_status action_status; struct ltt_session *session; enum lttng_error_code cmd_ret; + struct lttng_action *action = item->action; action_status = lttng_action_start_session_get_session_name( action, &session_name); @@ -211,12 +296,41 @@ static int action_executor_start_session_handler(struct action_executor *executo goto end; } + /* + * Validate if at the moment of the action was queued the session + * existed. If not skip the action altogether. + */ + if (!item->context.session_id.is_set) { + DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + ret = 0; + goto end; + } + session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); + goto error_unlock_list; + } + + /* + * Check if the session id is the same as when the work item was + * enqueued. + */ + if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) { + DBG("Session id for session `%s` (id: %" PRIu64 + " is not the same that was sampled (id: %" PRIu64 + " at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, session->id, + LTTNG_OPTIONAL_GET(item->context.session_id), + get_action_name(action), + get_trigger_name(work_item->trigger)); + ret = 0; goto error_unlock_list; } @@ -228,17 +342,18 @@ static int action_executor_start_session_handler(struct action_executor *executo cmd_ret = cmd_start_trace(session); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully started session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully started session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_TRACE_ALREADY_STARTED: - DBG("Attempted to start session `%s` on behalf of trigger `%p` but it was already started", - session_name, work_item->trigger); + DBG("Attempted to start session `%s` on behalf of trigger `%s` but it was already started", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to start session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to start session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); + lttng_action_increase_execution_failure_count(action); break; } @@ -251,15 +366,17 @@ end: return ret; } -static int action_executor_stop_session_handler(struct action_executor *executor, +static int action_executor_stop_session_handler( + struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { int ret = 0; const char *session_name; enum lttng_action_status action_status; struct ltt_session *session; enum lttng_error_code cmd_ret; + struct lttng_action *action = item->action; action_status = lttng_action_stop_session_get_session_name( action, &session_name); @@ -270,12 +387,42 @@ static int action_executor_stop_session_handler(struct action_executor *executor goto end; } + /* + * Validate if, at the moment the action was queued, the target session + * existed. If not, skip the action altogether. + */ + if (!item->context.session_id.is_set) { + DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + ret = 0; + goto end; + } + session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + goto error_unlock_list; + } + + /* + * Check if the session id is the same as when the work item was + * enqueued + */ + if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) { + DBG("Session id for session `%s` (id: %" PRIu64 + " is not the same that was sampled (id: %" PRIu64 + " at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, session->id, + LTTNG_OPTIONAL_GET(item->context.session_id), + get_action_name(action), + get_trigger_name(work_item->trigger)); + ret = 0; goto error_unlock_list; } @@ -287,17 +434,18 @@ static int action_executor_stop_session_handler(struct action_executor *executor cmd_ret = cmd_stop_trace(session); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully stopped session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully stopped session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_TRACE_ALREADY_STOPPED: - DBG("Attempted to stop session `%s` on behalf of trigger `%p` but it was already stopped", - session_name, work_item->trigger); + DBG("Attempted to stop session `%s` on behalf of trigger `%s` but it was already stopped", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to stop session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to stop session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); + lttng_action_increase_execution_failure_count(action); break; } @@ -310,15 +458,17 @@ end: return ret; } -static int action_executor_rotate_session_handler(struct action_executor *executor, +static int action_executor_rotate_session_handler( + struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { int ret = 0; const char *session_name; enum lttng_action_status action_status; struct ltt_session *session; enum lttng_error_code cmd_ret; + struct lttng_action *action = item->action; action_status = lttng_action_rotate_session_get_session_name( action, &session_name); @@ -329,12 +479,42 @@ static int action_executor_rotate_session_handler(struct action_executor *execut goto end; } + /* + * Validate if, at the moment the action was queued, the target session + * existed. If not, skip the action altogether. + */ + if (!item->context.session_id.is_set) { + DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + ret = 0; + goto end; + } + session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + goto error_unlock_list; + } + + /* + * Check if the session id is the same as when the work item was + * enqueued. + */ + if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) { + DBG("Session id for session `%s` (id: %" PRIu64 + " is not the same that was sampled (id: %" PRIu64 + " at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, session->id, + LTTNG_OPTIONAL_GET(item->context.session_id), + get_action_name(action), + get_trigger_name(work_item->trigger)); + ret = 0; goto error_unlock_list; } @@ -347,22 +527,24 @@ static int action_executor_rotate_session_handler(struct action_executor *execut LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully started rotation of session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully started rotation of session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_ROTATION_PENDING: - DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation is already ongoing", - session_name, work_item->trigger); + DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation is already ongoing", + session_name, get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); break; case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP: case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR: - DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation has already been completed since the last stop or clear", - session_name, work_item->trigger); + DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation has already been completed since the last stop or clear", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to start a rotation of session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to start a rotation of session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); + lttng_action_increase_execution_failure_count(action); break; } @@ -375,9 +557,10 @@ end: return ret; } -static int action_executor_snapshot_session_handler(struct action_executor *executor, +static int action_executor_snapshot_session_handler( + struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { int ret = 0; const char *session_name; @@ -389,6 +572,20 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec const struct lttng_snapshot_output *snapshot_output = &default_snapshot_output; enum lttng_error_code cmd_ret; + struct lttng_action *action = item->action; + + /* + * Validate if, at the moment the action was queued, the target session + * existed. If not, skip the action altogether. + */ + if (!item->context.session_id.is_set) { + DBG("Session was not present at the moment the work item was enqueued for %s` action of trigger `%s`", + get_action_name(action), + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); + ret = 0; + goto end; + } action_status = lttng_action_snapshot_session_get_session_name( action, &session_name); @@ -412,12 +609,28 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); + lttng_action_increase_execution_failure_count(action); goto error_unlock_list; } + /* + * Check if the session id is the same as when the work item was + * enqueued. + */ + if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) { + DBG("Session id for session `%s` (id: %" PRIu64 + " is not the same that was sampled (id: %" PRIu64 + " at the moment the work item was enqueued for %s` action of trigger `%s`", + session_name, session->id, + LTTNG_OPTIONAL_GET(item->context.session_id), + get_action_name(action), + get_trigger_name(work_item->trigger)); + ret = 0; + goto error_unlock_list; + } session_lock(session); if (!is_trigger_allowed_for_session(work_item->trigger, session)) { @@ -427,13 +640,14 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec cmd_ret = cmd_snapshot_record(session, snapshot_output, 0); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to record snapshot of session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to record snapshot of session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); + lttng_action_increase_execution_failure_count(action); break; } @@ -446,69 +660,67 @@ end: return ret; } -static int action_executor_group_handler(struct action_executor *executor, +static int action_executor_list_handler(struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action_group) + struct action_work_subitem *item) { - int ret = 0; - unsigned int i, count; - enum lttng_action_status action_status; - - action_status = lttng_action_group_get_count(action_group, &count); - if (action_status != LTTNG_ACTION_STATUS_OK) { - /* Fatal error. */ - ERR("Failed to get count of action in action group"); - ret = -1; - goto end; - } - - DBG("Action group has %u action%s", count, count != 1 ? "s" : ""); - for (i = 0; i < count; i++) { - const struct lttng_action *action = - lttng_action_group_get_at_index( - action_group, i); - - ret = action_executor_generic_handler( - executor, work_item, action); - if (ret) { - ERR("Stopping the execution of the action group of trigger `%p` following a fatal error", - work_item->trigger); - goto end; - } - } -end: - return ret; + ERR("Execution of a list action by the action executor should never occur"); + abort(); } static int action_executor_generic_handler(struct action_executor *executor, const struct action_work_item *work_item, - const struct lttng_action *action) + struct action_work_subitem *item) { + int ret; + struct lttng_action *action = item->action; const enum lttng_action_type action_type = lttng_action_get_type(action); assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN); - DBG("Executing action `%s` of trigger `%p` action work item %" PRIu64, + lttng_action_increase_execution_request_count(action); + if (!lttng_action_should_execute(action)) { + DBG("Policy prevented execution of action `%s` of trigger `%s` action work item %" PRIu64, + get_action_name(action), + get_trigger_name(work_item->trigger), + work_item->id); + ret = 0; + goto end; + } + + lttng_action_increase_execution_count(action); + DBG("Executing action `%s` of trigger `%s` action work item %" PRIu64, get_action_name(action), - work_item->trigger, + get_trigger_name(work_item->trigger), work_item->id); - - return action_executors[action_type]( - executor, work_item, action); + ret = action_executors[action_type](executor, work_item, item); +end: + return ret; } static int action_work_item_execute(struct action_executor *executor, struct action_work_item *work_item) { int ret; - const struct lttng_action *action = - lttng_trigger_get_const_action(work_item->trigger); - - DBG("Starting execution of action work item %" PRIu64 " of trigger `%p`", - work_item->id, work_item->trigger); - ret = action_executor_generic_handler(executor, work_item, action); - DBG("Completed execution of action work item %" PRIu64 " of trigger `%p`", - work_item->id, work_item->trigger); + size_t count, i; + + DBG("Starting execution of action work item %" PRIu64 " of trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); + + count = lttng_dynamic_array_get_count(&work_item->subitems); + for (i = 0; i < count; i++) { + struct action_work_subitem *item; + + item = lttng_dynamic_array_get_element(&work_item->subitems, i); + ret = action_executor_generic_handler( + executor, work_item, item); + if (ret) { + goto end; + } + } +end: + DBG("Completed execution of action work item %" PRIu64 " of trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); return ret; } @@ -517,6 +729,7 @@ static void action_work_item_destroy(struct action_work_item *work_item) lttng_trigger_put(work_item->trigger); lttng_evaluation_destroy(work_item->evaluation); notification_client_list_put(work_item->client_list); + lttng_dynamic_array_reset(&work_item->subitems); free(work_item); } @@ -526,7 +739,8 @@ static void *action_executor_thread(void *_data) assert(executor); - health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR); + health_register(the_health_sessiond, + HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR); rcu_register_thread(); rcu_thread_online(); @@ -534,7 +748,7 @@ static void *action_executor_thread(void *_data) DBG("Entering work execution loop"); pthread_mutex_lock(&executor->work.lock); while (!executor->should_quit) { - int ret; + int ret = 0; struct action_work_item *work_item; health_code_update(); @@ -559,7 +773,31 @@ static void *action_executor_thread(void *_data) * allowing new items to be queued. */ pthread_mutex_unlock(&executor->work.lock); + + /* Execute item only if a trigger is registered. */ + lttng_trigger_lock(work_item->trigger); + if (!lttng_trigger_is_registered(work_item->trigger)) { + const char *trigger_name = NULL; + uid_t trigger_owner_uid; + enum lttng_trigger_status trigger_status; + + trigger_name = get_trigger_name(work_item->trigger); + + trigger_status = lttng_trigger_get_owner_uid( + work_item->trigger, &trigger_owner_uid); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + DBG("Work item skipped since the associated trigger is no longer registered: work item id = %" PRIu64 ", trigger name = '%s', trigger owner uid = %d", + work_item->id, trigger_name, + (int) trigger_owner_uid); + ret = 0; + goto skip_execute; + } + ret = action_work_item_execute(executor, work_item); + + skip_execute: + lttng_trigger_unlock(work_item->trigger); action_work_item_destroy(work_item); if (ret) { /* Fatal error. */ @@ -579,7 +817,7 @@ static void *action_executor_thread(void *_data) rcu_thread_offline(); rcu_unregister_thread(); - health_unregister(health_sessiond); + health_unregister(the_health_sessiond); return NULL; } @@ -588,8 +826,10 @@ static bool shutdown_action_executor_thread(void *_data) { struct action_executor *executor = _data; + pthread_mutex_lock(&executor->work.lock); executor->should_quit = true; pthread_cond_signal(&executor->work.cond); + pthread_mutex_unlock(&executor->work.lock); return true; } @@ -643,8 +883,8 @@ void action_executor_destroy(struct action_executor *executor) cds_list_for_each_entry_safe ( work_item, tmp, &executor->work.list, list_node) { WARN("Discarding action work item %" PRIu64 - " associated to trigger `%p`", - work_item->id, work_item->trigger); + " associated to trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); cds_list_del(&work_item->list_node); action_work_item_destroy(work_item); } @@ -653,32 +893,35 @@ void action_executor_destroy(struct action_executor *executor) } /* RCU read-lock must be held by the caller. */ -enum action_executor_status action_executor_enqueue( +enum action_executor_status action_executor_enqueue_trigger( struct action_executor *executor, struct lttng_trigger *trigger, struct lttng_evaluation *evaluation, const struct lttng_credentials *object_creds, struct notification_client_list *client_list) { + int ret; enum action_executor_status executor_status = ACTION_EXECUTOR_STATUS_OK; const uint64_t work_item_id = executor->next_work_item_id++; struct action_work_item *work_item; bool signal = false; + assert(trigger); + pthread_mutex_lock(&executor->work.lock); /* Check for queue overflow. */ if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) { /* Most likely spammy, remove if it is the case. */ - DBG("Refusing to enqueue action for trigger `%p` as work item %" PRIu64 - " (overflow)", trigger, work_item_id); + DBG("Refusing to enqueue action for trigger (overflow): trigger name = `%s`, work item id = %" PRIu64, + get_trigger_name(trigger), work_item_id); executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW; goto error_unlock; } work_item = zmalloc(sizeof(*work_item)); if (!work_item) { - PERROR("Failed to allocate action executor work item on behalf of trigger `%p`", - trigger); + PERROR("Failed to allocate action executor work item: trigger name = '%s'", + get_trigger_name(trigger)); executor_status = ACTION_EXECUTOR_STATUS_ERROR; goto error_unlock; } @@ -706,18 +949,165 @@ enum action_executor_status action_executor_enqueue( }; evaluation = NULL; + + /* Build the array of action work subitems for the passed trigger. */ + lttng_dynamic_array_init(&work_item->subitems, + sizeof(struct action_work_subitem), + action_work_subitem_destructor); + + ret = populate_subitem_array_from_trigger( + trigger, &work_item->subitems); + if (ret) { + ERR("Failed to populate work item sub items on behalf of trigger: trigger name = `%s`", + get_trigger_name(trigger)); + executor_status = ACTION_EXECUTOR_STATUS_ERROR; + goto error_unlock; + } + cds_list_add_tail(&work_item->list_node, &executor->work.list); executor->work.pending_count++; - DBG("Enqueued action for trigger `%p` as work item %" PRIu64, - trigger, work_item_id); + DBG("Enqueued action for trigger: trigger name = `%s`, work item id = %" PRIu64, + get_trigger_name(trigger), work_item_id); signal = true; error_unlock: - pthread_mutex_unlock(&executor->work.lock); if (signal) { pthread_cond_signal(&executor->work.cond); } + pthread_mutex_unlock(&executor->work.lock); lttng_evaluation_destroy(evaluation); return executor_status; } + +static int add_action_to_subitem_array(struct lttng_action *action, + struct lttng_dynamic_array *subitems) +{ + int ret = 0; + enum lttng_action_type type = lttng_action_get_type(action); + const char *session_name = NULL; + enum lttng_action_status status; + struct action_work_subitem subitem = { + .action = NULL, + .context = { + .session_id = LTTNG_OPTIONAL_INIT_UNSET, + }, + }; + + assert(action); + assert(subitems); + + if (type == LTTNG_ACTION_TYPE_LIST) { + unsigned int count, i; + + status = lttng_action_list_get_count(action, &count); + assert(status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + struct lttng_action *inner_action = NULL; + + inner_action = lttng_action_list_borrow_mutable_at_index( + action, i); + assert(inner_action); + ret = add_action_to_subitem_array( + inner_action, subitems); + if (ret) { + goto end; + } + } + + /* + * Go directly to the end since there is no need to add the + * list action by itself to the subitems array. + */ + goto end; + } + + /* Gather execution context. */ + switch (type) { + case LTTNG_ACTION_TYPE_NOTIFY: + break; + case LTTNG_ACTION_TYPE_START_SESSION: + status = lttng_action_start_session_get_session_name( + action, &session_name); + assert(status == LTTNG_ACTION_STATUS_OK); + break; + case LTTNG_ACTION_TYPE_STOP_SESSION: + status = lttng_action_stop_session_get_session_name( + action, &session_name); + assert(status == LTTNG_ACTION_STATUS_OK); + break; + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + status = lttng_action_rotate_session_get_session_name( + action, &session_name); + assert(status == LTTNG_ACTION_STATUS_OK); + break; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + status = lttng_action_snapshot_session_get_session_name( + action, &session_name); + assert(status == LTTNG_ACTION_STATUS_OK); + break; + case LTTNG_ACTION_TYPE_LIST: + case LTTNG_ACTION_TYPE_UNKNOWN: + /* Fallthrough */ + default: + abort(); + break; + } + + /* + * Fetch the session execution context info as needed. + * Note that we could decide to not add an action for which we know the + * execution will not happen (i.e no session exists for that name). For + * now we leave the decision to skip to the action executor for sake of + * simplicity and consistency. + */ + if (session_name != NULL) { + uint64_t session_id; + + /* + * Instantaneous sampling of the session id if present. + * + * This method is preferred over `sessiond_find_by_name` then + * fetching the session'd id since `sessiond_find_by_name` + * requires the session list lock to be taken. + * + * Taking the session list lock can lead to a deadlock + * between the action executor and the notification thread + * (caller of add_action_to_subitem_array). It is okay if the + * session state changes between the enqueuing time and the + * execution time. The execution context is validated at + * execution time. + */ + if (sample_session_id_by_name(session_name, &session_id)) { + LTTNG_OPTIONAL_SET(&subitem.context.session_id, + session_id); + } + } + + /* Get a reference to the action. */ + lttng_action_get(action); + subitem.action = action; + + ret = lttng_dynamic_array_add_element(subitems, &subitem); + if (ret) { + ERR("Failed to add work subitem to the subitem array"); + lttng_action_put(action); + ret = -1; + goto end; + } + +end: + return ret; +} + +static int populate_subitem_array_from_trigger(struct lttng_trigger *trigger, + struct lttng_dynamic_array *subitems) +{ + struct lttng_action *action; + + action = lttng_trigger_get_action(trigger); + assert(action); + + return add_action_to_subitem_array(action, subitems); +}