diff options
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace.c | 117 |
1 files changed, 87 insertions, 30 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 85bee775a03e..e4c40c868d67 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -64,6 +64,37 @@ static cpumask_t __read_mostly tracing_buffer_mask; | |||
64 | 64 | ||
65 | static int tracing_disabled = 1; | 65 | static int tracing_disabled = 1; |
66 | 66 | ||
67 | /* | ||
68 | * ftrace_dump_on_oops - variable to dump ftrace buffer on oops | ||
69 | * | ||
70 | * If there is an oops (or kernel panic) and the ftrace_dump_on_oops | ||
71 | * is set, then ftrace_dump is called. This will output the contents | ||
72 | * of the ftrace buffers to the console. This is very useful for | ||
73 | * capturing traces that lead to crashes and outputing it to a | ||
74 | * serial console. | ||
75 | * | ||
76 | * It is default off, but you can enable it with either specifying | ||
77 | * "ftrace_dump_on_oops" in the kernel command line, or setting | ||
78 | * /proc/sys/kernel/ftrace_dump_on_oops to true. | ||
79 | */ | ||
80 | int ftrace_dump_on_oops; | ||
81 | |||
82 | static int tracing_set_tracer(char *buf); | ||
83 | |||
84 | static int __init set_ftrace(char *str) | ||
85 | { | ||
86 | tracing_set_tracer(str); | ||
87 | return 1; | ||
88 | } | ||
89 | __setup("ftrace", set_ftrace); | ||
90 | |||
91 | static int __init set_ftrace_dump_on_oops(char *str) | ||
92 | { | ||
93 | ftrace_dump_on_oops = 1; | ||
94 | return 1; | ||
95 | } | ||
96 | __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); | ||
97 | |||
67 | long | 98 | long |
68 | ns2usecs(cycle_t nsec) | 99 | ns2usecs(cycle_t nsec) |
69 | { | 100 | { |
@@ -2374,29 +2405,11 @@ tracing_set_trace_read(struct file *filp, char __user *ubuf, | |||
2374 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | 2405 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); |
2375 | } | 2406 | } |
2376 | 2407 | ||
2377 | static ssize_t | 2408 | static int tracing_set_tracer(char *buf) |
2378 | tracing_set_trace_write(struct file *filp, const char __user *ubuf, | ||
2379 | size_t cnt, loff_t *ppos) | ||
2380 | { | 2409 | { |
2381 | struct trace_array *tr = &global_trace; | 2410 | struct trace_array *tr = &global_trace; |
2382 | struct tracer *t; | 2411 | struct tracer *t; |
2383 | char buf[max_tracer_type_len+1]; | 2412 | int ret = 0; |
2384 | int i; | ||
2385 | size_t ret; | ||
2386 | |||
2387 | ret = cnt; | ||
2388 | |||
2389 | if (cnt > max_tracer_type_len) | ||
2390 | cnt = max_tracer_type_len; | ||
2391 | |||
2392 | if (copy_from_user(&buf, ubuf, cnt)) | ||
2393 | return -EFAULT; | ||
2394 | |||
2395 | buf[cnt] = 0; | ||
2396 | |||
2397 | /* strip ending whitespace. */ | ||
2398 | for (i = cnt - 1; i > 0 && isspace(buf[i]); i--) | ||
2399 | buf[i] = 0; | ||
2400 | 2413 | ||
2401 | mutex_lock(&trace_types_lock); | 2414 | mutex_lock(&trace_types_lock); |
2402 | for (t = trace_types; t; t = t->next) { | 2415 | for (t = trace_types; t; t = t->next) { |
@@ -2420,6 +2433,33 @@ tracing_set_trace_write(struct file *filp, const char __user *ubuf, | |||
2420 | out: | 2433 | out: |
2421 | mutex_unlock(&trace_types_lock); | 2434 | mutex_unlock(&trace_types_lock); |
2422 | 2435 | ||
2436 | return ret; | ||
2437 | } | ||
2438 | |||
2439 | static ssize_t | ||
2440 | tracing_set_trace_write(struct file *filp, const char __user *ubuf, | ||
2441 | size_t cnt, loff_t *ppos) | ||
2442 | { | ||
2443 | char buf[max_tracer_type_len+1]; | ||
2444 | int i; | ||
2445 | size_t ret; | ||
2446 | |||
2447 | if (cnt > max_tracer_type_len) | ||
2448 | cnt = max_tracer_type_len; | ||
2449 | |||
2450 | if (copy_from_user(&buf, ubuf, cnt)) | ||
2451 | return -EFAULT; | ||
2452 | |||
2453 | buf[cnt] = 0; | ||
2454 | |||
2455 | /* strip ending whitespace. */ | ||
2456 | for (i = cnt - 1; i > 0 && isspace(buf[i]); i--) | ||
2457 | buf[i] = 0; | ||
2458 | |||
2459 | ret = tracing_set_tracer(buf); | ||
2460 | if (!ret) | ||
2461 | ret = cnt; | ||
2462 | |||
2423 | if (ret > 0) | 2463 | if (ret > 0) |
2424 | filp->f_pos += ret; | 2464 | filp->f_pos += ret; |
2425 | 2465 | ||
@@ -2822,22 +2862,38 @@ static struct file_operations tracing_mark_fops = { | |||
2822 | 2862 | ||
2823 | #ifdef CONFIG_DYNAMIC_FTRACE | 2863 | #ifdef CONFIG_DYNAMIC_FTRACE |
2824 | 2864 | ||
2865 | int __weak ftrace_arch_read_dyn_info(char *buf, int size) | ||
2866 | { | ||
2867 | return 0; | ||
2868 | } | ||
2869 | |||
2825 | static ssize_t | 2870 | static ssize_t |
2826 | tracing_read_long(struct file *filp, char __user *ubuf, | 2871 | tracing_read_dyn_info(struct file *filp, char __user *ubuf, |
2827 | size_t cnt, loff_t *ppos) | 2872 | size_t cnt, loff_t *ppos) |
2828 | { | 2873 | { |
2874 | static char ftrace_dyn_info_buffer[1024]; | ||
2875 | static DEFINE_MUTEX(dyn_info_mutex); | ||
2829 | unsigned long *p = filp->private_data; | 2876 | unsigned long *p = filp->private_data; |
2830 | char buf[64]; | 2877 | char *buf = ftrace_dyn_info_buffer; |
2878 | int size = ARRAY_SIZE(ftrace_dyn_info_buffer); | ||
2831 | int r; | 2879 | int r; |
2832 | 2880 | ||
2833 | r = sprintf(buf, "%ld\n", *p); | 2881 | mutex_lock(&dyn_info_mutex); |
2882 | r = sprintf(buf, "%ld ", *p); | ||
2834 | 2883 | ||
2835 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | 2884 | r += ftrace_arch_read_dyn_info(buf+r, (size-1)-r); |
2885 | buf[r++] = '\n'; | ||
2886 | |||
2887 | r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
2888 | |||
2889 | mutex_unlock(&dyn_info_mutex); | ||
2890 | |||
2891 | return r; | ||
2836 | } | 2892 | } |
2837 | 2893 | ||
2838 | static struct file_operations tracing_read_long_fops = { | 2894 | static struct file_operations tracing_dyn_info_fops = { |
2839 | .open = tracing_open_generic, | 2895 | .open = tracing_open_generic, |
2840 | .read = tracing_read_long, | 2896 | .read = tracing_read_dyn_info, |
2841 | }; | 2897 | }; |
2842 | #endif | 2898 | #endif |
2843 | 2899 | ||
@@ -2946,7 +3002,7 @@ static __init int tracer_init_debugfs(void) | |||
2946 | #ifdef CONFIG_DYNAMIC_FTRACE | 3002 | #ifdef CONFIG_DYNAMIC_FTRACE |
2947 | entry = debugfs_create_file("dyn_ftrace_total_info", 0444, d_tracer, | 3003 | entry = debugfs_create_file("dyn_ftrace_total_info", 0444, d_tracer, |
2948 | &ftrace_update_tot_cnt, | 3004 | &ftrace_update_tot_cnt, |
2949 | &tracing_read_long_fops); | 3005 | &tracing_dyn_info_fops); |
2950 | if (!entry) | 3006 | if (!entry) |
2951 | pr_warning("Could not create debugfs " | 3007 | pr_warning("Could not create debugfs " |
2952 | "'dyn_ftrace_total_info' entry\n"); | 3008 | "'dyn_ftrace_total_info' entry\n"); |
@@ -3027,7 +3083,8 @@ EXPORT_SYMBOL_GPL(__ftrace_printk); | |||
3027 | static int trace_panic_handler(struct notifier_block *this, | 3083 | static int trace_panic_handler(struct notifier_block *this, |
3028 | unsigned long event, void *unused) | 3084 | unsigned long event, void *unused) |
3029 | { | 3085 | { |
3030 | ftrace_dump(); | 3086 | if (ftrace_dump_on_oops) |
3087 | ftrace_dump(); | ||
3031 | return NOTIFY_OK; | 3088 | return NOTIFY_OK; |
3032 | } | 3089 | } |
3033 | 3090 | ||
@@ -3043,7 +3100,8 @@ static int trace_die_handler(struct notifier_block *self, | |||
3043 | { | 3100 | { |
3044 | switch (val) { | 3101 | switch (val) { |
3045 | case DIE_OOPS: | 3102 | case DIE_OOPS: |
3046 | ftrace_dump(); | 3103 | if (ftrace_dump_on_oops) |
3104 | ftrace_dump(); | ||
3047 | break; | 3105 | break; |
3048 | default: | 3106 | default: |
3049 | break; | 3107 | break; |
@@ -3084,7 +3142,6 @@ trace_printk_seq(struct trace_seq *s) | |||
3084 | trace_seq_reset(s); | 3142 | trace_seq_reset(s); |
3085 | } | 3143 | } |
3086 | 3144 | ||
3087 | |||
3088 | void ftrace_dump(void) | 3145 | void ftrace_dump(void) |
3089 | { | 3146 | { |
3090 | static DEFINE_SPINLOCK(ftrace_dump_lock); | 3147 | static DEFINE_SPINLOCK(ftrace_dump_lock); |