-/*
+/* SPDX-License-Identifier: (GPL-2.0 OR LGPL-2.1)
+ *
* probes/lttng-uprobes.c
*
* LTTng uprobes integration module.
* Copyright (C) 2013 Yannick Brosseau <yannick.brosseau@gmail.com>
* Copyright (C) 2009-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; only
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/fdtable.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/namei.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <lttng-events.h>
#include <lttng-tracer.h>
#include <wrapper/irqflags.h>
static
int lttng_uprobes_handler_pre(struct uprobe_consumer *uc, struct pt_regs *regs)
{
- struct lttng_event *event =
- container_of(uc, struct lttng_event, u.uprobe.up_consumer);
+ struct lttng_uprobe_handler *uprobe_handler =
+ container_of(uc, struct lttng_uprobe_handler, up_consumer);
+ struct lttng_event *event = uprobe_handler->event;
struct lttng_probe_ctx lttng_probe_ctx = {
.event = event,
.interruptible = !lttng_regs_irqs_disabled(regs),
struct {
unsigned long ip;
- } payload;
+ } payload;
- if (unlikely(!ACCESS_ONCE(chan->session->active)))
+ if (unlikely(!READ_ONCE(chan->session->active)))
return 0;
- if (unlikely(!ACCESS_ONCE(chan->enabled)))
+ if (unlikely(!READ_ONCE(chan->enabled)))
return 0;
- if (unlikely(!ACCESS_ONCE(event->enabled)))
+ if (unlikely(!READ_ONCE(event->enabled)))
return 0;
lib_ring_buffer_ctx_init(&ctx, chan->chan, <tng_probe_ctx,
return 0;
/* Event payload. */
- payload.ip = regs->ip;
+ payload.ip = (unsigned long)instruction_pointer(regs);
+
lib_ring_buffer_align_ctx(&ctx, lttng_alignof(payload));
chan->ops->event_write(&ctx, &payload, sizeof(payload));
chan->ops->event_commit(&ctx);
return ret;
}
-int lttng_uprobes_register(const char *name,
- const char *path_name,
- uint64_t offset,
- struct lttng_event *event)
+/*
+ * 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
+ * iput().
+ */
+static struct inode *get_inode_from_fd(int fd)
{
- int ret;
-
- /* Shoudl we fail if the path is empty, it should be checked before */
- if (path_name[0] == '\0')
- path_name = NULL;
-
- ret = lttng_create_uprobe_event(name, event);
- if (ret)
+ struct file *file;
+ struct inode *inode;
+
+ rcu_read_lock();
+ /*
+ * Returns the file backing the given fd. Needs to be done inside an RCU
+ * critical section.
+ */
+ file = fcheck(fd);
+ if (file == NULL) {
+ printk(KERN_WARNING "Cannot access file backing the fd(%d)\n", fd);
+ inode = NULL;
goto error;
+ }
- memset(&event->u.uprobe.up_consumer, 0,
- sizeof(event->u.uprobe.up_consumer));
+ /* Grab a reference on the inode. */
+ inode = igrab(file->f_path.dentry->d_inode);
+ if (inode == NULL)
+ printk(KERN_WARNING "Cannot grab a reference on the inode.\n");
+error:
+ rcu_read_unlock();
+ return inode;
+}
- event->u.uprobe.up_consumer.handler = lttng_uprobes_handler_pre;
- if (path_name) {
- struct path path;
- ret = kern_path(path_name, LOOKUP_FOLLOW, &path);
- if (ret)
- goto path_error;
+int lttng_uprobes_add_callsite(struct lttng_event *event,
+ struct lttng_kernel_event_callsite __user *callsite)
+{
+ int ret = 0;
+ struct lttng_uprobe_handler *uprobe_handler;
- event->u.uprobe.inode = igrab(path.dentry->d_inode);
+ if (!event) {
+ ret = -EINVAL;
+ goto end;
}
- event->u.uprobe.offset = offset;
- /* Ensure the memory we just allocated don't trigger page faults. */
+ uprobe_handler = kzalloc(sizeof(struct lttng_uprobe_handler), GFP_KERNEL);
+ if (!uprobe_handler) {
+ printk(KERN_WARNING "Error allocating uprobe_uprobe_handlers");
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ /* Ensure the memory we just allocated don't trigger page faults. */
wrapper_vmalloc_sync_all();
- printk(KERN_WARNING "Registering probe on inode %lu and offset %llu\n", event->u.uprobe.inode->i_ino, event->u.uprobe.offset);
+
+ uprobe_handler->event = event;
+ uprobe_handler->up_consumer.handler = lttng_uprobes_handler_pre;
+
+ ret = copy_from_user(&uprobe_handler->offset, &callsite->u.uprobe.offset, sizeof(uint64_t));
+ if (ret) {
+ goto register_error;
+ }
+
ret = wrapper_uprobe_register(event->u.uprobe.inode,
- event->u.uprobe.offset,
- &event->u.uprobe.up_consumer);
+ uprobe_handler->offset, &uprobe_handler->up_consumer);
if (ret) {
printk(KERN_WARNING "Error registering probe on inode %lu "
- "and offset %llu\n", event->u.uprobe.inode->i_ino,
- event->u.uprobe.offset);
+ "and offset 0x%llx\n", event->u.uprobe.inode->i_ino,
+ uprobe_handler->offset);
+ ret = -1;
goto register_error;
}
- return 0;
+
+ list_add(&uprobe_handler->node, &event->u.uprobe.head);
+
+ return ret;
register_error:
- iput(event->u.uprobe.inode);
-path_error:
+ kfree(uprobe_handler);
+end:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_add_callsite);
+
+int lttng_uprobes_register(const char *name, int fd, struct lttng_event *event)
+{
+ int ret = 0;
+ struct inode *inode;
+
+ ret = lttng_create_uprobe_event(name, event);
+ if (ret)
+ goto error;
+
+ inode = get_inode_from_fd(fd);
+ if (!inode) {
+ printk(KERN_WARNING "Cannot get inode from fd\n");
+ ret = -EBADF;
+ goto inode_error;
+ }
+ event->u.uprobe.inode = inode;
+ INIT_LIST_HEAD(&event->u.uprobe.head);
+
+ return 0;
+
+inode_error:
kfree(event->desc->name);
kfree(event->desc);
error:
void lttng_uprobes_unregister(struct lttng_event *event)
{
- wrapper_uprobe_unregister(event->u.uprobe.inode,
- event->u.uprobe.offset,
- &event->u.uprobe.up_consumer);
+ 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_del(&iter->node);
+ kfree(iter);
+ }
}
EXPORT_SYMBOL_GPL(lttng_uprobes_unregister);