diff options
author | Namhyung Kim <namhyung.kim@lge.com> | 2013-10-14 04:24:26 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-10-18 22:23:16 -0400 |
commit | 29ad23b00474c34e3b5040dda508c78d33a1a3eb (patch) | |
tree | f6969382cf228535853c8ea3b60056e58678b673 /kernel/trace | |
parent | 6a10108bdbbfb66e5c431fd1056534e9717d34eb (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>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 33 | ||||
-rw-r--r-- | kernel/trace/trace.h | 22 | ||||
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 56 |
3 files changed, 108 insertions, 3 deletions
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 = { | |||
3776 | static DEFINE_MUTEX(graph_lock); | 3776 | static DEFINE_MUTEX(graph_lock); |
3777 | 3777 | ||
3778 | int ftrace_graph_count; | 3778 | int ftrace_graph_count; |
3779 | int ftrace_graph_notrace_count; | ||
3779 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | 3780 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; |
3781 | unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | ||
3780 | 3782 | ||
3781 | struct ftrace_graph_data { | 3783 | struct 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 | ||
3893 | static int | 3895 | static int |
3896 | ftrace_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 | |||
3915 | static int | ||
3894 | ftrace_graph_release(struct inode *inode, struct file *file) | 3916 | ftrace_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 | |||
4037 | static 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 | ||
4016 | static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) | 4046 | static __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 |
733 | extern int ftrace_graph_count; | 733 | extern int ftrace_graph_count; |
734 | extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS]; | 734 | extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS]; |
735 | extern int ftrace_graph_notrace_count; | ||
736 | extern unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS]; | ||
735 | 737 | ||
736 | static inline int ftrace_graph_addr(unsigned long addr) | 738 | static 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 | |||
763 | static 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 |
761 | static inline int ftrace_graph_addr(unsigned long addr) | 778 | static inline int ftrace_graph_addr(unsigned long addr) |
762 | { | 779 | { |
763 | return 1; | 780 | return 1; |
764 | } | 781 | } |
782 | |||
783 | static 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 */ |
767 | static inline enum print_line_t | 789 | static 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); |