diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-05-12 15:20:45 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2008-05-23 14:41:06 -0400 |
commit | 77a2b37d227483fe52aead242652aee406c25bf0 (patch) | |
tree | cecb6a3a02a567530e2ce3502de18f371c9db7c1 | |
parent | 7bd2f24c2f769e3f8f1d4fc8b9fddf689825f6a7 (diff) |
ftrace: startup tester on dynamic tracing.
This patch adds a startup self test on dynamic code modification
and filters. The test filters on a specific function, makes sure that
no other function is traced, exectutes the function, then makes sure that
the function is traced.
This patch also fixes a slight bug with the ftrace selftest, where
tracer_enabled was not being set.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | include/linux/ftrace.h | 2 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 19 | ||||
-rw-r--r-- | kernel/trace/trace_selftest.c | 113 |
3 files changed, 130 insertions, 4 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 953a36d6a199..a842d96c6343 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -55,6 +55,7 @@ struct dyn_ftrace { | |||
55 | }; | 55 | }; |
56 | 56 | ||
57 | int ftrace_force_update(void); | 57 | int ftrace_force_update(void); |
58 | void ftrace_set_filter(unsigned char *buf, int len, int reset); | ||
58 | 59 | ||
59 | /* defined in arch */ | 60 | /* defined in arch */ |
60 | extern int ftrace_ip_converted(unsigned long ip); | 61 | extern int ftrace_ip_converted(unsigned long ip); |
@@ -70,6 +71,7 @@ extern void ftrace_call(void); | |||
70 | extern void mcount_call(void); | 71 | extern void mcount_call(void); |
71 | #else | 72 | #else |
72 | # define ftrace_force_update() ({ 0; }) | 73 | # define ftrace_force_update() ({ 0; }) |
74 | # define ftrace_set_filter(buf, len, reset) do { } while (0) | ||
73 | #endif | 75 | #endif |
74 | 76 | ||
75 | static inline void tracer_disable(void) | 77 | static inline void tracer_disable(void) |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6d4d2e86debc..5e9389faaf75 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -1010,6 +1010,25 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1010 | return ret; | 1010 | return ret; |
1011 | } | 1011 | } |
1012 | 1012 | ||
1013 | /** | ||
1014 | * ftrace_set_filter - set a function to filter on in ftrace | ||
1015 | * @buf - the string that holds the function filter text. | ||
1016 | * @len - the length of the string. | ||
1017 | * @reset - non zero to reset all filters before applying this filter. | ||
1018 | * | ||
1019 | * Filters denote which functions should be enabled when tracing is enabled. | ||
1020 | * If @buf is NULL and reset is set, all functions will be enabled for tracing. | ||
1021 | */ | ||
1022 | notrace void ftrace_set_filter(unsigned char *buf, int len, int reset) | ||
1023 | { | ||
1024 | mutex_lock(&ftrace_filter_lock); | ||
1025 | if (reset) | ||
1026 | ftrace_filter_reset(); | ||
1027 | if (buf) | ||
1028 | ftrace_match(buf, len); | ||
1029 | mutex_unlock(&ftrace_filter_lock); | ||
1030 | } | ||
1031 | |||
1013 | static int notrace | 1032 | static int notrace |
1014 | ftrace_filter_release(struct inode *inode, struct file *file) | 1033 | ftrace_filter_release(struct inode *inode, struct file *file) |
1015 | { | 1034 | { |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index c01874c3b1f9..4c8a1b2d8231 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
@@ -99,6 +99,100 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) | |||
99 | } | 99 | } |
100 | 100 | ||
101 | #ifdef CONFIG_FTRACE | 101 | #ifdef CONFIG_FTRACE |
102 | |||
103 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
104 | |||
105 | #define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func | ||
106 | #define __STR(x) #x | ||
107 | #define STR(x) __STR(x) | ||
108 | static int DYN_FTRACE_TEST_NAME(void) | ||
109 | { | ||
110 | /* used to call mcount */ | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | /* Test dynamic code modification and ftrace filters */ | ||
115 | int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | ||
116 | struct trace_array *tr, | ||
117 | int (*func)(void)) | ||
118 | { | ||
119 | unsigned long count; | ||
120 | int ret; | ||
121 | int save_ftrace_enabled = ftrace_enabled; | ||
122 | int save_tracer_enabled = tracer_enabled; | ||
123 | |||
124 | /* The ftrace test PASSED */ | ||
125 | printk(KERN_CONT "PASSED\n"); | ||
126 | pr_info("Testing dynamic ftrace: "); | ||
127 | |||
128 | /* enable tracing, and record the filter function */ | ||
129 | ftrace_enabled = 1; | ||
130 | tracer_enabled = 1; | ||
131 | |||
132 | /* passed in by parameter to fool gcc from optimizing */ | ||
133 | func(); | ||
134 | |||
135 | /* update the records */ | ||
136 | ret = ftrace_force_update(); | ||
137 | if (ret) { | ||
138 | printk(KERN_CONT ".. ftraced failed .. "); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | /* filter only on our function */ | ||
143 | ftrace_set_filter(STR(DYN_FTRACE_TEST_NAME), | ||
144 | sizeof(STR(DYN_FTRACE_TEST_NAME)), 1); | ||
145 | |||
146 | /* enable tracing */ | ||
147 | tr->ctrl = 1; | ||
148 | trace->init(tr); | ||
149 | /* Sleep for a 1/10 of a second */ | ||
150 | msleep(100); | ||
151 | |||
152 | /* we should have nothing in the buffer */ | ||
153 | ret = trace_test_buffer(tr, &count); | ||
154 | if (ret) | ||
155 | goto out; | ||
156 | |||
157 | if (count) { | ||
158 | ret = -1; | ||
159 | printk(KERN_CONT ".. filter did not filter .. "); | ||
160 | goto out; | ||
161 | } | ||
162 | |||
163 | /* call our function again */ | ||
164 | func(); | ||
165 | |||
166 | /* sleep again */ | ||
167 | msleep(100); | ||
168 | |||
169 | /* stop the tracing. */ | ||
170 | tr->ctrl = 0; | ||
171 | trace->ctrl_update(tr); | ||
172 | ftrace_enabled = 0; | ||
173 | |||
174 | /* check the trace buffer */ | ||
175 | ret = trace_test_buffer(tr, &count); | ||
176 | trace->reset(tr); | ||
177 | |||
178 | /* we should only have one item */ | ||
179 | if (!ret && count != 1) { | ||
180 | printk(KERN_CONT ".. filter failed .."); | ||
181 | ret = -1; | ||
182 | goto out; | ||
183 | } | ||
184 | out: | ||
185 | ftrace_enabled = save_ftrace_enabled; | ||
186 | tracer_enabled = save_tracer_enabled; | ||
187 | |||
188 | /* Enable tracing on all functions again */ | ||
189 | ftrace_set_filter(NULL, 0, 1); | ||
190 | |||
191 | return ret; | ||
192 | } | ||
193 | #else | ||
194 | # define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) | ||
195 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
102 | /* | 196 | /* |
103 | * Simple verification test of ftrace function tracer. | 197 | * Simple verification test of ftrace function tracer. |
104 | * Enable ftrace, sleep 1/10 second, and then read the trace | 198 | * Enable ftrace, sleep 1/10 second, and then read the trace |
@@ -109,8 +203,13 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
109 | { | 203 | { |
110 | unsigned long count; | 204 | unsigned long count; |
111 | int ret; | 205 | int ret; |
206 | int save_ftrace_enabled = ftrace_enabled; | ||
207 | int save_tracer_enabled = tracer_enabled; | ||
112 | 208 | ||
113 | /* make sure functions have been recorded */ | 209 | /* make sure msleep has been recorded */ |
210 | msleep(1); | ||
211 | |||
212 | /* force the recorded functions to be traced */ | ||
114 | ret = ftrace_force_update(); | 213 | ret = ftrace_force_update(); |
115 | if (ret) { | 214 | if (ret) { |
116 | printk(KERN_CONT ".. ftraced failed .. "); | 215 | printk(KERN_CONT ".. ftraced failed .. "); |
@@ -119,6 +218,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
119 | 218 | ||
120 | /* start the tracing */ | 219 | /* start the tracing */ |
121 | ftrace_enabled = 1; | 220 | ftrace_enabled = 1; |
221 | tracer_enabled = 1; | ||
122 | 222 | ||
123 | tr->ctrl = 1; | 223 | tr->ctrl = 1; |
124 | trace->init(tr); | 224 | trace->init(tr); |
@@ -136,8 +236,16 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
136 | if (!ret && !count) { | 236 | if (!ret && !count) { |
137 | printk(KERN_CONT ".. no entries found .."); | 237 | printk(KERN_CONT ".. no entries found .."); |
138 | ret = -1; | 238 | ret = -1; |
239 | goto out; | ||
139 | } | 240 | } |
140 | 241 | ||
242 | ret = trace_selftest_startup_dynamic_tracing(trace, tr, | ||
243 | DYN_FTRACE_TEST_NAME); | ||
244 | |||
245 | out: | ||
246 | ftrace_enabled = save_ftrace_enabled; | ||
247 | tracer_enabled = save_tracer_enabled; | ||
248 | |||
141 | return ret; | 249 | return ret; |
142 | } | 250 | } |
143 | #endif /* CONFIG_FTRACE */ | 251 | #endif /* CONFIG_FTRACE */ |
@@ -415,6 +523,3 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr | |||
415 | return ret; | 523 | return ret; |
416 | } | 524 | } |
417 | #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ | 525 | #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ |
418 | |||
419 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
420 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||