aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung.kim@lge.com>2013-10-14 04:24:26 -0400
committerSteven Rostedt <rostedt@goodmis.org>2013-10-18 22:23:16 -0400
commit29ad23b00474c34e3b5040dda508c78d33a1a3eb (patch)
treef6969382cf228535853c8ea3b60056e58678b673
parent6a10108bdbbfb66e5c431fd1056534e9717d34eb (diff)
ftrace: Add set_graph_notrace filter
The set_graph_notrace filter is analogous to set_ftrace_notrace and can be used for eliminating uninteresting part of function graph trace output. It also works with set_graph_function nicely. # cd /sys/kernel/debug/tracing/ # echo do_page_fault > set_graph_function # perf ftrace live true 2) | do_page_fault() { 2) | __do_page_fault() { 2) 0.381 us | down_read_trylock(); 2) 0.055 us | __might_sleep(); 2) 0.696 us | find_vma(); 2) | handle_mm_fault() { 2) | handle_pte_fault() { 2) | __do_fault() { 2) | filemap_fault() { 2) | find_get_page() { 2) 0.033 us | __rcu_read_lock(); 2) 0.035 us | __rcu_read_unlock(); 2) 1.696 us | } 2) 0.031 us | __might_sleep(); 2) 2.831 us | } 2) | _raw_spin_lock() { 2) 0.046 us | add_preempt_count(); 2) 0.841 us | } 2) 0.033 us | page_add_file_rmap(); 2) | _raw_spin_unlock() { 2) 0.057 us | sub_preempt_count(); 2) 0.568 us | } 2) | unlock_page() { 2) 0.084 us | page_waitqueue(); 2) 0.126 us | __wake_up_bit(); 2) 1.117 us | } 2) 7.729 us | } 2) 8.397 us | } 2) 8.956 us | } 2) 0.085 us | up_read(); 2) + 12.745 us | } 2) + 13.401 us | } ... # echo handle_mm_fault > set_graph_notrace # perf ftrace live true 1) | do_page_fault() { 1) | __do_page_fault() { 1) 0.205 us | down_read_trylock(); 1) 0.041 us | __might_sleep(); 1) 0.344 us | find_vma(); 1) 0.069 us | up_read(); 1) 4.692 us | } 1) 5.311 us | } ... Link: http://lkml.kernel.org/r/1381739066-7531-5-git-send-email-namhyung@kernel.org Signed-off-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--include/linux/ftrace.h1
-rw-r--r--kernel/trace/ftrace.c33
-rw-r--r--kernel/trace/trace.h22
-rw-r--r--kernel/trace/trace_functions_graph.c56
4 files changed, 109 insertions, 3 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 9f15c0064c50..ec85d48619e1 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -721,6 +721,7 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
721extern char __irqentry_text_start[]; 721extern char __irqentry_text_start[];
722extern char __irqentry_text_end[]; 722extern char __irqentry_text_end[];
723 723
724#define FTRACE_NOTRACE_DEPTH 65536
724#define FTRACE_RETFUNC_DEPTH 50 725#define FTRACE_RETFUNC_DEPTH 50
725#define FTRACE_RETSTACK_ALLOC_SIZE 32 726#define FTRACE_RETSTACK_ALLOC_SIZE 32
726extern int register_ftrace_graph(trace_func_graph_ret_t retfunc, 727extern int register_ftrace_graph(trace_func_graph_ret_t retfunc,
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 26a229ab0c19..44e826a79665 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -3776,7 +3776,9 @@ static const struct file_operations ftrace_notrace_fops = {
3776static DEFINE_MUTEX(graph_lock); 3776static DEFINE_MUTEX(graph_lock);
3777 3777
3778int ftrace_graph_count; 3778int ftrace_graph_count;
3779int ftrace_graph_notrace_count;
3779unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; 3780unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly;
3781unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly;
3780 3782
3781struct ftrace_graph_data { 3783struct ftrace_graph_data {
3782 unsigned long *table; 3784 unsigned long *table;
@@ -3891,6 +3893,26 @@ ftrace_graph_open(struct inode *inode, struct file *file)
3891} 3893}
3892 3894
3893static int 3895static int
3896ftrace_graph_notrace_open(struct inode *inode, struct file *file)
3897{
3898 struct ftrace_graph_data *fgd;
3899
3900 if (unlikely(ftrace_disabled))
3901 return -ENODEV;
3902
3903 fgd = kmalloc(sizeof(*fgd), GFP_KERNEL);
3904 if (fgd == NULL)
3905 return -ENOMEM;
3906
3907 fgd->table = ftrace_graph_notrace_funcs;
3908 fgd->size = FTRACE_GRAPH_MAX_FUNCS;
3909 fgd->count = &ftrace_graph_notrace_count;
3910 fgd->seq_ops = &ftrace_graph_seq_ops;
3911
3912 return __ftrace_graph_open(inode, file, fgd);
3913}
3914
3915static int
3894ftrace_graph_release(struct inode *inode, struct file *file) 3916ftrace_graph_release(struct inode *inode, struct file *file)
3895{ 3917{
3896 if (file->f_mode & FMODE_READ) { 3918 if (file->f_mode & FMODE_READ) {
@@ -4011,6 +4033,14 @@ static const struct file_operations ftrace_graph_fops = {
4011 .llseek = ftrace_filter_lseek, 4033 .llseek = ftrace_filter_lseek,
4012 .release = ftrace_graph_release, 4034 .release = ftrace_graph_release,
4013}; 4035};
4036
4037static const struct file_operations ftrace_graph_notrace_fops = {
4038 .open = ftrace_graph_notrace_open,
4039 .read = seq_read,
4040 .write = ftrace_graph_write,
4041 .llseek = ftrace_filter_lseek,
4042 .release = ftrace_graph_release,
4043};
4014#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 4044#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
4015 4045
4016static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) 4046static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
@@ -4032,6 +4062,9 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
4032 trace_create_file("set_graph_function", 0444, d_tracer, 4062 trace_create_file("set_graph_function", 0444, d_tracer,
4033 NULL, 4063 NULL,
4034 &ftrace_graph_fops); 4064 &ftrace_graph_fops);
4065 trace_create_file("set_graph_notrace", 0444, d_tracer,
4066 NULL,
4067 &ftrace_graph_notrace_fops);
4035#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 4068#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
4036 4069
4037 return 0; 4070 return 0;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 40211cef2796..d1cf5159bec0 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -732,6 +732,8 @@ extern void __trace_graph_return(struct trace_array *tr,
732#define FTRACE_GRAPH_MAX_FUNCS 32 732#define FTRACE_GRAPH_MAX_FUNCS 32
733extern int ftrace_graph_count; 733extern int ftrace_graph_count;
734extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS]; 734extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS];
735extern int ftrace_graph_notrace_count;
736extern unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS];
735 737
736static inline int ftrace_graph_addr(unsigned long addr) 738static inline int ftrace_graph_addr(unsigned long addr)
737{ 739{
@@ -757,11 +759,31 @@ static inline int ftrace_graph_addr(unsigned long addr)
757 759
758 return 0; 760 return 0;
759} 761}
762
763static inline int ftrace_graph_notrace_addr(unsigned long addr)
764{
765 int i;
766
767 if (!ftrace_graph_notrace_count)
768 return 0;
769
770 for (i = 0; i < ftrace_graph_notrace_count; i++) {
771 if (addr == ftrace_graph_notrace_funcs[i])
772 return 1;
773 }
774
775 return 0;
776}
760#else 777#else
761static inline int ftrace_graph_addr(unsigned long addr) 778static inline int ftrace_graph_addr(unsigned long addr)
762{ 779{
763 return 1; 780 return 1;
764} 781}
782
783static inline int ftrace_graph_notrace_addr(unsigned long addr)
784{
785 return 0;
786}
765#endif /* CONFIG_DYNAMIC_FTRACE */ 787#endif /* CONFIG_DYNAMIC_FTRACE */
766#else /* CONFIG_FUNCTION_GRAPH_TRACER */ 788#else /* CONFIG_FUNCTION_GRAPH_TRACER */
767static inline enum print_line_t 789static inline enum print_line_t
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index b5c09242683d..e08c030b8f38 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -114,16 +114,37 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,
114 return -EBUSY; 114 return -EBUSY;
115 } 115 }
116 116
117 /*
118 * The curr_ret_stack is an index to ftrace return stack of
119 * current task. Its value should be in [0, FTRACE_RETFUNC_
120 * DEPTH) when the function graph tracer is used. To support
121 * filtering out specific functions, it makes the index
122 * negative by subtracting huge value (FTRACE_NOTRACE_DEPTH)
123 * so when it sees a negative index the ftrace will ignore
124 * the record. And the index gets recovered when returning
125 * from the filtered function by adding the FTRACE_NOTRACE_
126 * DEPTH and then it'll continue to record functions normally.
127 *
128 * The curr_ret_stack is initialized to -1 and get increased
129 * in this function. So it can be less than -1 only if it was
130 * filtered out via ftrace_graph_notrace_addr() which can be
131 * set from set_graph_notrace file in debugfs by user.
132 */
133 if (current->curr_ret_stack < -1)
134 return -EBUSY;
135
117 calltime = trace_clock_local(); 136 calltime = trace_clock_local();
118 137
119 index = ++current->curr_ret_stack; 138 index = ++current->curr_ret_stack;
139 if (ftrace_graph_notrace_addr(func))
140 current->curr_ret_stack -= FTRACE_NOTRACE_DEPTH;
120 barrier(); 141 barrier();
121 current->ret_stack[index].ret = ret; 142 current->ret_stack[index].ret = ret;
122 current->ret_stack[index].func = func; 143 current->ret_stack[index].func = func;
123 current->ret_stack[index].calltime = calltime; 144 current->ret_stack[index].calltime = calltime;
124 current->ret_stack[index].subtime = 0; 145 current->ret_stack[index].subtime = 0;
125 current->ret_stack[index].fp = frame_pointer; 146 current->ret_stack[index].fp = frame_pointer;
126 *depth = index; 147 *depth = current->curr_ret_stack;
127 148
128 return 0; 149 return 0;
129} 150}
@@ -137,7 +158,17 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
137 158
138 index = current->curr_ret_stack; 159 index = current->curr_ret_stack;
139 160
140 if (unlikely(index < 0)) { 161 /*
162 * A negative index here means that it's just returned from a
163 * notrace'd function. Recover index to get an original
164 * return address. See ftrace_push_return_trace().
165 *
166 * TODO: Need to check whether the stack gets corrupted.
167 */
168 if (index < 0)
169 index += FTRACE_NOTRACE_DEPTH;
170
171 if (unlikely(index < 0 || index >= FTRACE_RETFUNC_DEPTH)) {
141 ftrace_graph_stop(); 172 ftrace_graph_stop();
142 WARN_ON(1); 173 WARN_ON(1);
143 /* Might as well panic, otherwise we have no where to go */ 174 /* Might as well panic, otherwise we have no where to go */
@@ -193,6 +224,15 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
193 trace.rettime = trace_clock_local(); 224 trace.rettime = trace_clock_local();
194 barrier(); 225 barrier();
195 current->curr_ret_stack--; 226 current->curr_ret_stack--;
227 /*
228 * The curr_ret_stack can be less than -1 only if it was
229 * filtered out and it's about to return from the function.
230 * Recover the index and continue to trace normal functions.
231 */
232 if (current->curr_ret_stack < -1) {
233 current->curr_ret_stack += FTRACE_NOTRACE_DEPTH;
234 return ret;
235 }
196 236
197 /* 237 /*
198 * The trace should run after decrementing the ret counter 238 * The trace should run after decrementing the ret counter
@@ -259,10 +299,20 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)
259 299
260 /* trace it when it is-nested-in or is a function enabled. */ 300 /* trace it when it is-nested-in or is a function enabled. */
261 if ((!(trace->depth || ftrace_graph_addr(trace->func)) || 301 if ((!(trace->depth || ftrace_graph_addr(trace->func)) ||
262 ftrace_graph_ignore_irqs()) || 302 ftrace_graph_ignore_irqs()) || (trace->depth < 0) ||
263 (max_depth && trace->depth >= max_depth)) 303 (max_depth && trace->depth >= max_depth))
264 return 0; 304 return 0;
265 305
306 /*
307 * Do not trace a function if it's filtered by set_graph_notrace.
308 * Make the index of ret stack negative to indicate that it should
309 * ignore further functions. But it needs its own ret stack entry
310 * to recover the original index in order to continue tracing after
311 * returning from the function.
312 */
313 if (ftrace_graph_notrace_addr(trace->func))
314 return 1;
315
266 local_irq_save(flags); 316 local_irq_save(flags);
267 cpu = raw_smp_processor_id(); 317 cpu = raw_smp_processor_id();
268 data = per_cpu_ptr(tr->trace_buffer.data, cpu); 318 data = per_cpu_ptr(tr->trace_buffer.data, cpu);