diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
| -rw-r--r-- | Documentation/trace/ftrace.txt | 6 | ||||
| -rw-r--r-- | drivers/char/sysrq.c | 2 | ||||
| -rw-r--r-- | include/linux/ftrace.h | 4 | ||||
| -rw-r--r-- | include/linux/kernel.h | 11 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 51 | ||||
| -rw-r--r-- | kernel/trace/trace_selftest.c | 5 |
7 files changed, 64 insertions, 21 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e2202e93b148..21a99474df93 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -784,8 +784,12 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 784 | as early as possible in order to facilitate early | 784 | as early as possible in order to facilitate early |
| 785 | boot debugging. | 785 | boot debugging. |
| 786 | 786 | ||
| 787 | ftrace_dump_on_oops | 787 | ftrace_dump_on_oops[=orig_cpu] |
| 788 | [FTRACE] will dump the trace buffers on oops. | 788 | [FTRACE] will dump the trace buffers on oops. |
| 789 | If no parameter is passed, ftrace will dump | ||
| 790 | buffers of all CPUs, but if you pass orig_cpu, it will | ||
| 791 | dump only the buffer of the CPU that triggered the | ||
| 792 | oops. | ||
| 789 | 793 | ||
| 790 | ftrace_filter=[function-list] | 794 | ftrace_filter=[function-list] |
| 791 | [FTRACE] Limit the functions traced by the function | 795 | [FTRACE] Limit the functions traced by the function |
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index 03485bfbd797..52011815c905 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt | |||
| @@ -1337,12 +1337,14 @@ ftrace_dump_on_oops must be set. To set ftrace_dump_on_oops, one | |||
| 1337 | can either use the sysctl function or set it via the proc system | 1337 | can either use the sysctl function or set it via the proc system |
| 1338 | interface. | 1338 | interface. |
| 1339 | 1339 | ||
| 1340 | sysctl kernel.ftrace_dump_on_oops=1 | 1340 | sysctl kernel.ftrace_dump_on_oops=n |
| 1341 | 1341 | ||
| 1342 | or | 1342 | or |
| 1343 | 1343 | ||
| 1344 | echo 1 > /proc/sys/kernel/ftrace_dump_on_oops | 1344 | echo n > /proc/sys/kernel/ftrace_dump_on_oops |
| 1345 | 1345 | ||
| 1346 | If n = 1, ftrace will dump buffers of all CPUs, if n = 2 ftrace will | ||
| 1347 | only dump the buffer of the CPU that triggered the oops. | ||
| 1346 | 1348 | ||
| 1347 | Here's an example of such a dump after a null pointer | 1349 | Here's an example of such a dump after a null pointer |
| 1348 | dereference in a kernel module: | 1350 | dereference in a kernel module: |
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 59de2525d303..d4e8b213a462 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c | |||
| @@ -289,7 +289,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = { | |||
| 289 | 289 | ||
| 290 | static void sysrq_ftrace_dump(int key, struct tty_struct *tty) | 290 | static void sysrq_ftrace_dump(int key, struct tty_struct *tty) |
| 291 | { | 291 | { |
| 292 | ftrace_dump(); | 292 | ftrace_dump(DUMP_ALL); |
| 293 | } | 293 | } |
| 294 | static struct sysrq_key_op sysrq_ftrace_dump_op = { | 294 | static struct sysrq_key_op sysrq_ftrace_dump_op = { |
| 295 | .handler = sysrq_ftrace_dump, | 295 | .handler = sysrq_ftrace_dump, |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 01e6adea07ec..ea5b1aae0e8b 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -492,7 +492,9 @@ static inline int test_tsk_trace_graph(struct task_struct *tsk) | |||
| 492 | return tsk->trace & TSK_TRACE_FL_GRAPH; | 492 | return tsk->trace & TSK_TRACE_FL_GRAPH; |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | extern int ftrace_dump_on_oops; | 495 | enum ftrace_dump_mode; |
| 496 | |||
| 497 | extern enum ftrace_dump_mode ftrace_dump_on_oops; | ||
| 496 | 498 | ||
| 497 | #ifdef CONFIG_PREEMPT | 499 | #ifdef CONFIG_PREEMPT |
| 498 | #define INIT_TRACE_RECURSION .trace_recursion = 0, | 500 | #define INIT_TRACE_RECURSION .trace_recursion = 0, |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 9365227dbaf6..9fb1c1299032 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
| @@ -490,6 +490,13 @@ static inline void tracing_off(void) { } | |||
| 490 | static inline void tracing_off_permanent(void) { } | 490 | static inline void tracing_off_permanent(void) { } |
| 491 | static inline int tracing_is_on(void) { return 0; } | 491 | static inline int tracing_is_on(void) { return 0; } |
| 492 | #endif | 492 | #endif |
| 493 | |||
| 494 | enum ftrace_dump_mode { | ||
| 495 | DUMP_NONE, | ||
| 496 | DUMP_ALL, | ||
| 497 | DUMP_ORIG, | ||
| 498 | }; | ||
| 499 | |||
| 493 | #ifdef CONFIG_TRACING | 500 | #ifdef CONFIG_TRACING |
| 494 | extern void tracing_start(void); | 501 | extern void tracing_start(void); |
| 495 | extern void tracing_stop(void); | 502 | extern void tracing_stop(void); |
| @@ -571,7 +578,7 @@ __ftrace_vbprintk(unsigned long ip, const char *fmt, va_list ap); | |||
| 571 | extern int | 578 | extern int |
| 572 | __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap); | 579 | __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap); |
| 573 | 580 | ||
| 574 | extern void ftrace_dump(void); | 581 | extern void ftrace_dump(enum ftrace_dump_mode oops_dump_mode); |
| 575 | #else | 582 | #else |
| 576 | static inline void | 583 | static inline void |
| 577 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } | 584 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } |
| @@ -592,7 +599,7 @@ ftrace_vprintk(const char *fmt, va_list ap) | |||
| 592 | { | 599 | { |
| 593 | return 0; | 600 | return 0; |
| 594 | } | 601 | } |
| 595 | static inline void ftrace_dump(void) { } | 602 | static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } |
| 596 | #endif /* CONFIG_TRACING */ | 603 | #endif /* CONFIG_TRACING */ |
| 597 | 604 | ||
| 598 | /* | 605 | /* |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index bed83cab6da2..7b516c7ef9a0 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -117,9 +117,12 @@ static cpumask_var_t __read_mostly tracing_buffer_mask; | |||
| 117 | * | 117 | * |
| 118 | * It is default off, but you can enable it with either specifying | 118 | * It is default off, but you can enable it with either specifying |
| 119 | * "ftrace_dump_on_oops" in the kernel command line, or setting | 119 | * "ftrace_dump_on_oops" in the kernel command line, or setting |
| 120 | * /proc/sys/kernel/ftrace_dump_on_oops to true. | 120 | * /proc/sys/kernel/ftrace_dump_on_oops |
| 121 | * Set 1 if you want to dump buffers of all CPUs | ||
| 122 | * Set 2 if you want to dump the buffer of the CPU that triggered oops | ||
| 121 | */ | 123 | */ |
| 122 | int ftrace_dump_on_oops; | 124 | |
| 125 | enum ftrace_dump_mode ftrace_dump_on_oops; | ||
| 123 | 126 | ||
| 124 | static int tracing_set_tracer(const char *buf); | 127 | static int tracing_set_tracer(const char *buf); |
| 125 | 128 | ||
| @@ -139,8 +142,17 @@ __setup("ftrace=", set_cmdline_ftrace); | |||
| 139 | 142 | ||
| 140 | static int __init set_ftrace_dump_on_oops(char *str) | 143 | static int __init set_ftrace_dump_on_oops(char *str) |
| 141 | { | 144 | { |
| 142 | ftrace_dump_on_oops = 1; | 145 | if (*str++ != '=' || !*str) { |
| 143 | return 1; | 146 | ftrace_dump_on_oops = DUMP_ALL; |
| 147 | return 1; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (!strcmp("orig_cpu", str)) { | ||
| 151 | ftrace_dump_on_oops = DUMP_ORIG; | ||
| 152 | return 1; | ||
| 153 | } | ||
| 154 | |||
| 155 | return 0; | ||
| 144 | } | 156 | } |
| 145 | __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); | 157 | __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); |
| 146 | 158 | ||
| @@ -4338,7 +4350,7 @@ static int trace_panic_handler(struct notifier_block *this, | |||
| 4338 | unsigned long event, void *unused) | 4350 | unsigned long event, void *unused) |
| 4339 | { | 4351 | { |
| 4340 | if (ftrace_dump_on_oops) | 4352 | if (ftrace_dump_on_oops) |
| 4341 | ftrace_dump(); | 4353 | ftrace_dump(ftrace_dump_on_oops); |
| 4342 | return NOTIFY_OK; | 4354 | return NOTIFY_OK; |
| 4343 | } | 4355 | } |
| 4344 | 4356 | ||
| @@ -4355,7 +4367,7 @@ static int trace_die_handler(struct notifier_block *self, | |||
| 4355 | switch (val) { | 4367 | switch (val) { |
| 4356 | case DIE_OOPS: | 4368 | case DIE_OOPS: |
| 4357 | if (ftrace_dump_on_oops) | 4369 | if (ftrace_dump_on_oops) |
| 4358 | ftrace_dump(); | 4370 | ftrace_dump(ftrace_dump_on_oops); |
| 4359 | break; | 4371 | break; |
| 4360 | default: | 4372 | default: |
| 4361 | break; | 4373 | break; |
| @@ -4396,7 +4408,8 @@ trace_printk_seq(struct trace_seq *s) | |||
| 4396 | trace_seq_init(s); | 4408 | trace_seq_init(s); |
| 4397 | } | 4409 | } |
| 4398 | 4410 | ||
| 4399 | static void __ftrace_dump(bool disable_tracing) | 4411 | static void |
| 4412 | __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | ||
| 4400 | { | 4413 | { |
| 4401 | static arch_spinlock_t ftrace_dump_lock = | 4414 | static arch_spinlock_t ftrace_dump_lock = |
| 4402 | (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; | 4415 | (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; |
| @@ -4429,12 +4442,25 @@ static void __ftrace_dump(bool disable_tracing) | |||
| 4429 | /* don't look at user memory in panic mode */ | 4442 | /* don't look at user memory in panic mode */ |
| 4430 | trace_flags &= ~TRACE_ITER_SYM_USEROBJ; | 4443 | trace_flags &= ~TRACE_ITER_SYM_USEROBJ; |
| 4431 | 4444 | ||
| 4432 | printk(KERN_TRACE "Dumping ftrace buffer:\n"); | ||
| 4433 | |||
| 4434 | /* Simulate the iterator */ | 4445 | /* Simulate the iterator */ |
| 4435 | iter.tr = &global_trace; | 4446 | iter.tr = &global_trace; |
| 4436 | iter.trace = current_trace; | 4447 | iter.trace = current_trace; |
| 4437 | iter.cpu_file = TRACE_PIPE_ALL_CPU; | 4448 | |
| 4449 | switch (oops_dump_mode) { | ||
| 4450 | case DUMP_ALL: | ||
| 4451 | iter.cpu_file = TRACE_PIPE_ALL_CPU; | ||
| 4452 | break; | ||
| 4453 | case DUMP_ORIG: | ||
| 4454 | iter.cpu_file = raw_smp_processor_id(); | ||
| 4455 | break; | ||
| 4456 | case DUMP_NONE: | ||
| 4457 | goto out_enable; | ||
| 4458 | default: | ||
| 4459 | printk(KERN_TRACE "Bad dumping mode, switching to all CPUs dump\n"); | ||
| 4460 | iter.cpu_file = TRACE_PIPE_ALL_CPU; | ||
| 4461 | } | ||
| 4462 | |||
| 4463 | printk(KERN_TRACE "Dumping ftrace buffer:\n"); | ||
| 4438 | 4464 | ||
| 4439 | /* | 4465 | /* |
| 4440 | * We need to stop all tracing on all CPUS to read the | 4466 | * We need to stop all tracing on all CPUS to read the |
| @@ -4473,6 +4499,7 @@ static void __ftrace_dump(bool disable_tracing) | |||
| 4473 | else | 4499 | else |
| 4474 | printk(KERN_TRACE "---------------------------------\n"); | 4500 | printk(KERN_TRACE "---------------------------------\n"); |
| 4475 | 4501 | ||
| 4502 | out_enable: | ||
| 4476 | /* Re-enable tracing if requested */ | 4503 | /* Re-enable tracing if requested */ |
| 4477 | if (!disable_tracing) { | 4504 | if (!disable_tracing) { |
| 4478 | trace_flags |= old_userobj; | 4505 | trace_flags |= old_userobj; |
| @@ -4489,9 +4516,9 @@ static void __ftrace_dump(bool disable_tracing) | |||
| 4489 | } | 4516 | } |
| 4490 | 4517 | ||
| 4491 | /* By default: disable tracing after the dump */ | 4518 | /* By default: disable tracing after the dump */ |
| 4492 | void ftrace_dump(void) | 4519 | void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) |
| 4493 | { | 4520 | { |
| 4494 | __ftrace_dump(true); | 4521 | __ftrace_dump(true, oops_dump_mode); |
| 4495 | } | 4522 | } |
| 4496 | 4523 | ||
| 4497 | __init static int tracer_alloc_buffers(void) | 4524 | __init static int tracer_alloc_buffers(void) |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 9398034f814a..6a9d36ddfcf2 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
| @@ -256,7 +256,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
| 256 | /* Maximum number of functions to trace before diagnosing a hang */ | 256 | /* Maximum number of functions to trace before diagnosing a hang */ |
| 257 | #define GRAPH_MAX_FUNC_TEST 100000000 | 257 | #define GRAPH_MAX_FUNC_TEST 100000000 |
| 258 | 258 | ||
| 259 | static void __ftrace_dump(bool disable_tracing); | 259 | static void |
| 260 | __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode); | ||
| 260 | static unsigned int graph_hang_thresh; | 261 | static unsigned int graph_hang_thresh; |
| 261 | 262 | ||
| 262 | /* Wrap the real function entry probe to avoid possible hanging */ | 263 | /* Wrap the real function entry probe to avoid possible hanging */ |
| @@ -267,7 +268,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) | |||
| 267 | ftrace_graph_stop(); | 268 | ftrace_graph_stop(); |
| 268 | printk(KERN_WARNING "BUG: Function graph tracer hang!\n"); | 269 | printk(KERN_WARNING "BUG: Function graph tracer hang!\n"); |
| 269 | if (ftrace_dump_on_oops) | 270 | if (ftrace_dump_on_oops) |
| 270 | __ftrace_dump(false); | 271 | __ftrace_dump(false, DUMP_ALL); |
| 271 | return 0; | 272 | return 0; |
| 272 | } | 273 | } |
| 273 | 274 | ||
