From 563fc9f54b49c4963cea21a2d902ae3ad32577e0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 28 May 2009 13:37:24 -0400 Subject: [PATCH] --- yaml --- r: 146198 b: refs/heads/master c: 2af15d6a44b871ad4c2a651302374cde8f335480 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/Documentation/kernel-parameters.txt | 17 +- trunk/include/trace/events/workqueue.h | 100 ----------- trunk/include/trace/workqueue.h | 25 +++ trunk/kernel/trace/ftrace.c | 42 +++++ trunk/kernel/trace/trace_stat.c | 206 +++++++++------------- trunk/kernel/trace/trace_workqueue.c | 23 ++- trunk/kernel/workqueue.c | 11 +- 8 files changed, 189 insertions(+), 237 deletions(-) delete mode 100644 trunk/include/trace/events/workqueue.h create mode 100644 trunk/include/trace/workqueue.h diff --git a/[refs] b/[refs] index 4e1ac106fac6..241856dde8fe 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 43bd1236234cacbc18d1476a9b57e7a306efddf5 +refs/heads/master: 2af15d6a44b871ad4c2a651302374cde8f335480 diff --git a/trunk/Documentation/kernel-parameters.txt b/trunk/Documentation/kernel-parameters.txt index 9243dd84f4d6..fcd3bfbe74e8 100644 --- a/trunk/Documentation/kernel-parameters.txt +++ b/trunk/Documentation/kernel-parameters.txt @@ -751,12 +751,25 @@ and is between 256 and 4096 characters. It is defined in the file ia64_pal_cache_flush instead of SAL_CACHE_FLUSH. ftrace=[tracer] - [ftrace] will set and start the specified tracer + [FTRACE] will set and start the specified tracer as early as possible in order to facilitate early boot debugging. ftrace_dump_on_oops - [ftrace] will dump the trace buffers on oops. + [FTRACE] will dump the trace buffers on oops. + + ftrace_filter=[function-list] + [FTRACE] Limit the functions traced by the function + tracer at boot up. function-list is a comma separated + list of functions. This list can be changed at run + time by the set_ftrace_filter file in the debugfs + tracing directory. + + ftrace_notrace=[function-list] + [FTRACE] Do not trace the functions specified in + function-list. This list can be changed at run time + by the set_ftrace_notrace file in the debugfs + tracing directory. gamecon.map[2|3]= [HW,JOY] Multisystem joystick and NES/SNES/PSX pad diff --git a/trunk/include/trace/events/workqueue.h b/trunk/include/trace/events/workqueue.h deleted file mode 100644 index 035f1bff288e..000000000000 --- a/trunk/include/trace/events/workqueue.h +++ /dev/null @@ -1,100 +0,0 @@ -#if !defined(_TRACE_WORKQUEUE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_WORKQUEUE_H - -#include -#include -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM workqueue - -TRACE_EVENT(workqueue_insertion, - - TP_PROTO(struct task_struct *wq_thread, struct work_struct *work), - - TP_ARGS(wq_thread, work), - - TP_STRUCT__entry( - __array(char, thread_comm, TASK_COMM_LEN) - __field(pid_t, thread_pid) - __field(work_func_t, func) - ), - - TP_fast_assign( - memcpy(__entry->thread_comm, wq_thread->comm, TASK_COMM_LEN); - __entry->thread_pid = wq_thread->pid; - __entry->func = work->func; - ), - - TP_printk("thread=%s:%d func=%pF", __entry->thread_comm, - __entry->thread_pid, __entry->func) -); - -TRACE_EVENT(workqueue_execution, - - TP_PROTO(struct task_struct *wq_thread, struct work_struct *work), - - TP_ARGS(wq_thread, work), - - TP_STRUCT__entry( - __array(char, thread_comm, TASK_COMM_LEN) - __field(pid_t, thread_pid) - __field(work_func_t, func) - ), - - TP_fast_assign( - memcpy(__entry->thread_comm, wq_thread->comm, TASK_COMM_LEN); - __entry->thread_pid = wq_thread->pid; - __entry->func = work->func; - ), - - TP_printk("thread=%s:%d func=%pF", __entry->thread_comm, - __entry->thread_pid, __entry->func) -); - -/* Trace the creation of one workqueue thread on a cpu */ -TRACE_EVENT(workqueue_creation, - - TP_PROTO(struct task_struct *wq_thread, int cpu), - - TP_ARGS(wq_thread, cpu), - - TP_STRUCT__entry( - __array(char, thread_comm, TASK_COMM_LEN) - __field(pid_t, thread_pid) - __field(int, cpu) - ), - - TP_fast_assign( - memcpy(__entry->thread_comm, wq_thread->comm, TASK_COMM_LEN); - __entry->thread_pid = wq_thread->pid; - __entry->cpu = cpu; - ), - - TP_printk("thread=%s:%d cpu=%d", __entry->thread_comm, - __entry->thread_pid, __entry->cpu) -); - -TRACE_EVENT(workqueue_destruction, - - TP_PROTO(struct task_struct *wq_thread), - - TP_ARGS(wq_thread), - - TP_STRUCT__entry( - __array(char, thread_comm, TASK_COMM_LEN) - __field(pid_t, thread_pid) - ), - - TP_fast_assign( - memcpy(__entry->thread_comm, wq_thread->comm, TASK_COMM_LEN); - __entry->thread_pid = wq_thread->pid; - ), - - TP_printk("thread=%s:%d", __entry->thread_comm, __entry->thread_pid) -); - -#endif /* _TRACE_WORKQUEUE_H */ - -/* This part must be outside protection */ -#include diff --git a/trunk/include/trace/workqueue.h b/trunk/include/trace/workqueue.h new file mode 100644 index 000000000000..7626523deeba --- /dev/null +++ b/trunk/include/trace/workqueue.h @@ -0,0 +1,25 @@ +#ifndef __TRACE_WORKQUEUE_H +#define __TRACE_WORKQUEUE_H + +#include +#include +#include + +DECLARE_TRACE(workqueue_insertion, + TP_PROTO(struct task_struct *wq_thread, struct work_struct *work), + TP_ARGS(wq_thread, work)); + +DECLARE_TRACE(workqueue_execution, + TP_PROTO(struct task_struct *wq_thread, struct work_struct *work), + TP_ARGS(wq_thread, work)); + +/* Trace the creation of one workqueue thread on a cpu */ +DECLARE_TRACE(workqueue_creation, + TP_PROTO(struct task_struct *wq_thread, int cpu), + TP_ARGS(wq_thread, cpu)); + +DECLARE_TRACE(workqueue_destruction, + TP_PROTO(struct task_struct *wq_thread), + TP_ARGS(wq_thread)); + +#endif /* __TRACE_WORKQUEUE_H */ diff --git a/trunk/kernel/trace/ftrace.c b/trunk/kernel/trace/ftrace.c index 140699a9a8a7..2074e5b7766b 100644 --- a/trunk/kernel/trace/ftrace.c +++ b/trunk/kernel/trace/ftrace.c @@ -32,6 +32,7 @@ #include #include +#include #include "trace_output.h" #include "trace_stat.h" @@ -2369,6 +2370,45 @@ void ftrace_set_notrace(unsigned char *buf, int len, int reset) ftrace_set_regex(buf, len, reset, 0); } +/* + * command line interface to allow users to set filters on boot up. + */ +#define FTRACE_FILTER_SIZE COMMAND_LINE_SIZE +static char ftrace_notrace_buf[FTRACE_FILTER_SIZE] __initdata; +static char ftrace_filter_buf[FTRACE_FILTER_SIZE] __initdata; + +static int __init set_ftrace_notrace(char *str) +{ + strncpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE); + return 1; +} +__setup("ftrace_notrace=", set_ftrace_notrace); + +static int __init set_ftrace_filter(char *str) +{ + strncpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE); + return 1; +} +__setup("ftrace_filter=", set_ftrace_filter); + +static void __init set_ftrace_early_filter(char *buf, int enable) +{ + char *func; + + while (buf) { + func = strsep(&buf, ","); + ftrace_set_regex(func, strlen(func), 0, enable); + } +} + +static void __init set_ftrace_early_filters(void) +{ + if (ftrace_filter_buf[0]) + set_ftrace_early_filter(ftrace_filter_buf, 1); + if (ftrace_notrace_buf[0]) + set_ftrace_early_filter(ftrace_notrace_buf, 0); +} + static int ftrace_regex_release(struct inode *inode, struct file *file, int enable) { @@ -2829,6 +2869,8 @@ void __init ftrace_init(void) if (ret) pr_warning("Failed to register trace ftrace module notifier\n"); + set_ftrace_early_filters(); + return; failed: ftrace_disabled = 1; diff --git a/trunk/kernel/trace/trace_stat.c b/trunk/kernel/trace/trace_stat.c index c00643733f4c..fdde3a4a94cd 100644 --- a/trunk/kernel/trace/trace_stat.c +++ b/trunk/kernel/trace/trace_stat.c @@ -1,7 +1,7 @@ /* * Infrastructure for statistic tracing (histogram output). * - * Copyright (C) 2008-2009 Frederic Weisbecker + * Copyright (C) 2008 Frederic Weisbecker * * Based on the code from trace_branch.c which is * Copyright (C) 2008 Steven Rostedt @@ -10,27 +10,22 @@ #include -#include #include #include "trace_stat.h" #include "trace.h" -/* - * List of stat red-black nodes from a tracer - * We use a such tree to sort quickly the stat - * entries from the tracer. - */ -struct stat_node { - struct rb_node node; +/* List of stat entries from a tracer */ +struct trace_stat_list { + struct list_head list; void *stat; }; /* A stat session is the stats output in one file */ -struct stat_session { +struct tracer_stat_session { struct list_head session_list; struct tracer_stat *ts; - struct rb_root stat_root; + struct list_head stat_list; struct mutex stat_mutex; struct dentry *file; }; @@ -42,48 +37,18 @@ static DEFINE_MUTEX(all_stat_sessions_mutex); /* The root directory for all stat files */ static struct dentry *stat_dir; -/* - * Iterate through the rbtree using a post order traversal path - * to release the next node. - * It won't necessary release one at each iteration - * but it will at least advance closer to the next one - * to be released. - */ -static struct rb_node *release_next(struct rb_node *node) -{ - struct stat_node *snode; - struct rb_node *parent = rb_parent(node); - - if (node->rb_left) - return node->rb_left; - else if (node->rb_right) - return node->rb_right; - else { - if (!parent) - ; - else if (parent->rb_left == node) - parent->rb_left = NULL; - else - parent->rb_right = NULL; - - snode = container_of(node, struct stat_node, node); - kfree(snode); - - return parent; - } -} -static void reset_stat_session(struct stat_session *session) +static void reset_stat_session(struct tracer_stat_session *session) { - struct rb_node *node = session->stat_root.rb_node; + struct trace_stat_list *node, *next; - while (node) - node = release_next(node); + list_for_each_entry_safe(node, next, &session->stat_list, list) + kfree(node); - session->stat_root = RB_ROOT; + INIT_LIST_HEAD(&session->stat_list); } -static void destroy_session(struct stat_session *session) +static void destroy_session(struct tracer_stat_session *session) { debugfs_remove(session->file); reset_stat_session(session); @@ -91,60 +56,25 @@ static void destroy_session(struct stat_session *session) kfree(session); } -typedef int (*cmp_stat_t)(void *, void *); - -static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp) -{ - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct stat_node *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - data->stat = stat; - - /* - * Figure out where to put new node - * This is a descendent sorting - */ - while (*new) { - struct stat_node *this; - int result; - - this = container_of(*new, struct stat_node, node); - result = cmp(data->stat, this->stat); - - parent = *new; - if (result >= 0) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - - rb_link_node(&data->node, parent, new); - rb_insert_color(&data->node, root); - return 0; -} - /* * For tracers that don't provide a stat_cmp callback. - * This one will force an insertion as right-most node - * in the rbtree. + * This one will force an immediate insertion on tail of + * the list. */ static int dummy_cmp(void *p1, void *p2) { - return -1; + return 1; } /* - * Initialize the stat rbtree at each trace_stat file opening. + * Initialize the stat list at each trace_stat file opening. * All of these copies and sorting are required on all opening * since the stats could have changed between two file sessions. */ -static int stat_seq_init(struct stat_session *session) +static int stat_seq_init(struct tracer_stat_session *session) { + struct trace_stat_list *iter_entry, *new_entry; struct tracer_stat *ts = session->ts; - struct rb_root *root = &session->stat_root; void *stat; int ret = 0; int i; @@ -159,12 +89,25 @@ static int stat_seq_init(struct stat_session *session) if (!stat) goto exit; - ret = insert_stat(root, stat, ts->stat_cmp); - if (ret) + /* + * The first entry. Actually this is the second, but the first + * one (the stat_list head) is pointless. + */ + new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL); + if (!new_entry) { + ret = -ENOMEM; goto exit; + } + + INIT_LIST_HEAD(&new_entry->list); + + list_add(&new_entry->list, &session->stat_list); + + new_entry->stat = stat; /* - * Iterate over the tracer stat entries and store them in an rbtree. + * Iterate over the tracer stat entries and store them in a sorted + * list. */ for (i = 1; ; i++) { stat = ts->stat_next(stat, i); @@ -173,16 +116,36 @@ static int stat_seq_init(struct stat_session *session) if (!stat) break; - ret = insert_stat(root, stat, ts->stat_cmp); - if (ret) - goto exit_free_rbtree; - } + new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL); + if (!new_entry) { + ret = -ENOMEM; + goto exit_free_list; + } + + INIT_LIST_HEAD(&new_entry->list); + new_entry->stat = stat; + + list_for_each_entry_reverse(iter_entry, &session->stat_list, + list) { + + /* Insertion with a descendent sorting */ + if (ts->stat_cmp(iter_entry->stat, + new_entry->stat) >= 0) { + list_add(&new_entry->list, &iter_entry->list); + break; + } + } + + /* The current larger value */ + if (list_empty(&new_entry->list)) + list_add(&new_entry->list, &session->stat_list); + } exit: mutex_unlock(&session->stat_mutex); return ret; -exit_free_rbtree: +exit_free_list: reset_stat_session(session); mutex_unlock(&session->stat_mutex); return ret; @@ -191,51 +154,38 @@ static int stat_seq_init(struct stat_session *session) static void *stat_seq_start(struct seq_file *s, loff_t *pos) { - struct stat_session *session = s->private; - struct rb_node *node; - int i; + struct tracer_stat_session *session = s->private; - /* Prevent from tracer switch or rbtree modification */ + /* Prevent from tracer switch or stat_list modification */ mutex_lock(&session->stat_mutex); /* If we are in the beginning of the file, print the headers */ - if (!*pos && session->ts->stat_headers) { - (*pos)++; + if (!*pos && session->ts->stat_headers) return SEQ_START_TOKEN; - } - node = rb_first(&session->stat_root); - for (i = 0; node && i < *pos; i++) - node = rb_next(node); - - (*pos)++; - - return node; + return seq_list_start(&session->stat_list, *pos); } static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) { - struct stat_session *session = s->private; - struct rb_node *node = p; - - (*pos)++; + struct tracer_stat_session *session = s->private; if (p == SEQ_START_TOKEN) - return rb_first(&session->stat_root); + return seq_list_start(&session->stat_list, *pos); - return rb_next(node); + return seq_list_next(p, &session->stat_list, pos); } static void stat_seq_stop(struct seq_file *s, void *p) { - struct stat_session *session = s->private; + struct tracer_stat_session *session = s->private; mutex_unlock(&session->stat_mutex); } static int stat_seq_show(struct seq_file *s, void *v) { - struct stat_session *session = s->private; - struct stat_node *l = container_of(v, struct stat_node, node); + struct tracer_stat_session *session = s->private; + struct trace_stat_list *l = list_entry(v, struct trace_stat_list, list); if (v == SEQ_START_TOKEN) return session->ts->stat_headers(s); @@ -255,7 +205,7 @@ static int tracing_stat_open(struct inode *inode, struct file *file) { int ret; - struct stat_session *session = inode->i_private; + struct tracer_stat_session *session = inode->i_private; ret = seq_open(file, &trace_stat_seq_ops); if (!ret) { @@ -268,11 +218,11 @@ static int tracing_stat_open(struct inode *inode, struct file *file) } /* - * Avoid consuming memory with our now useless rbtree. + * Avoid consuming memory with our now useless list. */ static int tracing_stat_release(struct inode *i, struct file *f) { - struct stat_session *session = i->i_private; + struct tracer_stat_session *session = i->i_private; mutex_lock(&session->stat_mutex); reset_stat_session(session); @@ -301,7 +251,7 @@ static int tracing_stat_init(void) return 0; } -static int init_stat_file(struct stat_session *session) +static int init_stat_file(struct tracer_stat_session *session) { if (!stat_dir && tracing_stat_init()) return -ENODEV; @@ -316,7 +266,7 @@ static int init_stat_file(struct stat_session *session) int register_stat_tracer(struct tracer_stat *trace) { - struct stat_session *session, *node; + struct tracer_stat_session *session, *node, *tmp; int ret; if (!trace) @@ -327,7 +277,7 @@ int register_stat_tracer(struct tracer_stat *trace) /* Already registered? */ mutex_lock(&all_stat_sessions_mutex); - list_for_each_entry(node, &all_stat_sessions, session_list) { + list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { if (node->ts == trace) { mutex_unlock(&all_stat_sessions_mutex); return -EINVAL; @@ -336,13 +286,15 @@ int register_stat_tracer(struct tracer_stat *trace) mutex_unlock(&all_stat_sessions_mutex); /* Init the session */ - session = kzalloc(sizeof(*session), GFP_KERNEL); + session = kmalloc(sizeof(struct tracer_stat_session), GFP_KERNEL); if (!session) return -ENOMEM; session->ts = trace; INIT_LIST_HEAD(&session->session_list); + INIT_LIST_HEAD(&session->stat_list); mutex_init(&session->stat_mutex); + session->file = NULL; ret = init_stat_file(session); if (ret) { @@ -360,7 +312,7 @@ int register_stat_tracer(struct tracer_stat *trace) void unregister_stat_tracer(struct tracer_stat *trace) { - struct stat_session *node, *tmp; + struct tracer_stat_session *node, *tmp; mutex_lock(&all_stat_sessions_mutex); list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { diff --git a/trunk/kernel/trace/trace_workqueue.c b/trunk/kernel/trace/trace_workqueue.c index 97fcea4acce1..984b9175c13d 100644 --- a/trunk/kernel/trace/trace_workqueue.c +++ b/trunk/kernel/trace/trace_workqueue.c @@ -6,7 +6,7 @@ */ -#include +#include #include #include #include "trace_stat.h" @@ -16,6 +16,8 @@ /* A cpu workqueue thread */ struct cpu_workqueue_stats { struct list_head list; +/* Useful to know if we print the cpu headers */ + bool first_entry; int cpu; pid_t pid; /* Can be inserted from interrupt or user context, need to be atomic */ @@ -45,11 +47,12 @@ probe_workqueue_insertion(struct task_struct *wq_thread, struct work_struct *work) { int cpu = cpumask_first(&wq_thread->cpus_allowed); - struct cpu_workqueue_stats *node; + struct cpu_workqueue_stats *node, *next; unsigned long flags; spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); - list_for_each_entry(node, &workqueue_cpu_stat(cpu)->list, list) { + list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, + list) { if (node->pid == wq_thread->pid) { atomic_inc(&node->inserted); goto found; @@ -66,11 +69,12 @@ probe_workqueue_execution(struct task_struct *wq_thread, struct work_struct *work) { int cpu = cpumask_first(&wq_thread->cpus_allowed); - struct cpu_workqueue_stats *node; + struct cpu_workqueue_stats *node, *next; unsigned long flags; spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); - list_for_each_entry(node, &workqueue_cpu_stat(cpu)->list, list) { + list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, + list) { if (node->pid == wq_thread->pid) { node->executed++; goto found; @@ -101,6 +105,8 @@ static void probe_workqueue_creation(struct task_struct *wq_thread, int cpu) cws->pid = wq_thread->pid; spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); + if (list_empty(&workqueue_cpu_stat(cpu)->list)) + cws->first_entry = true; list_add_tail(&cws->list, &workqueue_cpu_stat(cpu)->list); spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); } @@ -185,9 +191,16 @@ static void *workqueue_stat_next(void *prev, int idx) static int workqueue_stat_show(struct seq_file *s, void *p) { struct cpu_workqueue_stats *cws = p; + unsigned long flags; + int cpu = cws->cpu; struct pid *pid; struct task_struct *tsk; + spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); + if (&cws->list == workqueue_cpu_stat(cpu)->list.next) + seq_printf(s, "\n"); + spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); + pid = find_get_pid(cws->pid); if (pid) { tsk = get_pid_task(pid, PIDTYPE_PID); diff --git a/trunk/kernel/workqueue.c b/trunk/kernel/workqueue.c index 0668795d8818..f71fb2a08950 100644 --- a/trunk/kernel/workqueue.c +++ b/trunk/kernel/workqueue.c @@ -33,8 +33,7 @@ #include #include #include -#define CREATE_TRACE_POINTS -#include +#include /* * The per-CPU workqueue (if single thread, we always use the first @@ -125,6 +124,8 @@ struct cpu_workqueue_struct *get_wq_data(struct work_struct *work) return (void *) (atomic_long_read(&work->data) & WORK_STRUCT_WQ_DATA_MASK); } +DEFINE_TRACE(workqueue_insertion); + static void insert_work(struct cpu_workqueue_struct *cwq, struct work_struct *work, struct list_head *head) { @@ -261,6 +262,8 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(queue_delayed_work_on); +DEFINE_TRACE(workqueue_execution); + static void run_workqueue(struct cpu_workqueue_struct *cwq) { spin_lock_irq(&cwq->lock); @@ -750,6 +753,8 @@ init_cpu_workqueue(struct workqueue_struct *wq, int cpu) return cwq; } +DEFINE_TRACE(workqueue_creation); + static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu) { struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; @@ -855,6 +860,8 @@ struct workqueue_struct *__create_workqueue_key(const char *name, } EXPORT_SYMBOL_GPL(__create_workqueue_key); +DEFINE_TRACE(workqueue_destruction); + static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) { /*