diff options
Diffstat (limited to 'kernel/trace/trace.c')
-rw-r--r-- | kernel/trace/trace.c | 119 |
1 files changed, 94 insertions, 25 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4dcc4e85c5..4a87560073 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/poll.h> | 27 | #include <linux/poll.h> |
28 | #include <linux/gfp.h> | 28 | #include <linux/gfp.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | #include <linux/kprobes.h> | ||
30 | #include <linux/writeback.h> | 31 | #include <linux/writeback.h> |
31 | 32 | ||
32 | #include <linux/stacktrace.h> | 33 | #include <linux/stacktrace.h> |
@@ -42,11 +43,6 @@ static cpumask_t __read_mostly tracing_buffer_mask; | |||
42 | #define for_each_tracing_cpu(cpu) \ | 43 | #define for_each_tracing_cpu(cpu) \ |
43 | for_each_cpu_mask(cpu, tracing_buffer_mask) | 44 | for_each_cpu_mask(cpu, tracing_buffer_mask) |
44 | 45 | ||
45 | /* dummy trace to disable tracing */ | ||
46 | static struct tracer no_tracer __read_mostly = { | ||
47 | .name = "none", | ||
48 | }; | ||
49 | |||
50 | static int trace_alloc_page(void); | 46 | static int trace_alloc_page(void); |
51 | static int trace_free_page(void); | 47 | static int trace_free_page(void); |
52 | 48 | ||
@@ -134,6 +130,23 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait); | |||
134 | /* trace_flags holds iter_ctrl options */ | 130 | /* trace_flags holds iter_ctrl options */ |
135 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; | 131 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; |
136 | 132 | ||
133 | static notrace void no_trace_init(struct trace_array *tr) | ||
134 | { | ||
135 | int cpu; | ||
136 | |||
137 | if(tr->ctrl) | ||
138 | for_each_online_cpu(cpu) | ||
139 | tracing_reset(tr->data[cpu]); | ||
140 | tracer_enabled = 0; | ||
141 | } | ||
142 | |||
143 | /* dummy trace to disable tracing */ | ||
144 | static struct tracer no_tracer __read_mostly = { | ||
145 | .name = "none", | ||
146 | .init = no_trace_init | ||
147 | }; | ||
148 | |||
149 | |||
137 | /** | 150 | /** |
138 | * trace_wake_up - wake up tasks waiting for trace input | 151 | * trace_wake_up - wake up tasks waiting for trace input |
139 | * | 152 | * |
@@ -249,24 +262,32 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) | |||
249 | tracing_record_cmdline(current); | 262 | tracing_record_cmdline(current); |
250 | } | 263 | } |
251 | 264 | ||
265 | #define CHECK_COND(cond) \ | ||
266 | if (unlikely(cond)) { \ | ||
267 | tracing_disabled = 1; \ | ||
268 | WARN_ON(1); \ | ||
269 | return -1; \ | ||
270 | } | ||
271 | |||
252 | /** | 272 | /** |
253 | * check_pages - integrity check of trace buffers | 273 | * check_pages - integrity check of trace buffers |
254 | * | 274 | * |
255 | * As a safty measure we check to make sure the data pages have not | 275 | * As a safty measure we check to make sure the data pages have not |
256 | * been corrupted. TODO: configure to disable this because it adds | 276 | * been corrupted. |
257 | * a bit of overhead. | ||
258 | */ | 277 | */ |
259 | void check_pages(struct trace_array_cpu *data) | 278 | int check_pages(struct trace_array_cpu *data) |
260 | { | 279 | { |
261 | struct page *page, *tmp; | 280 | struct page *page, *tmp; |
262 | 281 | ||
263 | BUG_ON(data->trace_pages.next->prev != &data->trace_pages); | 282 | CHECK_COND(data->trace_pages.next->prev != &data->trace_pages); |
264 | BUG_ON(data->trace_pages.prev->next != &data->trace_pages); | 283 | CHECK_COND(data->trace_pages.prev->next != &data->trace_pages); |
265 | 284 | ||
266 | list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { | 285 | list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { |
267 | BUG_ON(page->lru.next->prev != &page->lru); | 286 | CHECK_COND(page->lru.next->prev != &page->lru); |
268 | BUG_ON(page->lru.prev->next != &page->lru); | 287 | CHECK_COND(page->lru.prev->next != &page->lru); |
269 | } | 288 | } |
289 | |||
290 | return 0; | ||
270 | } | 291 | } |
271 | 292 | ||
272 | /** | 293 | /** |
@@ -280,7 +301,6 @@ void *head_page(struct trace_array_cpu *data) | |||
280 | { | 301 | { |
281 | struct page *page; | 302 | struct page *page; |
282 | 303 | ||
283 | check_pages(data); | ||
284 | if (list_empty(&data->trace_pages)) | 304 | if (list_empty(&data->trace_pages)) |
285 | return NULL; | 305 | return NULL; |
286 | 306 | ||
@@ -645,9 +665,6 @@ static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; | |||
645 | static int cmdline_idx; | 665 | static int cmdline_idx; |
646 | static DEFINE_SPINLOCK(trace_cmdline_lock); | 666 | static DEFINE_SPINLOCK(trace_cmdline_lock); |
647 | 667 | ||
648 | /* trace in all context switches */ | ||
649 | atomic_t trace_record_cmdline_enabled __read_mostly; | ||
650 | |||
651 | /* temporary disable recording */ | 668 | /* temporary disable recording */ |
652 | atomic_t trace_record_cmdline_disabled __read_mostly; | 669 | atomic_t trace_record_cmdline_disabled __read_mostly; |
653 | 670 | ||
@@ -976,6 +993,30 @@ tracing_sched_wakeup_trace(struct trace_array *tr, | |||
976 | trace_wake_up(); | 993 | trace_wake_up(); |
977 | } | 994 | } |
978 | 995 | ||
996 | void | ||
997 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) | ||
998 | { | ||
999 | struct trace_array *tr = &global_trace; | ||
1000 | struct trace_array_cpu *data; | ||
1001 | unsigned long flags; | ||
1002 | long disabled; | ||
1003 | int cpu; | ||
1004 | |||
1005 | if (tracing_disabled || current_trace == &no_tracer || !tr->ctrl) | ||
1006 | return; | ||
1007 | |||
1008 | local_irq_save(flags); | ||
1009 | cpu = raw_smp_processor_id(); | ||
1010 | data = tr->data[cpu]; | ||
1011 | disabled = atomic_inc_return(&data->disabled); | ||
1012 | |||
1013 | if (likely(disabled == 1)) | ||
1014 | __trace_special(tr, data, arg1, arg2, arg3); | ||
1015 | |||
1016 | atomic_dec(&data->disabled); | ||
1017 | local_irq_restore(flags); | ||
1018 | } | ||
1019 | |||
979 | #ifdef CONFIG_FTRACE | 1020 | #ifdef CONFIG_FTRACE |
980 | static void | 1021 | static void |
981 | function_trace_call(unsigned long ip, unsigned long parent_ip) | 1022 | function_trace_call(unsigned long ip, unsigned long parent_ip) |
@@ -989,6 +1030,9 @@ function_trace_call(unsigned long ip, unsigned long parent_ip) | |||
989 | if (unlikely(!tracer_enabled)) | 1030 | if (unlikely(!tracer_enabled)) |
990 | return; | 1031 | return; |
991 | 1032 | ||
1033 | if (skip_trace(ip)) | ||
1034 | return; | ||
1035 | |||
992 | local_irq_save(flags); | 1036 | local_irq_save(flags); |
993 | cpu = raw_smp_processor_id(); | 1037 | cpu = raw_smp_processor_id(); |
994 | data = tr->data[cpu]; | 1038 | data = tr->data[cpu]; |
@@ -1213,6 +1257,20 @@ static void s_stop(struct seq_file *m, void *p) | |||
1213 | mutex_unlock(&trace_types_lock); | 1257 | mutex_unlock(&trace_types_lock); |
1214 | } | 1258 | } |
1215 | 1259 | ||
1260 | #define KRETPROBE_MSG "[unknown/kretprobe'd]" | ||
1261 | |||
1262 | #ifdef CONFIG_KRETPROBES | ||
1263 | static inline int kretprobed(unsigned long addr) | ||
1264 | { | ||
1265 | return addr == (unsigned long)kretprobe_trampoline; | ||
1266 | } | ||
1267 | #else | ||
1268 | static inline int kretprobed(unsigned long addr) | ||
1269 | { | ||
1270 | return 0; | ||
1271 | } | ||
1272 | #endif /* CONFIG_KRETPROBES */ | ||
1273 | |||
1216 | static int | 1274 | static int |
1217 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) | 1275 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) |
1218 | { | 1276 | { |
@@ -1448,7 +1506,10 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1448 | case TRACE_FN: | 1506 | case TRACE_FN: |
1449 | seq_print_ip_sym(s, entry->fn.ip, sym_flags); | 1507 | seq_print_ip_sym(s, entry->fn.ip, sym_flags); |
1450 | trace_seq_puts(s, " ("); | 1508 | trace_seq_puts(s, " ("); |
1451 | seq_print_ip_sym(s, entry->fn.parent_ip, sym_flags); | 1509 | if (kretprobed(entry->fn.parent_ip)) |
1510 | trace_seq_puts(s, KRETPROBE_MSG); | ||
1511 | else | ||
1512 | seq_print_ip_sym(s, entry->fn.parent_ip, sym_flags); | ||
1452 | trace_seq_puts(s, ")\n"); | 1513 | trace_seq_puts(s, ")\n"); |
1453 | break; | 1514 | break; |
1454 | case TRACE_CTX: | 1515 | case TRACE_CTX: |
@@ -1528,8 +1589,11 @@ static int print_trace_fmt(struct trace_iterator *iter) | |||
1528 | ret = trace_seq_printf(s, " <-"); | 1589 | ret = trace_seq_printf(s, " <-"); |
1529 | if (!ret) | 1590 | if (!ret) |
1530 | return 0; | 1591 | return 0; |
1531 | ret = seq_print_ip_sym(s, entry->fn.parent_ip, | 1592 | if (kretprobed(entry->fn.parent_ip)) |
1532 | sym_flags); | 1593 | ret = trace_seq_puts(s, KRETPROBE_MSG); |
1594 | else | ||
1595 | ret = seq_print_ip_sym(s, entry->fn.parent_ip, | ||
1596 | sym_flags); | ||
1533 | if (!ret) | 1597 | if (!ret) |
1534 | return 0; | 1598 | return 0; |
1535 | } | 1599 | } |
@@ -2608,7 +2672,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, | |||
2608 | { | 2672 | { |
2609 | unsigned long val; | 2673 | unsigned long val; |
2610 | char buf[64]; | 2674 | char buf[64]; |
2611 | int ret; | 2675 | int i, ret; |
2612 | 2676 | ||
2613 | if (cnt >= sizeof(buf)) | 2677 | if (cnt >= sizeof(buf)) |
2614 | return -EINVAL; | 2678 | return -EINVAL; |
@@ -2677,8 +2741,15 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, | |||
2677 | trace_free_page(); | 2741 | trace_free_page(); |
2678 | } | 2742 | } |
2679 | 2743 | ||
2744 | /* check integrity */ | ||
2745 | for_each_tracing_cpu(i) | ||
2746 | check_pages(global_trace.data[i]); | ||
2747 | |||
2680 | filp->f_pos += cnt; | 2748 | filp->f_pos += cnt; |
2681 | 2749 | ||
2750 | /* If check pages failed, return ENOMEM */ | ||
2751 | if (tracing_disabled) | ||
2752 | cnt = -ENOMEM; | ||
2682 | out: | 2753 | out: |
2683 | max_tr.entries = global_trace.entries; | 2754 | max_tr.entries = global_trace.entries; |
2684 | mutex_unlock(&trace_types_lock); | 2755 | mutex_unlock(&trace_types_lock); |
@@ -2969,8 +3040,6 @@ __init static int tracer_alloc_buffers(void) | |||
2969 | int ret = -ENOMEM; | 3040 | int ret = -ENOMEM; |
2970 | int i; | 3041 | int i; |
2971 | 3042 | ||
2972 | global_trace.ctrl = tracer_enabled; | ||
2973 | |||
2974 | /* TODO: make the number of buffers hot pluggable with CPUS */ | 3043 | /* TODO: make the number of buffers hot pluggable with CPUS */ |
2975 | tracing_nr_buffers = num_possible_cpus(); | 3044 | tracing_nr_buffers = num_possible_cpus(); |
2976 | tracing_buffer_mask = cpu_possible_map; | 3045 | tracing_buffer_mask = cpu_possible_map; |
@@ -3027,9 +3096,8 @@ __init static int tracer_alloc_buffers(void) | |||
3027 | } | 3096 | } |
3028 | max_tr.entries = global_trace.entries; | 3097 | max_tr.entries = global_trace.entries; |
3029 | 3098 | ||
3030 | pr_info("tracer: %d pages allocated for %ld", | 3099 | pr_info("tracer: %d pages allocated for %ld entries of %ld bytes\n", |
3031 | pages, trace_nr_entries); | 3100 | pages, trace_nr_entries, (long)TRACE_ENTRY_SIZE); |
3032 | pr_info(" entries of %ld bytes\n", (long)TRACE_ENTRY_SIZE); | ||
3033 | pr_info(" actual entries %ld\n", global_trace.entries); | 3101 | pr_info(" actual entries %ld\n", global_trace.entries); |
3034 | 3102 | ||
3035 | tracer_init_debugfs(); | 3103 | tracer_init_debugfs(); |
@@ -3040,6 +3108,7 @@ __init static int tracer_alloc_buffers(void) | |||
3040 | current_trace = &no_tracer; | 3108 | current_trace = &no_tracer; |
3041 | 3109 | ||
3042 | /* All seems OK, enable tracing */ | 3110 | /* All seems OK, enable tracing */ |
3111 | global_trace.ctrl = tracer_enabled; | ||
3043 | tracing_disabled = 0; | 3112 | tracing_disabled = 0; |
3044 | 3113 | ||
3045 | return 0; | 3114 | return 0; |