#include <linux/file.h>
#include <linux/anon_inodes.h>
#include <wrapper/file.h>
-#include <linux/jhash.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/uuid.h>
+#include <linux/dmi.h>
#include <wrapper/vmalloc.h> /* for wrapper_vmalloc_sync_all() */
#include <wrapper/random.h>
#include <lttng-abi-old.h>
#include <lttng-endian.h>
#include <lttng-string-utils.h>
+#include <lttng-utils.h>
#include <wrapper/ringbuffer/backend.h>
#include <wrapper/ringbuffer/frontend.h>
#define METADATA_CACHE_DEFAULT_SIZE 4096
static LIST_HEAD(sessions);
+static LIST_HEAD(trigger_groups);
static LIST_HEAD(lttng_transport_list);
/*
* Protect the sessions and metadata caches.
*/
static DEFINE_MUTEX(sessions_mutex);
static struct kmem_cache *event_cache;
+static struct kmem_cache *trigger_cache;
-static void lttng_session_lazy_sync_enablers(struct lttng_session *session);
-static void lttng_session_sync_enablers(struct lttng_session *session);
-static void lttng_enabler_destroy(struct lttng_enabler *enabler);
+static void lttng_session_lazy_sync_event_enablers(struct lttng_session *session);
+static void lttng_session_sync_event_enablers(struct lttng_session *session);
+static void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler);
+static void lttng_trigger_enabler_destroy(struct lttng_trigger_enabler *trigger_enabler);
+static void lttng_trigger_group_sync_enablers(struct lttng_trigger_group *trigger_group);
static void _lttng_event_destroy(struct lttng_event *event);
+static void _lttng_trigger_destroy(struct lttng_trigger *trigger);
static void _lttng_channel_destroy(struct lttng_channel *chan);
static int _lttng_event_unregister(struct lttng_event *event);
+static int _lttng_trigger_unregister(struct lttng_trigger *trigger);
static
int _lttng_event_metadata_statedump(struct lttng_session *session,
struct lttng_channel *chan,
void synchronize_trace(void)
{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0))
+ synchronize_rcu();
+#else
synchronize_sched();
+#endif
+
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
#ifdef CONFIG_PREEMPT_RT_FULL
synchronize_rcu();
mutex_unlock(&sessions_mutex);
}
+static struct lttng_transport *lttng_transport_find(const char *name)
+{
+ struct lttng_transport *transport;
+
+ list_for_each_entry(transport, <tng_transport_list, node) {
+ if (!strcmp(transport->name, name))
+ return transport;
+ }
+ return NULL;
+}
+
/*
* Called with sessions lock held.
*/
for (i = 0; i < LTTNG_EVENT_HT_SIZE; i++)
INIT_HLIST_HEAD(&session->events_ht.table[i]);
list_add(&session->list, &sessions);
+ session->pid_tracker.session = session;
+ session->pid_tracker.tracker_type = TRACKER_PID;
+ session->vpid_tracker.session = session;
+ session->vpid_tracker.tracker_type = TRACKER_VPID;
+ session->uid_tracker.session = session;
+ session->uid_tracker.tracker_type = TRACKER_UID;
+ session->vuid_tracker.session = session;
+ session->vuid_tracker.tracker_type = TRACKER_VUID;
+ session->gid_tracker.session = session;
+ session->gid_tracker.tracker_type = TRACKER_GID;
+ session->vgid_tracker.session = session;
+ session->vgid_tracker.tracker_type = TRACKER_VGID;
mutex_unlock(&sessions_mutex);
return session;
return NULL;
}
+struct lttng_trigger_group *lttng_trigger_group_create(void)
+{
+ struct lttng_transport *transport = NULL;
+ struct lttng_trigger_group *trigger_group;
+ const char *transport_name = "relay-trigger";
+ size_t subbuf_size = 4096; //TODO
+ size_t num_subbuf = 16; //TODO
+ unsigned int switch_timer_interval = 0;
+ unsigned int read_timer_interval = 0;
+ int i;
+
+ mutex_lock(&sessions_mutex);
+
+ transport = lttng_transport_find(transport_name);
+ if (!transport) {
+ printk(KERN_WARNING "LTTng transport %s not found\n",
+ transport_name);
+ goto notransport;
+ }
+ if (!try_module_get(transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ goto notransport;
+ }
+
+ trigger_group = lttng_kvzalloc(sizeof(struct lttng_trigger_group),
+ GFP_KERNEL);
+ if (!trigger_group)
+ goto nomem;
+
+ trigger_group->ops = &transport->ops;
+ trigger_group->chan = transport->ops.channel_create(transport_name,
+ trigger_group, NULL, subbuf_size, num_subbuf,
+ switch_timer_interval, read_timer_interval);
+ if (!trigger_group->chan)
+ goto create_error;
+
+ trigger_group->transport = transport;
+ INIT_LIST_HEAD(&trigger_group->enablers_head);
+ INIT_LIST_HEAD(&trigger_group->triggers_head);
+ for (i = 0; i < LTTNG_TRIGGER_HT_SIZE; i++)
+ INIT_HLIST_HEAD(&trigger_group->triggers_ht.table[i]);
+
+ list_add(&trigger_group->node, &trigger_groups);
+ mutex_unlock(&sessions_mutex);
+
+ return trigger_group;
+
+create_error:
+ lttng_kvfree(trigger_group);
+nomem:
+ if (transport)
+ module_put(transport->owner);
+notransport:
+ mutex_unlock(&sessions_mutex);
+ return NULL;
+}
+
void metadata_cache_destroy(struct kref *kref)
{
struct lttng_metadata_cache *cache =
struct lttng_channel *chan, *tmpchan;
struct lttng_event *event, *tmpevent;
struct lttng_metadata_stream *metadata_stream;
- struct lttng_enabler *enabler, *tmpenabler;
+ struct lttng_event_enabler *event_enabler, *tmp_event_enabler;
int ret;
mutex_lock(&sessions_mutex);
WRITE_ONCE(session->active, 0);
list_for_each_entry(chan, &session->chan, list) {
- ret = lttng_syscalls_unregister(chan);
+ ret = lttng_syscalls_unregister_event(chan);
WARN_ON(ret);
}
list_for_each_entry(event, &session->events, list) {
WARN_ON(ret);
}
synchronize_trace(); /* Wait for in-flight events to complete */
- list_for_each_entry_safe(enabler, tmpenabler,
+ list_for_each_entry_safe(event_enabler, tmp_event_enabler,
&session->enablers_head, node)
- lttng_enabler_destroy(enabler);
+ lttng_event_enabler_destroy(event_enabler);
list_for_each_entry_safe(event, tmpevent, &session->events, list)
_lttng_event_destroy(event);
list_for_each_entry_safe(chan, tmpchan, &session->chan, list) {
}
list_for_each_entry(metadata_stream, &session->metadata_cache->metadata_stream, list)
_lttng_metadata_channel_hangup(metadata_stream);
- if (session->pid_tracker)
- lttng_pid_tracker_destroy(session->pid_tracker);
+ lttng_id_tracker_destroy(&session->pid_tracker, false);
+ lttng_id_tracker_destroy(&session->vpid_tracker, false);
+ lttng_id_tracker_destroy(&session->uid_tracker, false);
+ lttng_id_tracker_destroy(&session->vuid_tracker, false);
+ lttng_id_tracker_destroy(&session->gid_tracker, false);
+ lttng_id_tracker_destroy(&session->vgid_tracker, false);
kref_put(&session->metadata_cache->refcount, metadata_cache_destroy);
list_del(&session->list);
mutex_unlock(&sessions_mutex);
lttng_kvfree(session);
}
+void lttng_trigger_group_destroy(struct lttng_trigger_group *trigger_group)
+{
+ struct lttng_trigger_enabler *trigger_enabler, *tmp_trigger_enabler;
+ struct lttng_trigger *trigger, *tmptrigger;
+ int ret;
+
+ if (!trigger_group)
+ return;
+
+ mutex_lock(&sessions_mutex);
+
+ ret = lttng_syscalls_unregister_trigger(trigger_group);
+ WARN_ON(ret);
+
+ list_for_each_entry_safe(trigger, tmptrigger,
+ &trigger_group->triggers_head, list) {
+ ret = _lttng_trigger_unregister(trigger);
+ WARN_ON(ret);
+ }
+
+ synchronize_trace(); /* Wait for in-flight triggers to complete */
+
+ irq_work_sync(&trigger_group->wakeup_pending);
+
+ list_for_each_entry_safe(trigger_enabler, tmp_trigger_enabler,
+ &trigger_group->enablers_head, node)
+ lttng_trigger_enabler_destroy(trigger_enabler);
+
+ list_for_each_entry_safe(trigger, tmptrigger,
+ &trigger_group->triggers_head, list)
+ _lttng_trigger_destroy(trigger);
+
+ trigger_group->ops->channel_destroy(trigger_group->chan);
+ module_put(trigger_group->transport->owner);
+ list_del(&trigger_group->node);
+ mutex_unlock(&sessions_mutex);
+ lttng_kvfree(trigger_group);
+}
+
int lttng_session_statedump(struct lttng_session *session)
{
int ret;
session->tstate = 1;
/* We need to sync enablers with session before activation. */
- lttng_session_sync_enablers(session);
+ lttng_session_sync_event_enablers(session);
/*
* Snapshot the number of events per channel to know the type of header
/* Set transient enabler state to "disabled" */
session->tstate = 0;
- lttng_session_sync_enablers(session);
+ lttng_session_sync_event_enablers(session);
/* Set each stream's quiescent state. */
list_for_each_entry(chan, &session->chan, list) {
}
/* Set transient enabler state to "enabled" */
channel->tstate = 1;
- lttng_session_sync_enablers(channel->session);
+ lttng_session_sync_event_enablers(channel->session);
/* Set atomically the state to "enabled" */
WRITE_ONCE(channel->enabled, 1);
end:
WRITE_ONCE(channel->enabled, 0);
/* Set transient enabler state to "enabled" */
channel->tstate = 0;
- lttng_session_sync_enablers(channel->session);
+ lttng_session_sync_event_enablers(channel->session);
end:
mutex_unlock(&sessions_mutex);
return ret;
return ret;
}
-static struct lttng_transport *lttng_transport_find(const char *name)
+int lttng_trigger_enable(struct lttng_trigger *trigger)
{
- struct lttng_transport *transport;
+ int ret = 0;
- list_for_each_entry(transport, <tng_transport_list, node) {
- if (!strcmp(transport->name, name))
- return transport;
+ mutex_lock(&sessions_mutex);
+ if (trigger->enabled) {
+ ret = -EEXIST;
+ goto end;
}
- return NULL;
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ case LTTNG_KERNEL_SYSCALL:
+ ret = -EINVAL;
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
+ WRITE_ONCE(trigger->enabled, 1);
+ break;
+ case LTTNG_KERNEL_FUNCTION:
+ case LTTNG_KERNEL_NOOP:
+ case LTTNG_KERNEL_KRETPROBE:
+ default:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ }
+end:
+ mutex_unlock(&sessions_mutex);
+ return ret;
+}
+
+int lttng_trigger_disable(struct lttng_trigger *trigger)
+{
+ int ret = 0;
+
+ mutex_lock(&sessions_mutex);
+ if (!trigger->enabled) {
+ ret = -EEXIST;
+ goto end;
+ }
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ case LTTNG_KERNEL_SYSCALL:
+ ret = -EINVAL;
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
+ WRITE_ONCE(trigger->enabled, 0);
+ break;
+ case LTTNG_KERNEL_FUNCTION:
+ case LTTNG_KERNEL_NOOP:
+ case LTTNG_KERNEL_KRETPROBE:
+ default:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ }
+end:
+ mutex_unlock(&sessions_mutex);
+ return ret;
}
struct lttng_channel *lttng_channel_create(struct lttng_session *session,
struct lttng_event *event;
const char *event_name;
struct hlist_head *head;
- size_t name_len;
- uint32_t hash;
int ret;
if (chan->free_event_id == -1U) {
ret = -EINVAL;
goto type_error;
}
- name_len = strlen(event_name);
- hash = jhash(event_name, name_len, 0);
- head = &session->events_ht.table[hash & (LTTNG_EVENT_HT_SIZE - 1)];
+
+ head = utils_borrow_hash_table_bucket(session->events_ht.table,
+ LTTNG_EVENT_HT_SIZE, event_name);
lttng_hlist_for_each_entry(event, head, hlist) {
WARN_ON_ONCE(!event->desc);
if (!strncmp(event->desc->name, event_name,
/* Event will be enabled by enabler sync. */
event->enabled = 0;
event->registered = 0;
- event->desc = lttng_event_get(event_name);
+ event->desc = lttng_event_desc_get(event_name);
if (!event->desc) {
ret = -ENOENT;
goto register_error;
* registration.
*/
smp_wmb();
- ret = lttng_kprobes_register(event_name,
+ ret = lttng_kprobes_register_event(event_name,
event_param->u.kprobe.symbol_name,
event_param->u.kprobe.offset,
event_param->u.kprobe.addr,
*/
smp_wmb();
- ret = lttng_uprobes_register(event_param->name,
+ ret = lttng_uprobes_register_event(event_param->name,
event_param->u.uprobe.fd,
event);
if (ret)
return ERR_PTR(ret);
}
+static
+void lttng_trigger_send_notification(struct lttng_trigger *trigger)
+{
+ struct lttng_trigger_group *trigger_group = trigger->group;
+ struct lib_ring_buffer_ctx ctx;
+ int ret;
+
+ if (unlikely(!READ_ONCE(trigger->enabled)))
+ return;
+
+ lib_ring_buffer_ctx_init(&ctx, trigger_group->chan, NULL, sizeof(trigger->id),
+ lttng_alignof(trigger->id), -1);
+ ret = trigger_group->ops->event_reserve(&ctx, 0);
+ if (ret < 0) {
+ //TODO: error handling with counter maps
+ //silently drop for now. WARN_ON_ONCE(1);
+ return;
+ }
+ lib_ring_buffer_align_ctx(&ctx, lttng_alignof(trigger->id));
+ trigger_group->ops->event_write(&ctx, &trigger->id, sizeof(trigger->id));
+ trigger_group->ops->event_commit(&ctx);
+ irq_work_queue(&trigger_group->wakeup_pending);
+}
+
+struct lttng_trigger *_lttng_trigger_create(
+ const struct lttng_event_desc *event_desc,
+ uint64_t id, struct lttng_trigger_group *trigger_group,
+ struct lttng_kernel_trigger *trigger_param, void *filter,
+ enum lttng_kernel_instrumentation itype)
+{
+ struct lttng_trigger *trigger;
+ const char *event_name;
+ struct hlist_head *head;
+ int ret;
+
+ switch (itype) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ event_name = event_desc->name;
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
+ case LTTNG_KERNEL_SYSCALL:
+ event_name = trigger_param->name;
+ break;
+ case LTTNG_KERNEL_KRETPROBE:
+ case LTTNG_KERNEL_FUNCTION:
+ case LTTNG_KERNEL_NOOP:
+ default:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ goto type_error;
+ }
+
+ head = utils_borrow_hash_table_bucket(trigger_group->triggers_ht.table,
+ LTTNG_TRIGGER_HT_SIZE, event_name);
+ lttng_hlist_for_each_entry(trigger, head, hlist) {
+ WARN_ON_ONCE(!trigger->desc);
+ if (!strncmp(trigger->desc->name, event_name,
+ LTTNG_KERNEL_SYM_NAME_LEN - 1)
+ && trigger_group == trigger->group
+ && id == trigger->id) {
+ ret = -EEXIST;
+ goto exist;
+ }
+ }
+
+ trigger = kmem_cache_zalloc(trigger_cache, GFP_KERNEL);
+ if (!trigger) {
+ ret = -ENOMEM;
+ goto cache_error;
+ }
+ trigger->group = trigger_group;
+ trigger->id = id;
+ trigger->filter = filter;
+ trigger->instrumentation = itype;
+ trigger->evtype = LTTNG_TYPE_EVENT;
+ trigger->send_notification = lttng_trigger_send_notification;
+ INIT_LIST_HEAD(&trigger->bytecode_runtime_head);
+ INIT_LIST_HEAD(&trigger->enablers_ref_head);
+
+ switch (itype) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ /* Event will be enabled by enabler sync. */
+ trigger->enabled = 0;
+ trigger->registered = 0;
+ trigger->desc = lttng_event_desc_get(event_name);
+ if (!trigger->desc) {
+ ret = -ENOENT;
+ goto register_error;
+ }
+ /* Populate lttng_trigger structure before event registration. */
+ smp_wmb();
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ /*
+ * Needs to be explicitly enabled after creation, since
+ * we may want to apply filters.
+ */
+ trigger->enabled = 0;
+ trigger->registered = 1;
+ /*
+ * Populate lttng_trigger structure before event
+ * registration.
+ */
+ smp_wmb();
+ ret = lttng_kprobes_register_trigger(
+ trigger_param->u.kprobe.symbol_name,
+ trigger_param->u.kprobe.offset,
+ trigger_param->u.kprobe.addr,
+ trigger);
+ if (ret) {
+ ret = -EINVAL;
+ goto register_error;
+ }
+ ret = try_module_get(trigger->desc->owner);
+ WARN_ON_ONCE(!ret);
+ break;
+ case LTTNG_KERNEL_NOOP:
+ case LTTNG_KERNEL_SYSCALL:
+ /*
+ * Needs to be explicitly enabled after creation, since
+ * we may want to apply filters.
+ */
+ trigger->enabled = 0;
+ trigger->registered = 0;
+ trigger->desc = event_desc;
+ if (!trigger->desc) {
+ ret = -EINVAL;
+ goto register_error;
+ }
+ break;
+ case LTTNG_KERNEL_UPROBE:
+ /*
+ * Needs to be explicitly enabled after creation, since
+ * we may want to apply filters.
+ */
+ trigger->enabled = 0;
+ trigger->registered = 1;
+
+ /*
+ * Populate lttng_trigger structure before trigger
+ * registration.
+ */
+ smp_wmb();
+
+ ret = lttng_uprobes_register_trigger(trigger_param->name,
+ trigger_param->u.uprobe.fd,
+ trigger);
+ if (ret)
+ goto register_error;
+ ret = try_module_get(trigger->desc->owner);
+ WARN_ON_ONCE(!ret);
+ break;
+ case LTTNG_KERNEL_KRETPROBE:
+ case LTTNG_KERNEL_FUNCTION:
+ default:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ goto register_error;
+ }
+
+ list_add(&trigger->list, &trigger_group->triggers_head);
+ hlist_add_head(&trigger->hlist, head);
+ return trigger;
+
+register_error:
+ kmem_cache_free(trigger_cache, trigger);
+cache_error:
+exist:
+type_error:
+ return ERR_PTR(ret);
+}
+
struct lttng_event *lttng_event_create(struct lttng_channel *chan,
struct lttng_kernel_event *event_param,
void *filter,
return event;
}
+struct lttng_trigger *lttng_trigger_create(
+ const struct lttng_event_desc *event_desc,
+ uint64_t id, struct lttng_trigger_group *trigger_group,
+ struct lttng_kernel_trigger *trigger_param, void *filter,
+ enum lttng_kernel_instrumentation itype)
+{
+ struct lttng_trigger *trigger;
+
+ mutex_lock(&sessions_mutex);
+ trigger = _lttng_trigger_create(event_desc, id, trigger_group,
+ trigger_param, filter, itype);
+ mutex_unlock(&sessions_mutex);
+ return trigger;
+}
+
/* Only used for tracepoints for now. */
static
void register_event(struct lttng_event *event)
event);
break;
case LTTNG_KERNEL_SYSCALL:
- ret = lttng_syscall_filter_enable(event->chan,
+ ret = lttng_syscall_filter_enable_event(event->chan,
desc->name);
break;
case LTTNG_KERNEL_KPROBE:
event);
break;
case LTTNG_KERNEL_KPROBE:
- lttng_kprobes_unregister(event);
+ lttng_kprobes_unregister_event(event);
ret = 0;
break;
case LTTNG_KERNEL_KRETPROBE:
ret = 0;
break;
case LTTNG_KERNEL_SYSCALL:
- ret = lttng_syscall_filter_disable(event->chan,
+ ret = lttng_syscall_filter_disable_event(event->chan,
desc->name);
break;
case LTTNG_KERNEL_NOOP:
ret = 0;
break;
case LTTNG_KERNEL_UPROBE:
- lttng_uprobes_unregister(event);
+ lttng_uprobes_unregister_event(event);
ret = 0;
break;
default:
return ret;
}
+/* Only used for tracepoints for now. */
+static
+void register_trigger(struct lttng_trigger *trigger)
+{
+ const struct lttng_event_desc *desc;
+ int ret = -EINVAL;
+
+ if (trigger->registered)
+ return;
+
+ desc = trigger->desc;
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ ret = lttng_wrapper_tracepoint_probe_register(desc->kname,
+ desc->trigger_callback,
+ trigger);
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+ ret = lttng_syscall_filter_enable_trigger(trigger);
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
+ ret = 0;
+ break;
+ case LTTNG_KERNEL_KRETPROBE:
+ case LTTNG_KERNEL_FUNCTION:
+ case LTTNG_KERNEL_NOOP:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ if (!ret)
+ trigger->registered = 1;
+}
+
+static
+int _lttng_trigger_unregister(struct lttng_trigger *trigger)
+{
+ const struct lttng_event_desc *desc;
+ int ret = -EINVAL;
+
+ if (!trigger->registered)
+ return 0;
+
+ desc = trigger->desc;
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ ret = lttng_wrapper_tracepoint_probe_unregister(trigger->desc->kname,
+ trigger->desc->trigger_callback,
+ trigger);
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ lttng_kprobes_unregister_trigger(trigger);
+ ret = 0;
+ break;
+ case LTTNG_KERNEL_UPROBE:
+ lttng_uprobes_unregister_trigger(trigger);
+ ret = 0;
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+ ret = lttng_syscall_filter_disable_trigger(trigger);
+ break;
+ case LTTNG_KERNEL_KRETPROBE:
+ case LTTNG_KERNEL_FUNCTION:
+ case LTTNG_KERNEL_NOOP:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ if (!ret)
+ trigger->registered = 0;
+ return ret;
+}
+
/*
* Only used internally at session destruction.
*/
{
switch (event->instrumentation) {
case LTTNG_KERNEL_TRACEPOINT:
- lttng_event_put(event->desc);
+ lttng_event_desc_put(event->desc);
break;
case LTTNG_KERNEL_KPROBE:
module_put(event->desc->owner);
- lttng_kprobes_destroy_private(event);
+ lttng_kprobes_destroy_event_private(event);
break;
case LTTNG_KERNEL_KRETPROBE:
module_put(event->desc->owner);
break;
case LTTNG_KERNEL_UPROBE:
module_put(event->desc->owner);
- lttng_uprobes_destroy_private(event);
+ lttng_uprobes_destroy_event_private(event);
break;
default:
WARN_ON_ONCE(1);
kmem_cache_free(event_cache, event);
}
-int lttng_session_track_pid(struct lttng_session *session, int pid)
+/*
+ * Only used internally at session destruction.
+ */
+static
+void _lttng_trigger_destroy(struct lttng_trigger *trigger)
+{
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ lttng_event_desc_put(trigger->desc);
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ module_put(trigger->desc->owner);
+ lttng_kprobes_destroy_trigger_private(trigger);
+ break;
+ case LTTNG_KERNEL_NOOP:
+ case LTTNG_KERNEL_SYSCALL:
+ break;
+ case LTTNG_KERNEL_UPROBE:
+ module_put(trigger->desc->owner);
+ lttng_uprobes_destroy_trigger_private(trigger);
+ break;
+ case LTTNG_KERNEL_KRETPROBE:
+ case LTTNG_KERNEL_FUNCTION:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ list_del(&trigger->list);
+ kmem_cache_free(trigger_cache, trigger);
+}
+
+struct lttng_id_tracker *get_tracker(struct lttng_session *session,
+ enum tracker_type tracker_type)
+{
+ switch (tracker_type) {
+ case TRACKER_PID:
+ return &session->pid_tracker;
+ case TRACKER_VPID:
+ return &session->vpid_tracker;
+ case TRACKER_UID:
+ return &session->uid_tracker;
+ case TRACKER_VUID:
+ return &session->vuid_tracker;
+ case TRACKER_GID:
+ return &session->gid_tracker;
+ case TRACKER_VGID:
+ return &session->vgid_tracker;
+ default:
+ WARN_ON_ONCE(1);
+ return NULL;
+ }
+}
+
+int lttng_session_track_id(struct lttng_session *session,
+ enum tracker_type tracker_type, int id)
{
+ struct lttng_id_tracker *tracker;
int ret;
- if (pid < -1)
+ tracker = get_tracker(session, tracker_type);
+ if (!tracker)
+ return -EINVAL;
+ if (id < -1)
return -EINVAL;
mutex_lock(&sessions_mutex);
- if (pid == -1) {
- /* track all pids: destroy tracker. */
- if (session->pid_tracker) {
- struct lttng_pid_tracker *lpf;
-
- lpf = session->pid_tracker;
- rcu_assign_pointer(session->pid_tracker, NULL);
- synchronize_trace();
- lttng_pid_tracker_destroy(lpf);
- }
+ if (id == -1) {
+ /* track all ids: destroy tracker. */
+ lttng_id_tracker_destroy(tracker, true);
ret = 0;
} else {
- if (!session->pid_tracker) {
- struct lttng_pid_tracker *lpf;
-
- lpf = lttng_pid_tracker_create();
- if (!lpf) {
- ret = -ENOMEM;
- goto unlock;
- }
- ret = lttng_pid_tracker_add(lpf, pid);
- rcu_assign_pointer(session->pid_tracker, lpf);
- } else {
- ret = lttng_pid_tracker_add(session->pid_tracker, pid);
- }
+ ret = lttng_id_tracker_add(tracker, id);
}
-unlock:
mutex_unlock(&sessions_mutex);
return ret;
}
-int lttng_session_untrack_pid(struct lttng_session *session, int pid)
+int lttng_session_untrack_id(struct lttng_session *session,
+ enum tracker_type tracker_type, int id)
{
+ struct lttng_id_tracker *tracker;
int ret;
- if (pid < -1)
+ tracker = get_tracker(session, tracker_type);
+ if (!tracker)
+ return -EINVAL;
+ if (id < -1)
return -EINVAL;
mutex_lock(&sessions_mutex);
- if (pid == -1) {
- /* untrack all pids: replace by empty tracker. */
- struct lttng_pid_tracker *old_lpf = session->pid_tracker;
- struct lttng_pid_tracker *lpf;
-
- lpf = lttng_pid_tracker_create();
- if (!lpf) {
- ret = -ENOMEM;
- goto unlock;
- }
- rcu_assign_pointer(session->pid_tracker, lpf);
- synchronize_trace();
- if (old_lpf)
- lttng_pid_tracker_destroy(old_lpf);
- ret = 0;
+ if (id == -1) {
+ /* untrack all ids: replace by empty tracker. */
+ ret = lttng_id_tracker_empty_set(tracker);
} else {
- if (!session->pid_tracker) {
- ret = -ENOENT;
- goto unlock;
- }
- ret = lttng_pid_tracker_del(session->pid_tracker, pid);
+ ret = lttng_id_tracker_del(tracker, id);
}
-unlock:
mutex_unlock(&sessions_mutex);
return ret;
}
static
-void *pid_list_start(struct seq_file *m, loff_t *pos)
+void *id_list_start(struct seq_file *m, loff_t *pos)
{
- struct lttng_session *session = m->private;
- struct lttng_pid_tracker *lpf;
- struct lttng_pid_hash_node *e;
+ struct lttng_id_tracker *id_tracker = m->private;
+ struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p;
+ struct lttng_id_hash_node *e;
int iter = 0, i;
mutex_lock(&sessions_mutex);
- lpf = session->pid_tracker;
- if (lpf) {
- for (i = 0; i < LTTNG_PID_TABLE_SIZE; i++) {
- struct hlist_head *head = &lpf->pid_hash[i];
+ if (id_tracker_p) {
+ for (i = 0; i < LTTNG_ID_TABLE_SIZE; i++) {
+ struct hlist_head *head = &id_tracker_p->id_hash[i];
lttng_hlist_for_each_entry(e, head, hlist) {
if (iter++ >= *pos)
}
}
} else {
- /* PID tracker disabled. */
+ /* ID tracker disabled. */
if (iter >= *pos && iter == 0) {
- return session; /* empty tracker */
+ return id_tracker_p; /* empty tracker */
}
iter++;
}
/* Called with sessions_mutex held. */
static
-void *pid_list_next(struct seq_file *m, void *p, loff_t *ppos)
+void *id_list_next(struct seq_file *m, void *p, loff_t *ppos)
{
- struct lttng_session *session = m->private;
- struct lttng_pid_tracker *lpf;
- struct lttng_pid_hash_node *e;
+ struct lttng_id_tracker *id_tracker = m->private;
+ struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p;
+ struct lttng_id_hash_node *e;
int iter = 0, i;
(*ppos)++;
- lpf = session->pid_tracker;
- if (lpf) {
- for (i = 0; i < LTTNG_PID_TABLE_SIZE; i++) {
- struct hlist_head *head = &lpf->pid_hash[i];
+ if (id_tracker_p) {
+ for (i = 0; i < LTTNG_ID_TABLE_SIZE; i++) {
+ struct hlist_head *head = &id_tracker_p->id_hash[i];
lttng_hlist_for_each_entry(e, head, hlist) {
if (iter++ >= *ppos)
}
}
} else {
- /* PID tracker disabled. */
+ /* ID tracker disabled. */
if (iter >= *ppos && iter == 0)
- return session; /* empty tracker */
+ return p; /* empty tracker */
iter++;
}
}
static
-void pid_list_stop(struct seq_file *m, void *p)
+void id_list_stop(struct seq_file *m, void *p)
{
mutex_unlock(&sessions_mutex);
}
static
-int pid_list_show(struct seq_file *m, void *p)
+int id_list_show(struct seq_file *m, void *p)
{
- int pid;
+ struct lttng_id_tracker *id_tracker = m->private;
+ struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p;
+ int id;
- if (p == m->private) {
+ if (p == id_tracker_p) {
/* Tracker disabled. */
- pid = -1;
+ id = -1;
} else {
- const struct lttng_pid_hash_node *e = p;
+ const struct lttng_id_hash_node *e = p;
- pid = lttng_pid_tracker_get_node_pid(e);
+ id = lttng_id_tracker_get_node_id(e);
+ }
+ switch (id_tracker->tracker_type) {
+ case TRACKER_PID:
+ seq_printf(m, "process { pid = %d; };\n", id);
+ break;
+ case TRACKER_VPID:
+ seq_printf(m, "process { vpid = %d; };\n", id);
+ break;
+ case TRACKER_UID:
+ seq_printf(m, "user { uid = %d; };\n", id);
+ break;
+ case TRACKER_VUID:
+ seq_printf(m, "user { vuid = %d; };\n", id);
+ break;
+ case TRACKER_GID:
+ seq_printf(m, "group { gid = %d; };\n", id);
+ break;
+ case TRACKER_VGID:
+ seq_printf(m, "group { vgid = %d; };\n", id);
+ break;
+ default:
+ seq_printf(m, "UNKNOWN { field = %d };\n", id);
}
- seq_printf(m, "process { pid = %d; };\n", pid);
return 0;
}
static
-const struct seq_operations lttng_tracker_pids_list_seq_ops = {
- .start = pid_list_start,
- .next = pid_list_next,
- .stop = pid_list_stop,
- .show = pid_list_show,
+const struct seq_operations lttng_tracker_ids_list_seq_ops = {
+ .start = id_list_start,
+ .next = id_list_next,
+ .stop = id_list_stop,
+ .show = id_list_show,
};
static
-int lttng_tracker_pids_list_open(struct inode *inode, struct file *file)
+int lttng_tracker_ids_list_open(struct inode *inode, struct file *file)
{
- return seq_open(file, <tng_tracker_pids_list_seq_ops);
+ return seq_open(file, <tng_tracker_ids_list_seq_ops);
}
static
-int lttng_tracker_pids_list_release(struct inode *inode, struct file *file)
+int lttng_tracker_ids_list_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
- struct lttng_session *session = m->private;
+ struct lttng_id_tracker *id_tracker = m->private;
int ret;
- WARN_ON_ONCE(!session);
+ WARN_ON_ONCE(!id_tracker);
ret = seq_release(inode, file);
- if (!ret && session)
- fput(session->file);
+ if (!ret)
+ fput(id_tracker->session->file);
return ret;
}
-const struct file_operations lttng_tracker_pids_list_fops = {
+const struct file_operations lttng_tracker_ids_list_fops = {
.owner = THIS_MODULE,
- .open = lttng_tracker_pids_list_open,
+ .open = lttng_tracker_ids_list_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = lttng_tracker_pids_list_release,
+ .release = lttng_tracker_ids_list_release,
};
-int lttng_session_list_tracker_pids(struct lttng_session *session)
+int lttng_session_list_tracker_ids(struct lttng_session *session,
+ enum tracker_type tracker_type)
{
- struct file *tracker_pids_list_file;
+ struct file *tracker_ids_list_file;
struct seq_file *m;
int file_fd, ret;
goto fd_error;
}
- tracker_pids_list_file = anon_inode_getfile("[lttng_tracker_pids_list]",
- <tng_tracker_pids_list_fops,
+ tracker_ids_list_file = anon_inode_getfile("[lttng_tracker_ids_list]",
+ <tng_tracker_ids_list_fops,
NULL, O_RDWR);
- if (IS_ERR(tracker_pids_list_file)) {
- ret = PTR_ERR(tracker_pids_list_file);
+ if (IS_ERR(tracker_ids_list_file)) {
+ ret = PTR_ERR(tracker_ids_list_file);
goto file_error;
}
- if (atomic_long_add_unless(&session->file->f_count,
- 1, INT_MAX) == INT_MAX) {
+ if (!atomic_long_add_unless(&session->file->f_count, 1, LONG_MAX)) {
+ ret = -EOVERFLOW;
goto refcount_error;
}
- ret = lttng_tracker_pids_list_fops.open(NULL, tracker_pids_list_file);
+ ret = lttng_tracker_ids_list_fops.open(NULL, tracker_ids_list_file);
if (ret < 0)
goto open_error;
- m = tracker_pids_list_file->private_data;
- m->private = session;
- fd_install(file_fd, tracker_pids_list_file);
+ m = tracker_ids_list_file->private_data;
+
+ m->private = get_tracker(session, tracker_type);
+ BUG_ON(!m->private);
+ fd_install(file_fd, tracker_ids_list_file);
return file_fd;
open_error:
atomic_long_dec(&session->file->f_count);
refcount_error:
- fput(tracker_pids_list_file);
+ fput(tracker_ids_list_file);
file_error:
put_unused_fd(file_fd);
fd_error:
return 1;
}
-static
int lttng_desc_match_enabler(const struct lttng_event_desc *desc,
struct lttng_enabler *enabler)
{
WARN_ON_ONCE(1);
return -EINVAL;
}
- switch (enabler->type) {
- case LTTNG_ENABLER_STAR_GLOB:
+ switch (enabler->format_type) {
+ case LTTNG_ENABLER_FORMAT_STAR_GLOB:
return lttng_match_enabler_star_glob(desc_name, enabler_name);
- case LTTNG_ENABLER_NAME:
+ case LTTNG_ENABLER_FORMAT_NAME:
return lttng_match_enabler_name(desc_name, enabler_name);
default:
return -EINVAL;
}
static
-int lttng_event_match_enabler(struct lttng_event *event,
- struct lttng_enabler *enabler)
+int lttng_event_enabler_match_event(struct lttng_event_enabler *event_enabler,
+ struct lttng_event *event)
+{
+ struct lttng_enabler *base_enabler = lttng_event_enabler_as_enabler(
+ event_enabler);
+
+ if (base_enabler->event_param.instrumentation != event->instrumentation)
+ return 0;
+ if (lttng_desc_match_enabler(event->desc, base_enabler)
+ && event->chan == event_enabler->chan)
+ return 1;
+ else
+ return 0;
+}
+
+static
+int lttng_trigger_enabler_match_trigger(struct lttng_trigger_enabler *trigger_enabler,
+ struct lttng_trigger *trigger)
{
- if (enabler->event_param.instrumentation != event->instrumentation)
+ struct lttng_enabler *base_enabler = lttng_trigger_enabler_as_enabler(
+ trigger_enabler);
+
+ if (base_enabler->event_param.instrumentation != trigger->instrumentation)
return 0;
- if (lttng_desc_match_enabler(event->desc, enabler)
- && event->chan == enabler->chan)
+ if (lttng_desc_match_enabler(trigger->desc, base_enabler)
+ && trigger->group == trigger_enabler->group
+ && trigger->id == trigger_enabler->id)
return 1;
else
return 0;
}
static
-struct lttng_enabler_ref *lttng_event_enabler_ref(struct lttng_event *event,
+struct lttng_enabler_ref *lttng_enabler_ref(
+ struct list_head *enablers_ref_list,
struct lttng_enabler *enabler)
{
struct lttng_enabler_ref *enabler_ref;
- list_for_each_entry(enabler_ref,
- &event->enablers_ref_head, node) {
+ list_for_each_entry(enabler_ref, enablers_ref_list, node) {
if (enabler_ref->ref == enabler)
return enabler_ref;
}
- return NULL;
+ return NULL;
+}
+
+static
+void lttng_create_tracepoint_event_if_missing(struct lttng_event_enabler *event_enabler)
+{
+ struct lttng_session *session = event_enabler->chan->session;
+ struct lttng_probe_desc *probe_desc;
+ const struct lttng_event_desc *desc;
+ int i;
+ struct list_head *probe_list;
+
+ probe_list = lttng_get_probe_list_head();
+ /*
+ * For each probe event, if we find that a probe event matches
+ * our enabler, create an associated lttng_event if not
+ * already present.
+ */
+ list_for_each_entry(probe_desc, probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ int found = 0;
+ struct hlist_head *head;
+ struct lttng_event *event;
+
+ desc = probe_desc->event_desc[i];
+ if (!lttng_desc_match_enabler(desc,
+ lttng_event_enabler_as_enabler(event_enabler)))
+ continue;
+
+ /*
+ * Check if already created.
+ */
+ head = utils_borrow_hash_table_bucket(
+ session->events_ht.table, LTTNG_EVENT_HT_SIZE,
+ desc->name);
+ lttng_hlist_for_each_entry(event, head, hlist) {
+ if (event->desc == desc
+ && event->chan == event_enabler->chan)
+ found = 1;
+ }
+ if (found)
+ continue;
+
+ /*
+ * We need to create an event for this
+ * event probe.
+ */
+ event = _lttng_event_create(event_enabler->chan,
+ NULL, NULL, desc,
+ LTTNG_KERNEL_TRACEPOINT);
+ if (!event) {
+ printk(KERN_INFO "Unable to create event %s\n",
+ probe_desc->event_desc[i]->name);
+ }
+ }
+ }
}
static
-void lttng_create_tracepoint_if_missing(struct lttng_enabler *enabler)
+void lttng_create_tracepoint_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler)
{
- struct lttng_session *session = enabler->chan->session;
+ struct lttng_trigger_group *trigger_group = trigger_enabler->group;
struct lttng_probe_desc *probe_desc;
const struct lttng_event_desc *desc;
int i;
probe_list = lttng_get_probe_list_head();
/*
* For each probe event, if we find that a probe event matches
- * our enabler, create an associated lttng_event if not
+ * our enabler, create an associated lttng_trigger if not
* already present.
*/
list_for_each_entry(probe_desc, probe_list, head) {
for (i = 0; i < probe_desc->nr_events; i++) {
int found = 0;
struct hlist_head *head;
- const char *event_name;
- size_t name_len;
- uint32_t hash;
- struct lttng_event *event;
+ struct lttng_trigger *trigger;
desc = probe_desc->event_desc[i];
- if (!lttng_desc_match_enabler(desc, enabler))
+ if (!lttng_desc_match_enabler(desc,
+ lttng_trigger_enabler_as_enabler(trigger_enabler)))
continue;
- event_name = desc->name;
- name_len = strlen(event_name);
/*
* Check if already created.
*/
- hash = jhash(event_name, name_len, 0);
- head = &session->events_ht.table[hash & (LTTNG_EVENT_HT_SIZE - 1)];
- lttng_hlist_for_each_entry(event, head, hlist) {
- if (event->desc == desc
- && event->chan == enabler->chan)
+ head = utils_borrow_hash_table_bucket(
+ trigger_group->triggers_ht.table,
+ LTTNG_TRIGGER_HT_SIZE, desc->name);
+ lttng_hlist_for_each_entry(trigger, head, hlist) {
+ if (trigger->desc == desc
+ && trigger->id == trigger_enabler->id)
found = 1;
}
if (found)
continue;
/*
- * We need to create an event for this
- * event probe.
+ * We need to create a trigger for this event probe.
*/
- event = _lttng_event_create(enabler->chan,
- NULL, NULL, desc,
- LTTNG_KERNEL_TRACEPOINT);
- if (!event) {
- printk(KERN_INFO "Unable to create event %s\n",
+ trigger = _lttng_trigger_create(desc,
+ trigger_enabler->id, trigger_group, NULL, NULL,
+ LTTNG_KERNEL_TRACEPOINT);
+ if (IS_ERR(trigger)) {
+ printk(KERN_INFO "Unable to create trigger %s\n",
probe_desc->event_desc[i]->name);
}
}
}
static
-void lttng_create_syscall_if_missing(struct lttng_enabler *enabler)
+void lttng_create_syscall_event_if_missing(struct lttng_event_enabler *event_enabler)
+{
+ int ret;
+
+ ret = lttng_syscalls_register_event(event_enabler->chan, NULL);
+ WARN_ON_ONCE(ret);
+}
+
+static
+void lttng_create_syscall_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler)
{
int ret;
- ret = lttng_syscalls_register(enabler->chan, NULL);
+ ret = lttng_syscalls_register_trigger(trigger_enabler, NULL);
+ WARN_ON_ONCE(ret);
+ ret = lttng_syscals_create_matching_triggers(trigger_enabler, NULL);
WARN_ON_ONCE(ret);
}
* Should be called with sessions mutex held.
*/
static
-void lttng_create_event_if_missing(struct lttng_enabler *enabler)
+void lttng_create_event_if_missing(struct lttng_event_enabler *event_enabler)
{
- switch (enabler->event_param.instrumentation) {
+ switch (event_enabler->base.event_param.instrumentation) {
case LTTNG_KERNEL_TRACEPOINT:
- lttng_create_tracepoint_if_missing(enabler);
+ lttng_create_tracepoint_event_if_missing(event_enabler);
break;
case LTTNG_KERNEL_SYSCALL:
- lttng_create_syscall_if_missing(enabler);
+ lttng_create_syscall_event_if_missing(event_enabler);
break;
default:
WARN_ON_ONCE(1);
}
/*
- * Create events associated with an enabler (if not already present),
+ * Create events associated with an event_enabler (if not already present),
* and add backward reference from the event to the enabler.
* Should be called with sessions mutex held.
*/
static
-int lttng_enabler_ref_events(struct lttng_enabler *enabler)
+int lttng_event_enabler_ref_events(struct lttng_event_enabler *event_enabler)
{
- struct lttng_session *session = enabler->chan->session;
+ struct lttng_session *session = event_enabler->chan->session;
struct lttng_event *event;
/* First ensure that probe events are created for this enabler. */
- lttng_create_event_if_missing(enabler);
+ lttng_create_event_if_missing(event_enabler);
- /* For each event matching enabler in session event list. */
+ /* For each event matching event_enabler in session event list. */
list_for_each_entry(event, &session->events, list) {
struct lttng_enabler_ref *enabler_ref;
- if (!lttng_event_match_enabler(event, enabler))
+ if (!lttng_event_enabler_match_event(event_enabler, event))
continue;
- enabler_ref = lttng_event_enabler_ref(event, enabler);
+ enabler_ref = lttng_enabler_ref(&event->enablers_ref_head,
+ lttng_event_enabler_as_enabler(event_enabler));
if (!enabler_ref) {
/*
* If no backward ref, create it.
- * Add backward ref from event to enabler.
+ * Add backward ref from event to event_enabler.
*/
enabler_ref = kzalloc(sizeof(*enabler_ref), GFP_KERNEL);
if (!enabler_ref)
return -ENOMEM;
- enabler_ref->ref = enabler;
+ enabler_ref->ref = lttng_event_enabler_as_enabler(event_enabler);
list_add(&enabler_ref->node,
&event->enablers_ref_head);
}
/*
* Link filter bytecodes if not linked yet.
*/
- lttng_enabler_event_link_bytecode(event, enabler);
+ lttng_enabler_link_bytecode(event->desc,
+ lttng_static_ctx,
+ &event->bytecode_runtime_head,
+ lttng_event_enabler_as_enabler(event_enabler));
/* TODO: merge event context. */
}
return 0;
}
+/*
+ * Create struct lttng_trigger if it is missing and present in the list of
+ * tracepoint probes.
+ * Should be called with sessions mutex held.
+ */
+static
+void lttng_create_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler)
+{
+ switch (trigger_enabler->base.event_param.instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ lttng_create_tracepoint_trigger_if_missing(trigger_enabler);
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+ lttng_create_syscall_trigger_if_missing(trigger_enabler);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+}
+
+/*
+ * Create triggers associated with a trigger enabler (if not already present).
+ */
+static
+int lttng_trigger_enabler_ref_triggers(struct lttng_trigger_enabler *trigger_enabler)
+{
+ struct lttng_trigger_group *trigger_group = trigger_enabler->group;
+ struct lttng_trigger *trigger;
+
+ /* First ensure that probe triggers are created for this enabler. */
+ lttng_create_trigger_if_missing(trigger_enabler);
+
+ /* Link the created trigger with its associated enabler. */
+ list_for_each_entry(trigger, &trigger_group->triggers_head, list) {
+ struct lttng_enabler_ref *enabler_ref;
+
+ if (!lttng_trigger_enabler_match_trigger(trigger_enabler, trigger))
+ continue;
+
+ enabler_ref = lttng_enabler_ref(&trigger->enablers_ref_head,
+ lttng_trigger_enabler_as_enabler(trigger_enabler));
+ if (!enabler_ref) {
+ /*
+ * If no backward ref, create it.
+ * Add backward ref from trigger to enabler.
+ */
+ enabler_ref = kzalloc(sizeof(*enabler_ref), GFP_KERNEL);
+ if (!enabler_ref)
+ return -ENOMEM;
+
+ enabler_ref->ref = lttng_trigger_enabler_as_enabler(
+ trigger_enabler);
+ list_add(&enabler_ref->node,
+ &trigger->enablers_ref_head);
+ }
+
+ /*
+ * Link filter bytecodes if not linked yet.
+ */
+ lttng_enabler_link_bytecode(trigger->desc,
+ trigger_group->ctx, &trigger->bytecode_runtime_head,
+ lttng_trigger_enabler_as_enabler(trigger_enabler));
+ }
+ return 0;
+}
+
/*
* Called at module load: connect the probe on all enablers matching
* this event.
struct lttng_session *session;
list_for_each_entry(session, &sessions, list)
- lttng_session_lazy_sync_enablers(session);
+ lttng_session_lazy_sync_event_enablers(session);
+ return 0;
+}
+
+static bool lttng_trigger_group_has_active_triggers(
+ struct lttng_trigger_group *trigger_group)
+{
+ struct lttng_trigger_enabler *trigger_enabler;
+
+ list_for_each_entry(trigger_enabler, &trigger_group->enablers_head,
+ node) {
+ if (trigger_enabler->base.enabled)
+ return true;
+ }
+ return false;
+}
+
+bool lttng_trigger_active(void)
+{
+ struct lttng_trigger_group *trigger_group;
+
+ list_for_each_entry(trigger_group, &trigger_groups, node) {
+ if (lttng_trigger_group_has_active_triggers(trigger_group))
+ return true;
+ }
+ return false;
+}
+
+int lttng_fix_pending_triggers(void)
+{
+ struct lttng_trigger_group *trigger_group;
+
+ list_for_each_entry(trigger_group, &trigger_groups, node)
+ lttng_trigger_group_sync_enablers(trigger_group);
return 0;
}
-struct lttng_enabler *lttng_enabler_create(enum lttng_enabler_type type,
+struct lttng_event_enabler *lttng_event_enabler_create(
+ enum lttng_enabler_format_type format_type,
struct lttng_kernel_event *event_param,
struct lttng_channel *chan)
{
- struct lttng_enabler *enabler;
+ struct lttng_event_enabler *event_enabler;
- enabler = kzalloc(sizeof(*enabler), GFP_KERNEL);
- if (!enabler)
+ event_enabler = kzalloc(sizeof(*event_enabler), GFP_KERNEL);
+ if (!event_enabler)
return NULL;
- enabler->type = type;
- INIT_LIST_HEAD(&enabler->filter_bytecode_head);
- memcpy(&enabler->event_param, event_param,
- sizeof(enabler->event_param));
- enabler->chan = chan;
+ event_enabler->base.format_type = format_type;
+ INIT_LIST_HEAD(&event_enabler->base.filter_bytecode_head);
+ memcpy(&event_enabler->base.event_param, event_param,
+ sizeof(event_enabler->base.event_param));
+ event_enabler->chan = chan;
/* ctx left NULL */
- enabler->enabled = 0;
- enabler->evtype = LTTNG_TYPE_ENABLER;
+ event_enabler->base.enabled = 0;
+ event_enabler->base.evtype = LTTNG_TYPE_ENABLER;
mutex_lock(&sessions_mutex);
- list_add(&enabler->node, &enabler->chan->session->enablers_head);
- lttng_session_lazy_sync_enablers(enabler->chan->session);
+ list_add(&event_enabler->node, &event_enabler->chan->session->enablers_head);
+ lttng_session_lazy_sync_event_enablers(event_enabler->chan->session);
mutex_unlock(&sessions_mutex);
- return enabler;
+ return event_enabler;
}
-int lttng_enabler_enable(struct lttng_enabler *enabler)
+int lttng_event_enabler_enable(struct lttng_event_enabler *event_enabler)
{
mutex_lock(&sessions_mutex);
- enabler->enabled = 1;
- lttng_session_lazy_sync_enablers(enabler->chan->session);
+ lttng_event_enabler_as_enabler(event_enabler)->enabled = 1;
+ lttng_session_lazy_sync_event_enablers(event_enabler->chan->session);
mutex_unlock(&sessions_mutex);
return 0;
}
-int lttng_enabler_disable(struct lttng_enabler *enabler)
+int lttng_event_enabler_disable(struct lttng_event_enabler *event_enabler)
{
mutex_lock(&sessions_mutex);
- enabler->enabled = 0;
- lttng_session_lazy_sync_enablers(enabler->chan->session);
+ lttng_event_enabler_as_enabler(event_enabler)->enabled = 0;
+ lttng_session_lazy_sync_event_enablers(event_enabler->chan->session);
mutex_unlock(&sessions_mutex);
return 0;
}
+static
int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler,
struct lttng_kernel_filter_bytecode __user *bytecode)
{
sizeof(*bytecode) + bytecode_len);
if (ret)
goto error_free;
+
bytecode_node->enabler = enabler;
/* Enforce length based on allocated size */
bytecode_node->bc.len = bytecode_len;
list_add_tail(&bytecode_node->node, &enabler->filter_bytecode_head);
- lttng_session_lazy_sync_enablers(enabler->chan->session);
+
return 0;
error_free:
return ret;
}
+int lttng_event_enabler_attach_bytecode(struct lttng_event_enabler *event_enabler,
+ struct lttng_kernel_filter_bytecode __user *bytecode)
+{
+ int ret;
+ ret = lttng_enabler_attach_bytecode(
+ lttng_event_enabler_as_enabler(event_enabler), bytecode);
+ if (ret)
+ goto error;
+
+ lttng_session_lazy_sync_event_enablers(event_enabler->chan->session);
+ return 0;
+
+error:
+ return ret;
+}
+
int lttng_event_add_callsite(struct lttng_event *event,
struct lttng_kernel_event_callsite __user *callsite)
{
switch (event->instrumentation) {
case LTTNG_KERNEL_UPROBE:
- return lttng_uprobes_add_callsite(event, callsite);
+ return lttng_uprobes_event_add_callsite(event, callsite);
default:
return -EINVAL;
}
}
-int lttng_enabler_attach_context(struct lttng_enabler *enabler,
+int lttng_event_enabler_attach_context(struct lttng_event_enabler *event_enabler,
struct lttng_kernel_context *context_param)
{
return -ENOSYS;
&enabler->filter_bytecode_head, node) {
kfree(filter_node);
}
+}
+
+static
+void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler)
+{
+ lttng_enabler_destroy(lttng_event_enabler_as_enabler(event_enabler));
/* Destroy contexts */
- lttng_destroy_context(enabler->ctx);
+ lttng_destroy_context(event_enabler->ctx);
+
+ list_del(&event_enabler->node);
+ kfree(event_enabler);
+}
+
+struct lttng_trigger_enabler *lttng_trigger_enabler_create(
+ struct lttng_trigger_group *trigger_group,
+ enum lttng_enabler_format_type format_type,
+ struct lttng_kernel_trigger *trigger_param)
+{
+ struct lttng_trigger_enabler *trigger_enabler;
+
+ trigger_enabler = kzalloc(sizeof(*trigger_enabler), GFP_KERNEL);
+ if (!trigger_enabler)
+ return NULL;
+
+ trigger_enabler->base.format_type = format_type;
+ INIT_LIST_HEAD(&trigger_enabler->base.filter_bytecode_head);
+
+ trigger_enabler->id = trigger_param->id;
+
+ memcpy(&trigger_enabler->base.event_param.name, trigger_param->name,
+ sizeof(trigger_enabler->base.event_param.name));
+ trigger_enabler->base.event_param.instrumentation = trigger_param->instrumentation;
+ trigger_enabler->base.evtype = LTTNG_TYPE_ENABLER;
+
+ trigger_enabler->base.enabled = 0;
+ trigger_enabler->group = trigger_group;
+
+ mutex_lock(&sessions_mutex);
+ list_add(&trigger_enabler->node, &trigger_enabler->group->enablers_head);
+ lttng_trigger_group_sync_enablers(trigger_enabler->group);
+
+ mutex_unlock(&sessions_mutex);
+
+ return trigger_enabler;
+}
+
+int lttng_trigger_enabler_enable(struct lttng_trigger_enabler *trigger_enabler)
+{
+ mutex_lock(&sessions_mutex);
+ lttng_trigger_enabler_as_enabler(trigger_enabler)->enabled = 1;
+ lttng_trigger_group_sync_enablers(trigger_enabler->group);
+ mutex_unlock(&sessions_mutex);
+ return 0;
+}
+
+int lttng_trigger_enabler_disable(struct lttng_trigger_enabler *trigger_enabler)
+{
+ mutex_lock(&sessions_mutex);
+ lttng_trigger_enabler_as_enabler(trigger_enabler)->enabled = 0;
+ lttng_trigger_group_sync_enablers(trigger_enabler->group);
+ mutex_unlock(&sessions_mutex);
+ return 0;
+}
+
+int lttng_trigger_enabler_attach_bytecode(struct lttng_trigger_enabler *trigger_enabler,
+ struct lttng_kernel_filter_bytecode __user *bytecode)
+{
+ int ret;
+
+ ret = lttng_enabler_attach_bytecode(
+ lttng_trigger_enabler_as_enabler(trigger_enabler), bytecode);
+ if (ret)
+ goto error;
+
+ lttng_trigger_group_sync_enablers(trigger_enabler->group);
+ return 0;
+
+error:
+ return ret;
+}
+
+int lttng_trigger_add_callsite(struct lttng_trigger *trigger,
+ struct lttng_kernel_event_callsite __user *callsite)
+{
+
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_UPROBE:
+ return lttng_uprobes_trigger_add_callsite(trigger, callsite);
+ default:
+ return -EINVAL;
+ }
+}
+
+int lttng_trigger_enabler_attach_context(struct lttng_trigger_enabler *trigger_enabler,
+ struct lttng_kernel_context *context_param)
+{
+ return -ENOSYS;
+}
- list_del(&enabler->node);
- kfree(enabler);
+static
+void lttng_trigger_enabler_destroy(struct lttng_trigger_enabler *trigger_enabler)
+{
+ if (!trigger_enabler) {
+ return;
+ }
+
+ list_del(&trigger_enabler->node);
+
+ lttng_enabler_destroy(lttng_trigger_enabler_as_enabler(trigger_enabler));
+ kfree(trigger_enabler);
}
/*
- * lttng_session_sync_enablers should be called just before starting a
+ * lttng_session_sync_event_enablers should be called just before starting a
* session.
* Should be called with sessions mutex held.
*/
static
-void lttng_session_sync_enablers(struct lttng_session *session)
+void lttng_session_sync_event_enablers(struct lttng_session *session)
{
- struct lttng_enabler *enabler;
+ struct lttng_event_enabler *event_enabler;
struct lttng_event *event;
- list_for_each_entry(enabler, &session->enablers_head, node)
- lttng_enabler_ref_events(enabler);
+ list_for_each_entry(event_enabler, &session->enablers_head, node)
+ lttng_event_enabler_ref_events(event_enabler);
/*
* For each event, if at least one of its enablers is enabled,
* and its channel and session transient states are enabled, we
* Should be called with sessions mutex held.
*/
static
-void lttng_session_lazy_sync_enablers(struct lttng_session *session)
+void lttng_session_lazy_sync_event_enablers(struct lttng_session *session)
{
/* We can skip if session is not active */
if (!session->active)
return;
- lttng_session_sync_enablers(session);
+ lttng_session_sync_event_enablers(session);
+}
+
+static
+void lttng_trigger_group_sync_enablers(struct lttng_trigger_group *trigger_group)
+{
+ struct lttng_trigger_enabler *trigger_enabler;
+ struct lttng_trigger *trigger;
+
+ list_for_each_entry(trigger_enabler, &trigger_group->enablers_head, node)
+ lttng_trigger_enabler_ref_triggers(trigger_enabler);
+
+ /*
+ * For each trigger, if at least one of its enablers is enabled,
+ * we enable the trigger, else we disable it.
+ */
+ list_for_each_entry(trigger, &trigger_group->triggers_head, list) {
+ struct lttng_enabler_ref *enabler_ref;
+ struct lttng_bytecode_runtime *runtime;
+ int enabled = 0, has_enablers_without_bytecode = 0;
+
+ switch (trigger->instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ case LTTNG_KERNEL_SYSCALL:
+ /* Enable triggers */
+ list_for_each_entry(enabler_ref,
+ &trigger->enablers_ref_head, node) {
+ if (enabler_ref->ref->enabled) {
+ enabled = 1;
+ break;
+ }
+ }
+ break;
+ default:
+ /* Not handled with sync. */
+ continue;
+ }
+
+ WRITE_ONCE(trigger->enabled, enabled);
+ /*
+ * Sync tracepoint registration with trigger enabled
+ * state.
+ */
+ if (enabled) {
+ if (!trigger->registered)
+ register_trigger(trigger);
+ } else {
+ if (trigger->registered)
+ _lttng_trigger_unregister(trigger);
+ }
+
+ /* Check if has enablers without bytecode enabled */
+ list_for_each_entry(enabler_ref,
+ &trigger->enablers_ref_head, node) {
+ if (enabler_ref->ref->enabled
+ && list_empty(&enabler_ref->ref->filter_bytecode_head)) {
+ has_enablers_without_bytecode = 1;
+ break;
+ }
+ }
+ trigger->has_enablers_without_bytecode =
+ has_enablers_without_bytecode;
+
+ /* Enable filters */
+ list_for_each_entry(runtime,
+ &trigger->bytecode_runtime_head, node)
+ lttng_filter_sync_state(runtime);
+ }
}
/*
return offset;
}
+static
+int print_escaped_ctf_string(struct lttng_session *session, const char *string)
+{
+ int ret;
+ size_t i;
+ char cur;
+
+ i = 0;
+ cur = string[i];
+ while (cur != '\0') {
+ switch (cur) {
+ case '\n':
+ ret = lttng_metadata_printf(session, "%s", "\\n");
+ break;
+ case '\\':
+ case '"':
+ ret = lttng_metadata_printf(session, "%c", '\\');
+ if (ret)
+ goto error;
+ /* We still print the current char */
+ /* Fallthrough */
+ default:
+ ret = lttng_metadata_printf(session, "%c", cur);
+ break;
+ }
+
+ if (ret)
+ goto error;
+
+ cur = string[++i];
+ }
+error:
+ return ret;
+}
+
+static
+int print_metadata_escaped_field(struct lttng_session *session, const char *field,
+ const char *field_value)
+{
+ int ret;
+
+ ret = lttng_metadata_printf(session, " %s = \"", field);
+ if (ret)
+ goto error;
+
+ ret = print_escaped_ctf_string(session, field_value);
+ if (ret)
+ goto error;
+
+ ret = lttng_metadata_printf(session, "\";\n");
+
+error:
+ return ret;
+}
+
/*
* Output metadata into this session's metadata buffers.
* Must be called with sessions_mutex held.
{
unsigned char *uuid_c = session->uuid.b;
unsigned char uuid_s[37], clock_uuid_s[BOOT_ID_LEN];
+ const char *product_uuid;
struct lttng_channel *chan;
struct lttng_event *event;
int ret = 0;
" tracer_major = %d;\n"
" tracer_minor = %d;\n"
" tracer_patchlevel = %d;\n"
- "};\n\n",
+ " trace_buffering_scheme = \"global\";\n",
current->nsproxy->uts_ns->name.nodename,
utsname()->sysname,
utsname()->release,
if (ret)
goto end;
+ ret = print_metadata_escaped_field(session, "trace_name", session->name);
+ if (ret)
+ goto end;
+ ret = print_metadata_escaped_field(session, "trace_creation_datetime",
+ session->creation_time);
+ if (ret)
+ goto end;
+
+ /* Add the product UUID to the 'env' section */
+ product_uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
+ if (product_uuid) {
+ ret = lttng_metadata_printf(session,
+ " product_uuid = \"%s\";\n",
+ product_uuid
+ );
+ if (ret)
+ goto end;
+ }
+
+ /* Close the 'env' section */
+ ret = lttng_metadata_printf(session, "};\n\n");
+ if (ret)
+ goto end;
+
ret = lttng_metadata_printf(session,
"clock {\n"
" name = \"%s\";\n",
event_cache = KMEM_CACHE(lttng_event, 0);
if (!event_cache) {
ret = -ENOMEM;
- goto error_kmem;
+ goto error_kmem_event;
+ }
+ trigger_cache = KMEM_CACHE(lttng_trigger, 0);
+ if (!trigger_cache) {
+ ret = -ENOMEM;
+ goto error_kmem_trigger;
}
ret = lttng_abi_init();
if (ret)
error_logger:
lttng_abi_exit();
error_abi:
+ kmem_cache_destroy(trigger_cache);
+error_kmem_trigger:
kmem_cache_destroy(event_cache);
-error_kmem:
+error_kmem_event:
lttng_tracepoint_exit();
error_tp:
lttng_context_exit();
list_for_each_entry_safe(session, tmpsession, &sessions, list)
lttng_session_destroy(session);
kmem_cache_destroy(event_cache);
+ kmem_cache_destroy(trigger_cache);
lttng_tracepoint_exit();
lttng_context_exit();
printk(KERN_NOTICE "LTTng: Unloaded modules v%s.%s.%s%s (%s)%s%s\n",