From: Mathieu Desnoyers Date: Mon, 17 Jan 2011 06:55:08 +0000 (-0500) Subject: Remove deprecated code X-Git-Tag: v2.0-pre1~177 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=a92a3841af62e5963a584480abefdbde4eeada45;p=lttng-modules.git Remove deprecated code Signed-off-by: Mathieu Desnoyers --- diff --git a/discard/ltt-filter.c b/discard/ltt-filter.c new file mode 100644 index 00000000..ec113af6 --- /dev/null +++ b/discard/ltt-filter.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Mathieu Desnoyers + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Linux Trace Toolkit Filter"); diff --git a/discard/ltt-kprobes.c b/discard/ltt-kprobes.c new file mode 100644 index 00000000..7539381b --- /dev/null +++ b/discard/ltt-kprobes.c @@ -0,0 +1,493 @@ +/* + * (C) Copyright 2009 - + * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) + * + * LTTng kprobes integration module. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/discard/ltt-statedump.c b/discard/ltt-statedump.c new file mode 100644 index 00000000..06ade69a --- /dev/null +++ b/discard/ltt-statedump.c @@ -0,0 +1,441 @@ +/* + * Linux Trace Toolkit Kernel State Dump + * + * Copyright 2005 - + * Jean-Hugues Deschenes + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ltt-tracer.h" + +#ifdef CONFIG_GENERIC_HARDIRQS +#include +#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"); diff --git a/discard/ltt-trace-control.c b/discard/ltt-trace-control.c new file mode 100644 index 00000000..0a02549d --- /dev/null +++ b/discard/ltt-trace-control.c @@ -0,0 +1,1426 @@ +/* + * LTT trace control module over debugfs. + * + * Copyright 2008 - Zhaolei + * + * Copyright 2009 - Gui Jianfeng + * 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 +#include +#include +#include +#include +#include +#include + +#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: + * `-- + * `-- + * `-- + * |-- + * | |-- 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 "); +MODULE_DESCRIPTION("Linux Trace Toolkit Trace Controller"); diff --git a/discard/ltt-userspace-event.c b/discard/ltt-userspace-event.c new file mode 100644 index 00000000..c716d724 --- /dev/null +++ b/discard/ltt-userspace-event.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 Mathieu Desnoyers + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Linux Trace Toolkit Userspace Event"); diff --git a/ltt-filter.c b/ltt-filter.c deleted file mode 100644 index ec113af6..00000000 --- a/ltt-filter.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2008 Mathieu Desnoyers - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include -#include -#include -#include - -#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 "); -MODULE_DESCRIPTION("Linux Trace Toolkit Filter"); diff --git a/ltt-kprobes.c b/ltt-kprobes.c deleted file mode 100644 index 7539381b..00000000 --- a/ltt-kprobes.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * (C) Copyright 2009 - - * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) - * - * LTTng kprobes integration module. - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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"); diff --git a/ltt-statedump.c b/ltt-statedump.c deleted file mode 100644 index 06ade69a..00000000 --- a/ltt-statedump.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Linux Trace Toolkit Kernel State Dump - * - * Copyright 2005 - - * Jean-Hugues Deschenes - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ltt-tracer.h" - -#ifdef CONFIG_GENERIC_HARDIRQS -#include -#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"); diff --git a/ltt-trace-control.c b/ltt-trace-control.c deleted file mode 100644 index 0a02549d..00000000 --- a/ltt-trace-control.c +++ /dev/null @@ -1,1426 +0,0 @@ -/* - * LTT trace control module over debugfs. - * - * Copyright 2008 - Zhaolei - * - * Copyright 2009 - Gui Jianfeng - * 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 -#include -#include -#include -#include -#include -#include - -#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: - * `-- - * `-- - * `-- - * |-- - * | |-- 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 "); -MODULE_DESCRIPTION("Linux Trace Toolkit Trace Controller"); diff --git a/ltt-tracer.h b/ltt-tracer.h index 84b3731f..2f947b7e 100644 --- a/ltt-tracer.h +++ b/ltt-tracer.h @@ -72,71 +72,6 @@ enum ltt_channels { 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 * @@ -326,70 +261,6 @@ slow_path: 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 diff --git a/ltt-userspace-event.c b/ltt-userspace-event.c deleted file mode 100644 index c716d724..00000000 --- a/ltt-userspace-event.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2008 Mathieu Desnoyers - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include -#include -#include -#include -#include -#include -#include - -#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 "); -MODULE_DESCRIPTION("Linux Trace Toolkit Userspace Event");