Add uprobes support
authorYannick Brosseau <yannick.brosseau@gmail.com>
Mon, 12 Sep 2016 19:00:12 +0000 (15:00 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 27 Aug 2018 21:37:59 +0000 (17:37 -0400)
The added support is basic. It create an event with no data associated
to the file path + offset specified.

Signed-off-by: Yannick Brosseau <yannick.brosseau@gmail.com>
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Makefile
lttng-abi.c
lttng-abi.h
lttng-events.c
lttng-events.h
probes/lttng-uprobes.c [new file with mode: 0644]
wrapper/uprobes.h [new file with mode: 0644]

index ae2b19538c1a52f1ec942656f86077cb76e95aa4..463b3141e7911675232c6fbdf1aaf80d0f0aee26 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -59,7 +59,8 @@ ifneq ($(KERNELRELEASE),)
                        lttng-filter-specialize.o \
                        lttng-filter-validator.o \
                        probes/lttng-probe-user.o \
-                       lttng-tp-mempool.o
+                       lttng-tp-mempool.o \
+                       probes/lttng-uprobes.o
 
   ifneq ($(CONFIG_HAVE_SYSCALL_TRACEPOINTS),)
     lttng-tracer-objs += lttng-syscalls.o
index c535cb95d4814a0f0e50debc496dbec66b767ff3..32fa0bfa13011c218c1185caa373fa56efeb9d10 100644 (file)
@@ -1306,7 +1306,6 @@ old_ctx_end:
        default:
                return -ENOIOCTLCMD;
        }
-
 }
 
 /**
index f0fe8efb8d0fd8f6a7d3a4dbccdf5ba9d79f190c..24877dc863c6513c93221631239e35bffab6b6e9 100644 (file)
@@ -28,6 +28,7 @@ enum lttng_kernel_instrumentation {
        LTTNG_KERNEL_KRETPROBE  = 3,
        LTTNG_KERNEL_NOOP       = 4,    /* not hooked */
        LTTNG_KERNEL_SYSCALL    = 5,
+       LTTNG_KERNEL_UPROBE     = 6,
 };
 
 /*
@@ -73,6 +74,11 @@ struct lttng_kernel_function_tracer {
        char symbol_name[LTTNG_KERNEL_SYM_NAME_LEN];
 } __attribute__((packed));
 
+struct lttng_kernel_uprobe {
+       char path[LTTNG_KERNEL_SYM_NAME_LEN];
+       uint64_t offset;
+} __attribute__((packed));
+
 /*
  * For syscall tracing, name = "*" means "enable all".
  */
@@ -88,6 +94,7 @@ struct lttng_kernel_event {
                struct lttng_kernel_kretprobe kretprobe;
                struct lttng_kernel_kprobe kprobe;
                struct lttng_kernel_function_tracer ftrace;
+               struct lttng_kernel_uprobe uprobe;
                char padding[LTTNG_KERNEL_EVENT_PADDING2];
        } u;
 } __attribute__((packed));
index 48e1760174569e449a330c766a7c3fca3ecba206..636a1891be0c188a9619d5a31ee103a4f57e2c86 100644 (file)
@@ -395,6 +395,7 @@ int lttng_event_enable(struct lttng_event *event)
                break;
        case LTTNG_KERNEL_KPROBE:
        case LTTNG_KERNEL_FUNCTION:
+       case LTTNG_KERNEL_UPROBE:
        case LTTNG_KERNEL_NOOP:
                WRITE_ONCE(event->enabled, 1);
                break;
@@ -430,6 +431,7 @@ int lttng_event_disable(struct lttng_event *event)
                break;
        case LTTNG_KERNEL_KPROBE:
        case LTTNG_KERNEL_FUNCTION:
+       case LTTNG_KERNEL_UPROBE:
        case LTTNG_KERNEL_NOOP:
                WRITE_ONCE(event->enabled, 0);
                break;
@@ -576,6 +578,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan,
                event_name = event_desc->name;
                break;
        case LTTNG_KERNEL_KPROBE:
+       case LTTNG_KERNEL_UPROBE:
        case LTTNG_KERNEL_KRETPROBE:
        case LTTNG_KERNEL_FUNCTION:
        case LTTNG_KERNEL_NOOP:
@@ -739,6 +742,17 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan,
                        goto register_error;
                }
                break;
+       case LTTNG_KERNEL_UPROBE:
+
+               ret = lttng_uprobes_register(event_param->name,
+                               event_param->u.uprobe.path,
+                               event_param->u.uprobe.offset,
+                               event);
+               if (ret)
+                       goto register_error;
+               ret = try_module_get(event->desc->owner);
+               WARN_ON_ONCE(!ret);
+               break;
        default:
                WARN_ON_ONCE(1);
                ret = -EINVAL;
@@ -850,6 +864,10 @@ int _lttng_event_unregister(struct lttng_event *event)
        case LTTNG_KERNEL_NOOP:
                ret = 0;
                break;
+       case LTTNG_KERNEL_UPROBE:
+               lttng_uprobes_unregister(event);
+               ret = 0;
+               break;
        default:
                WARN_ON_ONCE(1);
        }
@@ -883,6 +901,10 @@ void _lttng_event_destroy(struct lttng_event *event)
        case LTTNG_KERNEL_NOOP:
        case LTTNG_KERNEL_SYSCALL:
                break;
+       case LTTNG_KERNEL_UPROBE:
+               module_put(event->desc->owner);
+               lttng_uprobes_destroy_private(event);
+               break;
        default:
                WARN_ON_ONCE(1);
        }
index 8b0803e40b4c22ebd6129c790596393af4629808..4093bd8abd6a39567bd33213cb33746218c9f3f0 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kref.h>
 #include <lttng-cpuhotplug.h>
 #include <wrapper/uuid.h>
+#include <wrapper/uprobes.h>
 #include <lttng-tracer.h>
 #include <lttng-abi.h>
 #include <lttng-abi-old.h>
@@ -309,6 +310,11 @@ struct lttng_event {
                struct {
                        char *symbol_name;
                } ftrace;
+               struct {
+                       struct uprobe_consumer up_consumer;
+                       struct inode *inode;
+                       loff_t offset;
+               } uprobe;
        } u;
        struct list_head list;          /* Event list in session */
        unsigned int metadata_dumped:1;
@@ -771,6 +777,34 @@ void lttng_kprobes_destroy_private(struct lttng_event *event)
 }
 #endif
 
+#ifdef CONFIG_UPROBES
+int lttng_uprobes_register(const char *name,
+               const char *path,
+               uint64_t offset,
+               struct lttng_event *event);
+void lttng_uprobes_unregister(struct lttng_event *event);
+void lttng_uprobes_destroy_private(struct lttng_event *event);
+#else
+static inline
+int lttng_uprobes_register(const char *name,
+               const char *path,
+               uint64_t offset,
+               struct lttng_event *event)
+{
+       return -ENOSYS;
+}
+
+static inline
+void lttng_uprobes_unregister(struct lttng_event *event)
+{
+}
+
+static inline
+void lttng_uprobes_destroy_private(struct lttng_event *event)
+{
+}
+#endif
+
 #ifdef CONFIG_KRETPROBES
 int lttng_kretprobes_register(const char *name,
                const char *symbol_name,
diff --git a/probes/lttng-uprobes.c b/probes/lttng-uprobes.c
new file mode 100644 (file)
index 0000000..0906992
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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/module.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <lttng-events.h>
+#include <lttng-tracer.h>
+#include <wrapper/irqflags.h>
+#include <wrapper/ringbuffer/frontend_types.h>
+#include <wrapper/uprobes.h>
+#include <wrapper/vmalloc.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_probe_ctx lttng_probe_ctx = {
+               .event = event,
+               .interruptible = !lttng_regs_irqs_disabled(regs),
+       };
+       struct lttng_channel *chan = event->chan;
+       struct lib_ring_buffer_ctx ctx;
+       int ret;
+
+       struct {
+               unsigned long ip;
+        } payload;
+
+       if (unlikely(!ACCESS_ONCE(chan->session->active)))
+               return 0;
+       if (unlikely(!ACCESS_ONCE(chan->enabled)))
+               return 0;
+       if (unlikely(!ACCESS_ONCE(event->enabled)))
+               return 0;
+
+       lib_ring_buffer_ctx_init(&ctx, chan->chan, &lttng_probe_ctx,
+               sizeof(payload), lttng_alignof(payload), -1);
+
+       ret = chan->ops->event_reserve(&ctx, event->id);
+       if (ret < 0)
+               return 0;
+
+       /* Event payload. */
+       payload.ip = regs->ip;
+       lib_ring_buffer_align_ctx(&ctx, lttng_alignof(payload));
+       chan->ops->event_write(&ctx, &payload, sizeof(payload));
+       chan->ops->event_commit(&ctx);
+       return 0;
+}
+
+/*
+ * Create event description.
+ */
+static
+int lttng_create_uprobe_event(const char *name, struct lttng_event *event)
+{
+       struct lttng_event_desc *desc;
+       struct lttng_event_field *fields;
+       int ret;
+
+       desc = kzalloc(sizeof(*event->desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+       desc->name = kstrdup(name, GFP_KERNEL);
+       if (!desc->name) {
+               ret = -ENOMEM;
+               goto error_str;
+       }
+
+       desc->nr_fields = 1;
+       desc->fields = fields =
+               kzalloc(1 * sizeof(struct lttng_event_field), GFP_KERNEL);
+
+       if (!desc->fields) {
+               ret = -ENOMEM;
+               goto error_fields;
+       }
+       fields[0].name = "ip";
+       fields[0].type.atype = atype_integer;
+       fields[0].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT;
+       fields[0].type.u.basic.integer.alignment = lttng_alignof(unsigned long) * CHAR_BIT;
+       fields[0].type.u.basic.integer.signedness = lttng_is_signed_type(unsigned long);
+       fields[0].type.u.basic.integer.reverse_byte_order = 0;
+       fields[0].type.u.basic.integer.base = 16;
+       fields[0].type.u.basic.integer.encoding = lttng_encode_none;
+
+       desc->owner = THIS_MODULE;
+       event->desc = desc;
+
+       return 0;
+
+error_fields:
+       kfree(desc->name);
+error_str:
+       kfree(desc);
+       return ret;
+}
+
+int lttng_uprobes_register(const char *name,
+                          const char *path_name,
+                          uint64_t offset,
+                          struct lttng_event *event)
+{
+       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)
+               goto error;
+
+       memset(&event->u.uprobe.up_consumer, 0,
+              sizeof(event->u.uprobe.up_consumer));
+
+       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;
+
+               event->u.uprobe.inode = igrab(path.dentry->d_inode);
+       }
+       event->u.uprobe.offset = offset;
+
+        /* 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);
+       ret = wrapper_uprobe_register(event->u.uprobe.inode,
+                       event->u.uprobe.offset,
+                       &event->u.uprobe.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);
+               goto register_error;
+       }
+       return 0;
+
+register_error:
+       iput(event->u.uprobe.inode);
+path_error:
+       kfree(event->desc->name);
+       kfree(event->desc);
+error:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_register);
+
+void lttng_uprobes_unregister(struct lttng_event *event)
+{
+       wrapper_uprobe_unregister(event->u.uprobe.inode,
+                       event->u.uprobe.offset,
+                       &event->u.uprobe.up_consumer);
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_unregister);
+
+void lttng_uprobes_destroy_private(struct lttng_event *event)
+{
+       iput(event->u.uprobe.inode);
+       kfree(event->desc->name);
+       kfree(event->desc);
+}
+EXPORT_SYMBOL_GPL(lttng_uprobes_destroy_private);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Yannick Brosseau");
+MODULE_DESCRIPTION("Linux Trace Toolkit Uprobes Support");
diff --git a/wrapper/uprobes.h b/wrapper/uprobes.h
new file mode 100644 (file)
index 0000000..e38a541
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef _LTTNG_WRAPPER_UPROBES_H
+#define _LTTNG_WRAPPER_UPROBES_H
+
+/*
+ * wrapper/uprobes.h
+ *
+ * wrapper around uprobes. Using KALLSYMS to get its address when
+ * available, else we need to have a kernel that exports this function to GPL
+ * modules.
+ *
+ * Copyright (C) 2013 Yannick Brosseau <yannick.brosseau@gmail.com>
+ * Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@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/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
+#include <linux/uprobes.h>
+
+/* Use kallsym lookup for version before 3.9. */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0))
+
+static inline
+int wrapper_uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
+{
+       return uprobe_register(inode, offset, uc);
+}
+
+static inline
+void wrapper_uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
+{
+       uprobe_unregister(inode, offset, uc);
+}
+
+#else /* Version < 3.9, use kallsym lookup. */
+#include "kallsyms.h"
+
+static inline
+int wrapper_uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
+{
+       int (*uprobe_register_sym)(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
+
+       uprobe_register_sym = (void *) kallsyms_lookup_funcptr("uprobe_register");
+
+       if (uprobe_register_sym) {
+               return uprobe_register_sym(inode, offset, uc);
+       } else {
+                printk(KERN_WARNING "LTTng: uprobe_register symbol lookup failed.\n");
+                return -EINVAL;
+       }
+}
+
+static inline
+void wrapper_uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
+{
+        int (*uprobe_unregister_sym)(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
+
+        uprobe_unregister_sym = (void *) kallsyms_lookup_funcptr("uprobe_unregister");
+
+        if (uprobe_unregister_sym) {
+                uprobe_unregister_sym(inode, offset, uc);
+        } else {
+                printk(KERN_WARNING "LTTng: uprobe_unregister symbol lookup failed.\n");
+                WARN_ON(1);
+        }
+}
+#endif
+#else
+/* Version <  3.5, before uprobe was added. */
+struct uprobe_consumer {};
+
+#endif
+#endif
This page took 0.03227 seconds and 4 git commands to generate.