diff options
| -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) |
