#include <common/macros.h>
#include <lttng/condition/condition.h>
#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/domain-internal.h>
#include <lttng/notification/notification-internal.h>
#include <lttng/condition/condition-internal.h>
#include <lttng/condition/buffer-usage-internal.h>
#include <lttng/condition/session-consumed-size-internal.h>
#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/on-event-internal.h>
#include <lttng/domain-internal.h>
#include <lttng/notification/channel-internal.h>
#include <lttng/trigger/trigger-internal.h>
#include <fcntl.h>
#include "condition-internal.h"
+#include "event-notifier-error-accounting.h"
#include "notification-thread.h"
#include "notification-thread-events.h"
#include "notification-thread-commands.h"
#define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP)
#define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT)
+/* The tracers currently limit the capture size to PIPE_BUF (4kb on linux). */
+#define MAX_CAPTURE_SIZE (PIPE_BUF)
+
enum lttng_object_type {
LTTNG_OBJECT_TYPE_UNKNOWN,
LTTNG_OBJECT_TYPE_NONE,
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
return LTTNG_OBJECT_TYPE_SESSION;
- case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ case LTTNG_CONDITION_TYPE_ON_EVENT:
return LTTNG_OBJECT_TYPE_NONE;
default:
return LTTNG_OBJECT_TYPE_UNKNOWN;
* the pipe is closed but empty.
*/
ret = lttng_poll_wait_interruptible(&events, 0);
- if (ret == 0 || (LTTNG_POLL_GETEV(&events, 1) & LPOLLIN) == 0) {
+ if (ret == 0 || (LTTNG_POLL_GETEV(&events, 0) & LPOLLIN) == 0) {
/* No more notification to be read on this pipe. */
ret = 0;
goto end;
return ret;
}
+static
+int condition_on_event_update_error_count(struct lttng_trigger *trigger)
+{
+ int ret = 0;
+ uint64_t error_count = 0;
+ struct lttng_condition *condition;
+ enum event_notifier_error_accounting_status status;
+
+ condition = lttng_trigger_get_condition(trigger);
+ assert(lttng_condition_get_type(condition) ==
+ LTTNG_CONDITION_TYPE_ON_EVENT);
+
+ status = event_notifier_error_accounting_get_count(trigger, &error_count);
+ if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) {
+ uid_t trigger_owner_uid;
+ const char *trigger_name;
+ const enum lttng_trigger_status trigger_status =
+ lttng_trigger_get_owner_uid(
+ trigger, &trigger_owner_uid);
+
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+ if (lttng_trigger_get_name(trigger, &trigger_name) != LTTNG_TRIGGER_STATUS_OK) {
+ trigger_name = "(unnamed)";
+ }
+
+ ERR("Failed to get event notifier error count of trigger for update: trigger owner = %d, trigger name = '%s'",
+ trigger_owner_uid, trigger_name);
+ ret = -1;
+ }
+
+ lttng_condition_on_event_set_error_count(condition, error_count);
+ return ret;
+}
+
int handle_notification_thread_remove_tracer_event_source_no_result(
struct notification_thread_state *state,
int tracer_event_source_fd)
continue;
}
+ if (lttng_trigger_needs_tracer_notifier(trigger_ht_element->trigger)) {
+ ret = condition_on_event_update_error_count(
+ trigger_ht_element->trigger);
+ assert(!ret);
+ }
+
ret = lttng_triggers_add(local_triggers,
trigger_ht_element->trigger);
if (ret < 0) {
is_supported = kernel_supports_ring_buffer_snapshot_sample_positions() == 1;
break;
}
- case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ case LTTNG_CONDITION_TYPE_ON_EVENT:
{
const struct lttng_event_rule *event_rule;
enum lttng_domain_type domain;
const enum lttng_condition_status status =
- lttng_condition_event_rule_get_rule(
+ lttng_condition_on_event_get_rule(
condition, &event_rule);
assert(status == LTTNG_CONDITION_STATUS_OK);
return ret_code;
}
+static inline
+void notif_thread_state_remove_trigger_ht_elem(
+ struct notification_thread_state *state,
+ struct lttng_trigger_ht_element *trigger_ht_element)
+{
+ assert(state);
+ assert(trigger_ht_element);
+
+ cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+ cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
+}
+
+static
+enum lttng_error_code setup_tracer_notifier(
+ struct notification_thread_state *state,
+ struct lttng_trigger *trigger)
+{
+ enum lttng_error_code ret;
+ enum event_notifier_error_accounting_status error_accounting_status;
+ struct cds_lfht_node *node;
+ uint64_t error_counter_index = 0;
+ struct lttng_condition *condition = lttng_trigger_get_condition(trigger);
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
+
+ trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
+ if (!trigger_tokens_ht_element) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /* Add trigger token to the trigger_tokens_ht. */
+ cds_lfht_node_init(&trigger_tokens_ht_element->node);
+ trigger_tokens_ht_element->token = LTTNG_OPTIONAL_GET(trigger->tracer_token);
+ trigger_tokens_ht_element->trigger = trigger;
+
+ node = cds_lfht_add_unique(state->trigger_tokens_ht,
+ hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed),
+ match_trigger_token,
+ &trigger_tokens_ht_element->token,
+ &trigger_tokens_ht_element->node);
+ if (node != &trigger_tokens_ht_element->node) {
+ ret = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+
+ error_accounting_status = event_notifier_error_accounting_register_event_notifier(
+ trigger, &error_counter_index);
+ if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) {
+ if (error_accounting_status == EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE) {
+ DBG("Trigger group error accounting counter full.");
+ ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL;
+ } else {
+ ERR("Error registering trigger for error accounting");
+ ret = LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION;
+ }
+
+ goto error_remove_ht_element;
+ }
+
+ lttng_condition_on_event_set_error_counter_index(
+ condition, error_counter_index);
+
+ ret = LTTNG_OK;
+ goto end;
+
+error_remove_ht_element:
+ cds_lfht_del(state->trigger_tokens_ht, &trigger_tokens_ht_element->node);
+error_free_ht_element:
+ free(trigger_tokens_ht_element);
+end:
+ return ret;
+}
+
/*
* FIXME A client's credentials are not checked when registering a trigger.
*
struct notification_client_list *client_list = NULL;
struct lttng_trigger_ht_element *trigger_ht_element = NULL;
struct notification_client_list_element *client_list_element;
- struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
struct cds_lfht_node *node;
struct cds_lfht_iter iter;
const char* trigger_name;
goto error_free_ht_element;
}
- if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
- trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
- if (!trigger_tokens_ht_element) {
- /* Fatal error. */
- ret = -1;
- cds_lfht_del(state->triggers_ht,
- &trigger_ht_element->node);
- cds_lfht_del(state->triggers_by_name_uid_ht,
- &trigger_ht_element->node_by_name_uid);
- goto error_free_ht_element;
- }
+ /*
+ * Some triggers might need a tracer notifier depending on its
+ * condition and actions.
+ */
+ if (lttng_trigger_needs_tracer_notifier(trigger)) {
+ enum lttng_error_code error_code;
+
+ error_code = setup_tracer_notifier(state, trigger);
+ if (error_code != LTTNG_OK) {
+ notif_thread_state_remove_trigger_ht_elem(state,
+ trigger_ht_element);
+ if (error_code == LTTNG_ERR_NOMEM) {
+ ret = -1;
+ } else {
+ *cmd_result = error_code;
+ ret = 0;
+ }
- /* Add trigger token to the trigger_tokens_ht. */
- cds_lfht_node_init(&trigger_tokens_ht_element->node);
- trigger_tokens_ht_element->token =
- LTTNG_OPTIONAL_GET(trigger->tracer_token);
- trigger_tokens_ht_element->trigger = trigger;
-
- node = cds_lfht_add_unique(state->trigger_tokens_ht,
- hash_key_u64(&trigger_tokens_ht_element->token,
- lttng_ht_seed),
- match_trigger_token,
- &trigger_tokens_ht_element->token,
- &trigger_tokens_ht_element->node);
- if (node != &trigger_tokens_ht_element->node) {
- /* Internal corruption, fatal error. */
- ret = -1;
- *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
- cds_lfht_del(state->triggers_ht,
- &trigger_ht_element->node);
- cds_lfht_del(state->triggers_by_name_uid_ht,
- &trigger_ht_element->node_by_name_uid);
goto error_free_ht_element;
}
}
* Ownership of the trigger and of its wrapper was transfered to
* the triggers_ht. Same for token ht element if necessary.
*/
- trigger_tokens_ht_element = NULL;
trigger_ht_element = NULL;
free_trigger = false;
call_rcu(&trigger_ht_element->rcu_node,
free_lttng_trigger_ht_element_rcu);
}
-
- free(trigger_tokens_ht_element);
error:
if (free_trigger) {
lttng_trigger_destroy(trigger);
rcu_node));
}
+static
+void teardown_tracer_notifier(struct notification_thread_state *state,
+ const struct lttng_trigger *trigger)
+{
+ struct cds_lfht_iter iter;
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element;
+
+ cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter,
+ trigger_tokens_ht_element, node) {
+
+ if (!lttng_trigger_is_equal(trigger,
+ trigger_tokens_ht_element->trigger)) {
+ continue;
+ }
+
+ event_notifier_error_accounting_unregister_event_notifier(
+ trigger_tokens_ht_element->trigger);
+
+ /* TODO talk to all app and remove it */
+ DBG("[notification-thread] Removed trigger from tokens_ht");
+ cds_lfht_del(state->trigger_tokens_ht,
+ &trigger_tokens_ht_element->node);
+
+ call_rcu(&trigger_tokens_ht_element->rcu_node,
+ free_notification_trigger_tokens_ht_element_rcu);
+
+ break;
+ }
+}
+
static
int handle_notification_thread_command_unregister_trigger(
struct notification_thread_state *state,
}
}
- if (lttng_condition_get_type(condition) ==
- LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
- struct notification_trigger_tokens_ht_element
- *trigger_tokens_ht_element;
-
- cds_lfht_for_each_entry (state->trigger_tokens_ht, &iter,
- trigger_tokens_ht_element, node) {
- if (!lttng_trigger_is_equal(trigger,
- trigger_tokens_ht_element->trigger)) {
- continue;
- }
-
- DBG("[notification-thread] Removed trigger from tokens_ht");
- cds_lfht_del(state->trigger_tokens_ht,
- &trigger_tokens_ht_element->node);
- call_rcu(&trigger_tokens_ht_element->rcu_node,
- free_notification_trigger_tokens_ht_element_rcu);
-
- break;
- }
+ if (lttng_trigger_needs_tracer_notifier(trigger)) {
+ teardown_tracer_notifier(state, trigger);
}
if (is_trigger_action_notify(trigger)) {
client_list = NULL;
}
- /* Remove trigger from triggers_ht. */
trigger_ht_element = caa_container_of(triggers_ht_node,
struct lttng_trigger_ht_element, node);
- cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
- cds_lfht_del(state->triggers_ht, triggers_ht_node);
+
+ /* Remove trigger from triggers_ht. */
+ notif_thread_state_remove_trigger_ht_elem(state, trigger_ht_element);
/* Release the ownership of the trigger. */
lttng_trigger_destroy(trigger_ht_element->trigger);
int ret;
uint64_t token;
struct lttng_event_notifier_notification *notification = NULL;
+ char *capture_buffer = NULL;
+ size_t capture_buffer_size;
void *reception_buffer;
size_t reception_size;
- struct lttng_ust_event_notifier_notification ust_notification;
+ struct lttng_ust_abi_event_notifier_notification ust_notification;
struct lttng_kernel_event_notifier_notification kernel_notification;
/* Init lttng_event_notifier_notification */
switch(domain) {
case LTTNG_DOMAIN_UST:
token = ust_notification.token;
+ capture_buffer_size = ust_notification.capture_buf_size;
break;
case LTTNG_DOMAIN_KERNEL:
token = kernel_notification.token;
+ capture_buffer_size = kernel_notification.capture_buf_size;
break;
default:
abort();
}
- notification = lttng_event_notifier_notification_create(
- token, domain);
+ if (capture_buffer_size == 0) {
+ capture_buffer = NULL;
+ goto skip_capture;
+ }
+
+ if (capture_buffer_size > MAX_CAPTURE_SIZE) {
+ ERR("[notification-thread] Event notifier has a capture payload size which exceeds the maximum allowed size: capture_payload_size = %zu bytes, max allowed size = %d bytes",
+ capture_buffer_size, MAX_CAPTURE_SIZE);
+ goto end;
+ }
+
+ capture_buffer = zmalloc(capture_buffer_size);
+ if (!capture_buffer) {
+ ERR("[notification-thread] Failed to allocate capture buffer");
+ goto end;
+ }
+
+ /* Fetch additional payload (capture). */
+ ret = lttng_read(notification_pipe_read_fd, capture_buffer, capture_buffer_size);
+ if (ret != capture_buffer_size) {
+ ERR("[notification-thread] Failed to read from event source pipe (fd = %i)",
+ notification_pipe_read_fd);
+ goto end;
+ }
+
+skip_capture:
+ notification = lttng_event_notifier_notification_create(token, domain,
+ capture_buffer, capture_buffer_size);
+ if (notification == NULL) {
+ goto end;
+ }
+
+ /*
+ * Ownership transfered to the lttng_event_notifier_notification object.
+ */
+ capture_buffer = NULL;
+
end:
+ free(capture_buffer);
return notification;
}
struct notification_client_list *client_list = NULL;
const char *trigger_name;
int ret;
+ unsigned int capture_count = 0;
/* Find triggers associated with this token. */
rcu_read_lock();
trigger_status = lttng_trigger_get_name(element->trigger, &trigger_name);
assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
- evaluation = lttng_evaluation_event_rule_create(
- trigger_name);
+ if (lttng_condition_on_event_get_capture_descriptor_count(
+ lttng_trigger_get_const_condition(element->trigger),
+ &capture_count) != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to get capture count");
+ ret = -1;
+ goto end;
+ }
+
+ if (!notification->capture_buffer && capture_count != 0) {
+ ERR("Expected capture but capture buffer is null");
+ ret = -1;
+ goto end;
+ }
+
+ evaluation = lttng_evaluation_on_event_create(
+ container_of(lttng_trigger_get_const_condition(
+ element->trigger),
+ struct lttng_condition_on_event,
+ parent),
+ trigger_name,
+ notification->capture_buffer,
+ notification->capture_buf_size, false);
+
if (evaluation == NULL) {
ERR("[notification-thread] Failed to create event rule hit evaluation while creating and enqueuing action executor job");
ret = -1;
goto end_unlock;
}
-
client_list = get_client_list_from_condition(state,
lttng_trigger_get_const_condition(element->trigger));
executor_status = action_executor_enqueue(state->executor,
pthread_mutex_unlock(&client_list->lock);
break;
}
+ case ACTION_EXECUTOR_STATUS_INVALID:
case ACTION_EXECUTOR_STATUS_ERROR:
/* Fatal error, shut down everything. */
ERR("Fatal error encoutered while enqueuing action to the action executor");
end_unlock:
notification_client_list_put(client_list);
rcu_read_unlock();
+end:
return ret;
}
struct notification_thread_state *state,
int pipe, enum lttng_domain_type domain)
{
- int ret;
+ int ret = 0;
struct lttng_event_notifier_notification *notification = NULL;
notification = recv_one_event_notifier_notification(pipe, domain);
if (notification == NULL) {
+ /* Reception failed, don't consider it fatal. */
ERR("[notification-thread] Error receiving an event notifier notification from tracer: fd = %i, domain = %s",
pipe, lttng_domain_type_str(domain));
- ret = -1;
goto end;
}