diff options
author | Jiri Olsa <jolsa@redhat.com> | 2010-04-02 13:01:22 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-04-27 12:36:53 -0400 |
commit | 62b915f1060996a8e1f69be50e3b8e9e43b710cb (patch) | |
tree | 26741f065b30b67022d680c3bc2afbda319638e1 | |
parent | d7a8d9e907cc294ec7a4a7046d1886375fbcc82e (diff) |
tracing: Add graph output support for irqsoff tracer
Add function graph output to irqsoff tracer.
The graph output is enabled by setting new 'display-graph' trace option.
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
LKML-Reference: <1270227683-14631-4-git-send-email-jolsa@redhat.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | include/linux/ftrace.h | 15 | ||||
-rw-r--r-- | kernel/trace/trace.c | 35 | ||||
-rw-r--r-- | kernel/trace/trace.h | 21 | ||||
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 15 | ||||
-rw-r--r-- | kernel/trace/trace_irqsoff.c | 271 |
5 files changed, 324 insertions, 33 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ea5b1aae0e8b..8415a522f430 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -352,6 +352,10 @@ struct ftrace_graph_ret { | |||
352 | int depth; | 352 | int depth; |
353 | }; | 353 | }; |
354 | 354 | ||
355 | /* Type of the callback handlers for tracing function graph*/ | ||
356 | typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */ | ||
357 | typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ | ||
358 | |||
355 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 359 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
356 | 360 | ||
357 | /* for init task */ | 361 | /* for init task */ |
@@ -400,10 +404,6 @@ extern char __irqentry_text_end[]; | |||
400 | 404 | ||
401 | #define FTRACE_RETFUNC_DEPTH 50 | 405 | #define FTRACE_RETFUNC_DEPTH 50 |
402 | #define FTRACE_RETSTACK_ALLOC_SIZE 32 | 406 | #define FTRACE_RETSTACK_ALLOC_SIZE 32 |
403 | /* Type of the callback handlers for tracing function graph*/ | ||
404 | typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */ | ||
405 | typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ | ||
406 | |||
407 | extern int register_ftrace_graph(trace_func_graph_ret_t retfunc, | 407 | extern int register_ftrace_graph(trace_func_graph_ret_t retfunc, |
408 | trace_func_graph_ent_t entryfunc); | 408 | trace_func_graph_ent_t entryfunc); |
409 | 409 | ||
@@ -441,6 +441,13 @@ static inline void unpause_graph_tracing(void) | |||
441 | static inline void ftrace_graph_init_task(struct task_struct *t) { } | 441 | static inline void ftrace_graph_init_task(struct task_struct *t) { } |
442 | static inline void ftrace_graph_exit_task(struct task_struct *t) { } | 442 | static inline void ftrace_graph_exit_task(struct task_struct *t) { } |
443 | 443 | ||
444 | static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc, | ||
445 | trace_func_graph_ent_t entryfunc) | ||
446 | { | ||
447 | return -1; | ||
448 | } | ||
449 | static inline void unregister_ftrace_graph(void) { } | ||
450 | |||
444 | static inline int task_curr_ret_stack(struct task_struct *tsk) | 451 | static inline int task_curr_ret_stack(struct task_struct *tsk) |
445 | { | 452 | { |
446 | return -1; | 453 | return -1; |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7b516c7ef9a0..8b9ba41ec146 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -1808,7 +1808,7 @@ static void print_func_help_header(struct seq_file *m) | |||
1808 | } | 1808 | } |
1809 | 1809 | ||
1810 | 1810 | ||
1811 | static void | 1811 | void |
1812 | print_trace_header(struct seq_file *m, struct trace_iterator *iter) | 1812 | print_trace_header(struct seq_file *m, struct trace_iterator *iter) |
1813 | { | 1813 | { |
1814 | unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); | 1814 | unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); |
@@ -2017,7 +2017,7 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter) | |||
2017 | return event ? event->binary(iter, 0) : TRACE_TYPE_HANDLED; | 2017 | return event ? event->binary(iter, 0) : TRACE_TYPE_HANDLED; |
2018 | } | 2018 | } |
2019 | 2019 | ||
2020 | static int trace_empty(struct trace_iterator *iter) | 2020 | int trace_empty(struct trace_iterator *iter) |
2021 | { | 2021 | { |
2022 | int cpu; | 2022 | int cpu; |
2023 | 2023 | ||
@@ -2084,6 +2084,23 @@ static enum print_line_t print_trace_line(struct trace_iterator *iter) | |||
2084 | return print_trace_fmt(iter); | 2084 | return print_trace_fmt(iter); |
2085 | } | 2085 | } |
2086 | 2086 | ||
2087 | void trace_default_header(struct seq_file *m) | ||
2088 | { | ||
2089 | struct trace_iterator *iter = m->private; | ||
2090 | |||
2091 | if (iter->iter_flags & TRACE_FILE_LAT_FMT) { | ||
2092 | /* print nothing if the buffers are empty */ | ||
2093 | if (trace_empty(iter)) | ||
2094 | return; | ||
2095 | print_trace_header(m, iter); | ||
2096 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | ||
2097 | print_lat_help_header(m); | ||
2098 | } else { | ||
2099 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | ||
2100 | print_func_help_header(m); | ||
2101 | } | ||
2102 | } | ||
2103 | |||
2087 | static int s_show(struct seq_file *m, void *v) | 2104 | static int s_show(struct seq_file *m, void *v) |
2088 | { | 2105 | { |
2089 | struct trace_iterator *iter = v; | 2106 | struct trace_iterator *iter = v; |
@@ -2096,17 +2113,9 @@ static int s_show(struct seq_file *m, void *v) | |||
2096 | } | 2113 | } |
2097 | if (iter->trace && iter->trace->print_header) | 2114 | if (iter->trace && iter->trace->print_header) |
2098 | iter->trace->print_header(m); | 2115 | iter->trace->print_header(m); |
2099 | else if (iter->iter_flags & TRACE_FILE_LAT_FMT) { | 2116 | else |
2100 | /* print nothing if the buffers are empty */ | 2117 | trace_default_header(m); |
2101 | if (trace_empty(iter)) | 2118 | |
2102 | return 0; | ||
2103 | print_trace_header(m, iter); | ||
2104 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | ||
2105 | print_lat_help_header(m); | ||
2106 | } else { | ||
2107 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | ||
2108 | print_func_help_header(m); | ||
2109 | } | ||
2110 | } else if (iter->leftover) { | 2119 | } else if (iter->leftover) { |
2111 | /* | 2120 | /* |
2112 | * If we filled the seq_file buffer earlier, we | 2121 | * If we filled the seq_file buffer earlier, we |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 970004c5fa79..911e9864e94a 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -378,6 +378,9 @@ void trace_function(struct trace_array *tr, | |||
378 | unsigned long ip, | 378 | unsigned long ip, |
379 | unsigned long parent_ip, | 379 | unsigned long parent_ip, |
380 | unsigned long flags, int pc); | 380 | unsigned long flags, int pc); |
381 | void trace_default_header(struct seq_file *m); | ||
382 | void print_trace_header(struct seq_file *m, struct trace_iterator *iter); | ||
383 | int trace_empty(struct trace_iterator *iter); | ||
381 | 384 | ||
382 | void trace_graph_return(struct ftrace_graph_ret *trace); | 385 | void trace_graph_return(struct ftrace_graph_ret *trace); |
383 | int trace_graph_entry(struct ftrace_graph_ent *trace); | 386 | int trace_graph_entry(struct ftrace_graph_ent *trace); |
@@ -491,11 +494,29 @@ extern int trace_clock_id; | |||
491 | 494 | ||
492 | /* Standard output formatting function used for function return traces */ | 495 | /* Standard output formatting function used for function return traces */ |
493 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 496 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
497 | |||
498 | /* Flag options */ | ||
499 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 | ||
500 | #define TRACE_GRAPH_PRINT_CPU 0x2 | ||
501 | #define TRACE_GRAPH_PRINT_OVERHEAD 0x4 | ||
502 | #define TRACE_GRAPH_PRINT_PROC 0x8 | ||
503 | #define TRACE_GRAPH_PRINT_DURATION 0x10 | ||
504 | #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 | ||
505 | |||
494 | extern enum print_line_t | 506 | extern enum print_line_t |
495 | print_graph_function_flags(struct trace_iterator *iter, u32 flags); | 507 | print_graph_function_flags(struct trace_iterator *iter, u32 flags); |
496 | extern void print_graph_headers_flags(struct seq_file *s, u32 flags); | 508 | extern void print_graph_headers_flags(struct seq_file *s, u32 flags); |
497 | extern enum print_line_t | 509 | extern enum print_line_t |
498 | trace_print_graph_duration(unsigned long long duration, struct trace_seq *s); | 510 | trace_print_graph_duration(unsigned long long duration, struct trace_seq *s); |
511 | extern void graph_trace_open(struct trace_iterator *iter); | ||
512 | extern void graph_trace_close(struct trace_iterator *iter); | ||
513 | extern int __trace_graph_entry(struct trace_array *tr, | ||
514 | struct ftrace_graph_ent *trace, | ||
515 | unsigned long flags, int pc); | ||
516 | extern void __trace_graph_return(struct trace_array *tr, | ||
517 | struct ftrace_graph_ret *trace, | ||
518 | unsigned long flags, int pc); | ||
519 | |||
499 | 520 | ||
500 | #ifdef CONFIG_DYNAMIC_FTRACE | 521 | #ifdef CONFIG_DYNAMIC_FTRACE |
501 | /* TODO: make this variable */ | 522 | /* TODO: make this variable */ |
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index de5f6518aba0..dd11c830eb84 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -179,7 +179,7 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer) | |||
179 | return ret; | 179 | return ret; |
180 | } | 180 | } |
181 | 181 | ||
182 | static int __trace_graph_entry(struct trace_array *tr, | 182 | int __trace_graph_entry(struct trace_array *tr, |
183 | struct ftrace_graph_ent *trace, | 183 | struct ftrace_graph_ent *trace, |
184 | unsigned long flags, | 184 | unsigned long flags, |
185 | int pc) | 185 | int pc) |
@@ -246,7 +246,7 @@ int trace_graph_thresh_entry(struct ftrace_graph_ent *trace) | |||
246 | return trace_graph_entry(trace); | 246 | return trace_graph_entry(trace); |
247 | } | 247 | } |
248 | 248 | ||
249 | static void __trace_graph_return(struct trace_array *tr, | 249 | void __trace_graph_return(struct trace_array *tr, |
250 | struct ftrace_graph_ret *trace, | 250 | struct ftrace_graph_ret *trace, |
251 | unsigned long flags, | 251 | unsigned long flags, |
252 | int pc) | 252 | int pc) |
@@ -1093,6 +1093,11 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) | |||
1093 | trace_assign_type(field, entry); | 1093 | trace_assign_type(field, entry); |
1094 | return print_graph_return(&field->ret, s, entry, iter, flags); | 1094 | return print_graph_return(&field->ret, s, entry, iter, flags); |
1095 | } | 1095 | } |
1096 | case TRACE_STACK: | ||
1097 | case TRACE_FN: | ||
1098 | /* dont trace stack and functions as comments */ | ||
1099 | return TRACE_TYPE_UNHANDLED; | ||
1100 | |||
1096 | default: | 1101 | default: |
1097 | return print_graph_comment(s, entry, iter, flags); | 1102 | return print_graph_comment(s, entry, iter, flags); |
1098 | } | 1103 | } |
@@ -1170,12 +1175,12 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags) | |||
1170 | seq_printf(s, " | | | |\n"); | 1175 | seq_printf(s, " | | | |\n"); |
1171 | } | 1176 | } |
1172 | 1177 | ||
1173 | static void print_graph_headers(struct seq_file *s) | 1178 | void print_graph_headers(struct seq_file *s) |
1174 | { | 1179 | { |
1175 | print_graph_headers_flags(s, tracer_flags.val); | 1180 | print_graph_headers_flags(s, tracer_flags.val); |
1176 | } | 1181 | } |
1177 | 1182 | ||
1178 | static void graph_trace_open(struct trace_iterator *iter) | 1183 | void graph_trace_open(struct trace_iterator *iter) |
1179 | { | 1184 | { |
1180 | /* pid and depth on the last trace processed */ | 1185 | /* pid and depth on the last trace processed */ |
1181 | struct fgraph_data *data; | 1186 | struct fgraph_data *data; |
@@ -1210,7 +1215,7 @@ static void graph_trace_open(struct trace_iterator *iter) | |||
1210 | pr_warning("function graph tracer: not enough memory\n"); | 1215 | pr_warning("function graph tracer: not enough memory\n"); |
1211 | } | 1216 | } |
1212 | 1217 | ||
1213 | static void graph_trace_close(struct trace_iterator *iter) | 1218 | void graph_trace_close(struct trace_iterator *iter) |
1214 | { | 1219 | { |
1215 | struct fgraph_data *data = iter->private; | 1220 | struct fgraph_data *data = iter->private; |
1216 | 1221 | ||
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 2974bc7538c7..6fd486e0cef4 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c | |||
@@ -34,6 +34,9 @@ static int trace_type __read_mostly; | |||
34 | 34 | ||
35 | static int save_lat_flag; | 35 | static int save_lat_flag; |
36 | 36 | ||
37 | static void stop_irqsoff_tracer(struct trace_array *tr, int graph); | ||
38 | static int start_irqsoff_tracer(struct trace_array *tr, int graph); | ||
39 | |||
37 | #ifdef CONFIG_PREEMPT_TRACER | 40 | #ifdef CONFIG_PREEMPT_TRACER |
38 | static inline int | 41 | static inline int |
39 | preempt_trace(void) | 42 | preempt_trace(void) |
@@ -55,6 +58,23 @@ irq_trace(void) | |||
55 | # define irq_trace() (0) | 58 | # define irq_trace() (0) |
56 | #endif | 59 | #endif |
57 | 60 | ||
61 | #define TRACE_DISPLAY_GRAPH 1 | ||
62 | |||
63 | static struct tracer_opt trace_opts[] = { | ||
64 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
65 | /* display latency trace as call graph */ | ||
66 | { TRACER_OPT(display-graph, TRACE_DISPLAY_GRAPH) }, | ||
67 | #endif | ||
68 | { } /* Empty entry */ | ||
69 | }; | ||
70 | |||
71 | static struct tracer_flags tracer_flags = { | ||
72 | .val = 0, | ||
73 | .opts = trace_opts, | ||
74 | }; | ||
75 | |||
76 | #define is_graph() (tracer_flags.val & TRACE_DISPLAY_GRAPH) | ||
77 | |||
58 | /* | 78 | /* |
59 | * Sequence count - we record it when starting a measurement and | 79 | * Sequence count - we record it when starting a measurement and |
60 | * skip the latency if the sequence has changed - some other section | 80 | * skip the latency if the sequence has changed - some other section |
@@ -108,6 +128,202 @@ static struct ftrace_ops trace_ops __read_mostly = | |||
108 | }; | 128 | }; |
109 | #endif /* CONFIG_FUNCTION_TRACER */ | 129 | #endif /* CONFIG_FUNCTION_TRACER */ |
110 | 130 | ||
131 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
132 | static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) | ||
133 | { | ||
134 | int cpu; | ||
135 | |||
136 | if (!(bit & TRACE_DISPLAY_GRAPH)) | ||
137 | return -EINVAL; | ||
138 | |||
139 | if (!(is_graph() ^ set)) | ||
140 | return 0; | ||
141 | |||
142 | stop_irqsoff_tracer(irqsoff_trace, !set); | ||
143 | |||
144 | for_each_possible_cpu(cpu) | ||
145 | per_cpu(tracing_cpu, cpu) = 0; | ||
146 | |||
147 | tracing_max_latency = 0; | ||
148 | tracing_reset_online_cpus(irqsoff_trace); | ||
149 | |||
150 | return start_irqsoff_tracer(irqsoff_trace, set); | ||
151 | } | ||
152 | |||
153 | static int irqsoff_graph_entry(struct ftrace_graph_ent *trace) | ||
154 | { | ||
155 | struct trace_array *tr = irqsoff_trace; | ||
156 | struct trace_array_cpu *data; | ||
157 | unsigned long flags; | ||
158 | long disabled; | ||
159 | int ret; | ||
160 | int cpu; | ||
161 | int pc; | ||
162 | |||
163 | cpu = raw_smp_processor_id(); | ||
164 | if (likely(!per_cpu(tracing_cpu, cpu))) | ||
165 | return 0; | ||
166 | |||
167 | local_save_flags(flags); | ||
168 | /* slight chance to get a false positive on tracing_cpu */ | ||
169 | if (!irqs_disabled_flags(flags)) | ||
170 | return 0; | ||
171 | |||
172 | data = tr->data[cpu]; | ||
173 | disabled = atomic_inc_return(&data->disabled); | ||
174 | |||
175 | if (likely(disabled == 1)) { | ||
176 | pc = preempt_count(); | ||
177 | ret = __trace_graph_entry(tr, trace, flags, pc); | ||
178 | } else | ||
179 | ret = 0; | ||
180 | |||
181 | atomic_dec(&data->disabled); | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static void irqsoff_graph_return(struct ftrace_graph_ret *trace) | ||
186 | { | ||
187 | struct trace_array *tr = irqsoff_trace; | ||
188 | struct trace_array_cpu *data; | ||
189 | unsigned long flags; | ||
190 | long disabled; | ||
191 | int cpu; | ||
192 | int pc; | ||
193 | |||
194 | cpu = raw_smp_processor_id(); | ||
195 | if (likely(!per_cpu(tracing_cpu, cpu))) | ||
196 | return; | ||
197 | |||
198 | local_save_flags(flags); | ||
199 | /* slight chance to get a false positive on tracing_cpu */ | ||
200 | if (!irqs_disabled_flags(flags)) | ||
201 | return; | ||
202 | |||
203 | data = tr->data[cpu]; | ||
204 | disabled = atomic_inc_return(&data->disabled); | ||
205 | |||
206 | if (likely(disabled == 1)) { | ||
207 | pc = preempt_count(); | ||
208 | __trace_graph_return(tr, trace, flags, pc); | ||
209 | } | ||
210 | |||
211 | atomic_dec(&data->disabled); | ||
212 | } | ||
213 | |||
214 | static void irqsoff_trace_open(struct trace_iterator *iter) | ||
215 | { | ||
216 | if (is_graph()) | ||
217 | graph_trace_open(iter); | ||
218 | |||
219 | } | ||
220 | |||
221 | static void irqsoff_trace_close(struct trace_iterator *iter) | ||
222 | { | ||
223 | if (iter->private) | ||
224 | graph_trace_close(iter); | ||
225 | } | ||
226 | |||
227 | #define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_CPU | \ | ||
228 | TRACE_GRAPH_PRINT_PROC) | ||
229 | |||
230 | static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) | ||
231 | { | ||
232 | u32 flags = GRAPH_TRACER_FLAGS; | ||
233 | |||
234 | if (trace_flags & TRACE_ITER_LATENCY_FMT) | ||
235 | flags |= TRACE_GRAPH_PRINT_DURATION; | ||
236 | else | ||
237 | flags |= TRACE_GRAPH_PRINT_ABS_TIME; | ||
238 | |||
239 | /* | ||
240 | * In graph mode call the graph tracer output function, | ||
241 | * otherwise go with the TRACE_FN event handler | ||
242 | */ | ||
243 | if (is_graph()) | ||
244 | return print_graph_function_flags(iter, flags); | ||
245 | |||
246 | return TRACE_TYPE_UNHANDLED; | ||
247 | } | ||
248 | |||
249 | static void irqsoff_print_header(struct seq_file *s) | ||
250 | { | ||
251 | if (is_graph()) { | ||
252 | struct trace_iterator *iter = s->private; | ||
253 | u32 flags = GRAPH_TRACER_FLAGS; | ||
254 | |||
255 | if (trace_flags & TRACE_ITER_LATENCY_FMT) { | ||
256 | /* print nothing if the buffers are empty */ | ||
257 | if (trace_empty(iter)) | ||
258 | return; | ||
259 | |||
260 | print_trace_header(s, iter); | ||
261 | flags |= TRACE_GRAPH_PRINT_DURATION; | ||
262 | } else | ||
263 | flags |= TRACE_GRAPH_PRINT_ABS_TIME; | ||
264 | |||
265 | print_graph_headers_flags(s, flags); | ||
266 | } else | ||
267 | trace_default_header(s); | ||
268 | } | ||
269 | |||
270 | static void | ||
271 | trace_graph_function(struct trace_array *tr, | ||
272 | unsigned long ip, unsigned long flags, int pc) | ||
273 | { | ||
274 | u64 time = trace_clock_local(); | ||
275 | struct ftrace_graph_ent ent = { | ||
276 | .func = ip, | ||
277 | .depth = 0, | ||
278 | }; | ||
279 | struct ftrace_graph_ret ret = { | ||
280 | .func = ip, | ||
281 | .depth = 0, | ||
282 | .calltime = time, | ||
283 | .rettime = time, | ||
284 | }; | ||
285 | |||
286 | __trace_graph_entry(tr, &ent, flags, pc); | ||
287 | __trace_graph_return(tr, &ret, flags, pc); | ||
288 | } | ||
289 | |||
290 | static void | ||
291 | __trace_function(struct trace_array *tr, | ||
292 | unsigned long ip, unsigned long parent_ip, | ||
293 | unsigned long flags, int pc) | ||
294 | { | ||
295 | if (!is_graph()) | ||
296 | trace_function(tr, ip, parent_ip, flags, pc); | ||
297 | else { | ||
298 | trace_graph_function(tr, parent_ip, flags, pc); | ||
299 | trace_graph_function(tr, ip, flags, pc); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | #else | ||
304 | #define __trace_function trace_function | ||
305 | |||
306 | static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) | ||
307 | { | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | |||
311 | static int irqsoff_graph_entry(struct ftrace_graph_ent *trace) | ||
312 | { | ||
313 | return -1; | ||
314 | } | ||
315 | |||
316 | static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) | ||
317 | { | ||
318 | return TRACE_TYPE_UNHANDLED; | ||
319 | } | ||
320 | |||
321 | static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { } | ||
322 | static void irqsoff_print_header(struct seq_file *s) { } | ||
323 | static void irqsoff_trace_open(struct trace_iterator *iter) { } | ||
324 | static void irqsoff_trace_close(struct trace_iterator *iter) { } | ||
325 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
326 | |||
111 | /* | 327 | /* |
112 | * Should this new latency be reported/recorded? | 328 | * Should this new latency be reported/recorded? |
113 | */ | 329 | */ |
@@ -150,7 +366,7 @@ check_critical_timing(struct trace_array *tr, | |||
150 | if (!report_latency(delta)) | 366 | if (!report_latency(delta)) |
151 | goto out_unlock; | 367 | goto out_unlock; |
152 | 368 | ||
153 | trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); | 369 | __trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); |
154 | /* Skip 5 functions to get to the irq/preempt enable function */ | 370 | /* Skip 5 functions to get to the irq/preempt enable function */ |
155 | __trace_stack(tr, flags, 5, pc); | 371 | __trace_stack(tr, flags, 5, pc); |
156 | 372 | ||
@@ -172,7 +388,7 @@ out_unlock: | |||
172 | out: | 388 | out: |
173 | data->critical_sequence = max_sequence; | 389 | data->critical_sequence = max_sequence; |
174 | data->preempt_timestamp = ftrace_now(cpu); | 390 | data->preempt_timestamp = ftrace_now(cpu); |
175 | trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); | 391 | __trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); |
176 | } | 392 | } |
177 | 393 | ||
178 | static inline void | 394 | static inline void |
@@ -204,7 +420,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) | |||
204 | 420 | ||
205 | local_save_flags(flags); | 421 | local_save_flags(flags); |
206 | 422 | ||
207 | trace_function(tr, ip, parent_ip, flags, preempt_count()); | 423 | __trace_function(tr, ip, parent_ip, flags, preempt_count()); |
208 | 424 | ||
209 | per_cpu(tracing_cpu, cpu) = 1; | 425 | per_cpu(tracing_cpu, cpu) = 1; |
210 | 426 | ||
@@ -238,7 +454,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) | |||
238 | atomic_inc(&data->disabled); | 454 | atomic_inc(&data->disabled); |
239 | 455 | ||
240 | local_save_flags(flags); | 456 | local_save_flags(flags); |
241 | trace_function(tr, ip, parent_ip, flags, preempt_count()); | 457 | __trace_function(tr, ip, parent_ip, flags, preempt_count()); |
242 | check_critical_timing(tr, data, parent_ip ? : ip, cpu); | 458 | check_critical_timing(tr, data, parent_ip ? : ip, cpu); |
243 | data->critical_start = 0; | 459 | data->critical_start = 0; |
244 | atomic_dec(&data->disabled); | 460 | atomic_dec(&data->disabled); |
@@ -347,19 +563,32 @@ void trace_preempt_off(unsigned long a0, unsigned long a1) | |||
347 | } | 563 | } |
348 | #endif /* CONFIG_PREEMPT_TRACER */ | 564 | #endif /* CONFIG_PREEMPT_TRACER */ |
349 | 565 | ||
350 | static void start_irqsoff_tracer(struct trace_array *tr) | 566 | static int start_irqsoff_tracer(struct trace_array *tr, int graph) |
351 | { | 567 | { |
352 | register_ftrace_function(&trace_ops); | 568 | int ret = 0; |
353 | if (tracing_is_enabled()) | 569 | |
570 | if (!graph) | ||
571 | ret = register_ftrace_function(&trace_ops); | ||
572 | else | ||
573 | ret = register_ftrace_graph(&irqsoff_graph_return, | ||
574 | &irqsoff_graph_entry); | ||
575 | |||
576 | if (!ret && tracing_is_enabled()) | ||
354 | tracer_enabled = 1; | 577 | tracer_enabled = 1; |
355 | else | 578 | else |
356 | tracer_enabled = 0; | 579 | tracer_enabled = 0; |
580 | |||
581 | return ret; | ||
357 | } | 582 | } |
358 | 583 | ||
359 | static void stop_irqsoff_tracer(struct trace_array *tr) | 584 | static void stop_irqsoff_tracer(struct trace_array *tr, int graph) |
360 | { | 585 | { |
361 | tracer_enabled = 0; | 586 | tracer_enabled = 0; |
362 | unregister_ftrace_function(&trace_ops); | 587 | |
588 | if (!graph) | ||
589 | unregister_ftrace_function(&trace_ops); | ||
590 | else | ||
591 | unregister_ftrace_graph(); | ||
363 | } | 592 | } |
364 | 593 | ||
365 | static void __irqsoff_tracer_init(struct trace_array *tr) | 594 | static void __irqsoff_tracer_init(struct trace_array *tr) |
@@ -372,12 +601,14 @@ static void __irqsoff_tracer_init(struct trace_array *tr) | |||
372 | /* make sure that the tracer is visible */ | 601 | /* make sure that the tracer is visible */ |
373 | smp_wmb(); | 602 | smp_wmb(); |
374 | tracing_reset_online_cpus(tr); | 603 | tracing_reset_online_cpus(tr); |
375 | start_irqsoff_tracer(tr); | 604 | |
605 | if (start_irqsoff_tracer(tr, is_graph())) | ||
606 | printk(KERN_ERR "failed to start irqsoff tracer\n"); | ||
376 | } | 607 | } |
377 | 608 | ||
378 | static void irqsoff_tracer_reset(struct trace_array *tr) | 609 | static void irqsoff_tracer_reset(struct trace_array *tr) |
379 | { | 610 | { |
380 | stop_irqsoff_tracer(tr); | 611 | stop_irqsoff_tracer(tr, is_graph()); |
381 | 612 | ||
382 | if (!save_lat_flag) | 613 | if (!save_lat_flag) |
383 | trace_flags &= ~TRACE_ITER_LATENCY_FMT; | 614 | trace_flags &= ~TRACE_ITER_LATENCY_FMT; |
@@ -409,9 +640,15 @@ static struct tracer irqsoff_tracer __read_mostly = | |||
409 | .start = irqsoff_tracer_start, | 640 | .start = irqsoff_tracer_start, |
410 | .stop = irqsoff_tracer_stop, | 641 | .stop = irqsoff_tracer_stop, |
411 | .print_max = 1, | 642 | .print_max = 1, |
643 | .print_header = irqsoff_print_header, | ||
644 | .print_line = irqsoff_print_line, | ||
645 | .flags = &tracer_flags, | ||
646 | .set_flag = irqsoff_set_flag, | ||
412 | #ifdef CONFIG_FTRACE_SELFTEST | 647 | #ifdef CONFIG_FTRACE_SELFTEST |
413 | .selftest = trace_selftest_startup_irqsoff, | 648 | .selftest = trace_selftest_startup_irqsoff, |
414 | #endif | 649 | #endif |
650 | .open = irqsoff_trace_open, | ||
651 | .close = irqsoff_trace_close, | ||
415 | }; | 652 | }; |
416 | # define register_irqsoff(trace) register_tracer(&trace) | 653 | # define register_irqsoff(trace) register_tracer(&trace) |
417 | #else | 654 | #else |
@@ -435,9 +672,15 @@ static struct tracer preemptoff_tracer __read_mostly = | |||
435 | .start = irqsoff_tracer_start, | 672 | .start = irqsoff_tracer_start, |
436 | .stop = irqsoff_tracer_stop, | 673 | .stop = irqsoff_tracer_stop, |
437 | .print_max = 1, | 674 | .print_max = 1, |
675 | .print_header = irqsoff_print_header, | ||
676 | .print_line = irqsoff_print_line, | ||
677 | .flags = &tracer_flags, | ||
678 | .set_flag = irqsoff_set_flag, | ||
438 | #ifdef CONFIG_FTRACE_SELFTEST | 679 | #ifdef CONFIG_FTRACE_SELFTEST |
439 | .selftest = trace_selftest_startup_preemptoff, | 680 | .selftest = trace_selftest_startup_preemptoff, |
440 | #endif | 681 | #endif |
682 | .open = irqsoff_trace_open, | ||
683 | .close = irqsoff_trace_close, | ||
441 | }; | 684 | }; |
442 | # define register_preemptoff(trace) register_tracer(&trace) | 685 | # define register_preemptoff(trace) register_tracer(&trace) |
443 | #else | 686 | #else |
@@ -463,9 +706,15 @@ static struct tracer preemptirqsoff_tracer __read_mostly = | |||
463 | .start = irqsoff_tracer_start, | 706 | .start = irqsoff_tracer_start, |
464 | .stop = irqsoff_tracer_stop, | 707 | .stop = irqsoff_tracer_stop, |
465 | .print_max = 1, | 708 | .print_max = 1, |
709 | .print_header = irqsoff_print_header, | ||
710 | .print_line = irqsoff_print_line, | ||
711 | .flags = &tracer_flags, | ||
712 | .set_flag = irqsoff_set_flag, | ||
466 | #ifdef CONFIG_FTRACE_SELFTEST | 713 | #ifdef CONFIG_FTRACE_SELFTEST |
467 | .selftest = trace_selftest_startup_preemptirqsoff, | 714 | .selftest = trace_selftest_startup_preemptirqsoff, |
468 | #endif | 715 | #endif |
716 | .open = irqsoff_trace_open, | ||
717 | .close = irqsoff_trace_close, | ||
469 | }; | 718 | }; |
470 | 719 | ||
471 | # define register_preemptirqsoff(trace) register_tracer(&trace) | 720 | # define register_preemptirqsoff(trace) register_tracer(&trace) |