[lttng-dev] [PATCH] Create kernel events by writing to proc file
Mathieu Desnoyers
mathieu.desnoyers at efficios.com
Wed May 9 14:04:24 EDT 2012
* Francis Giraldeau (francis.giraldeau at gmail.com) wrote:
> This feature allow to write events in the kernel trace stream by writing to the
> file /proc/lttng_user_event. The content is written unmodified to the trace as
> string type. The maximum string length saved per event is 256 bytes. The event
> type and the proc file are accessibles when the module lttng-user-event is
> loaded.
>
> This is a prototype implementation. The final implementation should avoid the
> temp copy of the string on the kernel stack.
> ---
> Makefile | 1 +
> instrumentation/events/lttng-module/lttng.h | 19 +++
> probes/Makefile | 1 +
> probes/lttng-user-event.c | 159 +++++++++++++++++++++++++++
> 4 files changed, 180 insertions(+), 0 deletions(-)
> create mode 100644 probes/lttng-user-event.c
>
> diff --git a/Makefile b/Makefile
> index dfa0792..6bd203d 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -24,6 +24,7 @@ lttng-tracer-objs := lttng-events.o lttng-abi.o \
>
> obj-m += lttng-statedump.o
> lttng-statedump-objs := lttng-statedump-impl.o wrapper/irqdesc.o
> +lttng-user-event-objs := lttng-user-event.o
>
> ifneq ($(CONFIG_HAVE_SYSCALL_TRACEPOINTS),)
> lttng-tracer-objs += lttng-syscalls.o
> diff --git a/instrumentation/events/lttng-module/lttng.h b/instrumentation/events/lttng-module/lttng.h
> index 6f3d6d1..9da3c7e 100644
> --- a/instrumentation/events/lttng-module/lttng.h
> +++ b/instrumentation/events/lttng-module/lttng.h
> @@ -6,6 +6,8 @@
>
> #include <linux/tracepoint.h>
>
> +#define LTTNG_UEVENT_SIZE 256
> +
> TRACE_EVENT(lttng_metadata,
>
> TP_PROTO(const char *str),
> @@ -28,6 +30,23 @@ TRACE_EVENT(lttng_metadata,
> TP_printk("")
> )
>
> +TRACE_EVENT(lttng_uevent,
> +
> + TP_PROTO(char *str),
> +
> + TP_ARGS(str),
> +
> + TP_STRUCT__entry(
> + __array_text( char, text, LTTNG_UEVENT_SIZE )
> + ),
> +
> + TP_fast_assign(
> + tp_memcpy(text, str, LTTNG_UEVENT_SIZE)
FYI, we have __string_from_user and tp_copy_string_from_user for that
(you won't have to do the copy).
> + ),
> +
> + TP_printk("text=%s", __entry->text)
> +)
> +
> #endif /* _TRACE_LTTNG_H */
>
> /* This part must be outside protection */
> diff --git a/probes/Makefile b/probes/Makefile
> index 698a9c9..31ac769 100644
> --- a/probes/Makefile
> +++ b/probes/Makefile
> @@ -14,6 +14,7 @@ obj-m += lttng-probe-sched.o
> obj-m += lttng-probe-irq.o
> obj-m += lttng-probe-signal.o
> obj-m += lttng-probe-timer.o
> +obj-m += lttng-user-event.o
>
> obj-m += lttng-probe-statedump.o
>
> diff --git a/probes/lttng-user-event.c b/probes/lttng-user-event.c
> new file mode 100644
> index 0000000..d3b66e6
> --- /dev/null
> +++ b/probes/lttng-user-event.c
> @@ -0,0 +1,159 @@
> +/*
> + * lttng-user-event.c
> + *
> + * Copyright (C) 2006 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> + *
> + * 2012-05-08 Ported to lttng 2 by Francis Giraldeau
> + *
> + * 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 file should be GPLv2, as below one function states that it is
inspired from tracing_mark_write in the Linux kernel, which is licensed
GPLv2. Or simply reimplement write_event: it will become _trivial_ with
the proper zero-copy approach hinted at above.
> + *
> + * 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
> + */
> +
> +/* is there a prefered include order? */
nope
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +#include <linux/highmem.h>
> +#include <linux/sched.h>
> +#include <linux/gfp.h>
> +#include <linux/fs.h>
> +#include <linux/debugfs.h>
> +#include <linux/proc_fs.h>
> +#include <linux/slab.h>
> +#include <linux/mm.h>
> +#include <linux/string.h>
> +#include <linux/irqflags.h>
> +
> +#include "../instrumentation/events/lttng-module/lttng.h"
> +
> +DEFINE_TRACE(lttng_uevent);
> +
> +#define LTTNG_UEVENT_FILE "lttng_user_event"
> +
> +struct proc_dir_entry *lttng_root;
static.
> +static struct proc_dir_entry *write_file;
> +
> +/**
> + * write_event - write a userspace string into the trace system
> + * @file: file pointer
> + * @user_buf: user string
> + * @count: length to copy, including the final NULL
> + * @ppos: unused
> + *
> + * Copy a string into a trace event "user_event". Pins pages in memory to avoid
> + * intermediate copy.
> + *
> + * On success, returns the number of bytes copied from the source.
> + *
> + * Inspired from tracing_mark_write implementation from Steven Rostedt and
> + * Ingo Molnar.
> + */
> +static
> +ssize_t write_event(struct file *file, const char __user *user_buf,
> + size_t count, loff_t *fpos)
> +{
> + char tmp[LTTNG_UEVENT_SIZE];
> + unsigned long addr = (unsigned long)user_buf;
> + struct page *pages[2];
> + int nr_pages = 1;
> + void *page1, *page2;
> + ssize_t written;
> + int offset, ret, len;
> +
> + if (count >= LTTNG_UEVENT_SIZE)
> + count = LTTNG_UEVENT_SIZE - 1;
> +
> + BUILD_BUG_ON(LTTNG_UEVENT_SIZE >= PAGE_SIZE);
> +
> + if ((addr & PAGE_MASK) != ((addr + count) & PAGE_MASK))
> + nr_pages = 2;
> +
> + offset = addr & (PAGE_SIZE - 1);
> + addr &= PAGE_MASK;
> +
> + ret = get_user_pages_fast(addr, nr_pages, 0, pages);
> + if (ret < nr_pages) {
> + while (--ret >= 0)
> + put_page(pages[ret]);
> + written = -EFAULT;
> + goto out;
> + }
> +
> + page1 = kmap_atomic(pages[0]);
> + if (nr_pages == 2)
> + page2 = kmap_atomic(pages[1]);
> +
> + if (nr_pages == 2) {
> + len = PAGE_SIZE - offset;
> + memcpy(tmp, page1 + offset, len);
> + memcpy(tmp + len, page2, count - len);
> + } else
> + memcpy(tmp, page1 + offset, count);
> +
> + /* make sure the string is null terminated */
> + tmp[count] = '\0';
> + trace_lttng_uevent(tmp);
> + written = count;
> +
> + if (nr_pages == 2)
> + kunmap_atomic(page2);
> + kunmap_atomic(page1);
> + while (nr_pages > 0)
> + put_page(pages[--nr_pages]);
> + out:
> + return written;
> +}
> +
> +static const struct file_operations write_file_ops = {
> + .owner = THIS_MODULE,
> + .write = write_event
> +};
> +
> +static int __init lttng_user_event_init(void)
> +{
> + int err = 0;
> +
> + /* lttng is already a file with the current abi, not a directory */
> + /*
> + lttng_root = proc_mkdir("lttng", NULL);
why do you mkdir lttng ? It conflicts with /proc/lttng.
Thanks,
Mathieu
> + if (lttng_root == NULL)
> + return -ENOMEM;
> + */
> + write_file = create_proc_entry(LTTNG_UEVENT_FILE, 0644, lttng_root);
> + if (!write_file) {
> + err = -ENOENT;
> + goto err_procfs;
> + }
> + write_file->proc_fops = &write_file_ops;
> + write_file->mode = S_IFREG | S_IWUGO;
> + write_file->uid = 0;
> + write_file->gid = 0;
> + return err;
> +
> +err_procfs:
> + remove_proc_entry(LTTNG_UEVENT_FILE, lttng_root);
> + return err;
> +}
> +
> +static void __exit lttng_user_event_exit(void)
> +{
> + remove_proc_entry(LTTNG_UEVENT_FILE, lttng_root);
> + printk(KERN_INFO "/proc/%s removed\n", LTTNG_UEVENT_FILE);
> +}
> +
> +module_init(lttng_user_event_init);
> +module_exit(lttng_user_event_exit);
> +
> +MODULE_LICENSE("GPL and additional rights");
> +MODULE_AUTHOR("Francis Giraldeau <francis.giraldeau at polymtl.ca>");
> +MODULE_DESCRIPTION("Append custom events to kernel trace from userspace");
> --
> 1.7.5.4
>
>
> _______________________________________________
> lttng-dev mailing list
> lttng-dev at lists.lttng.org
> http://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list