struct lttng_uprobe_handler {
union {
struct lttng_event *event;
+ struct lttng_event_notifier *event_notifier;
} u;
loff_t offset;
struct uprobe_consumer up_consumer;
enum lttng_kernel_instrumentation instrumentation;
union {
struct lttng_kprobe kprobe;
+ struct lttng_uprobe uprobe;
} u;
/* Backward references: list of lttng_enabler_ref (ref to enablers) */
int lttng_event_add_callsite(struct lttng_event *event,
struct lttng_kernel_event_callsite *callsite);
+int lttng_event_notifier_add_callsite(struct lttng_event_notifier *event_notifier,
+ struct lttng_kernel_event_callsite *callsite);
+
#ifdef CONFIG_UPROBES
int lttng_uprobes_register_event(const char *name,
int fd, struct lttng_event *event);
struct lttng_kernel_event_callsite *callsite);
void lttng_uprobes_unregister_event(struct lttng_event *event);
void lttng_uprobes_destroy_event_private(struct lttng_event *event);
+int lttng_uprobes_register_event_notifier(const char *name,
+ int fd, struct lttng_event_notifier *event_notifier);
+int lttng_uprobes_event_notifier_add_callsite(struct lttng_event_notifier *event_notifier,
+ struct lttng_kernel_event_callsite *callsite);
+void lttng_uprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier);
+void lttng_uprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier);
#else
static inline
int lttng_uprobes_register_event(const char *name,
void lttng_uprobes_destroy_event_private(struct lttng_event *event)
{
}
+
+static inline
+int lttng_uprobes_register_event_notifier(const char *name,
+ int fd, struct lttng_event_notifier *event_notifier)
+{
+ return -ENOSYS;
+}
+
+static inline
+int lttng_uprobes_event_notifier_add_callsite(struct lttng_event_notifier *event_notifier,
+ struct lttng_kernel_event_callsite *callsite)
+{
+ return -ENOSYS;
+}
+
+static inline
+void lttng_uprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+}
+
+static inline
+void lttng_uprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier)
+{
+}
#endif
#ifdef CONFIG_KRETPROBES
WARN_ON_ONCE(1);
return -ENOSYS;
}
+ case LTTNG_KERNEL_ADD_CALLSITE:
+ switch (*evtype) {
+ case LTTNG_TYPE_EVENT:
+ event_notifier = file->private_data;
+ return lttng_event_notifier_add_callsite(event_notifier,
+ (struct lttng_kernel_event_callsite __user *) arg);
+ case LTTNG_TYPE_ENABLER:
+ return -EINVAL;
+ default:
+ WARN_ON_ONCE(1);
+ return -ENOSYS;
+ }
default:
return -ENOIOCTLCMD;
}
switch (event_notifier_param->event.instrumentation) {
case LTTNG_KERNEL_TRACEPOINT:
+ case LTTNG_KERNEL_UPROBE:
break;
case LTTNG_KERNEL_KPROBE:
event_notifier_param->event.u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
break;
case LTTNG_KERNEL_KRETPROBE:
- /* Placing a trigger on kretprobe is not supported. */
- case LTTNG_KERNEL_UPROBE:
+ /* Placing an event notifier on kretprobe is not supported. */
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
case LTTNG_KERNEL_SYSCALL:
ret = -EINVAL;
break;
case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
WRITE_ONCE(event_notifier->enabled, 1);
break;
case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_FUNCTION:
- case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_NOOP:
case LTTNG_KERNEL_KRETPROBE:
default:
ret = -EINVAL;
break;
case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
WRITE_ONCE(event_notifier->enabled, 0);
break;
case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_FUNCTION:
- case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_NOOP:
case LTTNG_KERNEL_KRETPROBE:
default:
event_name = event_desc->name;
break;
case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
event_name = event_notifier_param->event.name;
break;
- case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
WARN_ON_ONCE(!ret);
break;
case LTTNG_KERNEL_UPROBE:
+ /*
+ * Needs to be explicitly enabled after creation, since
+ * we may want to apply filters.
+ */
+ event_notifier->enabled = 0;
+ event_notifier->registered = 1;
+
+ /*
+ * Populate lttng_event_notifier structure before
+ * event_notifier registration.
+ */
+ smp_wmb();
+
+ ret = lttng_uprobes_register_event_notifier(
+ event_notifier_param->event.name,
+ event_notifier_param->event.u.uprobe.fd,
+ event_notifier);
+ if (ret)
+ goto register_error;
+ ret = try_module_get(event_notifier->desc->owner);
+ WARN_ON_ONCE(!ret);
+ break;
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
event_notifier);
break;
case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
ret = 0;
break;
case LTTNG_KERNEL_SYSCALL:
- case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
lttng_kprobes_unregister_event_notifier(event_notifier);
ret = 0;
break;
+ case LTTNG_KERNEL_UPROBE:
+ lttng_uprobes_unregister_event_notifier(event_notifier);
+ ret = 0;
+ break;
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_NOOP:
- case LTTNG_KERNEL_UPROBE:
default:
WARN_ON_ONCE(1);
}
module_put(event_notifier->desc->owner);
lttng_kprobes_destroy_event_notifier_private(event_notifier);
break;
+ case LTTNG_KERNEL_UPROBE:
+ module_put(event_notifier->desc->owner);
+ lttng_uprobes_destroy_event_notifier_private(event_notifier);
+ break;
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
case LTTNG_KERNEL_SYSCALL:
- case LTTNG_KERNEL_UPROBE:
default:
WARN_ON_ONCE(1);
}
return ret;
}
+int lttng_event_notifier_add_callsite(struct lttng_event_notifier *event_notifier,
+ struct lttng_kernel_event_callsite __user *callsite)
+{
+
+ switch (event_notifier->instrumentation) {
+ case LTTNG_KERNEL_UPROBE:
+ return lttng_uprobes_event_notifier_add_callsite(event_notifier,
+ callsite);
+ default:
+ return -EINVAL;
+ }
+}
+
int lttng_event_notifier_enabler_attach_context(
struct lttng_event_notifier_enabler *event_notifier_enabler,
struct lttng_kernel_context *context_param)
return 0;
}
+static
+int lttng_uprobes_event_notifier_handler_pre(struct uprobe_consumer *uc, struct pt_regs *regs)
+{
+ struct lttng_uprobe_handler *uprobe_handler =
+ container_of(uc, struct lttng_uprobe_handler, up_consumer);
+ struct lttng_event_notifier *event_notifier = uprobe_handler->u.event_notifier;
+
+ if (unlikely(!READ_ONCE(event_notifier->enabled)))
+ return 0;
+
+ event_notifier->send_notification(event_notifier);
+ return 0;
+}
+
/*
* Create event description.
*/
return ret;
}
+/*
+ * Create event_notifier description.
+ */
+static
+int lttng_create_uprobe_event_notifier(const char *name, struct lttng_event_notifier *event_notifier)
+{
+ struct lttng_event_desc *desc;
+ int ret;
+
+ desc = kzalloc(sizeof(*event_notifier->desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+ desc->name = kstrdup(name, GFP_KERNEL);
+ if (!desc->name) {
+ ret = -ENOMEM;
+ goto error_str;
+ }
+
+ desc->nr_fields = 0;
+
+ desc->owner = THIS_MODULE;
+ event_notifier->desc = desc;
+
+ return 0;
+
+error_str:
+ kfree(desc);
+ return ret;
+}
+
/*
* Returns the inode struct from the current task and an fd. The inode is
* grabbed by this function and must be put once we are done with it using
goto end;
}
- /* Ensure the memory we just allocated don't trigger page faults. */
+ /* Ensure the memory we just allocated don't event_notifier page faults. */
wrapper_vmalloc_sync_mappings();
uprobe_handler->u.event = priv_data;
}
EXPORT_SYMBOL_GPL(lttng_uprobes_event_add_callsite);
+int lttng_uprobes_event_notifier_add_callsite(struct lttng_event_notifier *event_notifier,
+ struct lttng_kernel_event_callsite __user *callsite)
+{
+ return lttng_uprobes_add_callsite(&event_notifier->u.uprobe, callsite,
+ lttng_uprobes_event_notifier_handler_pre, event_notifier);
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_event_notifier_add_callsite);
+
static
int lttng_uprobes_register(struct lttng_uprobe *uprobe, int fd)
{
}
EXPORT_SYMBOL_GPL(lttng_uprobes_register_event);
-void lttng_uprobes_unregister_event(struct lttng_event *event)
+int lttng_uprobes_register_event_notifier(const char *name, int fd,
+ struct lttng_event_notifier *event_notifier)
+{
+ int ret = 0;
+
+ ret = lttng_create_uprobe_event_notifier(name, event_notifier);
+ if (ret)
+ goto error;
+
+ ret = lttng_uprobes_register(&event_notifier->u.uprobe, fd);
+ if (ret)
+ goto register_error;
+
+ return 0;
+
+register_error:
+ kfree(event_notifier->desc->name);
+ kfree(event_notifier->desc);
+error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_register_event_notifier);
+
+static
+void lttng_uprobes_unregister(struct inode *inode, struct list_head *head)
{
struct lttng_uprobe_handler *iter, *tmp;
* Iterate over the list of handler, remove each handler from the list
* and free the struct.
*/
- list_for_each_entry_safe(iter, tmp, &event->u.uprobe.head, node) {
- wrapper_uprobe_unregister(event->u.uprobe.inode, iter->offset,
- &iter->up_consumer);
+ list_for_each_entry_safe(iter, tmp, head, node) {
+ wrapper_uprobe_unregister(inode, iter->offset, &iter->up_consumer);
list_del(&iter->node);
kfree(iter);
}
+
+}
+
+void lttng_uprobes_unregister_event(struct lttng_event *event)
+{
+ lttng_uprobes_unregister(event->u.uprobe.inode, &event->u.uprobe.head);
}
EXPORT_SYMBOL_GPL(lttng_uprobes_unregister_event);
+void lttng_uprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+ lttng_uprobes_unregister(event_notifier->u.uprobe.inode, &event_notifier->u.uprobe.head);
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_unregister_event_notifier);
+
void lttng_uprobes_destroy_event_private(struct lttng_event *event)
{
iput(event->u.uprobe.inode);
}
EXPORT_SYMBOL_GPL(lttng_uprobes_destroy_event_private);
+void lttng_uprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier)
+{
+ iput(event_notifier->u.uprobe.inode);
+ kfree(event_notifier->desc->name);
+ kfree(event_notifier->desc);
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_destroy_event_notifier_private);
+
MODULE_LICENSE("GPL and additional rights");
MODULE_AUTHOR("Yannick Brosseau");
MODULE_DESCRIPTION("Linux Trace Toolkit Uprobes Support");