Add some of the scafolding to support event-rule hit conditions.
This includes the hashing of event rule conditions and, consequently,
of event rules and the various probe location types.
The kernel module ABI is checked to verity that the kernel tracer
supports event notifiers.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: Iab4db4fc7e9f0c5a7206106fa6a4781b6b95d306
const struct lttng_event_rule *event_rule);
typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)(
const struct lttng_event_rule *event_rule);
+typedef unsigned long (*event_rule_hash_cb)(
+ const struct lttng_event_rule *event_rule);
struct lttng_event_rule {
struct urcu_ref ref;
event_rule_get_filter_cb get_filter;
event_rule_get_filter_bytecode_cb get_filter_bytecode;
event_rule_generate_exclusions_cb generate_exclusions;
+ event_rule_hash_cb hash;
};
struct lttng_event_rule_comm {
LTTNG_HIDDEN
const char *lttng_event_rule_type_str(enum lttng_event_rule_type type);
+LTTNG_HIDDEN
+unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule);
+
#endif /* LTTNG_EVENT_RULE_INTERNAL_H */
typedef ssize_t (*kernel_probe_location_create_from_payload_cb)(
struct lttng_payload_view *view,
struct lttng_kernel_probe_location **kernel_probe_location);
+typedef unsigned long (*kernel_probe_location_hash_cb)(
+ const struct lttng_kernel_probe_location *location);
struct lttng_kernel_probe_location_comm {
/* enum lttng_kernel_probe_location_type */
enum lttng_kernel_probe_location_type type;
kernel_probe_location_equal_cb equal;
kernel_probe_location_serialize_cb serialize;
+ kernel_probe_location_hash_cb hash;
};
struct lttng_kernel_probe_location_symbol {
struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy(
const struct lttng_kernel_probe_location *location);
+LTTNG_HIDDEN
+unsigned long lttng_kernel_probe_location_hash(
+ const struct lttng_kernel_probe_location *location);
+
#endif /* LTTNG_KERNEL_PROBE_INTERNAL_H */
typedef bool (*userspace_probe_location_equal_cb)(
const struct lttng_userspace_probe_location *a,
const struct lttng_userspace_probe_location *b);
+typedef unsigned long (*userspace_probe_location_hash_cb)(
+ const struct lttng_userspace_probe_location *location);
/*
* No elf-specific comm structure is defined since no elf-specific payload is
enum lttng_userspace_probe_location_type type;
struct lttng_userspace_probe_location_lookup_method *lookup_method;
userspace_probe_location_equal_cb equal;
+ userspace_probe_location_hash_cb hash;
};
struct lttng_userspace_probe_location_function {
const struct lttng_userspace_probe_location *a,
const struct lttng_userspace_probe_location *b);
+LTTNG_HIDDEN
+unsigned long lttng_userspace_probe_location_hash(
+ const struct lttng_userspace_probe_location *location);
+
#endif /* LTTNG_USERSPACE_PROBE_INTERNAL_H */
return kernctl_syscall_mask(chan_fd, syscall_mask, nr_bits);
}
-/*
- * Check for the support of the RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS via abi
- * version number.
- *
- * Return 1 on success, 0 when feature is not supported, negative value in case
- * of errors.
- */
-int kernel_supports_ring_buffer_snapshot_sample_positions(void)
+static
+int kernel_tracer_abi_greater_or_equal(unsigned int major, unsigned int minor)
{
- int ret = 0; // Not supported by default
+ int ret;
struct lttng_kernel_tracer_abi_version abi;
ret = kernctl_tracer_abi_version(kernel_tracer_fd, &abi);
goto error;
}
+ ret = abi.major > major || (abi.major == major && abi.minor >= minor);
+error:
+ return ret;
+}
+
+/*
+ * Check for the support of the RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS via abi
+ * version number.
+ *
+ * Return 1 on success, 0 when feature is not supported, negative value in case
+ * of errors.
+ */
+int kernel_supports_ring_buffer_snapshot_sample_positions(void)
+{
/*
* RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS was introduced in 2.3
*/
- if (abi.major >= 2 && abi.minor >= 3) {
- /* Supported */
- ret = 1;
- } else {
- /* Not supported */
- ret = 0;
- }
-error:
- return ret;
+ return kernel_tracer_abi_greater_or_equal(2, 3);
}
/*
*/
int kernel_supports_ring_buffer_packet_sequence_number(void)
{
- int ret = 0; // Not supported by default
- struct lttng_kernel_tracer_abi_version abi;
-
- ret = kernctl_tracer_abi_version(kernel_tracer_fd, &abi);
- if (ret < 0) {
- ERR("Failed to retrieve lttng-modules ABI version");
- goto error;
- }
-
/*
* Packet sequence number was introduced in LTTng 2.8,
* lttng-modules ABI 2.1.
*/
- if (abi.major >= 2 && abi.minor >= 1) {
- /* Supported */
- ret = 1;
- } else {
- /* Not supported */
- ret = 0;
- }
-error:
- return ret;
+ return kernel_tracer_abi_greater_or_equal(2, 1);
+}
+
+/*
+ * Check for the support of event notifiers via abi version number.
+ *
+ * Return 1 on success, 0 when feature is not supported, negative value in case
+ * of errors.
+ */
+int kernel_supports_event_notifiers(void)
+{
+ /*
+ * Event notifiers were introduced in LTTng 2.13, lttng-modules ABI 2.6.
+ */
+ return kernel_tracer_abi_greater_or_equal(2, 6);
}
/*
int init_kernel_workarounds(void);
int kernel_supports_ring_buffer_snapshot_sample_positions(void);
int kernel_supports_ring_buffer_packet_sequence_number(void);
+int kernel_supports_event_notifiers(void);
int init_kernel_tracer(void);
void cleanup_kernel_tracer(void);
bool kernel_tracer_is_initialized(void);
#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/notification/channel-internal.h>
#include <lttng/trigger/trigger-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
#include <time.h>
#include <unistd.h>
return hash;
}
+static
+unsigned long lttng_condition_event_rule_hash(
+ const struct lttng_condition *condition)
+{
+ unsigned long hash, condition_type;
+ enum lttng_condition_status condition_status;
+ const struct lttng_event_rule *event_rule;
+
+ condition_type = (unsigned long) condition->type;
+
+ condition_status = lttng_condition_event_rule_get_rule(condition,
+ &event_rule);
+ assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+ hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+ return hash ^ lttng_event_rule_hash(event_rule);
+}
+
/*
* The lttng_condition hashing code is kept in this file (rather than
* condition.c) since it makes use of GPLv2 code (hashtable utils), which we
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
return lttng_condition_session_rotation_hash(condition);
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ return lttng_condition_event_rule_hash(condition);
default:
ERR("[notification-thread] Unexpected condition type caught");
abort();
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:
+ return LTTNG_OBJECT_TYPE_NONE;
default:
return LTTNG_OBJECT_TYPE_UNKNOWN;
}
}
static
-int condition_is_supported(struct lttng_condition *condition)
+bool condition_is_supported(struct lttng_condition *condition)
{
- int ret;
+ bool is_supported;
switch (lttng_condition_get_type(condition)) {
case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
{
+ int ret;
enum lttng_domain_type domain;
ret = lttng_condition_buffer_usage_get_domain_type(condition,
&domain);
- if (ret) {
- ret = -1;
- goto end;
- }
+ assert(ret == 0);
if (domain != LTTNG_DOMAIN_KERNEL) {
- ret = 1;
+ is_supported = true;
goto end;
}
* Older kernel tracers don't expose the API to monitor their
* buffers. Therefore, we reject triggers that require that
* mechanism to be available to be evaluated.
+ *
+ * Assume unsupported on error.
*/
- ret = kernel_supports_ring_buffer_snapshot_sample_positions();
+ is_supported = kernel_supports_ring_buffer_snapshot_sample_positions() == 1;
+ break;
+ }
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ const struct lttng_event_rule *event_rule;
+ enum lttng_domain_type domain;
+ const enum lttng_condition_status status =
+ lttng_condition_event_rule_get_rule(
+ condition, &event_rule);
+
+ assert(status == LTTNG_CONDITION_STATUS_OK);
+
+ domain = lttng_event_rule_get_domain_type(event_rule);
+ if (domain != LTTNG_DOMAIN_KERNEL) {
+ is_supported = true;
+ goto end;
+ }
+
+ /*
+ * Older kernel tracers can't emit notification. Therefore, we
+ * reject triggers that require that mechanism to be available
+ * to be evaluated.
+ *
+ * Assume unsupported on error.
+ */
+ is_supported = kernel_supports_event_notifiers() == 1;
break;
}
default:
- ret = 1;
+ is_supported = true;
}
end:
- return ret;
+ return is_supported;
}
/* Must be called with RCU read lock held. */
condition = lttng_trigger_get_condition(trigger);
assert(condition);
- ret = condition_is_supported(condition);
- if (ret < 0) {
- goto error;
- } else if (ret == 0) {
+ /* Some conditions require tracers to implement a minimal ABI version. */
+ if (!condition_is_supported(condition)) {
*cmd_result = LTTNG_ERR_NOT_SUPPORTED;
goto error;
- } else {
- /* Feature is supported, continue. */
- ret = 0;
}
trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
#include <common/macros.h>
#include <common/payload.h>
#include <common/payload-view.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <lttng/event-rule/event-rule-internal.h>
#include <lttng/event-rule/kprobe-internal.h>
#include <lttng/event-rule/syscall-internal.h>
abort();
}
}
+
+LTTNG_HIDDEN
+unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule)
+{
+ assert(rule->hash);
+ return rule->hash(rule);
+}
#include <common/payload.h>
#include <common/payload-view.h>
#include <common/runas.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <ctype.h>
#include <lttng/constant.h>
#include <lttng/event-rule/event-rule-internal.h>
return NULL;
}
+static unsigned long
+lttng_event_rule_kprobe_hash(
+ const struct lttng_event_rule *rule)
+{
+ unsigned long hash;
+ struct lttng_event_rule_kprobe *krule =
+ container_of(rule, typeof(*krule), parent);
+
+ hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KPROBE,
+ lttng_ht_seed);
+ hash ^= hash_key_str(krule->name, lttng_ht_seed);
+ hash ^= lttng_kernel_probe_location_hash(krule->location);
+
+ return hash;
+}
+
struct lttng_event_rule *lttng_event_rule_kprobe_create()
{
struct lttng_event_rule *rule = NULL;
lttng_event_rule_kprobe_get_filter_bytecode;
krule->parent.generate_exclusions =
lttng_event_rule_kprobe_generate_exclusions;
+ krule->parent.hash = lttng_event_rule_kprobe_hash;
end:
return rule;
}
#include <common/payload.h>
#include <common/payload-view.h>
#include <common/runas.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <lttng/event-rule/event-rule-internal.h>
#include <lttng/event-rule/syscall-internal.h>
return NULL;
}
+static unsigned long
+lttng_event_rule_syscall_hash(
+ const struct lttng_event_rule *rule)
+{
+ unsigned long hash;
+ struct lttng_event_rule_syscall *syscall_rule =
+ container_of(rule, typeof(*syscall_rule), parent);
+
+ hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_SYSCALL,
+ lttng_ht_seed);
+ hash ^= hash_key_str(syscall_rule->pattern, lttng_ht_seed);
+ if (syscall_rule->filter_expression) {
+ hash ^= hash_key_str(syscall_rule->filter_expression,
+ lttng_ht_seed);
+ }
+
+ return hash;
+}
+
struct lttng_event_rule *lttng_event_rule_syscall_create()
{
struct lttng_event_rule *rule = NULL;
lttng_event_rule_syscall_get_internal_filter_bytecode;
syscall_rule->parent.generate_exclusions =
lttng_event_rule_syscall_generate_exclusions;
+ syscall_rule->parent.hash = lttng_event_rule_syscall_hash;
end:
return rule;
}
#include <common/payload.h>
#include <common/payload-view.h>
#include <common/runas.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <lttng/event-rule/event-rule-internal.h>
#include <lttng/event-rule/tracepoint-internal.h>
free(ptr);
}
+static unsigned long lttng_event_rule_tracepoint_hash(
+ const struct lttng_event_rule *rule)
+{
+ unsigned long hash;
+ unsigned int i, exclusion_count;
+ enum lttng_event_rule_status status;
+ struct lttng_event_rule_tracepoint *tp_rule =
+ container_of(rule, typeof(*tp_rule), parent);
+
+ hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_TRACEPOINT,
+ lttng_ht_seed);
+ hash ^= hash_key_ulong((void *) tp_rule->domain, lttng_ht_seed);
+ hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+ if (tp_rule->filter_expression) {
+ hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+ }
+
+ hash ^= hash_key_ulong((void *) tp_rule->loglevel.type,
+ lttng_ht_seed);
+ if (tp_rule->loglevel.type != LTTNG_EVENT_LOGLEVEL_ALL) {
+ hash ^= hash_key_ulong(
+ (void *) (unsigned long) tp_rule->loglevel.value,
+ lttng_ht_seed);
+ }
+
+ status = lttng_event_rule_tracepoint_get_exclusions_count(rule,
+ &exclusion_count);
+ assert(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+ for (i = 0; i < exclusion_count; i++) {
+ const char *exclusion;
+
+ status = lttng_event_rule_tracepoint_get_exclusion_at_index(
+ rule, i, &exclusion);
+ assert(status == LTTNG_EVENT_RULE_STATUS_OK);
+ hash ^= hash_key_str(exclusion, lttng_ht_seed);
+ }
+
+ return hash;
+}
+
struct lttng_event_rule *lttng_event_rule_tracepoint_create(
enum lttng_domain_type domain_type)
{
lttng_event_rule_tracepoint_get_internal_filter_bytecode;
tp_rule->parent.generate_exclusions =
lttng_event_rule_tracepoint_generate_exclusions;
+ tp_rule->parent.hash = lttng_event_rule_tracepoint_hash;
tp_rule->domain = domain_type;
tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
#include <common/payload.h>
#include <common/payload-view.h>
#include <common/runas.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <lttng/event-rule/event-rule-internal.h>
#include <lttng/event-rule/uprobe-internal.h>
#include <lttng/userspace-probe-internal.h>
return NULL;
}
+static unsigned long
+lttng_event_rule_uprobe_hash(
+ const struct lttng_event_rule *rule)
+{
+ unsigned long hash;
+ struct lttng_event_rule_uprobe *urule =
+ container_of(rule, typeof(*urule), parent);
+
+ hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_UPROBE,
+ lttng_ht_seed);
+ hash ^= hash_key_str(urule->name, lttng_ht_seed);
+ hash ^= lttng_userspace_probe_location_hash(urule->location);
+
+ return hash;
+}
+
struct lttng_event_rule *lttng_event_rule_uprobe_create()
{
struct lttng_event_rule *rule = NULL;
lttng_event_rule_uprobe_get_filter_bytecode;
urule->parent.generate_exclusions =
lttng_event_rule_uprobe_generate_exclusions;
+ urule->parent.hash = lttng_event_rule_uprobe_hash;
end:
return rule;
}
#include <common/macros.h>
#include <common/payload.h>
#include <common/payload-view.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <fcntl.h>
#include <lttng/constant.h>
#include <lttng/kernel-probe.h>
const struct lttng_kernel_probe_location *a,
const struct lttng_kernel_probe_location *b);
+static
+unsigned long lttng_kernel_probe_location_address_hash(
+ const struct lttng_kernel_probe_location *location);
+
+static
+unsigned long lttng_kernel_probe_location_symbol_hash(
+ const struct lttng_kernel_probe_location *location);
+
enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type(
const struct lttng_kernel_probe_location *location)
{
ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS;
ret->equal = lttng_kernel_probe_location_address_is_equal;
ret->serialize = lttng_kernel_probe_location_address_serialize;
+ ret->hash = lttng_kernel_probe_location_address_hash;
end:
return ret;
ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET;
ret->equal = lttng_kernel_probe_location_symbol_is_equal;
ret->serialize = lttng_kernel_probe_location_symbol_serialize;
+ ret->hash = lttng_kernel_probe_location_symbol_hash;
goto end;
error:
return ret;
}
+static
+unsigned long lttng_kernel_probe_location_address_hash(
+ const struct lttng_kernel_probe_location *location)
+{
+ unsigned long hash = hash_key_ulong(
+ (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS,
+ lttng_ht_seed);
+ struct lttng_kernel_probe_location_address *address_location =
+ container_of(location, typeof(*address_location),
+ parent);
+
+ hash ^= hash_key_u64(&address_location->address, lttng_ht_seed);
+
+ return hash;
+}
+
static
bool lttng_kernel_probe_location_address_is_equal(
const struct lttng_kernel_probe_location *_a,
return is_equal;
}
+static
+unsigned long lttng_kernel_probe_location_symbol_hash(
+ const struct lttng_kernel_probe_location *location)
+{
+ unsigned long hash = hash_key_ulong(
+ (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET,
+ lttng_ht_seed);
+ struct lttng_kernel_probe_location_symbol *symbol_location =
+ container_of(location, typeof(*symbol_location),
+ parent);
+
+ hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed);
+ hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed);
+
+ return hash;
+}
+
static
bool lttng_kernel_probe_location_symbol_is_equal(
const struct lttng_kernel_probe_location *_a,
err:
return new_location;
}
+
+LTTNG_HIDDEN
+unsigned long lttng_kernel_probe_location_hash(
+ const struct lttng_kernel_probe_location *location)
+{
+ return location->hash(location);
+}
#include <common/macros.h>
#include <common/payload.h>
#include <common/payload-view.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
#include <fcntl.h>
#include <lttng/constant.h>
#include <lttng/userspace-probe-internal.h>
return is_equal;
}
+static unsigned long lttng_userspace_probe_location_function_hash(
+ const struct lttng_userspace_probe_location *location)
+{
+ unsigned long hash = hash_key_ulong(
+ (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION,
+ lttng_ht_seed);
+ struct lttng_userspace_probe_location_function *function_location =
+ container_of(location, typeof(*function_location),
+ parent);
+
+ hash ^= hash_key_str(function_location->function_name, lttng_ht_seed);
+ hash ^= hash_key_str(function_location->binary_path, lttng_ht_seed);
+ /*
+ * No need to hash on the fd. Worst comes to worse,
+ * the equal function will discriminate.
+ */
+ return hash;
+}
+
static bool lttng_userspace_probe_location_function_is_equal(
const struct lttng_userspace_probe_location *_a,
const struct lttng_userspace_probe_location *_b)
ret->lookup_method = lookup_method;
ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION;
ret->equal = lttng_userspace_probe_location_function_is_equal;
+ ret->hash = lttng_userspace_probe_location_function_hash;
goto end;
error:
return ret;
}
+static unsigned long lttng_userspace_probe_location_tracepoint_hash(
+ const struct lttng_userspace_probe_location *location)
+{
+ unsigned long hash = hash_key_ulong(
+ (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT,
+ lttng_ht_seed);
+ struct lttng_userspace_probe_location_tracepoint *tp_location =
+ container_of(location, typeof(*tp_location), parent);
+
+ hash ^= hash_key_str(tp_location->probe_name, lttng_ht_seed);
+ hash ^= hash_key_str(tp_location->provider_name, lttng_ht_seed);
+ hash ^= hash_key_str(tp_location->binary_path, lttng_ht_seed);
+ /*
+ * No need to hash on the fd. Worst comes to worse,
+ * the equal function will discriminate.
+ */
+ return hash;
+}
+
static bool lttng_userspace_probe_location_tracepoint_is_equal(
const struct lttng_userspace_probe_location *_a,
const struct lttng_userspace_probe_location *_b)
ret->lookup_method = lookup_method;
ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT;
ret->equal = lttng_userspace_probe_location_tracepoint_is_equal;
+ ret->hash = lttng_userspace_probe_location_tracepoint_hash;
goto end;
error:
end:
return is_equal;
}
+
+LTTNG_HIDDEN
+unsigned long lttng_userspace_probe_location_hash(
+ const struct lttng_userspace_probe_location *location)
+{
+ return location->hash(location);
+}