diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/Kconfig | 22 | ||||
-rw-r--r-- | kernel/trace/Makefile | 6 | ||||
-rw-r--r-- | kernel/trace/trace.c | 29 | ||||
-rw-r--r-- | kernel/trace/trace.h | 39 | ||||
-rw-r--r-- | kernel/trace/trace_unlikely.c | 114 |
5 files changed, 210 insertions, 0 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index a604f24c755f..8abcaf821beb 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -175,6 +175,28 @@ config TRACE_UNLIKELY_PROFILE | |||
175 | 175 | ||
176 | Say N if unsure. | 176 | Say N if unsure. |
177 | 177 | ||
178 | config TRACING_UNLIKELY | ||
179 | bool | ||
180 | help | ||
181 | Selected by tracers that will trace the likely and unlikely | ||
182 | conditions. This prevents the tracers themselves from being | ||
183 | profiled. Profiling the tracing infrastructure can only happen | ||
184 | when the likelys and unlikelys are not being traced. | ||
185 | |||
186 | config UNLIKELY_TRACER | ||
187 | bool "Trace likely/unlikely instances" | ||
188 | depends on TRACE_UNLIKELY_PROFILE | ||
189 | select TRACING_UNLIKELY | ||
190 | help | ||
191 | This traces the events of likely and unlikely condition | ||
192 | calls in the kernel. The difference between this and the | ||
193 | "Trace likely/unlikely profiler" is that this is not a | ||
194 | histogram of the callers, but actually places the calling | ||
195 | events into a running trace buffer to see when and where the | ||
196 | events happened, as well as their results. | ||
197 | |||
198 | Say N if unsure. | ||
199 | |||
178 | config STACK_TRACER | 200 | config STACK_TRACER |
179 | bool "Trace max stack" | 201 | bool "Trace max stack" |
180 | depends on HAVE_FUNCTION_TRACER | 202 | depends on HAVE_FUNCTION_TRACER |
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 98e70ee27986..c938d03516c0 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -10,6 +10,12 @@ CFLAGS_trace_selftest_dynamic.o = -pg | |||
10 | obj-y += trace_selftest_dynamic.o | 10 | obj-y += trace_selftest_dynamic.o |
11 | endif | 11 | endif |
12 | 12 | ||
13 | # If unlikely tracing is enabled, do not trace these files | ||
14 | ifdef CONFIG_TRACING_UNLIKELY | ||
15 | KBUILD_CFLAGS += '-Dlikely(x)=likely_notrace(x)' | ||
16 | KBUILD_CFLAGS += '-Dunlikely(x)=unlikely_notrace(x)' | ||
17 | endif | ||
18 | |||
13 | obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o | 19 | obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o |
14 | obj-$(CONFIG_RING_BUFFER) += ring_buffer.o | 20 | obj-$(CONFIG_RING_BUFFER) += ring_buffer.o |
15 | 21 | ||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a3f7ae9cd8e1..83d38634bc90 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -258,6 +258,9 @@ static const char *trace_options[] = { | |||
258 | "sched-tree", | 258 | "sched-tree", |
259 | "ftrace_printk", | 259 | "ftrace_printk", |
260 | "ftrace_preempt", | 260 | "ftrace_preempt", |
261 | #ifdef CONFIG_UNLIKELY_TRACER | ||
262 | "unlikely", | ||
263 | #endif | ||
261 | NULL | 264 | NULL |
262 | }; | 265 | }; |
263 | 266 | ||
@@ -1648,6 +1651,18 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1648 | trace_seq_print_cont(s, iter); | 1651 | trace_seq_print_cont(s, iter); |
1649 | break; | 1652 | break; |
1650 | } | 1653 | } |
1654 | case TRACE_UNLIKELY: { | ||
1655 | struct trace_unlikely *field; | ||
1656 | |||
1657 | trace_assign_type(field, entry); | ||
1658 | |||
1659 | trace_seq_printf(s, "[%s] %s:%s:%d\n", | ||
1660 | field->correct ? "correct" : "INCORRECT", | ||
1661 | field->func, | ||
1662 | field->file, | ||
1663 | field->line); | ||
1664 | break; | ||
1665 | } | ||
1651 | default: | 1666 | default: |
1652 | trace_seq_printf(s, "Unknown type %d\n", entry->type); | 1667 | trace_seq_printf(s, "Unknown type %d\n", entry->type); |
1653 | } | 1668 | } |
@@ -1787,6 +1802,18 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) | |||
1787 | return print_return_function(iter); | 1802 | return print_return_function(iter); |
1788 | break; | 1803 | break; |
1789 | } | 1804 | } |
1805 | case TRACE_UNLIKELY: { | ||
1806 | struct trace_unlikely *field; | ||
1807 | |||
1808 | trace_assign_type(field, entry); | ||
1809 | |||
1810 | trace_seq_printf(s, "[%s] %s:%s:%d\n", | ||
1811 | field->correct ? "correct" : "INCORRECT", | ||
1812 | field->func, | ||
1813 | field->file, | ||
1814 | field->line); | ||
1815 | break; | ||
1816 | } | ||
1790 | } | 1817 | } |
1791 | return TRACE_TYPE_HANDLED; | 1818 | return TRACE_TYPE_HANDLED; |
1792 | } | 1819 | } |
@@ -2592,6 +2619,7 @@ static int tracing_set_tracer(char *buf) | |||
2592 | if (t == current_trace) | 2619 | if (t == current_trace) |
2593 | goto out; | 2620 | goto out; |
2594 | 2621 | ||
2622 | trace_unlikely_disable(); | ||
2595 | if (current_trace && current_trace->reset) | 2623 | if (current_trace && current_trace->reset) |
2596 | current_trace->reset(tr); | 2624 | current_trace->reset(tr); |
2597 | 2625 | ||
@@ -2599,6 +2627,7 @@ static int tracing_set_tracer(char *buf) | |||
2599 | if (t->init) | 2627 | if (t->init) |
2600 | t->init(tr); | 2628 | t->init(tr); |
2601 | 2629 | ||
2630 | trace_unlikely_enable(tr); | ||
2602 | out: | 2631 | out: |
2603 | mutex_unlock(&trace_types_lock); | 2632 | mutex_unlock(&trace_types_lock); |
2604 | 2633 | ||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index b5f91f198fd4..9635aa2c4fc1 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -22,6 +22,7 @@ enum trace_type { | |||
22 | TRACE_SPECIAL, | 22 | TRACE_SPECIAL, |
23 | TRACE_MMIO_RW, | 23 | TRACE_MMIO_RW, |
24 | TRACE_MMIO_MAP, | 24 | TRACE_MMIO_MAP, |
25 | TRACE_UNLIKELY, | ||
25 | TRACE_BOOT_CALL, | 26 | TRACE_BOOT_CALL, |
26 | TRACE_BOOT_RET, | 27 | TRACE_BOOT_RET, |
27 | TRACE_FN_RET, | 28 | TRACE_FN_RET, |
@@ -134,6 +135,16 @@ struct trace_boot_ret { | |||
134 | struct boot_trace_ret boot_ret; | 135 | struct boot_trace_ret boot_ret; |
135 | }; | 136 | }; |
136 | 137 | ||
138 | #define TRACE_FUNC_SIZE 30 | ||
139 | #define TRACE_FILE_SIZE 20 | ||
140 | struct trace_unlikely { | ||
141 | struct trace_entry ent; | ||
142 | unsigned line; | ||
143 | char func[TRACE_FUNC_SIZE+1]; | ||
144 | char file[TRACE_FILE_SIZE+1]; | ||
145 | char correct; | ||
146 | }; | ||
147 | |||
137 | /* | 148 | /* |
138 | * trace_flag_type is an enumeration that holds different | 149 | * trace_flag_type is an enumeration that holds different |
139 | * states when a trace occurs. These are: | 150 | * states when a trace occurs. These are: |
@@ -236,6 +247,7 @@ extern void __ftrace_bad_type(void); | |||
236 | TRACE_MMIO_MAP); \ | 247 | TRACE_MMIO_MAP); \ |
237 | IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\ | 248 | IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\ |
238 | IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\ | 249 | IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\ |
250 | IF_ASSIGN(var, ent, struct trace_unlikely, TRACE_UNLIKELY); \ | ||
239 | IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\ | 251 | IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\ |
240 | __ftrace_bad_type(); \ | 252 | __ftrace_bad_type(); \ |
241 | } while (0) | 253 | } while (0) |
@@ -456,6 +468,9 @@ enum trace_iterator_flags { | |||
456 | TRACE_ITER_SCHED_TREE = 0x200, | 468 | TRACE_ITER_SCHED_TREE = 0x200, |
457 | TRACE_ITER_PRINTK = 0x400, | 469 | TRACE_ITER_PRINTK = 0x400, |
458 | TRACE_ITER_PREEMPTONLY = 0x800, | 470 | TRACE_ITER_PREEMPTONLY = 0x800, |
471 | #ifdef CONFIG_UNLIKELY_TRACER | ||
472 | TRACE_ITER_UNLIKELY = 0x1000, | ||
473 | #endif | ||
459 | }; | 474 | }; |
460 | 475 | ||
461 | /* | 476 | /* |
@@ -515,4 +530,28 @@ static inline void ftrace_preempt_enable(int resched) | |||
515 | preempt_enable_notrace(); | 530 | preempt_enable_notrace(); |
516 | } | 531 | } |
517 | 532 | ||
533 | #ifdef CONFIG_UNLIKELY_TRACER | ||
534 | extern int enable_unlikely_tracing(struct trace_array *tr); | ||
535 | extern void disable_unlikely_tracing(void); | ||
536 | static inline int trace_unlikely_enable(struct trace_array *tr) | ||
537 | { | ||
538 | if (trace_flags & TRACE_ITER_UNLIKELY) | ||
539 | return enable_unlikely_tracing(tr); | ||
540 | return 0; | ||
541 | } | ||
542 | static inline void trace_unlikely_disable(void) | ||
543 | { | ||
544 | /* due to races, always disable */ | ||
545 | disable_unlikely_tracing(); | ||
546 | } | ||
547 | #else | ||
548 | static inline int trace_unlikely_enable(struct trace_array *tr) | ||
549 | { | ||
550 | return 0; | ||
551 | } | ||
552 | static inline void trace_unlikely_disable(void) | ||
553 | { | ||
554 | } | ||
555 | #endif /* CONFIG_UNLIKELY_TRACER */ | ||
556 | |||
518 | #endif /* _LINUX_KERNEL_TRACE_H */ | 557 | #endif /* _LINUX_KERNEL_TRACE_H */ |
diff --git a/kernel/trace/trace_unlikely.c b/kernel/trace/trace_unlikely.c index 94932696069f..7290e0e7b4e3 100644 --- a/kernel/trace/trace_unlikely.c +++ b/kernel/trace/trace_unlikely.c | |||
@@ -15,8 +15,122 @@ | |||
15 | #include <asm/local.h> | 15 | #include <asm/local.h> |
16 | #include "trace.h" | 16 | #include "trace.h" |
17 | 17 | ||
18 | #ifdef CONFIG_UNLIKELY_TRACER | ||
19 | |||
20 | static int unlikely_tracing_enabled __read_mostly; | ||
21 | static DEFINE_MUTEX(unlikely_tracing_mutex); | ||
22 | static struct trace_array *unlikely_tracer; | ||
23 | |||
24 | static void | ||
25 | probe_likely_condition(struct ftrace_likely_data *f, int val, int expect) | ||
26 | { | ||
27 | struct trace_array *tr = unlikely_tracer; | ||
28 | struct ring_buffer_event *event; | ||
29 | struct trace_unlikely *entry; | ||
30 | unsigned long flags, irq_flags; | ||
31 | int cpu, pc; | ||
32 | const char *p; | ||
33 | |||
34 | /* | ||
35 | * I would love to save just the ftrace_likely_data pointer, but | ||
36 | * this code can also be used by modules. Ugly things can happen | ||
37 | * if the module is unloaded, and then we go and read the | ||
38 | * pointer. This is slower, but much safer. | ||
39 | */ | ||
40 | |||
41 | if (unlikely(!tr)) | ||
42 | return; | ||
43 | |||
44 | local_irq_save(flags); | ||
45 | cpu = raw_smp_processor_id(); | ||
46 | if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) | ||
47 | goto out; | ||
48 | |||
49 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), | ||
50 | &irq_flags); | ||
51 | if (!event) | ||
52 | goto out; | ||
53 | |||
54 | pc = preempt_count(); | ||
55 | entry = ring_buffer_event_data(event); | ||
56 | tracing_generic_entry_update(&entry->ent, flags, pc); | ||
57 | entry->ent.type = TRACE_UNLIKELY; | ||
58 | |||
59 | /* Strip off the path, only save the file */ | ||
60 | p = f->file + strlen(f->file); | ||
61 | while (p >= f->file && *p != '/') | ||
62 | p--; | ||
63 | p++; | ||
64 | |||
65 | strncpy(entry->func, f->func, TRACE_FUNC_SIZE); | ||
66 | strncpy(entry->file, p, TRACE_FILE_SIZE); | ||
67 | entry->func[TRACE_FUNC_SIZE] = 0; | ||
68 | entry->file[TRACE_FILE_SIZE] = 0; | ||
69 | entry->line = f->line; | ||
70 | entry->correct = val == expect; | ||
71 | |||
72 | ring_buffer_unlock_commit(tr->buffer, event, irq_flags); | ||
73 | |||
74 | out: | ||
75 | atomic_dec(&tr->data[cpu]->disabled); | ||
76 | local_irq_restore(flags); | ||
77 | } | ||
78 | |||
79 | static inline | ||
80 | void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect) | ||
81 | { | ||
82 | if (!unlikely_tracing_enabled) | ||
83 | return; | ||
84 | |||
85 | probe_likely_condition(f, val, expect); | ||
86 | } | ||
87 | |||
88 | int enable_unlikely_tracing(struct trace_array *tr) | ||
89 | { | ||
90 | int ret = 0; | ||
91 | |||
92 | mutex_lock(&unlikely_tracing_mutex); | ||
93 | unlikely_tracer = tr; | ||
94 | /* | ||
95 | * Must be seen before enabling. The reader is a condition | ||
96 | * where we do not need a matching rmb() | ||
97 | */ | ||
98 | smp_wmb(); | ||
99 | unlikely_tracing_enabled++; | ||
100 | mutex_unlock(&unlikely_tracing_mutex); | ||
101 | |||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | void disable_unlikely_tracing(void) | ||
106 | { | ||
107 | mutex_lock(&unlikely_tracing_mutex); | ||
108 | |||
109 | if (!unlikely_tracing_enabled) | ||
110 | goto out_unlock; | ||
111 | |||
112 | unlikely_tracing_enabled--; | ||
113 | |||
114 | out_unlock: | ||
115 | mutex_unlock(&unlikely_tracing_mutex); | ||
116 | } | ||
117 | #else | ||
118 | static inline | ||
119 | void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect) | ||
120 | { | ||
121 | } | ||
122 | #endif /* CONFIG_UNLIKELY_TRACER */ | ||
123 | |||
18 | void ftrace_likely_update(struct ftrace_likely_data *f, int val, int expect) | 124 | void ftrace_likely_update(struct ftrace_likely_data *f, int val, int expect) |
19 | { | 125 | { |
126 | /* | ||
127 | * I would love to have a trace point here instead, but the | ||
128 | * trace point code is so inundated with unlikely and likely | ||
129 | * conditions that the recursive nightmare that exists is too | ||
130 | * much to try to get working. At least for now. | ||
131 | */ | ||
132 | trace_likely_condition(f, val, expect); | ||
133 | |||
20 | /* FIXME: Make this atomic! */ | 134 | /* FIXME: Make this atomic! */ |
21 | if (val == expect) | 135 | if (val == expect) |
22 | f->correct++; | 136 | f->correct++; |