[lttng-dev] [lttng-modules PATCH] Add uprobes support
Mathieu Desnoyers
mathieu.desnoyers at efficios.com
Tue Jan 15 11:19:59 EST 2013
* Yannick Brosseau (yannick.brosseau at gmail.com) wrote:
> The added support is basic. It create an event with no data associated
> to the file path + offset specified.
> As per the structures currently used we cannot pass a file path bigger
> than 256 chars.
Using a pointer to a user-space string will allow you to overcome this
limitation. e.g. passing through the kernel ABI:
char name[LTTNG_KERNEL_SYM_NAME_LEN]
char __user *path;
uint64_t offset;
>
> Signed-off-by: Yannick Brosseau <yannick.brosseau at gmail.com>
> ---
> README | 5 +-
> lttng-abi.c | 3 +
> lttng-abi.h | 7 +++
> lttng-events.c | 18 ++++++
> lttng-events.h | 38 ++++++++++++
> probes/Makefile | 5 ++
> probes/lttng-uprobes.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 231 insertions(+), 1 deletion(-)
> create mode 100644 probes/lttng-uprobes.c
>
> diff --git a/README b/README
> index 1bcd5b2..7afdaa3 100644
> --- a/README
> +++ b/README
> @@ -9,7 +9,7 @@ need for additional patches. Other features:
> - Produces CTF (Common Trace Format) natively,
> (http://www.efficios.com/ctf)
> - Tracepoints, Function tracer, CPU Performance Monitoring Unit (PMU)
> - counters, kprobes, and kretprobes support,
> + counters, kprobes, kretprobes and uprobes support,
> - Integrated interface for both kernel and userspace tracing,
> - Have the ability to attach "context" information to events in the
> trace (e.g. any PMU counter, pid, ppid, tid, comm name, etc).
> @@ -86,6 +86,9 @@ CONFIG_KPROBES:
> CONFIG_KRETPROBES:
> Dynamic function entry/return probe.
> lttng enable-event -k --function ...
> +CONFIG_UPROBES:
> + Dynamic userspace probe.
> + lttng enable-event -k --uprobe ...
It would be great to implement this feature with integrated lookup of
file/line number (using dwarf). I think perf already has some code that
does it. Adding Masami in the loop would be very relevant. In terms of
usability it will make all different in the world. Of course, this won't
change the ABI nor lttng-modules implementation, only the lttng-tools
side.
>
>
> * Note about Perf PMU counters support
> diff --git a/lttng-abi.c b/lttng-abi.c
> index 25a350a..fe428b2 100644
> --- a/lttng-abi.c
> +++ b/lttng-abi.c
> @@ -610,6 +610,9 @@ int lttng_abi_create_event(struct file *channel_file,
> case LTTNG_KERNEL_FUNCTION:
> event_param->u.ftrace.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
> break;
> + case LTTNG_KERNEL_UPROBE:
> + event_param->u.uprobe.path[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
> + break;
> default:
> break;
> }
> diff --git a/lttng-abi.h b/lttng-abi.h
> index 8d3ecdd..5abdb39 100644
> --- a/lttng-abi.h
> +++ b/lttng-abi.h
> @@ -34,6 +34,7 @@ enum lttng_kernel_instrumentation {
> LTTNG_KERNEL_KRETPROBE = 3,
> LTTNG_KERNEL_NOOP = 4, /* not hooked */
> LTTNG_KERNEL_SYSCALL = 5,
> + LTTNG_KERNEL_UPROBE = 6,
> };
>
> /*
> @@ -79,6 +80,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 = '\0' means "enable all".
> */
> @@ -94,6 +100,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));
> diff --git a/lttng-events.c b/lttng-events.c
> index 4f30904..38c901f 100644
> --- a/lttng-events.c
> +++ b/lttng-events.c
> @@ -392,6 +392,16 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan,
> if (!event->desc)
> 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);
> }
> @@ -443,6 +453,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);
> }
> @@ -473,6 +487,10 @@ void _lttng_event_destroy(struct lttng_event *event)
> break;
> case LTTNG_KERNEL_NOOP:
> break;
> + case LTTNG_KERNEL_UPROBE:
> + module_put(event->desc->owner);
> + lttng_uprobes_destroy_private(event);
> + break;
> default:
> WARN_ON_ONCE(1);
> }
> diff --git a/lttng-events.h b/lttng-events.h
> index 09d5618..e630db0 100644
> --- a/lttng-events.h
> +++ b/lttng-events.h
> @@ -26,6 +26,9 @@
> #include <linux/version.h>
> #include <linux/list.h>
> #include <linux/kprobes.h>
> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
> +#include <linux/uprobes.h>
> +#endif
please just include a wrapper/uprobes.h , so we move the ifdef to
wrapper/.
> #include "wrapper/uuid.h"
> #include "lttng-abi.h"
> #include "lttng-abi-old.h"
> @@ -203,6 +206,13 @@ struct lttng_event {
> struct {
> char *symbol_name;
> } ftrace;
> + struct {
> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
> + struct uprobe_consumer up_consumer;
> +#endif
an ifdef in a wrapper that defines struct uprobe_consumer locally if
necessary would be better. We need to shove all this compatibility code
under wrappers/ whenever possible.
> + struct inode *inode;
> + loff_t offset;
> + } uprobe;
> } u;
> struct list_head list; /* Event list */
> unsigned int metadata_dumped:1;
> @@ -406,6 +416,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/Makefile b/probes/Makefile
> index 088cd5f..de0f1a7 100644
> --- a/probes/Makefile
> +++ b/probes/Makefile
> @@ -208,6 +208,11 @@ ifneq ($(CONFIG_DYNAMIC_FTRACE),)
> obj-m += lttng-ftrace.o
> endif
>
> +ifneq ($(CONFIG_UPROBES),)
> +obj-m += lttng-uprobes.o
> +endif
> +
> +
Extra whiteline to remove.
> endif
>
> else
> diff --git a/probes/lttng-uprobes.c b/probes/lttng-uprobes.c
> new file mode 100644
> index 0000000..1d68273
> --- /dev/null
> +++ b/probes/lttng-uprobes.c
> @@ -0,0 +1,156 @@
> +/*
> + * probes/lttng-uprobes.c
> + *
> + * LTTng uprobes integration module.
> + *
> + * Copyright (C) 2013 Yannick Brosseau <yannick.brosseau at gmail.com>
> + * Copyright (C) 2009-2012 Mathieu Desnoyers <mathieu.desnoyers at 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/module.h>
> +#include <linux/uprobes.h>
> +#include <linux/slab.h>
> +#include <linux/namei.h>
> +#include <linux/fs.h>
> +#include "../lttng-events.h"
> +#include "../wrapper/ringbuffer/frontend_types.h"
> +#include "../wrapper/vmalloc.h"
> +#include "../lttng-tracer.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_channel *chan = event->chan;
> + struct lib_ring_buffer_ctx ctx;
> + int ret;
> +
> + 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, event, 0,
> + 0, -1);
> + ret = chan->ops->event_reserve(&ctx, event->id);
> + if (ret < 0)
> + return 0;
> + lib_ring_buffer_align_ctx(&ctx, 0);
> + chan->ops->event_write(&ctx, 0, 0);
> + 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;
> + 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->owner = THIS_MODULE;
> + event->desc = desc;
> +
> + return 0;
> +
> +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;
> +
> + 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;
> +
> + /* TODO Is this also true for uprobes?
The memory we just zmalloc'd could theoretically trigger a minor page
fault (if the memory segment is ever very very large and the kernel
needs to fall back on vmalloc).
> + * Ensure the memory we just allocated don't trigger page faults.
> + * Well.. kprobes itself puts the page fault handler on the blacklist,
> + * but we can never be too careful.
> + */
> + wrapper_vmalloc_sync_all();
> +
> + ret = uprobe_register(event->u.uprobe.inode,
> + event->u.uprobe.offset,
> + &event->u.uprobe.up_consumer);
> + if (ret)
> + goto register_error;
> + return 0;
> +
> +register_error:
> + iput(event->u.uprobe.inode);
> +path_error:
> + kfree(event->desc->name);
> + kfree(event->desc);
not sure why kfree these, which are not allocated within this function ?
Should be cleaned up.
> +error:
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(lttng_uprobes_register);
> +
> +void lttng_uprobes_unregister(struct lttng_event *event)
> +{
> + uprobe_unregister(event->u.uprobe.inode,
> + event->u.uprobe.offset,
> + &event->u.uprobe.up_consumer);
Hrm, we should probably report to uprobes developers that
uprobe_unregister() should return errors instead of silently returning.
Thanks !
Mathieu
> +}
> +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");
> --
> 1.7.10.4
>
>
> _______________________________________________
> lttng-dev mailing list
> lttng-dev at lists.lttng.org
> http://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list