--- /dev/null
+/*
+ * Copyright (C) 2008 Mathieu Desnoyers
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+
+#include "ltt-tracer.h"
+
+#define LTT_FILTER_DIR "filter"
+
+/*
+ * Protects the ltt_filter_dir allocation.
+ */
+static DEFINE_MUTEX(ltt_filter_mutex);
+
+static struct dentry *ltt_filter_dir;
+
+struct dentry *get_filter_root(void)
+{
+ struct dentry *ltt_root_dentry;
+
+ mutex_lock(<t_filter_mutex);
+ if (!ltt_filter_dir) {
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry)
+ goto err_no_root;
+
+ ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR,
+ ltt_root_dentry);
+ if (!ltt_filter_dir)
+ printk(KERN_ERR
+ "ltt_filter_init: failed to create dir %s\n",
+ LTT_FILTER_DIR);
+ }
+err_no_root:
+ mutex_unlock(<t_filter_mutex);
+ return ltt_filter_dir;
+}
+EXPORT_SYMBOL_GPL(get_filter_root);
+
+static void __exit ltt_filter_exit(void)
+{
+ debugfs_remove(ltt_filter_dir);
+}
+
+module_exit(ltt_filter_exit);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
--- /dev/null
+/*
+ * (C) Copyright 2009 -
+ * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca)
+ *
+ * LTTng kprobes integration module.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/marker.h>
+#include <linux/mutex.h>
+#include <linux/jhash.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/kallsyms.h>
+
+#include "ltt-type-serializer.h"
+#include "ltt-tracer.h"
+
+#define LTT_KPROBES_DIR "kprobes"
+#define LTT_KPROBES_ENABLE "enable"
+#define LTT_KPROBES_DISABLE "disable"
+#define LTT_KPROBES_LIST "list"
+
+/* Active LTTng kprobes hash table */
+static DEFINE_MUTEX(ltt_kprobes_mutex);
+
+#define LTT_KPROBE_HASH_BITS 6
+#define LTT_KPROBE_TABLE_SIZE (1 << LTT_KPROBE_HASH_BITS)
+static struct hlist_head ltt_kprobe_table[LTT_KPROBE_TABLE_SIZE];
+
+struct kprobe_entry {
+ struct hlist_node hlist;
+ struct kprobe kp;
+ char key[0];
+};
+
+static struct dentry *ltt_kprobes_dir,
+ *ltt_kprobes_enable_dentry,
+ *ltt_kprobes_disable_dentry,
+ *ltt_kprobes_list_dentry;
+
+static int module_exit;
+
+
+static void trace_kprobe_table_entry(void *call_data, struct kprobe_entry *e)
+{
+ unsigned long addr;
+ char *namebuf = (char *)__get_free_page(GFP_KERNEL);
+
+ if (e->kp.addr) {
+ sprint_symbol(namebuf, (unsigned long)e->kp.addr);
+ addr = (unsigned long)e->kp.addr;
+ } else {
+ strncpy(namebuf, e->kp.symbol_name, PAGE_SIZE - 1);
+ /* TODO : add offset */
+ addr = kallsyms_lookup_name(namebuf);
+ }
+ if (addr)
+ __trace_mark(0, kprobe_state, kprobe_table, call_data,
+ "ip 0x%lX symbol %s", addr, namebuf);
+ free_page((unsigned long)namebuf);
+}
+
+DEFINE_MARKER(kernel, kprobe, "ip %lX");
+
+static int ltt_kprobe_handler_pre(struct kprobe *p, struct pt_regs *regs)
+{
+ struct marker *marker;
+ unsigned long data;
+
+ data = (unsigned long)p->addr;
+ marker = &GET_MARKER(kernel, kprobe);
+ ltt_specialized_trace(marker, marker->single.probe_private,
+ &data, sizeof(data), sizeof(data));
+ return 0;
+}
+
+static int ltt_register_kprobe(const char *key)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct kprobe_entry *e = NULL;
+ char *symbol_name = NULL;
+ unsigned long addr;
+ unsigned int offset = 0;
+ u32 hash;
+ size_t key_len = strlen(key) + 1;
+ int ret;
+
+ if (key_len == 1)
+ return -ENOENT; /* only \0 */
+
+ if (sscanf(key, "%li", &addr) != 1)
+ addr = 0;
+
+ if (!addr) {
+ const char *symbol_end = NULL;
+ unsigned int symbol_len; /* includes final \0 */
+
+ symbol_end = strchr(key, ' ');
+ if (symbol_end)
+ symbol_len = symbol_end - key + 1;
+ else
+ symbol_len = key_len;
+ symbol_name = kmalloc(symbol_len, GFP_KERNEL);
+ if (!symbol_name) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memcpy(symbol_name, key, symbol_len - 1);
+ symbol_name[symbol_len-1] = '\0';
+ if (symbol_end) {
+ symbol_end++; /* start of offset */
+ if (sscanf(symbol_end, "%i", &offset) != 1)
+ offset = 0;
+ }
+ }
+
+ hash = jhash(key, key_len-1, 0);
+ head = <t_kprobe_table[hash & ((1 << LTT_KPROBE_HASH_BITS)-1)];
+ hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp(key, e->key)) {
+ printk(KERN_NOTICE "Kprobe %s busy\n", key);
+ ret = -EBUSY;
+ goto error;
+ }
+ }
+ /*
+ * Using kzalloc here to allocate a variable length element. Could
+ * cause some memory fragmentation if overused.
+ */
+ e = kzalloc(sizeof(struct kprobe_entry) + key_len, GFP_KERNEL);
+ if (!e) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memcpy(e->key, key, key_len);
+ hlist_add_head(&e->hlist, head);
+ e->kp.pre_handler = ltt_kprobe_handler_pre;
+ e->kp.symbol_name = symbol_name;
+ e->kp.offset = offset;
+ e->kp.addr = (void *)addr;
+ ret = register_kprobe(&e->kp);
+ if (ret < 0)
+ goto error_list_del;
+ trace_kprobe_table_entry(NULL, e);
+ return 0;
+
+error_list_del:
+ hlist_del(&e->hlist);
+error:
+ kfree(symbol_name);
+ kfree(e);
+ return ret;
+}
+
+static int ltt_unregister_kprobe(const char *key)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct kprobe_entry *e;
+ int found = 0;
+ size_t key_len = strlen(key) + 1;
+ u32 hash;
+
+ hash = jhash(key, key_len-1, 0);
+ head = <t_kprobe_table[hash & ((1 << LTT_KPROBE_HASH_BITS)-1)];
+ hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp(key, e->key)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return -ENOENT;
+ hlist_del(&e->hlist);
+ unregister_kprobe(&e->kp);
+ kfree(e->kp.symbol_name);
+ kfree(e);
+ return 0;
+}
+
+static void ltt_unregister_all_kprobes(void)
+{
+ struct kprobe_entry *e;
+ struct hlist_head *head;
+ struct hlist_node *node, *tmp;
+ unsigned int i;
+
+ for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
+ head = <t_kprobe_table[i];
+ hlist_for_each_entry_safe(e, node, tmp, head, hlist) {
+ hlist_del(&e->hlist);
+ unregister_kprobe(&e->kp);
+ kfree(e->kp.symbol_name);
+ kfree(e);
+ }
+ }
+}
+
+/*
+ * Allows to specify either
+ * - symbol
+ * - symbol offset
+ * - address
+ */
+static ssize_t enable_op_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err, buf_size;
+ char *end;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+
+ mutex_lock(<t_kprobes_mutex);
+ if (module_exit) {
+ err = -EPERM;
+ goto error;
+ }
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto error;
+ buf[buf_size] = '\0';
+ end = strchr(buf, '\n');
+ if (end)
+ *end = '\0';
+ err = ltt_register_kprobe(buf);
+ if (err)
+ goto error;
+
+ mutex_unlock(<t_kprobes_mutex);
+ free_page((unsigned long)buf);
+ return count;
+error:
+ mutex_unlock(<t_kprobes_mutex);
+ free_page((unsigned long)buf);
+ return err;
+}
+
+static const struct file_operations ltt_kprobes_enable = {
+ .write = enable_op_write,
+};
+
+static ssize_t disable_op_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err, buf_size;
+ char *end;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+
+ mutex_lock(<t_kprobes_mutex);
+ if (module_exit)
+ goto end;
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto error;
+ buf[buf_size] = '\0';
+ end = strchr(buf, '\n');
+ if (end)
+ *end = '\0';
+ err = ltt_unregister_kprobe(buf);
+ if (err)
+ goto error;
+end:
+ mutex_unlock(<t_kprobes_mutex);
+ free_page((unsigned long)buf);
+ return count;
+error:
+ mutex_unlock(<t_kprobes_mutex);
+ free_page((unsigned long)buf);
+ return err;
+}
+
+static const struct file_operations ltt_kprobes_disable = {
+ .write = disable_op_write,
+};
+
+/*
+ * This seqfile read is not perfectly safe, as a kprobe could be removed from
+ * the hash table between two reads. This will result in an incomplete output.
+ */
+static struct kprobe_entry *ltt_find_next_kprobe(struct kprobe_entry *prev)
+{
+ struct kprobe_entry *e;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ unsigned int i;
+ int found = 0;
+
+ if (prev == (void *)-1UL)
+ return NULL;
+
+ if (!prev)
+ found = 1;
+
+ for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
+ head = <t_kprobe_table[i];
+ hlist_for_each_entry(e, node, head, hlist) {
+ if (found)
+ return e;
+ if (e == prev)
+ found = 1;
+ }
+ }
+ return NULL;
+}
+
+static void *lk_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ m->private = ltt_find_next_kprobe(m->private);
+ if (!m->private) {
+ m->private = (void *)-1UL;
+ return NULL;
+ }
+ return m->private;
+}
+
+static void *lk_start(struct seq_file *m, loff_t *pos)
+{
+ mutex_lock(<t_kprobes_mutex);
+ if (!*pos)
+ m->private = NULL;
+ m->private = ltt_find_next_kprobe(m->private);
+ if (!m->private) {
+ m->private = (void *)-1UL;
+ return NULL;
+ }
+ return m->private;
+}
+
+static void lk_stop(struct seq_file *m, void *p)
+{
+ mutex_unlock(<t_kprobes_mutex);
+}
+
+static int lk_show(struct seq_file *m, void *p)
+{
+ struct kprobe_entry *e = m->private;
+ seq_printf(m, "%s\n", e->key);
+ return 0;
+}
+
+static const struct seq_operations ltt_kprobes_list_op = {
+ .start = lk_start,
+ .next = lk_next,
+ .stop = lk_stop,
+ .show = lk_show,
+};
+
+static int ltt_kprobes_list_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ ret = seq_open(file, <t_kprobes_list_op);
+ if (ret == 0)
+ ((struct seq_file *)file->private_data)->private = NULL;
+ return ret;
+}
+
+static int ltt_kprobes_list_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq = file->private_data;
+
+ seq->private = NULL;
+ return seq_release(inode, file);
+}
+
+static const struct file_operations ltt_kprobes_list = {
+ .open = ltt_kprobes_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = ltt_kprobes_list_release,
+};
+
+/*
+ * kprobes table dump. Callback invoked by ltt-statedump. ltt-statedump must
+ * take a reference to this module before calling this callback.
+ */
+void ltt_dump_kprobes_table(void *call_data)
+{
+ struct kprobe_entry *e;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ unsigned int i;
+
+ for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
+ head = <t_kprobe_table[i];
+ hlist_for_each_entry(e, node, head, hlist)
+ trace_kprobe_table_entry(call_data, e);
+ }
+}
+EXPORT_SYMBOL_GPL(ltt_dump_kprobes_table);
+
+static int __init ltt_kprobes_init(void)
+{
+ struct dentry *ltt_root_dentry;
+ int ret = 0;
+
+ printk(KERN_INFO "LTT : ltt-kprobes init\n");
+ mutex_lock(<t_kprobes_mutex);
+
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry) {
+ ret = -ENOENT;
+ goto err_no_root;
+ }
+
+ ltt_kprobes_dir = debugfs_create_dir(LTT_KPROBES_DIR, ltt_root_dentry);
+ if (!ltt_kprobes_dir) {
+ printk(KERN_ERR
+ "ltt_kprobes_init: failed to create dir %s\n",
+ LTT_KPROBES_DIR);
+ ret = -ENOMEM;
+ goto err_no_dir;
+ }
+
+ ltt_kprobes_enable_dentry = debugfs_create_file(LTT_KPROBES_ENABLE,
+ S_IWUSR,
+ ltt_kprobes_dir, NULL,
+ <t_kprobes_enable);
+ if (IS_ERR(ltt_kprobes_enable_dentry) || !ltt_kprobes_enable_dentry) {
+ printk(KERN_ERR
+ "ltt_kprobes_init: failed to create file %s\n",
+ LTT_KPROBES_ENABLE);
+ ret = -ENOMEM;
+ goto err_no_enable;
+ }
+
+ ltt_kprobes_disable_dentry = debugfs_create_file(LTT_KPROBES_DISABLE,
+ S_IWUSR,
+ ltt_kprobes_dir, NULL,
+ <t_kprobes_disable);
+ if (IS_ERR(ltt_kprobes_disable_dentry) || !ltt_kprobes_disable_dentry) {
+ printk(KERN_ERR
+ "ltt_kprobes_init: failed to create file %s\n",
+ LTT_KPROBES_DISABLE);
+ ret = -ENOMEM;
+ goto err_no_disable;
+ }
+
+ ltt_kprobes_list_dentry = debugfs_create_file(LTT_KPROBES_LIST,
+ S_IWUSR, ltt_kprobes_dir,
+ NULL, <t_kprobes_list);
+ if (IS_ERR(ltt_kprobes_list_dentry) || !ltt_kprobes_list_dentry) {
+ printk(KERN_ERR
+ "ltt_kprobes_init: failed to create file %s\n",
+ LTT_KPROBES_LIST);
+ ret = -ENOMEM;
+ goto err_no_list;
+ }
+ ltt_statedump_register_kprobes_dump(ltt_dump_kprobes_table);
+
+ mutex_unlock(<t_kprobes_mutex);
+ return ret;
+
+err_no_list:
+ debugfs_remove(ltt_kprobes_disable_dentry);
+err_no_disable:
+ debugfs_remove(ltt_kprobes_enable_dentry);
+err_no_enable:
+ debugfs_remove(ltt_kprobes_dir);
+err_no_dir:
+err_no_root:
+ mutex_unlock(<t_kprobes_mutex);
+ return ret;
+}
+module_init(ltt_kprobes_init);
+
+static void __exit ltt_kprobes_exit(void)
+{
+ printk(KERN_INFO "LTT : ltt-kprobes exit\n");
+ mutex_lock(<t_kprobes_mutex);
+ module_exit = 1;
+ ltt_statedump_unregister_kprobes_dump(ltt_dump_kprobes_table);
+ debugfs_remove(ltt_kprobes_list_dentry);
+ debugfs_remove(ltt_kprobes_disable_dentry);
+ debugfs_remove(ltt_kprobes_enable_dentry);
+ debugfs_remove(ltt_kprobes_dir);
+ ltt_unregister_all_kprobes();
+ mutex_unlock(<t_kprobes_mutex);
+}
+module_exit(ltt_kprobes_exit);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Mathieu Desnoyers");
+MODULE_DESCRIPTION("Linux Trace Toolkit Kprobes Support");
--- /dev/null
+/*
+ * Linux Trace Toolkit Kernel State Dump
+ *
+ * Copyright 2005 -
+ * Jean-Hugues Deschenes <jean-hugues.deschenes@polymtl.ca>
+ *
+ * Changes:
+ * Eric Clement: Add listing of network IP interface
+ * 2006, 2007 Mathieu Desnoyers Fix kernel threads
+ * Various updates
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/inet.h>
+#include <linux/ip.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/file.h>
+#include <linux/interrupt.h>
+#include <linux/irqnr.h>
+#include <linux/cpu.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/marker.h>
+#include <linux/fdtable.h>
+#include <linux/swap.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+
+#include "ltt-tracer.h"
+
+#ifdef CONFIG_GENERIC_HARDIRQS
+#include <linux/irq.h>
+#endif
+
+#define NB_PROC_CHUNK 20
+
+/*
+ * Protected by the trace lock.
+ */
+static struct delayed_work cpu_work[NR_CPUS];
+static DECLARE_WAIT_QUEUE_HEAD(statedump_wq);
+static atomic_t kernel_threads_to_run;
+
+static void empty_cb(void *call_data)
+{
+}
+
+static DEFINE_MUTEX(statedump_cb_mutex);
+static void (*ltt_dump_kprobes_table_cb)(void *call_data) = empty_cb;
+
+enum lttng_thread_type {
+ LTTNG_USER_THREAD = 0,
+ LTTNG_KERNEL_THREAD = 1,
+};
+
+enum lttng_execution_mode {
+ LTTNG_USER_MODE = 0,
+ LTTNG_SYSCALL = 1,
+ LTTNG_TRAP = 2,
+ LTTNG_IRQ = 3,
+ LTTNG_SOFTIRQ = 4,
+ LTTNG_MODE_UNKNOWN = 5,
+};
+
+enum lttng_execution_submode {
+ LTTNG_NONE = 0,
+ LTTNG_UNKNOWN = 1,
+};
+
+enum lttng_process_status {
+ LTTNG_UNNAMED = 0,
+ LTTNG_WAIT_FORK = 1,
+ LTTNG_WAIT_CPU = 2,
+ LTTNG_EXIT = 3,
+ LTTNG_ZOMBIE = 4,
+ LTTNG_WAIT = 5,
+ LTTNG_RUN = 6,
+ LTTNG_DEAD = 7,
+};
+
+#ifdef CONFIG_INET
+static void ltt_enumerate_device(struct ltt_probe_private_data *call_data,
+ struct net_device *dev)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+
+ if (dev->flags & IFF_UP) {
+ in_dev = in_dev_get(dev);
+ if (in_dev) {
+ for (ifa = in_dev->ifa_list; ifa != NULL;
+ ifa = ifa->ifa_next)
+ __trace_mark(0, netif_state,
+ network_ipv4_interface,
+ call_data,
+ "name %s address #n4u%lu up %d",
+ dev->name,
+ (unsigned long)ifa->ifa_address,
+ 0);
+ in_dev_put(in_dev);
+ }
+ } else
+ __trace_mark(0, netif_state, network_ip_interface,
+ call_data, "name %s address #n4u%lu up %d",
+ dev->name, 0UL, 0);
+}
+
+static inline int
+ltt_enumerate_network_ip_interface(struct ltt_probe_private_data *call_data)
+{
+ struct net_device *dev;
+
+ read_lock(&dev_base_lock);
+ for_each_netdev(&init_net, dev)
+ ltt_enumerate_device(call_data, dev);
+ read_unlock(&dev_base_lock);
+
+ return 0;
+}
+#else /* CONFIG_INET */
+static inline int
+ltt_enumerate_network_ip_interface(struct ltt_probe_private_data *call_data)
+{
+ return 0;
+}
+#endif /* CONFIG_INET */
+
+
+static inline void
+ltt_enumerate_task_fd(struct ltt_probe_private_data *call_data,
+ struct task_struct *t, char *tmp)
+{
+ struct fdtable *fdt;
+ struct file *filp;
+ unsigned int i;
+ const unsigned char *path;
+
+ if (!t->files)
+ return;
+
+ spin_lock(&t->files->file_lock);
+ fdt = files_fdtable(t->files);
+ for (i = 0; i < fdt->max_fds; i++) {
+ filp = fcheck_files(t->files, i);
+ if (!filp)
+ continue;
+ path = d_path(&filp->f_path, tmp, PAGE_SIZE);
+ /* Make sure we give at least some info */
+ __trace_mark(0, fd_state, file_descriptor, call_data,
+ "filename %s pid %d fd %u",
+ (IS_ERR(path))?(filp->f_dentry->d_name.name):(path),
+ t->pid, i);
+ }
+ spin_unlock(&t->files->file_lock);
+}
+
+static inline int
+ltt_enumerate_file_descriptors(struct ltt_probe_private_data *call_data)
+{
+ struct task_struct *t = &init_task;
+ char *tmp = (char *)__get_free_page(GFP_KERNEL);
+
+ /* Enumerate active file descriptors */
+ do {
+ read_lock(&tasklist_lock);
+ if (t != &init_task)
+ atomic_dec(&t->usage);
+ t = next_task(t);
+ atomic_inc(&t->usage);
+ read_unlock(&tasklist_lock);
+ task_lock(t);
+ ltt_enumerate_task_fd(call_data, t, tmp);
+ task_unlock(t);
+ } while (t != &init_task);
+ free_page((unsigned long)tmp);
+ return 0;
+}
+
+static inline void
+ltt_enumerate_task_vm_maps(struct ltt_probe_private_data *call_data,
+ struct task_struct *t)
+{
+ struct mm_struct *mm;
+ struct vm_area_struct *map;
+ unsigned long ino;
+
+ /* get_task_mm does a task_lock... */
+ mm = get_task_mm(t);
+ if (!mm)
+ return;
+
+ map = mm->mmap;
+ if (map) {
+ down_read(&mm->mmap_sem);
+ while (map) {
+ if (map->vm_file)
+ ino = map->vm_file->f_dentry->d_inode->i_ino;
+ else
+ ino = 0;
+ __trace_mark(0, vm_state, vm_map, call_data,
+ "pid %d start %lu end %lu flags %lu "
+ "pgoff %lu inode %lu",
+ t->pid, map->vm_start, map->vm_end,
+ map->vm_flags, map->vm_pgoff << PAGE_SHIFT,
+ ino);
+ map = map->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ }
+ mmput(mm);
+}
+
+static inline int
+ltt_enumerate_vm_maps(struct ltt_probe_private_data *call_data)
+{
+ struct task_struct *t = &init_task;
+
+ do {
+ read_lock(&tasklist_lock);
+ if (t != &init_task)
+ atomic_dec(&t->usage);
+ t = next_task(t);
+ atomic_inc(&t->usage);
+ read_unlock(&tasklist_lock);
+ ltt_enumerate_task_vm_maps(call_data, t);
+ } while (t != &init_task);
+ return 0;
+}
+
+#ifdef CONFIG_GENERIC_HARDIRQS
+static inline void list_interrupts(struct ltt_probe_private_data *call_data)
+{
+ unsigned int irq;
+ unsigned long flags = 0;
+ struct irq_desc *desc;
+
+ /* needs irq_desc */
+ for_each_irq_desc(irq, desc) {
+ struct irqaction *action;
+ const char *irq_chip_name =
+ desc->chip->name ? : "unnamed_irq_chip";
+
+ local_irq_save(flags);
+ raw_spin_lock(&desc->lock);
+ for (action = desc->action; action; action = action->next)
+ __trace_mark(0, irq_state, interrupt, call_data,
+ "name %s action %s irq_id %u",
+ irq_chip_name, action->name, irq);
+ raw_spin_unlock(&desc->lock);
+ local_irq_restore(flags);
+ }
+}
+#else
+static inline void list_interrupts(struct ltt_probe_private_data *call_data)
+{
+}
+#endif
+
+static inline int
+ltt_enumerate_process_states(struct ltt_probe_private_data *call_data)
+{
+ struct task_struct *t = &init_task;
+ struct task_struct *p = t;
+ enum lttng_process_status status;
+ enum lttng_thread_type type;
+ enum lttng_execution_mode mode;
+ enum lttng_execution_submode submode;
+
+ do {
+ mode = LTTNG_MODE_UNKNOWN;
+ submode = LTTNG_UNKNOWN;
+
+ read_lock(&tasklist_lock);
+ if (t != &init_task) {
+ atomic_dec(&t->usage);
+ t = next_thread(t);
+ }
+ if (t == p) {
+ p = next_task(t);
+ t = p;
+ }
+ atomic_inc(&t->usage);
+ read_unlock(&tasklist_lock);
+
+ task_lock(t);
+
+ if (t->exit_state == EXIT_ZOMBIE)
+ status = LTTNG_ZOMBIE;
+ else if (t->exit_state == EXIT_DEAD)
+ status = LTTNG_DEAD;
+ else if (t->state == TASK_RUNNING) {
+ /* Is this a forked child that has not run yet? */
+ if (list_empty(&t->rt.run_list))
+ status = LTTNG_WAIT_FORK;
+ else
+ /*
+ * All tasks are considered as wait_cpu;
+ * the viewer will sort out if the task was
+ * really running at this time.
+ */
+ status = LTTNG_WAIT_CPU;
+ } else if (t->state &
+ (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)) {
+ /* Task is waiting for something to complete */
+ status = LTTNG_WAIT;
+ } else
+ status = LTTNG_UNNAMED;
+ submode = LTTNG_NONE;
+
+ /*
+ * Verification of t->mm is to filter out kernel threads;
+ * Viewer will further filter out if a user-space thread was
+ * in syscall mode or not.
+ */
+ if (t->mm)
+ type = LTTNG_USER_THREAD;
+ else
+ type = LTTNG_KERNEL_THREAD;
+
+ __trace_mark(0, task_state, process_state, call_data,
+ "pid %d parent_pid %d name %s type %d mode %d "
+ "submode %d status %d tgid %d",
+ t->pid, t->parent->pid, t->comm,
+ type, mode, submode, status, t->tgid);
+ task_unlock(t);
+ } while (t != &init_task);
+
+ return 0;
+}
+
+void ltt_statedump_register_kprobes_dump(void (*callback)(void *call_data))
+{
+ mutex_lock(&statedump_cb_mutex);
+ ltt_dump_kprobes_table_cb = callback;
+ mutex_unlock(&statedump_cb_mutex);
+}
+EXPORT_SYMBOL_GPL(ltt_statedump_register_kprobes_dump);
+
+void ltt_statedump_unregister_kprobes_dump(void (*callback)(void *call_data))
+{
+ mutex_lock(&statedump_cb_mutex);
+ ltt_dump_kprobes_table_cb = empty_cb;
+ mutex_unlock(&statedump_cb_mutex);
+}
+EXPORT_SYMBOL_GPL(ltt_statedump_unregister_kprobes_dump);
+
+void ltt_statedump_work_func(struct work_struct *work)
+{
+ if (atomic_dec_and_test(&kernel_threads_to_run))
+ /* If we are the last thread, wake up do_ltt_statedump */
+ wake_up(&statedump_wq);
+}
+
+static int do_ltt_statedump(struct ltt_probe_private_data *call_data)
+{
+ int cpu;
+ struct module *cb_owner;
+
+ printk(KERN_DEBUG "LTT state dump thread start\n");
+ ltt_enumerate_process_states(call_data);
+ ltt_enumerate_file_descriptors(call_data);
+ list_modules(call_data);
+ ltt_enumerate_vm_maps(call_data);
+ list_interrupts(call_data);
+ ltt_enumerate_network_ip_interface(call_data);
+ ltt_dump_swap_files(call_data);
+ ltt_dump_sys_call_table(call_data);
+ ltt_dump_softirq_vec(call_data);
+ ltt_dump_idt_table(call_data);
+
+ mutex_lock(&statedump_cb_mutex);
+
+ cb_owner = __module_address((unsigned long)ltt_dump_kprobes_table_cb);
+ __module_get(cb_owner);
+ ltt_dump_kprobes_table_cb(call_data);
+ module_put(cb_owner);
+
+ mutex_unlock(&statedump_cb_mutex);
+
+ /*
+ * Fire off a work queue on each CPU. Their sole purpose in life
+ * is to guarantee that each CPU has been in a state where is was in
+ * syscall mode (i.e. not in a trap, an IRQ or a soft IRQ).
+ */
+ get_online_cpus();
+ atomic_set(&kernel_threads_to_run, num_online_cpus());
+ for_each_online_cpu(cpu) {
+ INIT_DELAYED_WORK(&cpu_work[cpu], ltt_statedump_work_func);
+ schedule_delayed_work_on(cpu, &cpu_work[cpu], 0);
+ }
+ /* Wait for all threads to run */
+ __wait_event(statedump_wq, (atomic_read(&kernel_threads_to_run) != 0));
+ put_online_cpus();
+ /* Our work is done */
+ printk(KERN_DEBUG "LTT state dump end\n");
+ __trace_mark(0, global_state, statedump_end,
+ call_data, MARK_NOARGS);
+ return 0;
+}
+
+/*
+ * Called with trace lock held.
+ */
+int ltt_statedump_start(struct ltt_trace *trace)
+{
+ struct ltt_probe_private_data call_data;
+ printk(KERN_DEBUG "LTT state dump begin\n");
+
+ call_data.trace = trace;
+ call_data.serializer = NULL;
+ return do_ltt_statedump(&call_data);
+}
+
+static int __init statedump_init(void)
+{
+ int ret;
+ printk(KERN_DEBUG "LTT : State dump init\n");
+ ret = ltt_module_register(LTT_FUNCTION_STATEDUMP,
+ ltt_statedump_start, THIS_MODULE);
+ return ret;
+}
+
+static void __exit statedump_exit(void)
+{
+ printk(KERN_DEBUG "LTT : State dump exit\n");
+ ltt_module_unregister(LTT_FUNCTION_STATEDUMP);
+}
+
+module_init(statedump_init)
+module_exit(statedump_exit)
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Jean-Hugues Deschenes");
+MODULE_DESCRIPTION("Linux Trace Toolkit Statedump");
--- /dev/null
+/*
+ * LTT trace control module over debugfs.
+ *
+ * Copyright 2008 - Zhaolei <zhaolei@cn.fujitsu.com>
+ *
+ * Copyright 2009 - Gui Jianfeng <guijianfeng@cn.fujitsu.com>
+ * Make mark-control work in debugfs
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+/*
+ * Todo:
+ * Impl read operations for control file to read attributes
+ * Create a README file in ltt control dir, for display help info
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+#include <linux/jiffies.h>
+#include <linux/marker.h>
+
+#include "ltt-tracer.h"
+
+#define LTT_CONTROL_DIR "control"
+#define MARKERS_CONTROL_DIR "markers"
+#define LTT_SETUP_TRACE_FILE "setup_trace"
+#define LTT_DESTROY_TRACE_FILE "destroy_trace"
+
+#define LTT_WRITE_MAXLEN (128)
+
+struct dentry *ltt_control_dir, *ltt_setup_trace_file, *ltt_destroy_trace_file,
+ *markers_control_dir;
+
+/*
+ * the traces_lock nests inside control_lock.
+ * control_lock protects the consistency of directories presented in ltt
+ * directory.
+ */
+static DEFINE_MUTEX(control_lock);
+
+/*
+ * big note about locking for marker control files :
+ * If a marker control file is added/removed manually racing with module
+ * load/unload, there may be warning messages appearing, but those two
+ * operations should be able to execute concurrently without any lock
+ * synchronizing their operation one wrt another.
+ * Locking the marker mutex, module mutex and also keeping a mutex here
+ * from mkdir/rmdir _and_ from the notifier called from module load/unload makes
+ * life miserable and just asks for deadlocks.
+ */
+
+/*
+ * lookup a file/dir in parent dir.
+ * only designed to work well for debugfs.
+ * (although it maybe ok for other fs)
+ *
+ * return:
+ * file/dir's dentry on success
+ * NULL on failure
+ */
+static struct dentry *dir_lookup(struct dentry *parent, const char *name)
+{
+ struct qstr q;
+ struct dentry *d;
+
+ q.name = name;
+ q.len = strlen(name);
+ q.hash = full_name_hash(q.name, q.len);
+
+ d = d_lookup(parent, &q);
+ if (d)
+ dput(d);
+
+ return d;
+}
+
+
+static ssize_t alloc_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *cmd = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if ((cmd[0] != 'Y' && cmd[0] != 'y' && cmd[0] != '1') || cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ err = ltt_trace_alloc(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "alloc_write: ltt_trace_alloc failed: %d\n",
+ err);
+ goto err_alloc_trace;
+ }
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return count;
+
+err_alloc_trace:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return err;
+}
+
+static const struct file_operations ltt_alloc_operations = {
+ .write = alloc_write,
+};
+
+
+static ssize_t enabled_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *cmd = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if (cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ switch (cmd[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ err = ltt_trace_start(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "enabled_write: ltt_trace_start failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_start_trace;
+ }
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ err = ltt_trace_stop(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "enabled_write: ltt_trace_stop failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_stop_trace;
+ }
+ break;
+ default:
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return count;
+
+err_stop_trace:
+err_start_trace:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return err;
+}
+
+static const struct file_operations ltt_enabled_operations = {
+ .write = enabled_write,
+};
+
+
+static ssize_t trans_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *trans_name = (char *)__get_free_page(GFP_KERNEL);
+ int err = 0;
+ int buf_size;
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trans_name) != 1) {
+ err = -EPERM;
+ goto err_get_transname;
+ }
+
+ err = ltt_trace_set_type(file->f_dentry->d_parent->d_name.name,
+ trans_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "trans_write: ltt_trace_set_type failed: %d\n",
+ err);
+ goto err_set_trans;
+ }
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trans_name);
+ return count;
+
+err_set_trans:
+err_get_transname:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trans_name);
+ return err;
+}
+
+static const struct file_operations ltt_trans_operations = {
+ .write = trans_write,
+};
+
+
+static ssize_t channel_subbuf_num_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ unsigned int num;
+ const char *channel_name;
+ const char *trace_name;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%u", &num) != 1) {
+ err = -EPERM;
+ goto err_get_number;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ err = ltt_trace_set_channel_subbufcount(trace_name, channel_name, num);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_subbuf_num_write: "
+ "ltt_trace_set_channel_subbufcount failed: %d\n", err);
+ goto err_set_subbufcount;
+ }
+
+ free_page((unsigned long)buf);
+ return count;
+
+err_set_subbufcount:
+err_get_number:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ return err;
+}
+
+static const struct file_operations ltt_channel_subbuf_num_operations = {
+ .write = channel_subbuf_num_write,
+};
+
+
+static
+ssize_t channel_subbuf_size_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ unsigned int num;
+ const char *channel_name;
+ const char *trace_name;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%u", &num) != 1) {
+ err = -EPERM;
+ goto err_get_number;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ err = ltt_trace_set_channel_subbufsize(trace_name, channel_name, num);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_subbuf_size_write: "
+ "ltt_trace_set_channel_subbufsize failed: %d\n", err);
+ goto err_set_subbufsize;
+ }
+
+ free_page((unsigned long)buf);
+ return count;
+
+err_set_subbufsize:
+err_get_number:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ return err;
+}
+
+static const struct file_operations ltt_channel_subbuf_size_operations = {
+ .write = channel_subbuf_size_write,
+};
+
+static
+ssize_t channel_switch_timer_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ unsigned long num;
+ const char *channel_name;
+ const char *trace_name;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%lu", &num) != 1) {
+ err = -EPERM;
+ goto err_get_number;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ /* Convert from ms to us */
+ num *= 1000;
+
+ err = ltt_trace_set_channel_switch_timer(trace_name, channel_name, num);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_switch_timer_write: "
+ "ltt_trace_set_channel_switch_timer failed: %d\n", err);
+ goto err_set_switch_timer;
+ }
+
+ free_page((unsigned long)buf);
+ return count;
+
+err_set_switch_timer:
+err_get_number:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ return err;
+}
+
+static struct file_operations ltt_channel_switch_timer_operations = {
+ .write = channel_switch_timer_write,
+};
+
+static
+ssize_t channel_overwrite_write(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ const char *channel_name;
+ const char *trace_name;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *cmd = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if (cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ switch (cmd[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
+ 1);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_overwrite_write: "
+ "ltt_trace_set_channel_overwrite failed: %d\n",
+ err);
+ goto err_set_subbufsize;
+ }
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
+ 0);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_overwrite_write: "
+ "ltt_trace_set_channel_overwrite failed: %d\n",
+ err);
+ goto err_set_subbufsize;
+ }
+ break;
+ default:
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return count;
+
+err_set_subbufsize:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return err;
+}
+
+static const struct file_operations ltt_channel_overwrite_operations = {
+ .write = channel_overwrite_write,
+};
+
+
+static
+ssize_t channel_enable_write(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ const char *channel_name;
+ const char *trace_name;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *cmd = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if (cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ switch (cmd[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ err = ltt_trace_set_channel_enable(trace_name, channel_name,
+ 1);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_enable_write: "
+ "ltt_trace_set_channel_enable failed: %d\n",
+ err);
+ goto err_set_subbufsize;
+ }
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ err = ltt_trace_set_channel_enable(trace_name, channel_name,
+ 0);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "channel_enable_write: "
+ "ltt_trace_set_channel_enable failed: %d\n",
+ err);
+ goto err_set_subbufsize;
+ }
+ break;
+ default:
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return count;
+
+err_set_subbufsize:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)cmd);
+ return err;
+}
+
+static const struct file_operations ltt_channel_enable_operations = {
+ .write = channel_enable_write,
+};
+
+
+static int _create_trace_control_dir(const char *trace_name,
+ struct ltt_trace *trace)
+{
+ int err;
+ struct dentry *trace_root, *channel_root;
+ struct dentry *tmp_den;
+ int i;
+
+ /* debugfs/control/trace_name */
+ trace_root = debugfs_create_dir(trace_name, ltt_control_dir);
+ if (IS_ERR(trace_root) || !trace_root) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create control root dir of %s failed\n", trace_name);
+ err = -ENOMEM;
+ goto err_create_trace_root;
+ }
+
+ /* debugfs/control/trace_name/alloc */
+ tmp_den = debugfs_create_file("alloc", S_IWUSR, trace_root, NULL,
+ <t_alloc_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of alloc failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/trans */
+ tmp_den = debugfs_create_file("trans", S_IWUSR, trace_root, NULL,
+ <t_trans_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of trans failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/enabled */
+ tmp_den = debugfs_create_file("enabled", S_IWUSR, trace_root, NULL,
+ <t_enabled_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of enabled failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/channel/ */
+ channel_root = debugfs_create_dir("channel", trace_root);
+ if (IS_ERR(channel_root) || !channel_root) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create dir of channel failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /*
+ * Create dir and files in debugfs/ltt/control/trace_name/channel/
+ * Following things(without <>) will be created:
+ * `-- <control>
+ * `-- <trace_name>
+ * `-- <channel>
+ * |-- <channel_name>
+ * | |-- enable
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | |-- subbuf_size
+ * | `-- switch_timer
+ * `-- ...
+ */
+
+ for (i = 0; i < trace->nr_channels; i++) {
+ struct dentry *channel_den;
+ struct ltt_chan *chan;
+
+ chan = &trace->channels[i];
+ if (!chan->active)
+ continue;
+ channel_den = debugfs_create_dir(chan->a.filename,
+ channel_root);
+ if (IS_ERR(channel_den) || !channel_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create channel dir of %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("subbuf_num", S_IWUSR,
+ channel_den, NULL,
+ <t_channel_subbuf_num_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create subbuf_num in %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("subbuf_size", S_IWUSR,
+ channel_den, NULL,
+ <t_channel_subbuf_size_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create subbuf_size in %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("enable", S_IWUSR, channel_den,
+ NULL,
+ <t_channel_enable_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create enable in %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("overwrite", S_IWUSR, channel_den,
+ NULL,
+ <t_channel_overwrite_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create overwrite in %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("switch_timer", S_IWUSR,
+ channel_den, NULL,
+ <t_channel_switch_timer_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create switch_timer in %s failed\n",
+ chan->a.filename);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+ }
+
+ return 0;
+
+err_create_subdir:
+ debugfs_remove_recursive(trace_root);
+err_create_trace_root:
+ return err;
+}
+
+static
+ssize_t setup_trace_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ int buf_size;
+ struct ltt_trace *trace;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *trace_name = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trace_name) != 1) {
+ err = -EPERM;
+ goto err_get_tracename;
+ }
+
+ mutex_lock(&control_lock);
+ ltt_lock_traces();
+
+ err = _ltt_trace_setup(trace_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "setup_trace_write: ltt_trace_setup failed: %d\n", err);
+ goto err_setup_trace;
+ }
+ trace = _ltt_trace_find_setup(trace_name);
+ BUG_ON(!trace);
+ err = _create_trace_control_dir(trace_name, trace);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "setup_trace_write: "
+ "_create_trace_control_dir failed: %d\n", err);
+ goto err_create_trace_control_dir;
+ }
+
+ ltt_unlock_traces();
+ mutex_unlock(&control_lock);
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trace_name);
+ return count;
+
+err_create_trace_control_dir:
+ ltt_trace_destroy(trace_name);
+err_setup_trace:
+ ltt_unlock_traces();
+ mutex_unlock(&control_lock);
+err_get_tracename:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trace_name);
+ return err;
+}
+
+static const struct file_operations ltt_setup_trace_operations = {
+ .write = setup_trace_write,
+};
+
+static
+ssize_t destroy_trace_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *trace_den;
+ int buf_size;
+ int err = 0;
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ char *trace_name = (char *)__get_free_page(GFP_KERNEL);
+
+ buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trace_name) != 1) {
+ err = -EPERM;
+ goto err_get_tracename;
+ }
+
+ mutex_lock(&control_lock);
+
+ err = ltt_trace_destroy(trace_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "destroy_trace_write: ltt_trace_destroy failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_destroy_trace;
+ }
+
+ trace_den = dir_lookup(ltt_control_dir, trace_name);
+ if (!trace_den) {
+ printk(KERN_ERR
+ "destroy_trace_write: lookup for %s's dentry failed\n",
+ trace_name);
+ err = -ENOENT;
+ goto err_get_dentry;
+ }
+
+ debugfs_remove_recursive(trace_den);
+
+ mutex_unlock(&control_lock);
+
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trace_name);
+ return count;
+
+err_get_dentry:
+err_destroy_trace:
+ mutex_unlock(&control_lock);
+err_get_tracename:
+err_copy_from_user:
+ free_page((unsigned long)buf);
+ free_page((unsigned long)trace_name);
+ return err;
+}
+
+static const struct file_operations ltt_destroy_trace_operations = {
+ .write = destroy_trace_write,
+};
+
+static void init_marker_dir(struct dentry *dentry,
+ const struct inode_operations *opt)
+{
+ dentry->d_inode->i_op = opt;
+}
+
+static
+ssize_t marker_enable_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *buf;
+ const char *channel, *marker;
+ int len, enabled, present;
+
+ marker = filp->f_dentry->d_parent->d_name.name;
+ channel = filp->f_dentry->d_parent->d_parent->d_name.name;
+
+ len = 0;
+ buf = (char *)__get_free_page(GFP_KERNEL);
+
+ /*
+ * Note: we cannot take the marker lock to make these two checks
+ * atomic, because the marker mutex nests inside the module mutex, taken
+ * inside the marker present check.
+ */
+ enabled = is_marker_enabled(channel, marker);
+ present = is_marker_present(channel, marker);
+
+ if (enabled && present)
+ len = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+ else if (enabled && !present)
+ len = snprintf(buf, PAGE_SIZE, "%d\n", 2);
+ else
+ len = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+
+
+ if (len >= PAGE_SIZE) {
+ len = PAGE_SIZE;
+ buf[PAGE_SIZE] = '\0';
+ }
+ len = simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+ free_page((unsigned long)buf);
+
+ return len;
+}
+
+static
+ssize_t marker_enable_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *buf = (char *)__get_free_page(GFP_KERNEL);
+ int buf_size;
+ ssize_t ret = 0;
+ const char *channel, *marker;
+
+ marker = filp->f_dentry->d_parent->d_name.name;
+ channel = filp->f_dentry->d_parent->d_parent->d_name.name;
+
+ buf_size = min_t(size_t, cnt, PAGE_SIZE - 1);
+ ret = copy_from_user(buf, ubuf, buf_size);
+ if (ret)
+ goto end;
+
+ buf[buf_size] = 0;
+
+ switch (buf[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ ret = ltt_marker_connect(channel, marker, "default");
+ if (ret)
+ goto end;
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ ret = ltt_marker_disconnect(channel, marker, "default");
+ if (ret)
+ goto end;
+ break;
+ default:
+ ret = -EPERM;
+ goto end;
+ }
+ ret = cnt;
+end:
+ free_page((unsigned long)buf);
+ return ret;
+}
+
+static const struct file_operations enable_fops = {
+ .read = marker_enable_read,
+ .write = marker_enable_write,
+};
+
+/*
+ * In practice, the output size should never be larger than 4096 kB. If it
+ * ever happens, the output will simply be truncated.
+ */
+static
+ssize_t marker_info_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *buf;
+ const char *channel, *marker;
+ int len;
+ struct marker_iter iter;
+
+ marker = filp->f_dentry->d_parent->d_name.name;
+ channel = filp->f_dentry->d_parent->d_parent->d_name.name;
+
+ len = 0;
+ buf = (char *)__get_free_page(GFP_KERNEL);
+
+ if (is_marker_enabled(channel, marker) &&
+ !is_marker_present(channel, marker)) {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "Marker Pre-enabled\n");
+ goto out;
+ }
+
+ marker_iter_reset(&iter);
+ marker_iter_start(&iter);
+ for (; iter.marker != NULL; marker_iter_next(&iter)) {
+ if (!strcmp(iter.marker->channel, channel) &&
+ !strcmp(iter.marker->name, marker))
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "Location: %s\n"
+ "format: \"%s\"\nstate: %d\n"
+ "event_id: %hu\n"
+ "call: 0x%p\n"
+ "probe %s : 0x%p\n\n",
+#ifdef CONFIG_MODULES
+ iter.module ? iter.module->name :
+#endif
+ "Core Kernel",
+ iter.marker->format,
+ _imv_read(iter.marker->state),
+ iter.marker->event_id,
+ iter.marker->call,
+ iter.marker->ptype ?
+ "multi" : "single", iter.marker->ptype ?
+ (void *)iter.marker->multi :
+ (void *)iter.marker->single.func);
+ if (len >= PAGE_SIZE)
+ break;
+ }
+ marker_iter_stop(&iter);
+
+out:
+ if (len >= PAGE_SIZE) {
+ len = PAGE_SIZE;
+ buf[PAGE_SIZE] = '\0';
+ }
+
+ len = simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+ free_page((unsigned long)buf);
+
+ return len;
+}
+
+static const struct file_operations info_fops = {
+ .read = marker_info_read,
+};
+
+static int marker_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct dentry *marker_d, *enable_d, *info_d, *channel_d;
+ int ret;
+
+ ret = 0;
+ channel_d = (struct dentry *)dir->i_private;
+ mutex_unlock(&dir->i_mutex);
+
+ marker_d = debugfs_create_dir(dentry->d_name.name,
+ channel_d);
+ if (IS_ERR(marker_d)) {
+ ret = PTR_ERR(marker_d);
+ goto out;
+ }
+
+ enable_d = debugfs_create_file("enable", 0644, marker_d,
+ NULL, &enable_fops);
+ if (IS_ERR(enable_d) || !enable_d) {
+ printk(KERN_ERR
+ "%s: create file of %s failed\n",
+ __func__, "enable");
+ ret = -ENOMEM;
+ goto remove_marker_dir;
+ }
+
+ info_d = debugfs_create_file("info", 0644, marker_d,
+ NULL, &info_fops);
+ if (IS_ERR(info_d) || !info_d) {
+ printk(KERN_ERR
+ "%s: create file of %s failed\n",
+ __func__, "info");
+ ret = -ENOMEM;
+ goto remove_enable_dir;
+ }
+
+ goto out;
+
+remove_enable_dir:
+ debugfs_remove(enable_d);
+remove_marker_dir:
+ debugfs_remove(marker_d);
+out:
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+ return ret;
+}
+
+static int marker_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *marker_d, *channel_d;
+ const char *channel, *name;
+ int ret, enabled, present;
+
+ ret = 0;
+
+ channel_d = (struct dentry *)dir->i_private;
+ channel = channel_d->d_name.name;
+
+ marker_d = dir_lookup(channel_d, dentry->d_name.name);
+
+ if (!marker_d) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ name = marker_d->d_name.name;
+
+ enabled = is_marker_enabled(channel, name);
+ present = is_marker_present(channel, name);
+
+ if (present || (!present && enabled)) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ mutex_unlock(&dir->i_mutex);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ debugfs_remove_recursive(marker_d);
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+ mutex_lock(&dentry->d_inode->i_mutex);
+out:
+ return ret;
+}
+
+const struct inode_operations channel_dir_opt = {
+ .lookup = simple_lookup,
+ .mkdir = marker_mkdir,
+ .rmdir = marker_rmdir,
+};
+
+static int channel_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct dentry *channel_d;
+ int ret;
+
+ ret = 0;
+ mutex_unlock(&dir->i_mutex);
+
+ channel_d = debugfs_create_dir(dentry->d_name.name,
+ markers_control_dir);
+ if (IS_ERR(channel_d)) {
+ ret = PTR_ERR(channel_d);
+ goto out;
+ }
+
+ channel_d->d_inode->i_private = (void *)channel_d;
+ init_marker_dir(channel_d, &channel_dir_opt);
+out:
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+ return ret;
+}
+
+static int channel_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *channel_d;
+ int ret;
+
+ ret = 0;
+
+ channel_d = dir_lookup(markers_control_dir, dentry->d_name.name);
+ if (!channel_d) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (list_empty(&channel_d->d_subdirs)) {
+ mutex_unlock(&dir->i_mutex);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ debugfs_remove(channel_d);
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+ mutex_lock(&dentry->d_inode->i_mutex);
+ } else
+ ret = -EPERM;
+
+out:
+ return ret;
+}
+
+const struct inode_operations root_dir_opt = {
+ .lookup = simple_lookup,
+ .mkdir = channel_mkdir,
+ .rmdir = channel_rmdir
+};
+
+static int build_marker_file(struct marker *marker)
+{
+ struct dentry *channel_d, *marker_d, *enable_d, *info_d;
+ int err;
+
+ channel_d = dir_lookup(markers_control_dir, marker->channel);
+ if (!channel_d) {
+ channel_d = debugfs_create_dir(marker->channel,
+ markers_control_dir);
+ if (IS_ERR(channel_d) || !channel_d) {
+ printk(KERN_ERR
+ "%s: build channel dir of %s failed\n",
+ __func__, marker->channel);
+ err = -ENOMEM;
+ goto err_build_fail;
+ }
+ channel_d->d_inode->i_private = (void *)channel_d;
+ init_marker_dir(channel_d, &channel_dir_opt);
+ }
+
+ marker_d = dir_lookup(channel_d, marker->name);
+ if (!marker_d) {
+ marker_d = debugfs_create_dir(marker->name, channel_d);
+ if (IS_ERR(marker_d) || !marker_d) {
+ printk(KERN_ERR
+ "%s: marker dir of %s failed\n",
+ __func__, marker->name);
+ err = -ENOMEM;
+ goto err_build_fail;
+ }
+ }
+
+ enable_d = dir_lookup(marker_d, "enable");
+ if (!enable_d) {
+ enable_d = debugfs_create_file("enable", 0644, marker_d,
+ NULL, &enable_fops);
+ if (IS_ERR(enable_d) || !enable_d) {
+ printk(KERN_ERR
+ "%s: create file of %s failed\n",
+ __func__, "enable");
+ err = -ENOMEM;
+ goto err_build_fail;
+ }
+ }
+
+ info_d = dir_lookup(marker_d, "info");
+ if (!info_d) {
+ info_d = debugfs_create_file("info", 0444, marker_d,
+ NULL, &info_fops);
+ if (IS_ERR(info_d) || !info_d) {
+ printk(KERN_ERR
+ "%s: create file of %s failed\n",
+ __func__, "enable");
+ err = -ENOMEM;
+ goto err_build_fail;
+ }
+ }
+
+ return 0;
+
+err_build_fail:
+ return err;
+}
+
+static int build_marker_control_files(void)
+{
+ struct marker_iter iter;
+ int err;
+
+ err = 0;
+ if (!markers_control_dir)
+ return -EEXIST;
+
+ marker_iter_reset(&iter);
+ marker_iter_start(&iter);
+ for (; iter.marker != NULL; marker_iter_next(&iter)) {
+ err = build_marker_file(iter.marker);
+ if (err)
+ goto out;
+ }
+ marker_iter_stop(&iter);
+
+out:
+ return err;
+}
+
+#ifdef CONFIG_MODULES
+static int remove_marker_control_dir(struct module *mod, struct marker *marker)
+{
+ struct dentry *channel_d, *marker_d;
+ const char *channel, *name;
+ int count;
+ struct marker_iter iter;
+
+ count = 0;
+
+ channel_d = dir_lookup(markers_control_dir, marker->channel);
+ if (!channel_d)
+ return -ENOENT;
+ channel = channel_d->d_name.name;
+
+ marker_d = dir_lookup(channel_d, marker->name);
+ if (!marker_d)
+ return -ENOENT;
+ name = marker_d->d_name.name;
+
+ marker_iter_reset(&iter);
+ marker_iter_start(&iter);
+ for (; iter.marker != NULL; marker_iter_next(&iter)) {
+ if (!strcmp(iter.marker->channel, channel) &&
+ !strcmp(iter.marker->name, name) && mod != iter.module)
+ count++;
+ }
+
+ if (count > 0)
+ goto end;
+
+ debugfs_remove_recursive(marker_d);
+ if (list_empty(&channel_d->d_subdirs))
+ debugfs_remove(channel_d);
+
+end:
+ marker_iter_stop(&iter);
+ return 0;
+}
+
+static void cleanup_control_dir(struct module *mod, struct marker *begin,
+ struct marker *end)
+{
+ struct marker *iter;
+
+ if (!markers_control_dir)
+ return;
+
+ for (iter = begin; iter < end; iter++)
+ remove_marker_control_dir(mod, iter);
+
+ return;
+}
+
+static void build_control_dir(struct module *mod, struct marker *begin,
+ struct marker *end)
+{
+ struct marker *iter;
+ int err;
+
+ err = 0;
+ if (!markers_control_dir)
+ return;
+
+ for (iter = begin; iter < end; iter++) {
+ err = build_marker_file(iter);
+ if (err)
+ goto err_build_fail;
+ }
+
+ return;
+err_build_fail:
+ cleanup_control_dir(mod, begin, end);
+}
+
+static int module_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct module *mod = data;
+
+ switch (val) {
+ case MODULE_STATE_COMING:
+ build_control_dir(mod, mod->markers,
+ mod->markers + mod->num_markers);
+ break;
+ case MODULE_STATE_GOING:
+ cleanup_control_dir(mod, mod->markers,
+ mod->markers + mod->num_markers);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+#else
+static inline int module_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ return 0;
+}
+#endif
+
+static struct notifier_block module_nb = {
+ .notifier_call = module_notify,
+};
+
+static int __init ltt_trace_control_init(void)
+{
+ int err = 0;
+ struct dentry *ltt_root_dentry;
+
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry) {
+ err = -ENOENT;
+ goto err_no_root;
+ }
+
+ ltt_control_dir = debugfs_create_dir(LTT_CONTROL_DIR, ltt_root_dentry);
+ if (IS_ERR(ltt_control_dir) || !ltt_control_dir) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create dir of %s failed\n",
+ LTT_CONTROL_DIR);
+ err = -ENOMEM;
+ goto err_create_control_dir;
+ }
+
+ ltt_setup_trace_file = debugfs_create_file(LTT_SETUP_TRACE_FILE,
+ S_IWUSR, ltt_root_dentry,
+ NULL,
+ <t_setup_trace_operations);
+ if (IS_ERR(ltt_setup_trace_file) || !ltt_setup_trace_file) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create file of %s failed\n",
+ LTT_SETUP_TRACE_FILE);
+ err = -ENOMEM;
+ goto err_create_setup_trace_file;
+ }
+
+ ltt_destroy_trace_file = debugfs_create_file(LTT_DESTROY_TRACE_FILE,
+ S_IWUSR, ltt_root_dentry,
+ NULL,
+ <t_destroy_trace_operations);
+ if (IS_ERR(ltt_destroy_trace_file) || !ltt_destroy_trace_file) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create file of %s failed\n",
+ LTT_DESTROY_TRACE_FILE);
+ err = -ENOMEM;
+ goto err_create_destroy_trace_file;
+ }
+
+ markers_control_dir = debugfs_create_dir(MARKERS_CONTROL_DIR,
+ ltt_root_dentry);
+ if (IS_ERR(markers_control_dir) || !markers_control_dir) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create dir of %s failed\n",
+ MARKERS_CONTROL_DIR);
+ err = -ENOMEM;
+ goto err_create_marker_control_dir;
+ }
+
+ init_marker_dir(markers_control_dir, &root_dir_opt);
+
+ if (build_marker_control_files())
+ goto err_build_fail;
+
+ if (!register_module_notifier(&module_nb))
+ return 0;
+
+err_build_fail:
+ debugfs_remove_recursive(markers_control_dir);
+ markers_control_dir = NULL;
+err_create_marker_control_dir:
+ debugfs_remove(ltt_destroy_trace_file);
+err_create_destroy_trace_file:
+ debugfs_remove(ltt_setup_trace_file);
+err_create_setup_trace_file:
+ debugfs_remove(ltt_control_dir);
+err_create_control_dir:
+err_no_root:
+ return err;
+}
+
+static void __exit ltt_trace_control_exit(void)
+{
+ struct dentry *trace_dir;
+
+ /* destory all traces */
+ list_for_each_entry(trace_dir, <t_control_dir->d_subdirs,
+ d_u.d_child) {
+ ltt_trace_stop(trace_dir->d_name.name);
+ ltt_trace_destroy(trace_dir->d_name.name);
+ }
+
+ /* clean dirs in debugfs */
+ debugfs_remove(ltt_setup_trace_file);
+ debugfs_remove(ltt_destroy_trace_file);
+ debugfs_remove_recursive(ltt_control_dir);
+ debugfs_remove_recursive(markers_control_dir);
+ unregister_module_notifier(&module_nb);
+ put_ltt_root();
+}
+
+module_init(ltt_trace_control_init);
+module_exit(ltt_trace_control_exit);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Zhao Lei <zhaolei@cn.fujitsu.com>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Trace Controller");
--- /dev/null
+/*
+ * Copyright (C) 2008 Mathieu Desnoyers
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <linux/module.h>
+#include <linux/marker.h>
+#include <linux/uaccess.h>
+#include <linux/gfp.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include "ltt-type-serializer.h"
+
+#define LTT_WRITE_EVENT_FILE "write_event"
+
+DEFINE_MARKER(userspace, event, "string %s");
+static struct dentry *ltt_event_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, in channel "userspace", event "event".
+ * Copies until either \n or \0 is reached.
+ * On success, returns the number of bytes copied from the source, including the
+ * \n or \0 character (if there was one in the count range). It cannot return
+ * more than count.
+ * 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 *ppos)
+{
+ struct marker *marker;
+ char *buf, *end;
+ long copycount;
+ ssize_t ret;
+
+ buf = kmalloc(count + 1, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto string_out;
+ }
+ copycount = strncpy_from_user(buf, user_buf, count);
+ if (copycount < 0) {
+ ret = -EFAULT;
+ goto string_err;
+ }
+ /* Cut from the first nil or newline. */
+ buf[copycount] = '\0';
+ end = strchr(buf, '\n');
+ if (end) {
+ *end = '\0';
+ copycount = end - buf;
+ }
+ /* Add final \0 to copycount */
+ copycount++;
+ marker = &GET_MARKER(userspace, event);
+ ltt_specialized_trace(marker, marker->single.probe_private, buf,
+ copycount, sizeof(char));
+ /* If there is no \0 nor \n in count, do not return a larger value */
+ ret = min_t(size_t, copycount, count);
+string_err:
+ kfree(buf);
+string_out:
+ return ret;
+}
+
+static const struct file_operations ltt_userspace_operations = {
+ .write = write_event,
+};
+
+static int __init ltt_userspace_init(void)
+{
+ struct dentry *ltt_root_dentry;
+ int err = 0;
+
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry) {
+ err = -ENOENT;
+ goto err_no_root;
+ }
+
+ ltt_event_file = debugfs_create_file(LTT_WRITE_EVENT_FILE,
+ S_IWUGO,
+ ltt_root_dentry,
+ NULL,
+ <t_userspace_operations);
+ if (IS_ERR(ltt_event_file) || !ltt_event_file) {
+ printk(KERN_ERR
+ "ltt_userspace_init: failed to create file %s\n",
+ LTT_WRITE_EVENT_FILE);
+ err = -EPERM;
+ goto err_no_file;
+ }
+
+ return err;
+err_no_file:
+ put_ltt_root();
+err_no_root:
+ return err;
+}
+
+static void __exit ltt_userspace_exit(void)
+{
+ debugfs_remove(ltt_event_file);
+ put_ltt_root();
+}
+
+module_init(ltt_userspace_init);
+module_exit(ltt_userspace_exit);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Userspace Event");
+++ /dev/null
-/*
- * Copyright (C) 2008 Mathieu Desnoyers
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-
-#include "ltt-tracer.h"
-
-#define LTT_FILTER_DIR "filter"
-
-/*
- * Protects the ltt_filter_dir allocation.
- */
-static DEFINE_MUTEX(ltt_filter_mutex);
-
-static struct dentry *ltt_filter_dir;
-
-struct dentry *get_filter_root(void)
-{
- struct dentry *ltt_root_dentry;
-
- mutex_lock(<t_filter_mutex);
- if (!ltt_filter_dir) {
- ltt_root_dentry = get_ltt_root();
- if (!ltt_root_dentry)
- goto err_no_root;
-
- ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR,
- ltt_root_dentry);
- if (!ltt_filter_dir)
- printk(KERN_ERR
- "ltt_filter_init: failed to create dir %s\n",
- LTT_FILTER_DIR);
- }
-err_no_root:
- mutex_unlock(<t_filter_mutex);
- return ltt_filter_dir;
-}
-EXPORT_SYMBOL_GPL(get_filter_root);
-
-static void __exit ltt_filter_exit(void)
-{
- debugfs_remove(ltt_filter_dir);
-}
-
-module_exit(ltt_filter_exit);
-
-MODULE_LICENSE("GPL and additional rights");
-MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>");
-MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
+++ /dev/null
-/*
- * (C) Copyright 2009 -
- * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca)
- *
- * LTTng kprobes integration module.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <linux/module.h>
-#include <linux/kprobes.h>
-#include <linux/marker.h>
-#include <linux/mutex.h>
-#include <linux/jhash.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/debugfs.h>
-#include <linux/kallsyms.h>
-
-#include "ltt-type-serializer.h"
-#include "ltt-tracer.h"
-
-#define LTT_KPROBES_DIR "kprobes"
-#define LTT_KPROBES_ENABLE "enable"
-#define LTT_KPROBES_DISABLE "disable"
-#define LTT_KPROBES_LIST "list"
-
-/* Active LTTng kprobes hash table */
-static DEFINE_MUTEX(ltt_kprobes_mutex);
-
-#define LTT_KPROBE_HASH_BITS 6
-#define LTT_KPROBE_TABLE_SIZE (1 << LTT_KPROBE_HASH_BITS)
-static struct hlist_head ltt_kprobe_table[LTT_KPROBE_TABLE_SIZE];
-
-struct kprobe_entry {
- struct hlist_node hlist;
- struct kprobe kp;
- char key[0];
-};
-
-static struct dentry *ltt_kprobes_dir,
- *ltt_kprobes_enable_dentry,
- *ltt_kprobes_disable_dentry,
- *ltt_kprobes_list_dentry;
-
-static int module_exit;
-
-
-static void trace_kprobe_table_entry(void *call_data, struct kprobe_entry *e)
-{
- unsigned long addr;
- char *namebuf = (char *)__get_free_page(GFP_KERNEL);
-
- if (e->kp.addr) {
- sprint_symbol(namebuf, (unsigned long)e->kp.addr);
- addr = (unsigned long)e->kp.addr;
- } else {
- strncpy(namebuf, e->kp.symbol_name, PAGE_SIZE - 1);
- /* TODO : add offset */
- addr = kallsyms_lookup_name(namebuf);
- }
- if (addr)
- __trace_mark(0, kprobe_state, kprobe_table, call_data,
- "ip 0x%lX symbol %s", addr, namebuf);
- free_page((unsigned long)namebuf);
-}
-
-DEFINE_MARKER(kernel, kprobe, "ip %lX");
-
-static int ltt_kprobe_handler_pre(struct kprobe *p, struct pt_regs *regs)
-{
- struct marker *marker;
- unsigned long data;
-
- data = (unsigned long)p->addr;
- marker = &GET_MARKER(kernel, kprobe);
- ltt_specialized_trace(marker, marker->single.probe_private,
- &data, sizeof(data), sizeof(data));
- return 0;
-}
-
-static int ltt_register_kprobe(const char *key)
-{
- struct hlist_head *head;
- struct hlist_node *node;
- struct kprobe_entry *e = NULL;
- char *symbol_name = NULL;
- unsigned long addr;
- unsigned int offset = 0;
- u32 hash;
- size_t key_len = strlen(key) + 1;
- int ret;
-
- if (key_len == 1)
- return -ENOENT; /* only \0 */
-
- if (sscanf(key, "%li", &addr) != 1)
- addr = 0;
-
- if (!addr) {
- const char *symbol_end = NULL;
- unsigned int symbol_len; /* includes final \0 */
-
- symbol_end = strchr(key, ' ');
- if (symbol_end)
- symbol_len = symbol_end - key + 1;
- else
- symbol_len = key_len;
- symbol_name = kmalloc(symbol_len, GFP_KERNEL);
- if (!symbol_name) {
- ret = -ENOMEM;
- goto error;
- }
- memcpy(symbol_name, key, symbol_len - 1);
- symbol_name[symbol_len-1] = '\0';
- if (symbol_end) {
- symbol_end++; /* start of offset */
- if (sscanf(symbol_end, "%i", &offset) != 1)
- offset = 0;
- }
- }
-
- hash = jhash(key, key_len-1, 0);
- head = <t_kprobe_table[hash & ((1 << LTT_KPROBE_HASH_BITS)-1)];
- hlist_for_each_entry(e, node, head, hlist) {
- if (!strcmp(key, e->key)) {
- printk(KERN_NOTICE "Kprobe %s busy\n", key);
- ret = -EBUSY;
- goto error;
- }
- }
- /*
- * Using kzalloc here to allocate a variable length element. Could
- * cause some memory fragmentation if overused.
- */
- e = kzalloc(sizeof(struct kprobe_entry) + key_len, GFP_KERNEL);
- if (!e) {
- ret = -ENOMEM;
- goto error;
- }
- memcpy(e->key, key, key_len);
- hlist_add_head(&e->hlist, head);
- e->kp.pre_handler = ltt_kprobe_handler_pre;
- e->kp.symbol_name = symbol_name;
- e->kp.offset = offset;
- e->kp.addr = (void *)addr;
- ret = register_kprobe(&e->kp);
- if (ret < 0)
- goto error_list_del;
- trace_kprobe_table_entry(NULL, e);
- return 0;
-
-error_list_del:
- hlist_del(&e->hlist);
-error:
- kfree(symbol_name);
- kfree(e);
- return ret;
-}
-
-static int ltt_unregister_kprobe(const char *key)
-{
- struct hlist_head *head;
- struct hlist_node *node;
- struct kprobe_entry *e;
- int found = 0;
- size_t key_len = strlen(key) + 1;
- u32 hash;
-
- hash = jhash(key, key_len-1, 0);
- head = <t_kprobe_table[hash & ((1 << LTT_KPROBE_HASH_BITS)-1)];
- hlist_for_each_entry(e, node, head, hlist) {
- if (!strcmp(key, e->key)) {
- found = 1;
- break;
- }
- }
- if (!found)
- return -ENOENT;
- hlist_del(&e->hlist);
- unregister_kprobe(&e->kp);
- kfree(e->kp.symbol_name);
- kfree(e);
- return 0;
-}
-
-static void ltt_unregister_all_kprobes(void)
-{
- struct kprobe_entry *e;
- struct hlist_head *head;
- struct hlist_node *node, *tmp;
- unsigned int i;
-
- for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
- head = <t_kprobe_table[i];
- hlist_for_each_entry_safe(e, node, tmp, head, hlist) {
- hlist_del(&e->hlist);
- unregister_kprobe(&e->kp);
- kfree(e->kp.symbol_name);
- kfree(e);
- }
- }
-}
-
-/*
- * Allows to specify either
- * - symbol
- * - symbol offset
- * - address
- */
-static ssize_t enable_op_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err, buf_size;
- char *end;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
-
- mutex_lock(<t_kprobes_mutex);
- if (module_exit) {
- err = -EPERM;
- goto error;
- }
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto error;
- buf[buf_size] = '\0';
- end = strchr(buf, '\n');
- if (end)
- *end = '\0';
- err = ltt_register_kprobe(buf);
- if (err)
- goto error;
-
- mutex_unlock(<t_kprobes_mutex);
- free_page((unsigned long)buf);
- return count;
-error:
- mutex_unlock(<t_kprobes_mutex);
- free_page((unsigned long)buf);
- return err;
-}
-
-static const struct file_operations ltt_kprobes_enable = {
- .write = enable_op_write,
-};
-
-static ssize_t disable_op_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err, buf_size;
- char *end;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
-
- mutex_lock(<t_kprobes_mutex);
- if (module_exit)
- goto end;
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto error;
- buf[buf_size] = '\0';
- end = strchr(buf, '\n');
- if (end)
- *end = '\0';
- err = ltt_unregister_kprobe(buf);
- if (err)
- goto error;
-end:
- mutex_unlock(<t_kprobes_mutex);
- free_page((unsigned long)buf);
- return count;
-error:
- mutex_unlock(<t_kprobes_mutex);
- free_page((unsigned long)buf);
- return err;
-}
-
-static const struct file_operations ltt_kprobes_disable = {
- .write = disable_op_write,
-};
-
-/*
- * This seqfile read is not perfectly safe, as a kprobe could be removed from
- * the hash table between two reads. This will result in an incomplete output.
- */
-static struct kprobe_entry *ltt_find_next_kprobe(struct kprobe_entry *prev)
-{
- struct kprobe_entry *e;
- struct hlist_head *head;
- struct hlist_node *node;
- unsigned int i;
- int found = 0;
-
- if (prev == (void *)-1UL)
- return NULL;
-
- if (!prev)
- found = 1;
-
- for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
- head = <t_kprobe_table[i];
- hlist_for_each_entry(e, node, head, hlist) {
- if (found)
- return e;
- if (e == prev)
- found = 1;
- }
- }
- return NULL;
-}
-
-static void *lk_next(struct seq_file *m, void *p, loff_t *pos)
-{
- m->private = ltt_find_next_kprobe(m->private);
- if (!m->private) {
- m->private = (void *)-1UL;
- return NULL;
- }
- return m->private;
-}
-
-static void *lk_start(struct seq_file *m, loff_t *pos)
-{
- mutex_lock(<t_kprobes_mutex);
- if (!*pos)
- m->private = NULL;
- m->private = ltt_find_next_kprobe(m->private);
- if (!m->private) {
- m->private = (void *)-1UL;
- return NULL;
- }
- return m->private;
-}
-
-static void lk_stop(struct seq_file *m, void *p)
-{
- mutex_unlock(<t_kprobes_mutex);
-}
-
-static int lk_show(struct seq_file *m, void *p)
-{
- struct kprobe_entry *e = m->private;
- seq_printf(m, "%s\n", e->key);
- return 0;
-}
-
-static const struct seq_operations ltt_kprobes_list_op = {
- .start = lk_start,
- .next = lk_next,
- .stop = lk_stop,
- .show = lk_show,
-};
-
-static int ltt_kprobes_list_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- ret = seq_open(file, <t_kprobes_list_op);
- if (ret == 0)
- ((struct seq_file *)file->private_data)->private = NULL;
- return ret;
-}
-
-static int ltt_kprobes_list_release(struct inode *inode, struct file *file)
-{
- struct seq_file *seq = file->private_data;
-
- seq->private = NULL;
- return seq_release(inode, file);
-}
-
-static const struct file_operations ltt_kprobes_list = {
- .open = ltt_kprobes_list_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = ltt_kprobes_list_release,
-};
-
-/*
- * kprobes table dump. Callback invoked by ltt-statedump. ltt-statedump must
- * take a reference to this module before calling this callback.
- */
-void ltt_dump_kprobes_table(void *call_data)
-{
- struct kprobe_entry *e;
- struct hlist_head *head;
- struct hlist_node *node;
- unsigned int i;
-
- for (i = 0; i < LTT_KPROBE_TABLE_SIZE; i++) {
- head = <t_kprobe_table[i];
- hlist_for_each_entry(e, node, head, hlist)
- trace_kprobe_table_entry(call_data, e);
- }
-}
-EXPORT_SYMBOL_GPL(ltt_dump_kprobes_table);
-
-static int __init ltt_kprobes_init(void)
-{
- struct dentry *ltt_root_dentry;
- int ret = 0;
-
- printk(KERN_INFO "LTT : ltt-kprobes init\n");
- mutex_lock(<t_kprobes_mutex);
-
- ltt_root_dentry = get_ltt_root();
- if (!ltt_root_dentry) {
- ret = -ENOENT;
- goto err_no_root;
- }
-
- ltt_kprobes_dir = debugfs_create_dir(LTT_KPROBES_DIR, ltt_root_dentry);
- if (!ltt_kprobes_dir) {
- printk(KERN_ERR
- "ltt_kprobes_init: failed to create dir %s\n",
- LTT_KPROBES_DIR);
- ret = -ENOMEM;
- goto err_no_dir;
- }
-
- ltt_kprobes_enable_dentry = debugfs_create_file(LTT_KPROBES_ENABLE,
- S_IWUSR,
- ltt_kprobes_dir, NULL,
- <t_kprobes_enable);
- if (IS_ERR(ltt_kprobes_enable_dentry) || !ltt_kprobes_enable_dentry) {
- printk(KERN_ERR
- "ltt_kprobes_init: failed to create file %s\n",
- LTT_KPROBES_ENABLE);
- ret = -ENOMEM;
- goto err_no_enable;
- }
-
- ltt_kprobes_disable_dentry = debugfs_create_file(LTT_KPROBES_DISABLE,
- S_IWUSR,
- ltt_kprobes_dir, NULL,
- <t_kprobes_disable);
- if (IS_ERR(ltt_kprobes_disable_dentry) || !ltt_kprobes_disable_dentry) {
- printk(KERN_ERR
- "ltt_kprobes_init: failed to create file %s\n",
- LTT_KPROBES_DISABLE);
- ret = -ENOMEM;
- goto err_no_disable;
- }
-
- ltt_kprobes_list_dentry = debugfs_create_file(LTT_KPROBES_LIST,
- S_IWUSR, ltt_kprobes_dir,
- NULL, <t_kprobes_list);
- if (IS_ERR(ltt_kprobes_list_dentry) || !ltt_kprobes_list_dentry) {
- printk(KERN_ERR
- "ltt_kprobes_init: failed to create file %s\n",
- LTT_KPROBES_LIST);
- ret = -ENOMEM;
- goto err_no_list;
- }
- ltt_statedump_register_kprobes_dump(ltt_dump_kprobes_table);
-
- mutex_unlock(<t_kprobes_mutex);
- return ret;
-
-err_no_list:
- debugfs_remove(ltt_kprobes_disable_dentry);
-err_no_disable:
- debugfs_remove(ltt_kprobes_enable_dentry);
-err_no_enable:
- debugfs_remove(ltt_kprobes_dir);
-err_no_dir:
-err_no_root:
- mutex_unlock(<t_kprobes_mutex);
- return ret;
-}
-module_init(ltt_kprobes_init);
-
-static void __exit ltt_kprobes_exit(void)
-{
- printk(KERN_INFO "LTT : ltt-kprobes exit\n");
- mutex_lock(<t_kprobes_mutex);
- module_exit = 1;
- ltt_statedump_unregister_kprobes_dump(ltt_dump_kprobes_table);
- debugfs_remove(ltt_kprobes_list_dentry);
- debugfs_remove(ltt_kprobes_disable_dentry);
- debugfs_remove(ltt_kprobes_enable_dentry);
- debugfs_remove(ltt_kprobes_dir);
- ltt_unregister_all_kprobes();
- mutex_unlock(<t_kprobes_mutex);
-}
-module_exit(ltt_kprobes_exit);
-
-MODULE_LICENSE("GPL and additional rights");
-MODULE_AUTHOR("Mathieu Desnoyers");
-MODULE_DESCRIPTION("Linux Trace Toolkit Kprobes Support");
+++ /dev/null
-/*
- * Linux Trace Toolkit Kernel State Dump
- *
- * Copyright 2005 -
- * Jean-Hugues Deschenes <jean-hugues.deschenes@polymtl.ca>
- *
- * Changes:
- * Eric Clement: Add listing of network IP interface
- * 2006, 2007 Mathieu Desnoyers Fix kernel threads
- * Various updates
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/netlink.h>
-#include <linux/inet.h>
-#include <linux/ip.h>
-#include <linux/kthread.h>
-#include <linux/proc_fs.h>
-#include <linux/file.h>
-#include <linux/interrupt.h>
-#include <linux/irqnr.h>
-#include <linux/cpu.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/marker.h>
-#include <linux/fdtable.h>
-#include <linux/swap.h>
-#include <linux/wait.h>
-#include <linux/mutex.h>
-
-#include "ltt-tracer.h"
-
-#ifdef CONFIG_GENERIC_HARDIRQS
-#include <linux/irq.h>
-#endif
-
-#define NB_PROC_CHUNK 20
-
-/*
- * Protected by the trace lock.
- */
-static struct delayed_work cpu_work[NR_CPUS];
-static DECLARE_WAIT_QUEUE_HEAD(statedump_wq);
-static atomic_t kernel_threads_to_run;
-
-static void empty_cb(void *call_data)
-{
-}
-
-static DEFINE_MUTEX(statedump_cb_mutex);
-static void (*ltt_dump_kprobes_table_cb)(void *call_data) = empty_cb;
-
-enum lttng_thread_type {
- LTTNG_USER_THREAD = 0,
- LTTNG_KERNEL_THREAD = 1,
-};
-
-enum lttng_execution_mode {
- LTTNG_USER_MODE = 0,
- LTTNG_SYSCALL = 1,
- LTTNG_TRAP = 2,
- LTTNG_IRQ = 3,
- LTTNG_SOFTIRQ = 4,
- LTTNG_MODE_UNKNOWN = 5,
-};
-
-enum lttng_execution_submode {
- LTTNG_NONE = 0,
- LTTNG_UNKNOWN = 1,
-};
-
-enum lttng_process_status {
- LTTNG_UNNAMED = 0,
- LTTNG_WAIT_FORK = 1,
- LTTNG_WAIT_CPU = 2,
- LTTNG_EXIT = 3,
- LTTNG_ZOMBIE = 4,
- LTTNG_WAIT = 5,
- LTTNG_RUN = 6,
- LTTNG_DEAD = 7,
-};
-
-#ifdef CONFIG_INET
-static void ltt_enumerate_device(struct ltt_probe_private_data *call_data,
- struct net_device *dev)
-{
- struct in_device *in_dev;
- struct in_ifaddr *ifa;
-
- if (dev->flags & IFF_UP) {
- in_dev = in_dev_get(dev);
- if (in_dev) {
- for (ifa = in_dev->ifa_list; ifa != NULL;
- ifa = ifa->ifa_next)
- __trace_mark(0, netif_state,
- network_ipv4_interface,
- call_data,
- "name %s address #n4u%lu up %d",
- dev->name,
- (unsigned long)ifa->ifa_address,
- 0);
- in_dev_put(in_dev);
- }
- } else
- __trace_mark(0, netif_state, network_ip_interface,
- call_data, "name %s address #n4u%lu up %d",
- dev->name, 0UL, 0);
-}
-
-static inline int
-ltt_enumerate_network_ip_interface(struct ltt_probe_private_data *call_data)
-{
- struct net_device *dev;
-
- read_lock(&dev_base_lock);
- for_each_netdev(&init_net, dev)
- ltt_enumerate_device(call_data, dev);
- read_unlock(&dev_base_lock);
-
- return 0;
-}
-#else /* CONFIG_INET */
-static inline int
-ltt_enumerate_network_ip_interface(struct ltt_probe_private_data *call_data)
-{
- return 0;
-}
-#endif /* CONFIG_INET */
-
-
-static inline void
-ltt_enumerate_task_fd(struct ltt_probe_private_data *call_data,
- struct task_struct *t, char *tmp)
-{
- struct fdtable *fdt;
- struct file *filp;
- unsigned int i;
- const unsigned char *path;
-
- if (!t->files)
- return;
-
- spin_lock(&t->files->file_lock);
- fdt = files_fdtable(t->files);
- for (i = 0; i < fdt->max_fds; i++) {
- filp = fcheck_files(t->files, i);
- if (!filp)
- continue;
- path = d_path(&filp->f_path, tmp, PAGE_SIZE);
- /* Make sure we give at least some info */
- __trace_mark(0, fd_state, file_descriptor, call_data,
- "filename %s pid %d fd %u",
- (IS_ERR(path))?(filp->f_dentry->d_name.name):(path),
- t->pid, i);
- }
- spin_unlock(&t->files->file_lock);
-}
-
-static inline int
-ltt_enumerate_file_descriptors(struct ltt_probe_private_data *call_data)
-{
- struct task_struct *t = &init_task;
- char *tmp = (char *)__get_free_page(GFP_KERNEL);
-
- /* Enumerate active file descriptors */
- do {
- read_lock(&tasklist_lock);
- if (t != &init_task)
- atomic_dec(&t->usage);
- t = next_task(t);
- atomic_inc(&t->usage);
- read_unlock(&tasklist_lock);
- task_lock(t);
- ltt_enumerate_task_fd(call_data, t, tmp);
- task_unlock(t);
- } while (t != &init_task);
- free_page((unsigned long)tmp);
- return 0;
-}
-
-static inline void
-ltt_enumerate_task_vm_maps(struct ltt_probe_private_data *call_data,
- struct task_struct *t)
-{
- struct mm_struct *mm;
- struct vm_area_struct *map;
- unsigned long ino;
-
- /* get_task_mm does a task_lock... */
- mm = get_task_mm(t);
- if (!mm)
- return;
-
- map = mm->mmap;
- if (map) {
- down_read(&mm->mmap_sem);
- while (map) {
- if (map->vm_file)
- ino = map->vm_file->f_dentry->d_inode->i_ino;
- else
- ino = 0;
- __trace_mark(0, vm_state, vm_map, call_data,
- "pid %d start %lu end %lu flags %lu "
- "pgoff %lu inode %lu",
- t->pid, map->vm_start, map->vm_end,
- map->vm_flags, map->vm_pgoff << PAGE_SHIFT,
- ino);
- map = map->vm_next;
- }
- up_read(&mm->mmap_sem);
- }
- mmput(mm);
-}
-
-static inline int
-ltt_enumerate_vm_maps(struct ltt_probe_private_data *call_data)
-{
- struct task_struct *t = &init_task;
-
- do {
- read_lock(&tasklist_lock);
- if (t != &init_task)
- atomic_dec(&t->usage);
- t = next_task(t);
- atomic_inc(&t->usage);
- read_unlock(&tasklist_lock);
- ltt_enumerate_task_vm_maps(call_data, t);
- } while (t != &init_task);
- return 0;
-}
-
-#ifdef CONFIG_GENERIC_HARDIRQS
-static inline void list_interrupts(struct ltt_probe_private_data *call_data)
-{
- unsigned int irq;
- unsigned long flags = 0;
- struct irq_desc *desc;
-
- /* needs irq_desc */
- for_each_irq_desc(irq, desc) {
- struct irqaction *action;
- const char *irq_chip_name =
- desc->chip->name ? : "unnamed_irq_chip";
-
- local_irq_save(flags);
- raw_spin_lock(&desc->lock);
- for (action = desc->action; action; action = action->next)
- __trace_mark(0, irq_state, interrupt, call_data,
- "name %s action %s irq_id %u",
- irq_chip_name, action->name, irq);
- raw_spin_unlock(&desc->lock);
- local_irq_restore(flags);
- }
-}
-#else
-static inline void list_interrupts(struct ltt_probe_private_data *call_data)
-{
-}
-#endif
-
-static inline int
-ltt_enumerate_process_states(struct ltt_probe_private_data *call_data)
-{
- struct task_struct *t = &init_task;
- struct task_struct *p = t;
- enum lttng_process_status status;
- enum lttng_thread_type type;
- enum lttng_execution_mode mode;
- enum lttng_execution_submode submode;
-
- do {
- mode = LTTNG_MODE_UNKNOWN;
- submode = LTTNG_UNKNOWN;
-
- read_lock(&tasklist_lock);
- if (t != &init_task) {
- atomic_dec(&t->usage);
- t = next_thread(t);
- }
- if (t == p) {
- p = next_task(t);
- t = p;
- }
- atomic_inc(&t->usage);
- read_unlock(&tasklist_lock);
-
- task_lock(t);
-
- if (t->exit_state == EXIT_ZOMBIE)
- status = LTTNG_ZOMBIE;
- else if (t->exit_state == EXIT_DEAD)
- status = LTTNG_DEAD;
- else if (t->state == TASK_RUNNING) {
- /* Is this a forked child that has not run yet? */
- if (list_empty(&t->rt.run_list))
- status = LTTNG_WAIT_FORK;
- else
- /*
- * All tasks are considered as wait_cpu;
- * the viewer will sort out if the task was
- * really running at this time.
- */
- status = LTTNG_WAIT_CPU;
- } else if (t->state &
- (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)) {
- /* Task is waiting for something to complete */
- status = LTTNG_WAIT;
- } else
- status = LTTNG_UNNAMED;
- submode = LTTNG_NONE;
-
- /*
- * Verification of t->mm is to filter out kernel threads;
- * Viewer will further filter out if a user-space thread was
- * in syscall mode or not.
- */
- if (t->mm)
- type = LTTNG_USER_THREAD;
- else
- type = LTTNG_KERNEL_THREAD;
-
- __trace_mark(0, task_state, process_state, call_data,
- "pid %d parent_pid %d name %s type %d mode %d "
- "submode %d status %d tgid %d",
- t->pid, t->parent->pid, t->comm,
- type, mode, submode, status, t->tgid);
- task_unlock(t);
- } while (t != &init_task);
-
- return 0;
-}
-
-void ltt_statedump_register_kprobes_dump(void (*callback)(void *call_data))
-{
- mutex_lock(&statedump_cb_mutex);
- ltt_dump_kprobes_table_cb = callback;
- mutex_unlock(&statedump_cb_mutex);
-}
-EXPORT_SYMBOL_GPL(ltt_statedump_register_kprobes_dump);
-
-void ltt_statedump_unregister_kprobes_dump(void (*callback)(void *call_data))
-{
- mutex_lock(&statedump_cb_mutex);
- ltt_dump_kprobes_table_cb = empty_cb;
- mutex_unlock(&statedump_cb_mutex);
-}
-EXPORT_SYMBOL_GPL(ltt_statedump_unregister_kprobes_dump);
-
-void ltt_statedump_work_func(struct work_struct *work)
-{
- if (atomic_dec_and_test(&kernel_threads_to_run))
- /* If we are the last thread, wake up do_ltt_statedump */
- wake_up(&statedump_wq);
-}
-
-static int do_ltt_statedump(struct ltt_probe_private_data *call_data)
-{
- int cpu;
- struct module *cb_owner;
-
- printk(KERN_DEBUG "LTT state dump thread start\n");
- ltt_enumerate_process_states(call_data);
- ltt_enumerate_file_descriptors(call_data);
- list_modules(call_data);
- ltt_enumerate_vm_maps(call_data);
- list_interrupts(call_data);
- ltt_enumerate_network_ip_interface(call_data);
- ltt_dump_swap_files(call_data);
- ltt_dump_sys_call_table(call_data);
- ltt_dump_softirq_vec(call_data);
- ltt_dump_idt_table(call_data);
-
- mutex_lock(&statedump_cb_mutex);
-
- cb_owner = __module_address((unsigned long)ltt_dump_kprobes_table_cb);
- __module_get(cb_owner);
- ltt_dump_kprobes_table_cb(call_data);
- module_put(cb_owner);
-
- mutex_unlock(&statedump_cb_mutex);
-
- /*
- * Fire off a work queue on each CPU. Their sole purpose in life
- * is to guarantee that each CPU has been in a state where is was in
- * syscall mode (i.e. not in a trap, an IRQ or a soft IRQ).
- */
- get_online_cpus();
- atomic_set(&kernel_threads_to_run, num_online_cpus());
- for_each_online_cpu(cpu) {
- INIT_DELAYED_WORK(&cpu_work[cpu], ltt_statedump_work_func);
- schedule_delayed_work_on(cpu, &cpu_work[cpu], 0);
- }
- /* Wait for all threads to run */
- __wait_event(statedump_wq, (atomic_read(&kernel_threads_to_run) != 0));
- put_online_cpus();
- /* Our work is done */
- printk(KERN_DEBUG "LTT state dump end\n");
- __trace_mark(0, global_state, statedump_end,
- call_data, MARK_NOARGS);
- return 0;
-}
-
-/*
- * Called with trace lock held.
- */
-int ltt_statedump_start(struct ltt_trace *trace)
-{
- struct ltt_probe_private_data call_data;
- printk(KERN_DEBUG "LTT state dump begin\n");
-
- call_data.trace = trace;
- call_data.serializer = NULL;
- return do_ltt_statedump(&call_data);
-}
-
-static int __init statedump_init(void)
-{
- int ret;
- printk(KERN_DEBUG "LTT : State dump init\n");
- ret = ltt_module_register(LTT_FUNCTION_STATEDUMP,
- ltt_statedump_start, THIS_MODULE);
- return ret;
-}
-
-static void __exit statedump_exit(void)
-{
- printk(KERN_DEBUG "LTT : State dump exit\n");
- ltt_module_unregister(LTT_FUNCTION_STATEDUMP);
-}
-
-module_init(statedump_init)
-module_exit(statedump_exit)
-
-MODULE_LICENSE("GPL and additional rights");
-MODULE_AUTHOR("Jean-Hugues Deschenes");
-MODULE_DESCRIPTION("Linux Trace Toolkit Statedump");
+++ /dev/null
-/*
- * LTT trace control module over debugfs.
- *
- * Copyright 2008 - Zhaolei <zhaolei@cn.fujitsu.com>
- *
- * Copyright 2009 - Gui Jianfeng <guijianfeng@cn.fujitsu.com>
- * Make mark-control work in debugfs
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-/*
- * Todo:
- * Impl read operations for control file to read attributes
- * Create a README file in ltt control dir, for display help info
- */
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/uaccess.h>
-#include <linux/debugfs.h>
-#include <linux/notifier.h>
-#include <linux/jiffies.h>
-#include <linux/marker.h>
-
-#include "ltt-tracer.h"
-
-#define LTT_CONTROL_DIR "control"
-#define MARKERS_CONTROL_DIR "markers"
-#define LTT_SETUP_TRACE_FILE "setup_trace"
-#define LTT_DESTROY_TRACE_FILE "destroy_trace"
-
-#define LTT_WRITE_MAXLEN (128)
-
-struct dentry *ltt_control_dir, *ltt_setup_trace_file, *ltt_destroy_trace_file,
- *markers_control_dir;
-
-/*
- * the traces_lock nests inside control_lock.
- * control_lock protects the consistency of directories presented in ltt
- * directory.
- */
-static DEFINE_MUTEX(control_lock);
-
-/*
- * big note about locking for marker control files :
- * If a marker control file is added/removed manually racing with module
- * load/unload, there may be warning messages appearing, but those two
- * operations should be able to execute concurrently without any lock
- * synchronizing their operation one wrt another.
- * Locking the marker mutex, module mutex and also keeping a mutex here
- * from mkdir/rmdir _and_ from the notifier called from module load/unload makes
- * life miserable and just asks for deadlocks.
- */
-
-/*
- * lookup a file/dir in parent dir.
- * only designed to work well for debugfs.
- * (although it maybe ok for other fs)
- *
- * return:
- * file/dir's dentry on success
- * NULL on failure
- */
-static struct dentry *dir_lookup(struct dentry *parent, const char *name)
-{
- struct qstr q;
- struct dentry *d;
-
- q.name = name;
- q.len = strlen(name);
- q.hash = full_name_hash(q.name, q.len);
-
- d = d_lookup(parent, &q);
- if (d)
- dput(d);
-
- return d;
-}
-
-
-static ssize_t alloc_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *cmd = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", cmd) != 1) {
- err = -EPERM;
- goto err_get_cmd;
- }
-
- if ((cmd[0] != 'Y' && cmd[0] != 'y' && cmd[0] != '1') || cmd[1]) {
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- err = ltt_trace_alloc(file->f_dentry->d_parent->d_name.name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "alloc_write: ltt_trace_alloc failed: %d\n",
- err);
- goto err_alloc_trace;
- }
-
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return count;
-
-err_alloc_trace:
-err_bad_cmd:
-err_get_cmd:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return err;
-}
-
-static const struct file_operations ltt_alloc_operations = {
- .write = alloc_write,
-};
-
-
-static ssize_t enabled_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *cmd = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", cmd) != 1) {
- err = -EPERM;
- goto err_get_cmd;
- }
-
- if (cmd[1]) {
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- switch (cmd[0]) {
- case 'Y':
- case 'y':
- case '1':
- err = ltt_trace_start(file->f_dentry->d_parent->d_name.name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR
- "enabled_write: ltt_trace_start failed: %d\n",
- err);
- err = -EPERM;
- goto err_start_trace;
- }
- break;
- case 'N':
- case 'n':
- case '0':
- err = ltt_trace_stop(file->f_dentry->d_parent->d_name.name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR
- "enabled_write: ltt_trace_stop failed: %d\n",
- err);
- err = -EPERM;
- goto err_stop_trace;
- }
- break;
- default:
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return count;
-
-err_stop_trace:
-err_start_trace:
-err_bad_cmd:
-err_get_cmd:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return err;
-}
-
-static const struct file_operations ltt_enabled_operations = {
- .write = enabled_write,
-};
-
-
-static ssize_t trans_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *trans_name = (char *)__get_free_page(GFP_KERNEL);
- int err = 0;
- int buf_size;
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", trans_name) != 1) {
- err = -EPERM;
- goto err_get_transname;
- }
-
- err = ltt_trace_set_type(file->f_dentry->d_parent->d_name.name,
- trans_name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "trans_write: ltt_trace_set_type failed: %d\n",
- err);
- goto err_set_trans;
- }
-
- free_page((unsigned long)buf);
- free_page((unsigned long)trans_name);
- return count;
-
-err_set_trans:
-err_get_transname:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)trans_name);
- return err;
-}
-
-static const struct file_operations ltt_trans_operations = {
- .write = trans_write,
-};
-
-
-static ssize_t channel_subbuf_num_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- unsigned int num;
- const char *channel_name;
- const char *trace_name;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%u", &num) != 1) {
- err = -EPERM;
- goto err_get_number;
- }
-
- channel_name = file->f_dentry->d_parent->d_name.name;
- trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
-
- err = ltt_trace_set_channel_subbufcount(trace_name, channel_name, num);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_subbuf_num_write: "
- "ltt_trace_set_channel_subbufcount failed: %d\n", err);
- goto err_set_subbufcount;
- }
-
- free_page((unsigned long)buf);
- return count;
-
-err_set_subbufcount:
-err_get_number:
-err_copy_from_user:
- free_page((unsigned long)buf);
- return err;
-}
-
-static const struct file_operations ltt_channel_subbuf_num_operations = {
- .write = channel_subbuf_num_write,
-};
-
-
-static
-ssize_t channel_subbuf_size_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- unsigned int num;
- const char *channel_name;
- const char *trace_name;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%u", &num) != 1) {
- err = -EPERM;
- goto err_get_number;
- }
-
- channel_name = file->f_dentry->d_parent->d_name.name;
- trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
-
- err = ltt_trace_set_channel_subbufsize(trace_name, channel_name, num);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_subbuf_size_write: "
- "ltt_trace_set_channel_subbufsize failed: %d\n", err);
- goto err_set_subbufsize;
- }
-
- free_page((unsigned long)buf);
- return count;
-
-err_set_subbufsize:
-err_get_number:
-err_copy_from_user:
- free_page((unsigned long)buf);
- return err;
-}
-
-static const struct file_operations ltt_channel_subbuf_size_operations = {
- .write = channel_subbuf_size_write,
-};
-
-static
-ssize_t channel_switch_timer_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- unsigned long num;
- const char *channel_name;
- const char *trace_name;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%lu", &num) != 1) {
- err = -EPERM;
- goto err_get_number;
- }
-
- channel_name = file->f_dentry->d_parent->d_name.name;
- trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
-
- /* Convert from ms to us */
- num *= 1000;
-
- err = ltt_trace_set_channel_switch_timer(trace_name, channel_name, num);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_switch_timer_write: "
- "ltt_trace_set_channel_switch_timer failed: %d\n", err);
- goto err_set_switch_timer;
- }
-
- free_page((unsigned long)buf);
- return count;
-
-err_set_switch_timer:
-err_get_number:
-err_copy_from_user:
- free_page((unsigned long)buf);
- return err;
-}
-
-static struct file_operations ltt_channel_switch_timer_operations = {
- .write = channel_switch_timer_write,
-};
-
-static
-ssize_t channel_overwrite_write(struct file *file,
- const char __user *user_buf, size_t count,
- loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- const char *channel_name;
- const char *trace_name;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *cmd = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", cmd) != 1) {
- err = -EPERM;
- goto err_get_cmd;
- }
-
- if (cmd[1]) {
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- channel_name = file->f_dentry->d_parent->d_name.name;
- trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
-
- switch (cmd[0]) {
- case 'Y':
- case 'y':
- case '1':
- err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
- 1);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_overwrite_write: "
- "ltt_trace_set_channel_overwrite failed: %d\n",
- err);
- goto err_set_subbufsize;
- }
- break;
- case 'N':
- case 'n':
- case '0':
- err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
- 0);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_overwrite_write: "
- "ltt_trace_set_channel_overwrite failed: %d\n",
- err);
- goto err_set_subbufsize;
- }
- break;
- default:
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return count;
-
-err_set_subbufsize:
-err_bad_cmd:
-err_get_cmd:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return err;
-}
-
-static const struct file_operations ltt_channel_overwrite_operations = {
- .write = channel_overwrite_write,
-};
-
-
-static
-ssize_t channel_enable_write(struct file *file,
- const char __user *user_buf, size_t count,
- loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- const char *channel_name;
- const char *trace_name;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *cmd = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", cmd) != 1) {
- err = -EPERM;
- goto err_get_cmd;
- }
-
- if (cmd[1]) {
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- channel_name = file->f_dentry->d_parent->d_name.name;
- trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
-
- switch (cmd[0]) {
- case 'Y':
- case 'y':
- case '1':
- err = ltt_trace_set_channel_enable(trace_name, channel_name,
- 1);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_enable_write: "
- "ltt_trace_set_channel_enable failed: %d\n",
- err);
- goto err_set_subbufsize;
- }
- break;
- case 'N':
- case 'n':
- case '0':
- err = ltt_trace_set_channel_enable(trace_name, channel_name,
- 0);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "channel_enable_write: "
- "ltt_trace_set_channel_enable failed: %d\n",
- err);
- goto err_set_subbufsize;
- }
- break;
- default:
- err = -EPERM;
- goto err_bad_cmd;
- }
-
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return count;
-
-err_set_subbufsize:
-err_bad_cmd:
-err_get_cmd:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)cmd);
- return err;
-}
-
-static const struct file_operations ltt_channel_enable_operations = {
- .write = channel_enable_write,
-};
-
-
-static int _create_trace_control_dir(const char *trace_name,
- struct ltt_trace *trace)
-{
- int err;
- struct dentry *trace_root, *channel_root;
- struct dentry *tmp_den;
- int i;
-
- /* debugfs/control/trace_name */
- trace_root = debugfs_create_dir(trace_name, ltt_control_dir);
- if (IS_ERR(trace_root) || !trace_root) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create control root dir of %s failed\n", trace_name);
- err = -ENOMEM;
- goto err_create_trace_root;
- }
-
- /* debugfs/control/trace_name/alloc */
- tmp_den = debugfs_create_file("alloc", S_IWUSR, trace_root, NULL,
- <t_alloc_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create file of alloc failed\n");
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- /* debugfs/control/trace_name/trans */
- tmp_den = debugfs_create_file("trans", S_IWUSR, trace_root, NULL,
- <t_trans_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create file of trans failed\n");
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- /* debugfs/control/trace_name/enabled */
- tmp_den = debugfs_create_file("enabled", S_IWUSR, trace_root, NULL,
- <t_enabled_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create file of enabled failed\n");
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- /* debugfs/control/trace_name/channel/ */
- channel_root = debugfs_create_dir("channel", trace_root);
- if (IS_ERR(channel_root) || !channel_root) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create dir of channel failed\n");
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- /*
- * Create dir and files in debugfs/ltt/control/trace_name/channel/
- * Following things(without <>) will be created:
- * `-- <control>
- * `-- <trace_name>
- * `-- <channel>
- * |-- <channel_name>
- * | |-- enable
- * | |-- overwrite
- * | |-- subbuf_num
- * | |-- subbuf_size
- * | `-- switch_timer
- * `-- ...
- */
-
- for (i = 0; i < trace->nr_channels; i++) {
- struct dentry *channel_den;
- struct ltt_chan *chan;
-
- chan = &trace->channels[i];
- if (!chan->active)
- continue;
- channel_den = debugfs_create_dir(chan->a.filename,
- channel_root);
- if (IS_ERR(channel_den) || !channel_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create channel dir of %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- tmp_den = debugfs_create_file("subbuf_num", S_IWUSR,
- channel_den, NULL,
- <t_channel_subbuf_num_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create subbuf_num in %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- tmp_den = debugfs_create_file("subbuf_size", S_IWUSR,
- channel_den, NULL,
- <t_channel_subbuf_size_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create subbuf_size in %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- tmp_den = debugfs_create_file("enable", S_IWUSR, channel_den,
- NULL,
- <t_channel_enable_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create enable in %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- tmp_den = debugfs_create_file("overwrite", S_IWUSR, channel_den,
- NULL,
- <t_channel_overwrite_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create overwrite in %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
-
- tmp_den = debugfs_create_file("switch_timer", S_IWUSR,
- channel_den, NULL,
- <t_channel_switch_timer_operations);
- if (IS_ERR(tmp_den) || !tmp_den) {
- printk(KERN_ERR "_create_trace_control_dir: "
- "create switch_timer in %s failed\n",
- chan->a.filename);
- err = -ENOMEM;
- goto err_create_subdir;
- }
- }
-
- return 0;
-
-err_create_subdir:
- debugfs_remove_recursive(trace_root);
-err_create_trace_root:
- return err;
-}
-
-static
-ssize_t setup_trace_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int err = 0;
- int buf_size;
- struct ltt_trace *trace;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *trace_name = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", trace_name) != 1) {
- err = -EPERM;
- goto err_get_tracename;
- }
-
- mutex_lock(&control_lock);
- ltt_lock_traces();
-
- err = _ltt_trace_setup(trace_name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR
- "setup_trace_write: ltt_trace_setup failed: %d\n", err);
- goto err_setup_trace;
- }
- trace = _ltt_trace_find_setup(trace_name);
- BUG_ON(!trace);
- err = _create_trace_control_dir(trace_name, trace);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR "setup_trace_write: "
- "_create_trace_control_dir failed: %d\n", err);
- goto err_create_trace_control_dir;
- }
-
- ltt_unlock_traces();
- mutex_unlock(&control_lock);
-
- free_page((unsigned long)buf);
- free_page((unsigned long)trace_name);
- return count;
-
-err_create_trace_control_dir:
- ltt_trace_destroy(trace_name);
-err_setup_trace:
- ltt_unlock_traces();
- mutex_unlock(&control_lock);
-err_get_tracename:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)trace_name);
- return err;
-}
-
-static const struct file_operations ltt_setup_trace_operations = {
- .write = setup_trace_write,
-};
-
-static
-ssize_t destroy_trace_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct dentry *trace_den;
- int buf_size;
- int err = 0;
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- char *trace_name = (char *)__get_free_page(GFP_KERNEL);
-
- buf_size = min_t(size_t, count, PAGE_SIZE - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto err_copy_from_user;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", trace_name) != 1) {
- err = -EPERM;
- goto err_get_tracename;
- }
-
- mutex_lock(&control_lock);
-
- err = ltt_trace_destroy(trace_name);
- if (IS_ERR_VALUE(err)) {
- printk(KERN_ERR
- "destroy_trace_write: ltt_trace_destroy failed: %d\n",
- err);
- err = -EPERM;
- goto err_destroy_trace;
- }
-
- trace_den = dir_lookup(ltt_control_dir, trace_name);
- if (!trace_den) {
- printk(KERN_ERR
- "destroy_trace_write: lookup for %s's dentry failed\n",
- trace_name);
- err = -ENOENT;
- goto err_get_dentry;
- }
-
- debugfs_remove_recursive(trace_den);
-
- mutex_unlock(&control_lock);
-
- free_page((unsigned long)buf);
- free_page((unsigned long)trace_name);
- return count;
-
-err_get_dentry:
-err_destroy_trace:
- mutex_unlock(&control_lock);
-err_get_tracename:
-err_copy_from_user:
- free_page((unsigned long)buf);
- free_page((unsigned long)trace_name);
- return err;
-}
-
-static const struct file_operations ltt_destroy_trace_operations = {
- .write = destroy_trace_write,
-};
-
-static void init_marker_dir(struct dentry *dentry,
- const struct inode_operations *opt)
-{
- dentry->d_inode->i_op = opt;
-}
-
-static
-ssize_t marker_enable_read(struct file *filp, char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- char *buf;
- const char *channel, *marker;
- int len, enabled, present;
-
- marker = filp->f_dentry->d_parent->d_name.name;
- channel = filp->f_dentry->d_parent->d_parent->d_name.name;
-
- len = 0;
- buf = (char *)__get_free_page(GFP_KERNEL);
-
- /*
- * Note: we cannot take the marker lock to make these two checks
- * atomic, because the marker mutex nests inside the module mutex, taken
- * inside the marker present check.
- */
- enabled = is_marker_enabled(channel, marker);
- present = is_marker_present(channel, marker);
-
- if (enabled && present)
- len = snprintf(buf, PAGE_SIZE, "%d\n", 1);
- else if (enabled && !present)
- len = snprintf(buf, PAGE_SIZE, "%d\n", 2);
- else
- len = snprintf(buf, PAGE_SIZE, "%d\n", 0);
-
-
- if (len >= PAGE_SIZE) {
- len = PAGE_SIZE;
- buf[PAGE_SIZE] = '\0';
- }
- len = simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
- free_page((unsigned long)buf);
-
- return len;
-}
-
-static
-ssize_t marker_enable_write(struct file *filp, const char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- char *buf = (char *)__get_free_page(GFP_KERNEL);
- int buf_size;
- ssize_t ret = 0;
- const char *channel, *marker;
-
- marker = filp->f_dentry->d_parent->d_name.name;
- channel = filp->f_dentry->d_parent->d_parent->d_name.name;
-
- buf_size = min_t(size_t, cnt, PAGE_SIZE - 1);
- ret = copy_from_user(buf, ubuf, buf_size);
- if (ret)
- goto end;
-
- buf[buf_size] = 0;
-
- switch (buf[0]) {
- case 'Y':
- case 'y':
- case '1':
- ret = ltt_marker_connect(channel, marker, "default");
- if (ret)
- goto end;
- break;
- case 'N':
- case 'n':
- case '0':
- ret = ltt_marker_disconnect(channel, marker, "default");
- if (ret)
- goto end;
- break;
- default:
- ret = -EPERM;
- goto end;
- }
- ret = cnt;
-end:
- free_page((unsigned long)buf);
- return ret;
-}
-
-static const struct file_operations enable_fops = {
- .read = marker_enable_read,
- .write = marker_enable_write,
-};
-
-/*
- * In practice, the output size should never be larger than 4096 kB. If it
- * ever happens, the output will simply be truncated.
- */
-static
-ssize_t marker_info_read(struct file *filp, char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- char *buf;
- const char *channel, *marker;
- int len;
- struct marker_iter iter;
-
- marker = filp->f_dentry->d_parent->d_name.name;
- channel = filp->f_dentry->d_parent->d_parent->d_name.name;
-
- len = 0;
- buf = (char *)__get_free_page(GFP_KERNEL);
-
- if (is_marker_enabled(channel, marker) &&
- !is_marker_present(channel, marker)) {
- len += snprintf(buf + len, PAGE_SIZE - len,
- "Marker Pre-enabled\n");
- goto out;
- }
-
- marker_iter_reset(&iter);
- marker_iter_start(&iter);
- for (; iter.marker != NULL; marker_iter_next(&iter)) {
- if (!strcmp(iter.marker->channel, channel) &&
- !strcmp(iter.marker->name, marker))
- len += snprintf(buf + len, PAGE_SIZE - len,
- "Location: %s\n"
- "format: \"%s\"\nstate: %d\n"
- "event_id: %hu\n"
- "call: 0x%p\n"
- "probe %s : 0x%p\n\n",
-#ifdef CONFIG_MODULES
- iter.module ? iter.module->name :
-#endif
- "Core Kernel",
- iter.marker->format,
- _imv_read(iter.marker->state),
- iter.marker->event_id,
- iter.marker->call,
- iter.marker->ptype ?
- "multi" : "single", iter.marker->ptype ?
- (void *)iter.marker->multi :
- (void *)iter.marker->single.func);
- if (len >= PAGE_SIZE)
- break;
- }
- marker_iter_stop(&iter);
-
-out:
- if (len >= PAGE_SIZE) {
- len = PAGE_SIZE;
- buf[PAGE_SIZE] = '\0';
- }
-
- len = simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
- free_page((unsigned long)buf);
-
- return len;
-}
-
-static const struct file_operations info_fops = {
- .read = marker_info_read,
-};
-
-static int marker_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-{
- struct dentry *marker_d, *enable_d, *info_d, *channel_d;
- int ret;
-
- ret = 0;
- channel_d = (struct dentry *)dir->i_private;
- mutex_unlock(&dir->i_mutex);
-
- marker_d = debugfs_create_dir(dentry->d_name.name,
- channel_d);
- if (IS_ERR(marker_d)) {
- ret = PTR_ERR(marker_d);
- goto out;
- }
-
- enable_d = debugfs_create_file("enable", 0644, marker_d,
- NULL, &enable_fops);
- if (IS_ERR(enable_d) || !enable_d) {
- printk(KERN_ERR
- "%s: create file of %s failed\n",
- __func__, "enable");
- ret = -ENOMEM;
- goto remove_marker_dir;
- }
-
- info_d = debugfs_create_file("info", 0644, marker_d,
- NULL, &info_fops);
- if (IS_ERR(info_d) || !info_d) {
- printk(KERN_ERR
- "%s: create file of %s failed\n",
- __func__, "info");
- ret = -ENOMEM;
- goto remove_enable_dir;
- }
-
- goto out;
-
-remove_enable_dir:
- debugfs_remove(enable_d);
-remove_marker_dir:
- debugfs_remove(marker_d);
-out:
- mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
- return ret;
-}
-
-static int marker_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct dentry *marker_d, *channel_d;
- const char *channel, *name;
- int ret, enabled, present;
-
- ret = 0;
-
- channel_d = (struct dentry *)dir->i_private;
- channel = channel_d->d_name.name;
-
- marker_d = dir_lookup(channel_d, dentry->d_name.name);
-
- if (!marker_d) {
- ret = -ENOENT;
- goto out;
- }
-
- name = marker_d->d_name.name;
-
- enabled = is_marker_enabled(channel, name);
- present = is_marker_present(channel, name);
-
- if (present || (!present && enabled)) {
- ret = -EPERM;
- goto out;
- }
-
- mutex_unlock(&dir->i_mutex);
- mutex_unlock(&dentry->d_inode->i_mutex);
- debugfs_remove_recursive(marker_d);
- mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
- mutex_lock(&dentry->d_inode->i_mutex);
-out:
- return ret;
-}
-
-const struct inode_operations channel_dir_opt = {
- .lookup = simple_lookup,
- .mkdir = marker_mkdir,
- .rmdir = marker_rmdir,
-};
-
-static int channel_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-{
- struct dentry *channel_d;
- int ret;
-
- ret = 0;
- mutex_unlock(&dir->i_mutex);
-
- channel_d = debugfs_create_dir(dentry->d_name.name,
- markers_control_dir);
- if (IS_ERR(channel_d)) {
- ret = PTR_ERR(channel_d);
- goto out;
- }
-
- channel_d->d_inode->i_private = (void *)channel_d;
- init_marker_dir(channel_d, &channel_dir_opt);
-out:
- mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
- return ret;
-}
-
-static int channel_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct dentry *channel_d;
- int ret;
-
- ret = 0;
-
- channel_d = dir_lookup(markers_control_dir, dentry->d_name.name);
- if (!channel_d) {
- ret = -ENOENT;
- goto out;
- }
-
- if (list_empty(&channel_d->d_subdirs)) {
- mutex_unlock(&dir->i_mutex);
- mutex_unlock(&dentry->d_inode->i_mutex);
- debugfs_remove(channel_d);
- mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
- mutex_lock(&dentry->d_inode->i_mutex);
- } else
- ret = -EPERM;
-
-out:
- return ret;
-}
-
-const struct inode_operations root_dir_opt = {
- .lookup = simple_lookup,
- .mkdir = channel_mkdir,
- .rmdir = channel_rmdir
-};
-
-static int build_marker_file(struct marker *marker)
-{
- struct dentry *channel_d, *marker_d, *enable_d, *info_d;
- int err;
-
- channel_d = dir_lookup(markers_control_dir, marker->channel);
- if (!channel_d) {
- channel_d = debugfs_create_dir(marker->channel,
- markers_control_dir);
- if (IS_ERR(channel_d) || !channel_d) {
- printk(KERN_ERR
- "%s: build channel dir of %s failed\n",
- __func__, marker->channel);
- err = -ENOMEM;
- goto err_build_fail;
- }
- channel_d->d_inode->i_private = (void *)channel_d;
- init_marker_dir(channel_d, &channel_dir_opt);
- }
-
- marker_d = dir_lookup(channel_d, marker->name);
- if (!marker_d) {
- marker_d = debugfs_create_dir(marker->name, channel_d);
- if (IS_ERR(marker_d) || !marker_d) {
- printk(KERN_ERR
- "%s: marker dir of %s failed\n",
- __func__, marker->name);
- err = -ENOMEM;
- goto err_build_fail;
- }
- }
-
- enable_d = dir_lookup(marker_d, "enable");
- if (!enable_d) {
- enable_d = debugfs_create_file("enable", 0644, marker_d,
- NULL, &enable_fops);
- if (IS_ERR(enable_d) || !enable_d) {
- printk(KERN_ERR
- "%s: create file of %s failed\n",
- __func__, "enable");
- err = -ENOMEM;
- goto err_build_fail;
- }
- }
-
- info_d = dir_lookup(marker_d, "info");
- if (!info_d) {
- info_d = debugfs_create_file("info", 0444, marker_d,
- NULL, &info_fops);
- if (IS_ERR(info_d) || !info_d) {
- printk(KERN_ERR
- "%s: create file of %s failed\n",
- __func__, "enable");
- err = -ENOMEM;
- goto err_build_fail;
- }
- }
-
- return 0;
-
-err_build_fail:
- return err;
-}
-
-static int build_marker_control_files(void)
-{
- struct marker_iter iter;
- int err;
-
- err = 0;
- if (!markers_control_dir)
- return -EEXIST;
-
- marker_iter_reset(&iter);
- marker_iter_start(&iter);
- for (; iter.marker != NULL; marker_iter_next(&iter)) {
- err = build_marker_file(iter.marker);
- if (err)
- goto out;
- }
- marker_iter_stop(&iter);
-
-out:
- return err;
-}
-
-#ifdef CONFIG_MODULES
-static int remove_marker_control_dir(struct module *mod, struct marker *marker)
-{
- struct dentry *channel_d, *marker_d;
- const char *channel, *name;
- int count;
- struct marker_iter iter;
-
- count = 0;
-
- channel_d = dir_lookup(markers_control_dir, marker->channel);
- if (!channel_d)
- return -ENOENT;
- channel = channel_d->d_name.name;
-
- marker_d = dir_lookup(channel_d, marker->name);
- if (!marker_d)
- return -ENOENT;
- name = marker_d->d_name.name;
-
- marker_iter_reset(&iter);
- marker_iter_start(&iter);
- for (; iter.marker != NULL; marker_iter_next(&iter)) {
- if (!strcmp(iter.marker->channel, channel) &&
- !strcmp(iter.marker->name, name) && mod != iter.module)
- count++;
- }
-
- if (count > 0)
- goto end;
-
- debugfs_remove_recursive(marker_d);
- if (list_empty(&channel_d->d_subdirs))
- debugfs_remove(channel_d);
-
-end:
- marker_iter_stop(&iter);
- return 0;
-}
-
-static void cleanup_control_dir(struct module *mod, struct marker *begin,
- struct marker *end)
-{
- struct marker *iter;
-
- if (!markers_control_dir)
- return;
-
- for (iter = begin; iter < end; iter++)
- remove_marker_control_dir(mod, iter);
-
- return;
-}
-
-static void build_control_dir(struct module *mod, struct marker *begin,
- struct marker *end)
-{
- struct marker *iter;
- int err;
-
- err = 0;
- if (!markers_control_dir)
- return;
-
- for (iter = begin; iter < end; iter++) {
- err = build_marker_file(iter);
- if (err)
- goto err_build_fail;
- }
-
- return;
-err_build_fail:
- cleanup_control_dir(mod, begin, end);
-}
-
-static int module_notify(struct notifier_block *self,
- unsigned long val, void *data)
-{
- struct module *mod = data;
-
- switch (val) {
- case MODULE_STATE_COMING:
- build_control_dir(mod, mod->markers,
- mod->markers + mod->num_markers);
- break;
- case MODULE_STATE_GOING:
- cleanup_control_dir(mod, mod->markers,
- mod->markers + mod->num_markers);
- break;
- }
- return NOTIFY_DONE;
-}
-#else
-static inline int module_notify(struct notifier_block *self,
- unsigned long val, void *data)
-{
- return 0;
-}
-#endif
-
-static struct notifier_block module_nb = {
- .notifier_call = module_notify,
-};
-
-static int __init ltt_trace_control_init(void)
-{
- int err = 0;
- struct dentry *ltt_root_dentry;
-
- ltt_root_dentry = get_ltt_root();
- if (!ltt_root_dentry) {
- err = -ENOENT;
- goto err_no_root;
- }
-
- ltt_control_dir = debugfs_create_dir(LTT_CONTROL_DIR, ltt_root_dentry);
- if (IS_ERR(ltt_control_dir) || !ltt_control_dir) {
- printk(KERN_ERR
- "ltt_channel_control_init: create dir of %s failed\n",
- LTT_CONTROL_DIR);
- err = -ENOMEM;
- goto err_create_control_dir;
- }
-
- ltt_setup_trace_file = debugfs_create_file(LTT_SETUP_TRACE_FILE,
- S_IWUSR, ltt_root_dentry,
- NULL,
- <t_setup_trace_operations);
- if (IS_ERR(ltt_setup_trace_file) || !ltt_setup_trace_file) {
- printk(KERN_ERR
- "ltt_channel_control_init: create file of %s failed\n",
- LTT_SETUP_TRACE_FILE);
- err = -ENOMEM;
- goto err_create_setup_trace_file;
- }
-
- ltt_destroy_trace_file = debugfs_create_file(LTT_DESTROY_TRACE_FILE,
- S_IWUSR, ltt_root_dentry,
- NULL,
- <t_destroy_trace_operations);
- if (IS_ERR(ltt_destroy_trace_file) || !ltt_destroy_trace_file) {
- printk(KERN_ERR
- "ltt_channel_control_init: create file of %s failed\n",
- LTT_DESTROY_TRACE_FILE);
- err = -ENOMEM;
- goto err_create_destroy_trace_file;
- }
-
- markers_control_dir = debugfs_create_dir(MARKERS_CONTROL_DIR,
- ltt_root_dentry);
- if (IS_ERR(markers_control_dir) || !markers_control_dir) {
- printk(KERN_ERR
- "ltt_channel_control_init: create dir of %s failed\n",
- MARKERS_CONTROL_DIR);
- err = -ENOMEM;
- goto err_create_marker_control_dir;
- }
-
- init_marker_dir(markers_control_dir, &root_dir_opt);
-
- if (build_marker_control_files())
- goto err_build_fail;
-
- if (!register_module_notifier(&module_nb))
- return 0;
-
-err_build_fail:
- debugfs_remove_recursive(markers_control_dir);
- markers_control_dir = NULL;
-err_create_marker_control_dir:
- debugfs_remove(ltt_destroy_trace_file);
-err_create_destroy_trace_file:
- debugfs_remove(ltt_setup_trace_file);
-err_create_setup_trace_file:
- debugfs_remove(ltt_control_dir);
-err_create_control_dir:
-err_no_root:
- return err;
-}
-
-static void __exit ltt_trace_control_exit(void)
-{
- struct dentry *trace_dir;
-
- /* destory all traces */
- list_for_each_entry(trace_dir, <t_control_dir->d_subdirs,
- d_u.d_child) {
- ltt_trace_stop(trace_dir->d_name.name);
- ltt_trace_destroy(trace_dir->d_name.name);
- }
-
- /* clean dirs in debugfs */
- debugfs_remove(ltt_setup_trace_file);
- debugfs_remove(ltt_destroy_trace_file);
- debugfs_remove_recursive(ltt_control_dir);
- debugfs_remove_recursive(markers_control_dir);
- unregister_module_notifier(&module_nb);
- put_ltt_root();
-}
-
-module_init(ltt_trace_control_init);
-module_exit(ltt_trace_control_exit);
-
-MODULE_LICENSE("GPL and additional rights");
-MODULE_AUTHOR("Zhao Lei <zhaolei@cn.fujitsu.com>");
-MODULE_DESCRIPTION("Linux Trace Toolkit Trace Controller");
LTT_CHANNEL_DEFAULT,
};
-#if 0
-size_t ltt_serialize_printf(struct lib_ring_buffer *buf, unsigned long buf_offset,
- size_t *msg_size, char *output, size_t outlen,
- const char *fmt);
-
-/*
- * Unique ID assigned to each registered probe.
- */
-enum marker_id {
- MARKER_ID_SET_MARKER_ID = 0, /* Static IDs available (range 0-7) */
- MARKER_ID_SET_MARKER_FORMAT,
- MARKER_ID_COMPACT, /* Compact IDs (range: 8-127) */
- MARKER_ID_DYNAMIC, /* Dynamic IDs (range: 128-65535) */
-};
-
-/* static ids 0-1 reserved for internal use. */
-#define MARKER_CORE_IDS 2
-static __inline__ enum marker_id marker_id_type(uint16_t id)
-{
- if (id < MARKER_CORE_IDS)
- return (enum marker_id)id;
- else
- return MARKER_ID_DYNAMIC;
-}
-
-struct user_dbg_data {
- unsigned long avail_size;
- unsigned long write;
- unsigned long read;
-};
-
-enum trace_mode { LTT_TRACE_NORMAL, LTT_TRACE_FLIGHT, LTT_TRACE_HYBRID };
-
-#define CHANNEL_FLAG_ENABLE (1U<<0)
-#define CHANNEL_FLAG_OVERWRITE (1U<<1)
-#endif //0
-
-#if 0
-/* Per-trace information - each trace/flight recorder represented by one */
-struct ltt_trace {
- /* First 32 bytes cache-hot cacheline */
- struct list_head list;
- struct ltt_chan **channels;
- unsigned int nr_channels;
- int active;
- /* Second 32 bytes cache-hot cacheline */
- struct ltt_trace_ops *ops;
- u32 freq_scale;
- u64 start_freq;
- u64 start_tsc;
- unsigned long long start_monotonic;
- struct timeval start_time;
- struct ltt_channel_setting *settings;
- struct {
- struct dentry *trace_root;
- struct dentry *ascii_root;
- } dentry;
- struct kref kref; /* Each channel has a kref of the trace struct */
- struct ltt_transport *transport;
- struct kref ltt_transport_kref;
- wait_queue_head_t kref_wq; /* Place for ltt_trace_destroy to sleep */
- char trace_name[NAME_MAX];
-} ____cacheline_aligned;
-#endif //0
-
/*
* Hardcoded event headers
*
ltt_write_event_header_slow(config, ctx, eID, event_size);
}
-#if 0
-/*
- * ltt_read_event_header
- * buf_offset must aligned on 32 bits
- */
-static __inline__
-size_t ltt_read_event_header(struct ltt_chanbuf_alloc *bufa, long buf_offset,
- u64 *tsc, u32 *event_size, u16 *eID,
- unsigned int *rflags)
-{
- struct ltt_event_header header;
- u16 small_size;
-
- ltt_relay_read(bufa, buf_offset, &header, sizeof(header));
- buf_offset += sizeof(header);
-
- *event_size = INT_MAX;
- *eID = header.id_time >> LTT_TSC_BITS;
- *tsc = header.id_time & LTT_TSC_MASK;
-
- switch (*eID) {
- case 29:
- *rflags = LTT_RFLAG_ID_SIZE_TSC;
- ltt_relay_read(bufa, buf_offset, eID, sizeof(u16));
- buf_offset += sizeof(u16);
- ltt_relay_read(bufa, buf_offset, &small_size, sizeof(u16));
- buf_offset += sizeof(u16);
- if (small_size == LTT_MAX_SMALL_SIZE) {
- ltt_relay_read(bufa, buf_offset, event_size,
- sizeof(u32));
- buf_offset += sizeof(u32);
- } else
- *event_size = small_size;
- buf_offset += ltt_align(buf_offset, sizeof(u64));
- ltt_relay_read(bufa, buf_offset, tsc, sizeof(u64));
- buf_offset += sizeof(u64);
- break;
- case 30:
- *rflags = LTT_RFLAG_ID_SIZE;
- ltt_relay_read(bufa, buf_offset, eID, sizeof(u16));
- buf_offset += sizeof(u16);
- ltt_relay_read(bufa, buf_offset, &small_size, sizeof(u16));
- buf_offset += sizeof(u16);
- if (small_size == LTT_MAX_SMALL_SIZE) {
- ltt_relay_read(bufa, buf_offset, event_size,
- sizeof(u32));
- buf_offset += sizeof(u32);
- } else
- *event_size = small_size;
- break;
- case 31:
- *rflags = LTT_RFLAG_ID;
- ltt_relay_read(bufa, buf_offset, eID, sizeof(u16));
- buf_offset += sizeof(u16);
- break;
- default:
- *rflags = 0;
- break;
- }
-
- return buf_offset;
-}
-#endif //0
-
/* Tracer properties */
#define CTF_MAGIC_NUMBER 0xC1FC1FC1
#define LTT_TRACER_VERSION_MAJOR 3
+++ /dev/null
-/*
- * Copyright (C) 2008 Mathieu Desnoyers
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <linux/module.h>
-#include <linux/marker.h>
-#include <linux/uaccess.h>
-#include <linux/gfp.h>
-#include <linux/fs.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-
-#include "ltt-type-serializer.h"
-
-#define LTT_WRITE_EVENT_FILE "write_event"
-
-DEFINE_MARKER(userspace, event, "string %s");
-static struct dentry *ltt_event_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, in channel "userspace", event "event".
- * Copies until either \n or \0 is reached.
- * On success, returns the number of bytes copied from the source, including the
- * \n or \0 character (if there was one in the count range). It cannot return
- * more than count.
- * 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 *ppos)
-{
- struct marker *marker;
- char *buf, *end;
- long copycount;
- ssize_t ret;
-
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto string_out;
- }
- copycount = strncpy_from_user(buf, user_buf, count);
- if (copycount < 0) {
- ret = -EFAULT;
- goto string_err;
- }
- /* Cut from the first nil or newline. */
- buf[copycount] = '\0';
- end = strchr(buf, '\n');
- if (end) {
- *end = '\0';
- copycount = end - buf;
- }
- /* Add final \0 to copycount */
- copycount++;
- marker = &GET_MARKER(userspace, event);
- ltt_specialized_trace(marker, marker->single.probe_private, buf,
- copycount, sizeof(char));
- /* If there is no \0 nor \n in count, do not return a larger value */
- ret = min_t(size_t, copycount, count);
-string_err:
- kfree(buf);
-string_out:
- return ret;
-}
-
-static const struct file_operations ltt_userspace_operations = {
- .write = write_event,
-};
-
-static int __init ltt_userspace_init(void)
-{
- struct dentry *ltt_root_dentry;
- int err = 0;
-
- ltt_root_dentry = get_ltt_root();
- if (!ltt_root_dentry) {
- err = -ENOENT;
- goto err_no_root;
- }
-
- ltt_event_file = debugfs_create_file(LTT_WRITE_EVENT_FILE,
- S_IWUGO,
- ltt_root_dentry,
- NULL,
- <t_userspace_operations);
- if (IS_ERR(ltt_event_file) || !ltt_event_file) {
- printk(KERN_ERR
- "ltt_userspace_init: failed to create file %s\n",
- LTT_WRITE_EVENT_FILE);
- err = -EPERM;
- goto err_no_file;
- }
-
- return err;
-err_no_file:
- put_ltt_root();
-err_no_root:
- return err;
-}
-
-static void __exit ltt_userspace_exit(void)
-{
- debugfs_remove(ltt_event_file);
- put_ltt_root();
-}
-
-module_init(ltt_userspace_init);
-module_exit(ltt_userspace_exit);
-
-MODULE_LICENSE("GPL and additional rights");
-MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>");
-MODULE_DESCRIPTION("Linux Trace Toolkit Userspace Event");