diff options
-rw-r--r-- | Documentation/ftrace.txt | 74 | ||||
-rw-r--r-- | arch/x86/kernel/ds.c | 31 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 6 | ||||
-rw-r--r-- | include/linux/ftrace.h | 13 | ||||
-rw-r--r-- | kernel/trace/trace.h | 1 | ||||
-rw-r--r-- | kernel/trace/trace_hw_branches.c | 173 | ||||
-rw-r--r-- | kernel/trace/trace_workqueue.c | 64 |
7 files changed, 280 insertions, 82 deletions
diff --git a/Documentation/ftrace.txt b/Documentation/ftrace.txt index 803b1318b13d..758fb42a1b68 100644 --- a/Documentation/ftrace.txt +++ b/Documentation/ftrace.txt | |||
@@ -165,6 +165,8 @@ Here is the list of current tracers that may be configured. | |||
165 | nop - This is not a tracer. To remove all tracers from tracing | 165 | nop - This is not a tracer. To remove all tracers from tracing |
166 | simply echo "nop" into current_tracer. | 166 | simply echo "nop" into current_tracer. |
167 | 167 | ||
168 | hw-branch-tracer - traces branches on all cpu's in a circular buffer. | ||
169 | |||
168 | 170 | ||
169 | Examples of using the tracer | 171 | Examples of using the tracer |
170 | ---------------------------- | 172 | ---------------------------- |
@@ -1152,6 +1154,78 @@ int main (int argc, char **argv) | |||
1152 | return 0; | 1154 | return 0; |
1153 | } | 1155 | } |
1154 | 1156 | ||
1157 | |||
1158 | hw-branch-tracer (x86 only) | ||
1159 | --------------------------- | ||
1160 | |||
1161 | This tracer uses the x86 last branch tracing hardware feature to | ||
1162 | collect a branch trace on all cpus with relatively low overhead. | ||
1163 | |||
1164 | The tracer uses a fixed-size circular buffer per cpu and only | ||
1165 | traces ring 0 branches. The trace file dumps that buffer in the | ||
1166 | following format: | ||
1167 | |||
1168 | # tracer: hw-branch-tracer | ||
1169 | # | ||
1170 | # CPU# TO <- FROM | ||
1171 | 0 scheduler_tick+0xb5/0x1bf <- task_tick_idle+0x5/0x6 | ||
1172 | 2 run_posix_cpu_timers+0x2b/0x72a <- run_posix_cpu_timers+0x25/0x72a | ||
1173 | 0 scheduler_tick+0x139/0x1bf <- scheduler_tick+0xed/0x1bf | ||
1174 | 0 scheduler_tick+0x17c/0x1bf <- scheduler_tick+0x148/0x1bf | ||
1175 | 2 run_posix_cpu_timers+0x9e/0x72a <- run_posix_cpu_timers+0x5e/0x72a | ||
1176 | 0 scheduler_tick+0x1b6/0x1bf <- scheduler_tick+0x1aa/0x1bf | ||
1177 | |||
1178 | |||
1179 | The tracer may be used to dump the trace for the oops'ing cpu on a | ||
1180 | kernel oops into the system log. To enable this, ftrace_dump_on_oops | ||
1181 | must be set. To set ftrace_dump_on_oops, one can either use the sysctl | ||
1182 | function or set it via the proc system interface. | ||
1183 | |||
1184 | sysctl kernel.ftrace_dump_on_oops=1 | ||
1185 | |||
1186 | or | ||
1187 | |||
1188 | echo 1 > /proc/sys/kernel/ftrace_dump_on_oops | ||
1189 | |||
1190 | |||
1191 | Here's an example of such a dump after a null pointer dereference in a | ||
1192 | kernel module: | ||
1193 | |||
1194 | [57848.105921] BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 | ||
1195 | [57848.106019] IP: [<ffffffffa0000006>] open+0x6/0x14 [oops] | ||
1196 | [57848.106019] PGD 2354e9067 PUD 2375e7067 PMD 0 | ||
1197 | [57848.106019] Oops: 0002 [#1] SMP | ||
1198 | [57848.106019] last sysfs file: /sys/devices/pci0000:00/0000:00:1e.0/0000:20:05.0/local_cpus | ||
1199 | [57848.106019] Dumping ftrace buffer: | ||
1200 | [57848.106019] --------------------------------- | ||
1201 | [...] | ||
1202 | [57848.106019] 0 chrdev_open+0xe6/0x165 <- cdev_put+0x23/0x24 | ||
1203 | [57848.106019] 0 chrdev_open+0x117/0x165 <- chrdev_open+0xfa/0x165 | ||
1204 | [57848.106019] 0 chrdev_open+0x120/0x165 <- chrdev_open+0x11c/0x165 | ||
1205 | [57848.106019] 0 chrdev_open+0x134/0x165 <- chrdev_open+0x12b/0x165 | ||
1206 | [57848.106019] 0 open+0x0/0x14 [oops] <- chrdev_open+0x144/0x165 | ||
1207 | [57848.106019] 0 page_fault+0x0/0x30 <- open+0x6/0x14 [oops] | ||
1208 | [57848.106019] 0 error_entry+0x0/0x5b <- page_fault+0x4/0x30 | ||
1209 | [57848.106019] 0 error_kernelspace+0x0/0x31 <- error_entry+0x59/0x5b | ||
1210 | [57848.106019] 0 error_sti+0x0/0x1 <- error_kernelspace+0x2d/0x31 | ||
1211 | [57848.106019] 0 page_fault+0x9/0x30 <- error_sti+0x0/0x1 | ||
1212 | [57848.106019] 0 do_page_fault+0x0/0x881 <- page_fault+0x1a/0x30 | ||
1213 | [...] | ||
1214 | [57848.106019] 0 do_page_fault+0x66b/0x881 <- is_prefetch+0x1ee/0x1f2 | ||
1215 | [57848.106019] 0 do_page_fault+0x6e0/0x881 <- do_page_fault+0x67a/0x881 | ||
1216 | [57848.106019] 0 oops_begin+0x0/0x96 <- do_page_fault+0x6e0/0x881 | ||
1217 | [57848.106019] 0 trace_hw_branch_oops+0x0/0x2d <- oops_begin+0x9/0x96 | ||
1218 | [...] | ||
1219 | [57848.106019] 0 ds_suspend_bts+0x2a/0xe3 <- ds_suspend_bts+0x1a/0xe3 | ||
1220 | [57848.106019] --------------------------------- | ||
1221 | [57848.106019] CPU 0 | ||
1222 | [57848.106019] Modules linked in: oops | ||
1223 | [57848.106019] Pid: 5542, comm: cat Tainted: G W 2.6.28 #23 | ||
1224 | [57848.106019] RIP: 0010:[<ffffffffa0000006>] [<ffffffffa0000006>] open+0x6/0x14 [oops] | ||
1225 | [57848.106019] RSP: 0018:ffff880235457d48 EFLAGS: 00010246 | ||
1226 | [...] | ||
1227 | |||
1228 | |||
1155 | dynamic ftrace | 1229 | dynamic ftrace |
1156 | -------------- | 1230 | -------------- |
1157 | 1231 | ||
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index da91701a2348..169a120587be 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c | |||
@@ -15,8 +15,8 @@ | |||
15 | * - buffer allocation (memory accounting) | 15 | * - buffer allocation (memory accounting) |
16 | * | 16 | * |
17 | * | 17 | * |
18 | * Copyright (C) 2007-2008 Intel Corporation. | 18 | * Copyright (C) 2007-2009 Intel Corporation. |
19 | * Markus Metzger <markus.t.metzger@intel.com>, 2007-2008 | 19 | * Markus Metzger <markus.t.metzger@intel.com>, 2007-2009 |
20 | */ | 20 | */ |
21 | 21 | ||
22 | 22 | ||
@@ -890,7 +890,7 @@ int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value) | |||
890 | } | 890 | } |
891 | 891 | ||
892 | static const struct ds_configuration ds_cfg_netburst = { | 892 | static const struct ds_configuration ds_cfg_netburst = { |
893 | .name = "netburst", | 893 | .name = "Netburst", |
894 | .ctl[dsf_bts] = (1 << 2) | (1 << 3), | 894 | .ctl[dsf_bts] = (1 << 2) | (1 << 3), |
895 | .ctl[dsf_bts_kernel] = (1 << 5), | 895 | .ctl[dsf_bts_kernel] = (1 << 5), |
896 | .ctl[dsf_bts_user] = (1 << 6), | 896 | .ctl[dsf_bts_user] = (1 << 6), |
@@ -904,7 +904,7 @@ static const struct ds_configuration ds_cfg_netburst = { | |||
904 | #endif | 904 | #endif |
905 | }; | 905 | }; |
906 | static const struct ds_configuration ds_cfg_pentium_m = { | 906 | static const struct ds_configuration ds_cfg_pentium_m = { |
907 | .name = "pentium m", | 907 | .name = "Pentium M", |
908 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), | 908 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), |
909 | 909 | ||
910 | .sizeof_field = sizeof(long), | 910 | .sizeof_field = sizeof(long), |
@@ -915,8 +915,8 @@ static const struct ds_configuration ds_cfg_pentium_m = { | |||
915 | .sizeof_rec[ds_pebs] = sizeof(long) * 18, | 915 | .sizeof_rec[ds_pebs] = sizeof(long) * 18, |
916 | #endif | 916 | #endif |
917 | }; | 917 | }; |
918 | static const struct ds_configuration ds_cfg_core2 = { | 918 | static const struct ds_configuration ds_cfg_core2_atom = { |
919 | .name = "core 2", | 919 | .name = "Core 2/Atom", |
920 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), | 920 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), |
921 | .ctl[dsf_bts_kernel] = (1 << 9), | 921 | .ctl[dsf_bts_kernel] = (1 << 9), |
922 | .ctl[dsf_bts_user] = (1 << 10), | 922 | .ctl[dsf_bts_user] = (1 << 10), |
@@ -949,19 +949,22 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) | |||
949 | switch (c->x86) { | 949 | switch (c->x86) { |
950 | case 0x6: | 950 | case 0x6: |
951 | switch (c->x86_model) { | 951 | switch (c->x86_model) { |
952 | case 0 ... 0xC: | 952 | case 0x9: |
953 | /* sorry, don't know about them */ | 953 | case 0xd: /* Pentium M */ |
954 | break; | ||
955 | case 0xD: | ||
956 | case 0xE: /* Pentium M */ | ||
957 | ds_configure(&ds_cfg_pentium_m); | 954 | ds_configure(&ds_cfg_pentium_m); |
958 | break; | 955 | break; |
959 | default: /* Core2, Atom, ... */ | 956 | case 0xf: |
960 | ds_configure(&ds_cfg_core2); | 957 | case 0x17: /* Core2 */ |
958 | case 0x1c: /* Atom */ | ||
959 | ds_configure(&ds_cfg_core2_atom); | ||
960 | break; | ||
961 | case 0x1a: /* i7 */ | ||
962 | default: | ||
963 | /* sorry, don't know about them */ | ||
961 | break; | 964 | break; |
962 | } | 965 | } |
963 | break; | 966 | break; |
964 | case 0xF: | 967 | case 0xf: |
965 | switch (c->x86_model) { | 968 | switch (c->x86_model) { |
966 | case 0x0: | 969 | case 0x0: |
967 | case 0x1: | 970 | case 0x1: |
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 6b1f6f6f8661..077c9ea655fc 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/bug.h> | 14 | #include <linux/bug.h> |
15 | #include <linux/nmi.h> | 15 | #include <linux/nmi.h> |
16 | #include <linux/sysfs.h> | 16 | #include <linux/sysfs.h> |
17 | #include <linux/ftrace.h> | ||
17 | 18 | ||
18 | #include <asm/stacktrace.h> | 19 | #include <asm/stacktrace.h> |
19 | 20 | ||
@@ -195,6 +196,11 @@ unsigned __kprobes long oops_begin(void) | |||
195 | int cpu; | 196 | int cpu; |
196 | unsigned long flags; | 197 | unsigned long flags; |
197 | 198 | ||
199 | /* notify the hw-branch tracer so it may disable tracing and | ||
200 | add the last trace to the trace buffer - | ||
201 | the earlier this happens, the more useful the trace. */ | ||
202 | trace_hw_branch_oops(); | ||
203 | |||
198 | oops_enter(); | 204 | oops_enter(); |
199 | 205 | ||
200 | /* racy, but better than risking deadlock. */ | 206 | /* racy, but better than risking deadlock. */ |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 054721487574..9f7880d87c39 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -496,4 +496,17 @@ static inline int test_tsk_trace_graph(struct task_struct *tsk) | |||
496 | 496 | ||
497 | #endif /* CONFIG_TRACING */ | 497 | #endif /* CONFIG_TRACING */ |
498 | 498 | ||
499 | |||
500 | #ifdef CONFIG_HW_BRANCH_TRACER | ||
501 | |||
502 | void trace_hw_branch(u64 from, u64 to); | ||
503 | void trace_hw_branch_oops(void); | ||
504 | |||
505 | #else /* CONFIG_HW_BRANCH_TRACER */ | ||
506 | |||
507 | static inline void trace_hw_branch(u64 from, u64 to) {} | ||
508 | static inline void trace_hw_branch_oops(void) {} | ||
509 | |||
510 | #endif /* CONFIG_HW_BRANCH_TRACER */ | ||
511 | |||
499 | #endif /* _LINUX_FTRACE_H */ | 512 | #endif /* _LINUX_FTRACE_H */ |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 54b72781e920..b96037d970df 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -438,7 +438,6 @@ void trace_function(struct trace_array *tr, | |||
438 | 438 | ||
439 | void trace_graph_return(struct ftrace_graph_ret *trace); | 439 | void trace_graph_return(struct ftrace_graph_ret *trace); |
440 | int trace_graph_entry(struct ftrace_graph_ent *trace); | 440 | int trace_graph_entry(struct ftrace_graph_ent *trace); |
441 | void trace_hw_branch(struct trace_array *tr, u64 from, u64 to); | ||
442 | 441 | ||
443 | void tracing_start_cmdline_record(void); | 442 | void tracing_start_cmdline_record(void); |
444 | void tracing_stop_cmdline_record(void); | 443 | void tracing_stop_cmdline_record(void); |
diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c index df21c1e72b95..fff3545fc866 100644 --- a/kernel/trace/trace_hw_branches.c +++ b/kernel/trace/trace_hw_branches.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * h/w branch tracer for x86 based on bts | 2 | * h/w branch tracer for x86 based on bts |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com> | 4 | * Copyright (C) 2008-2009 Intel Corporation. |
5 | * Markus Metzger <markus.t.metzger@gmail.com>, 2008-2009 | ||
5 | * | 6 | * |
6 | */ | 7 | */ |
7 | 8 | ||
@@ -10,6 +11,9 @@ | |||
10 | #include <linux/debugfs.h> | 11 | #include <linux/debugfs.h> |
11 | #include <linux/ftrace.h> | 12 | #include <linux/ftrace.h> |
12 | #include <linux/kallsyms.h> | 13 | #include <linux/kallsyms.h> |
14 | #include <linux/mutex.h> | ||
15 | #include <linux/cpu.h> | ||
16 | #include <linux/smp.h> | ||
13 | 17 | ||
14 | #include <asm/ds.h> | 18 | #include <asm/ds.h> |
15 | 19 | ||
@@ -19,13 +23,32 @@ | |||
19 | 23 | ||
20 | #define SIZEOF_BTS (1 << 13) | 24 | #define SIZEOF_BTS (1 << 13) |
21 | 25 | ||
26 | /* The tracer mutex protects the below per-cpu tracer array. | ||
27 | It needs to be held to: | ||
28 | - start tracing on all cpus | ||
29 | - stop tracing on all cpus | ||
30 | - start tracing on a single hotplug cpu | ||
31 | - stop tracing on a single hotplug cpu | ||
32 | - read the trace from all cpus | ||
33 | - read the trace from a single cpu | ||
34 | */ | ||
35 | static DEFINE_MUTEX(bts_tracer_mutex); | ||
22 | static DEFINE_PER_CPU(struct bts_tracer *, tracer); | 36 | static DEFINE_PER_CPU(struct bts_tracer *, tracer); |
23 | static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer); | 37 | static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer); |
24 | 38 | ||
25 | #define this_tracer per_cpu(tracer, smp_processor_id()) | 39 | #define this_tracer per_cpu(tracer, smp_processor_id()) |
26 | #define this_buffer per_cpu(buffer, smp_processor_id()) | 40 | #define this_buffer per_cpu(buffer, smp_processor_id()) |
27 | 41 | ||
42 | static int __read_mostly trace_hw_branches_enabled; | ||
43 | static struct trace_array *hw_branch_trace __read_mostly; | ||
28 | 44 | ||
45 | |||
46 | /* | ||
47 | * Start tracing on the current cpu. | ||
48 | * The argument is ignored. | ||
49 | * | ||
50 | * pre: bts_tracer_mutex must be locked. | ||
51 | */ | ||
29 | static void bts_trace_start_cpu(void *arg) | 52 | static void bts_trace_start_cpu(void *arg) |
30 | { | 53 | { |
31 | if (this_tracer) | 54 | if (this_tracer) |
@@ -43,14 +66,20 @@ static void bts_trace_start_cpu(void *arg) | |||
43 | 66 | ||
44 | static void bts_trace_start(struct trace_array *tr) | 67 | static void bts_trace_start(struct trace_array *tr) |
45 | { | 68 | { |
46 | int cpu; | 69 | mutex_lock(&bts_tracer_mutex); |
47 | 70 | ||
48 | tracing_reset_online_cpus(tr); | 71 | on_each_cpu(bts_trace_start_cpu, NULL, 1); |
72 | trace_hw_branches_enabled = 1; | ||
49 | 73 | ||
50 | for_each_cpu(cpu, cpu_possible_mask) | 74 | mutex_unlock(&bts_tracer_mutex); |
51 | smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1); | ||
52 | } | 75 | } |
53 | 76 | ||
77 | /* | ||
78 | * Start tracing on the current cpu. | ||
79 | * The argument is ignored. | ||
80 | * | ||
81 | * pre: bts_tracer_mutex must be locked. | ||
82 | */ | ||
54 | static void bts_trace_stop_cpu(void *arg) | 83 | static void bts_trace_stop_cpu(void *arg) |
55 | { | 84 | { |
56 | if (this_tracer) { | 85 | if (this_tracer) { |
@@ -61,26 +90,63 @@ static void bts_trace_stop_cpu(void *arg) | |||
61 | 90 | ||
62 | static void bts_trace_stop(struct trace_array *tr) | 91 | static void bts_trace_stop(struct trace_array *tr) |
63 | { | 92 | { |
64 | int cpu; | 93 | mutex_lock(&bts_tracer_mutex); |
94 | |||
95 | trace_hw_branches_enabled = 0; | ||
96 | on_each_cpu(bts_trace_stop_cpu, NULL, 1); | ||
65 | 97 | ||
66 | for_each_cpu(cpu, cpu_possible_mask) | 98 | mutex_unlock(&bts_tracer_mutex); |
99 | } | ||
100 | |||
101 | static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb, | ||
102 | unsigned long action, void *hcpu) | ||
103 | { | ||
104 | unsigned int cpu = (unsigned long)hcpu; | ||
105 | |||
106 | mutex_lock(&bts_tracer_mutex); | ||
107 | |||
108 | if (!trace_hw_branches_enabled) | ||
109 | goto out; | ||
110 | |||
111 | switch (action) { | ||
112 | case CPU_ONLINE: | ||
113 | case CPU_DOWN_FAILED: | ||
114 | smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1); | ||
115 | break; | ||
116 | case CPU_DOWN_PREPARE: | ||
67 | smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1); | 117 | smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1); |
118 | break; | ||
119 | } | ||
120 | |||
121 | out: | ||
122 | mutex_unlock(&bts_tracer_mutex); | ||
123 | return NOTIFY_DONE; | ||
68 | } | 124 | } |
69 | 125 | ||
126 | static struct notifier_block bts_hotcpu_notifier __cpuinitdata = { | ||
127 | .notifier_call = bts_hotcpu_handler | ||
128 | }; | ||
129 | |||
70 | static int bts_trace_init(struct trace_array *tr) | 130 | static int bts_trace_init(struct trace_array *tr) |
71 | { | 131 | { |
132 | hw_branch_trace = tr; | ||
133 | |||
134 | register_hotcpu_notifier(&bts_hotcpu_notifier); | ||
72 | tracing_reset_online_cpus(tr); | 135 | tracing_reset_online_cpus(tr); |
73 | bts_trace_start(tr); | 136 | bts_trace_start(tr); |
74 | 137 | ||
75 | return 0; | 138 | return 0; |
76 | } | 139 | } |
77 | 140 | ||
141 | static void bts_trace_reset(struct trace_array *tr) | ||
142 | { | ||
143 | bts_trace_stop(tr); | ||
144 | unregister_hotcpu_notifier(&bts_hotcpu_notifier); | ||
145 | } | ||
146 | |||
78 | static void bts_trace_print_header(struct seq_file *m) | 147 | static void bts_trace_print_header(struct seq_file *m) |
79 | { | 148 | { |
80 | seq_puts(m, | 149 | seq_puts(m, "# CPU# TO <- FROM\n"); |
81 | "# CPU# FROM TO FUNCTION\n"); | ||
82 | seq_puts(m, | ||
83 | "# | | | |\n"); | ||
84 | } | 150 | } |
85 | 151 | ||
86 | static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | 152 | static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) |
@@ -88,15 +154,15 @@ static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | |||
88 | struct trace_entry *entry = iter->ent; | 154 | struct trace_entry *entry = iter->ent; |
89 | struct trace_seq *seq = &iter->seq; | 155 | struct trace_seq *seq = &iter->seq; |
90 | struct hw_branch_entry *it; | 156 | struct hw_branch_entry *it; |
157 | unsigned long symflags = TRACE_ITER_SYM_OFFSET; | ||
91 | 158 | ||
92 | trace_assign_type(it, entry); | 159 | trace_assign_type(it, entry); |
93 | 160 | ||
94 | if (entry->type == TRACE_HW_BRANCHES) { | 161 | if (entry->type == TRACE_HW_BRANCHES) { |
95 | if (trace_seq_printf(seq, "%4d ", entry->cpu) && | 162 | if (trace_seq_printf(seq, "%4d ", entry->cpu) && |
96 | trace_seq_printf(seq, "0x%016llx -> 0x%016llx ", | 163 | seq_print_ip_sym(seq, it->to, symflags) && |
97 | it->from, it->to) && | 164 | trace_seq_printf(seq, "\t <- ") && |
98 | (!it->from || | 165 | seq_print_ip_sym(seq, it->from, symflags) && |
99 | seq_print_ip_sym(seq, it->from, /* sym_flags = */ 0)) && | ||
100 | trace_seq_printf(seq, "\n")) | 166 | trace_seq_printf(seq, "\n")) |
101 | return TRACE_TYPE_HANDLED; | 167 | return TRACE_TYPE_HANDLED; |
102 | return TRACE_TYPE_PARTIAL_LINE;; | 168 | return TRACE_TYPE_PARTIAL_LINE;; |
@@ -104,26 +170,42 @@ static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | |||
104 | return TRACE_TYPE_UNHANDLED; | 170 | return TRACE_TYPE_UNHANDLED; |
105 | } | 171 | } |
106 | 172 | ||
107 | void trace_hw_branch(struct trace_array *tr, u64 from, u64 to) | 173 | void trace_hw_branch(u64 from, u64 to) |
108 | { | 174 | { |
175 | struct trace_array *tr = hw_branch_trace; | ||
109 | struct ring_buffer_event *event; | 176 | struct ring_buffer_event *event; |
110 | struct hw_branch_entry *entry; | 177 | struct hw_branch_entry *entry; |
111 | unsigned long irq; | 178 | unsigned long irq1, irq2; |
179 | int cpu; | ||
112 | 180 | ||
113 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq); | 181 | if (unlikely(!tr)) |
114 | if (!event) | ||
115 | return; | 182 | return; |
183 | |||
184 | if (unlikely(!trace_hw_branches_enabled)) | ||
185 | return; | ||
186 | |||
187 | local_irq_save(irq1); | ||
188 | cpu = raw_smp_processor_id(); | ||
189 | if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) | ||
190 | goto out; | ||
191 | |||
192 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq2); | ||
193 | if (!event) | ||
194 | goto out; | ||
116 | entry = ring_buffer_event_data(event); | 195 | entry = ring_buffer_event_data(event); |
117 | tracing_generic_entry_update(&entry->ent, 0, from); | 196 | tracing_generic_entry_update(&entry->ent, 0, from); |
118 | entry->ent.type = TRACE_HW_BRANCHES; | 197 | entry->ent.type = TRACE_HW_BRANCHES; |
119 | entry->ent.cpu = smp_processor_id(); | 198 | entry->ent.cpu = cpu; |
120 | entry->from = from; | 199 | entry->from = from; |
121 | entry->to = to; | 200 | entry->to = to; |
122 | ring_buffer_unlock_commit(tr->buffer, event, irq); | 201 | ring_buffer_unlock_commit(tr->buffer, event, irq2); |
202 | |||
203 | out: | ||
204 | atomic_dec(&tr->data[cpu]->disabled); | ||
205 | local_irq_restore(irq1); | ||
123 | } | 206 | } |
124 | 207 | ||
125 | static void trace_bts_at(struct trace_array *tr, | 208 | static void trace_bts_at(const struct bts_trace *trace, void *at) |
126 | const struct bts_trace *trace, void *at) | ||
127 | { | 209 | { |
128 | struct bts_struct bts; | 210 | struct bts_struct bts; |
129 | int err = 0; | 211 | int err = 0; |
@@ -138,18 +220,29 @@ static void trace_bts_at(struct trace_array *tr, | |||
138 | 220 | ||
139 | switch (bts.qualifier) { | 221 | switch (bts.qualifier) { |
140 | case BTS_BRANCH: | 222 | case BTS_BRANCH: |
141 | trace_hw_branch(tr, bts.variant.lbr.from, bts.variant.lbr.to); | 223 | trace_hw_branch(bts.variant.lbr.from, bts.variant.lbr.to); |
142 | break; | 224 | break; |
143 | } | 225 | } |
144 | } | 226 | } |
145 | 227 | ||
228 | /* | ||
229 | * Collect the trace on the current cpu and write it into the ftrace buffer. | ||
230 | * | ||
231 | * pre: bts_tracer_mutex must be locked | ||
232 | */ | ||
146 | static void trace_bts_cpu(void *arg) | 233 | static void trace_bts_cpu(void *arg) |
147 | { | 234 | { |
148 | struct trace_array *tr = (struct trace_array *) arg; | 235 | struct trace_array *tr = (struct trace_array *) arg; |
149 | const struct bts_trace *trace; | 236 | const struct bts_trace *trace; |
150 | unsigned char *at; | 237 | unsigned char *at; |
151 | 238 | ||
152 | if (!this_tracer) | 239 | if (unlikely(!tr)) |
240 | return; | ||
241 | |||
242 | if (unlikely(atomic_read(&tr->data[raw_smp_processor_id()]->disabled))) | ||
243 | return; | ||
244 | |||
245 | if (unlikely(!this_tracer)) | ||
153 | return; | 246 | return; |
154 | 247 | ||
155 | ds_suspend_bts(this_tracer); | 248 | ds_suspend_bts(this_tracer); |
@@ -159,11 +252,11 @@ static void trace_bts_cpu(void *arg) | |||
159 | 252 | ||
160 | for (at = trace->ds.top; (void *)at < trace->ds.end; | 253 | for (at = trace->ds.top; (void *)at < trace->ds.end; |
161 | at += trace->ds.size) | 254 | at += trace->ds.size) |
162 | trace_bts_at(tr, trace, at); | 255 | trace_bts_at(trace, at); |
163 | 256 | ||
164 | for (at = trace->ds.begin; (void *)at < trace->ds.top; | 257 | for (at = trace->ds.begin; (void *)at < trace->ds.top; |
165 | at += trace->ds.size) | 258 | at += trace->ds.size) |
166 | trace_bts_at(tr, trace, at); | 259 | trace_bts_at(trace, at); |
167 | 260 | ||
168 | out: | 261 | out: |
169 | ds_resume_bts(this_tracer); | 262 | ds_resume_bts(this_tracer); |
@@ -171,22 +264,38 @@ out: | |||
171 | 264 | ||
172 | static void trace_bts_prepare(struct trace_iterator *iter) | 265 | static void trace_bts_prepare(struct trace_iterator *iter) |
173 | { | 266 | { |
174 | int cpu; | 267 | mutex_lock(&bts_tracer_mutex); |
268 | |||
269 | on_each_cpu(trace_bts_cpu, iter->tr, 1); | ||
270 | |||
271 | mutex_unlock(&bts_tracer_mutex); | ||
272 | } | ||
273 | |||
274 | static void trace_bts_close(struct trace_iterator *iter) | ||
275 | { | ||
276 | tracing_reset_online_cpus(iter->tr); | ||
277 | } | ||
278 | |||
279 | void trace_hw_branch_oops(void) | ||
280 | { | ||
281 | mutex_lock(&bts_tracer_mutex); | ||
282 | |||
283 | trace_bts_cpu(hw_branch_trace); | ||
175 | 284 | ||
176 | for_each_cpu(cpu, cpu_possible_mask) | 285 | mutex_unlock(&bts_tracer_mutex); |
177 | smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1); | ||
178 | } | 286 | } |
179 | 287 | ||
180 | struct tracer bts_tracer __read_mostly = | 288 | struct tracer bts_tracer __read_mostly = |
181 | { | 289 | { |
182 | .name = "hw-branch-tracer", | 290 | .name = "hw-branch-tracer", |
183 | .init = bts_trace_init, | 291 | .init = bts_trace_init, |
184 | .reset = bts_trace_stop, | 292 | .reset = bts_trace_reset, |
185 | .print_header = bts_trace_print_header, | 293 | .print_header = bts_trace_print_header, |
186 | .print_line = bts_trace_print_line, | 294 | .print_line = bts_trace_print_line, |
187 | .start = bts_trace_start, | 295 | .start = bts_trace_start, |
188 | .stop = bts_trace_stop, | 296 | .stop = bts_trace_stop, |
189 | .open = trace_bts_prepare | 297 | .open = trace_bts_prepare, |
298 | .close = trace_bts_close | ||
190 | }; | 299 | }; |
191 | 300 | ||
192 | __init static int init_bts_trace(void) | 301 | __init static int init_bts_trace(void) |
diff --git a/kernel/trace/trace_workqueue.c b/kernel/trace/trace_workqueue.c index f8118d39ca9b..4664990fe9c5 100644 --- a/kernel/trace/trace_workqueue.c +++ b/kernel/trace/trace_workqueue.c | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include <trace/workqueue.h> | 9 | #include <trace/workqueue.h> |
10 | #include <linux/list.h> | 10 | #include <linux/list.h> |
11 | #include <linux/percpu.h> | ||
11 | #include "trace_stat.h" | 12 | #include "trace_stat.h" |
12 | #include "trace.h" | 13 | #include "trace.h" |
13 | 14 | ||
@@ -37,7 +38,8 @@ struct workqueue_global_stats { | |||
37 | /* Don't need a global lock because allocated before the workqueues, and | 38 | /* Don't need a global lock because allocated before the workqueues, and |
38 | * never freed. | 39 | * never freed. |
39 | */ | 40 | */ |
40 | static struct workqueue_global_stats *all_workqueue_stat; | 41 | static DEFINE_PER_CPU(struct workqueue_global_stats, all_workqueue_stat); |
42 | #define workqueue_cpu_stat(cpu) (&per_cpu(all_workqueue_stat, cpu)) | ||
41 | 43 | ||
42 | /* Insertion of a work */ | 44 | /* Insertion of a work */ |
43 | static void | 45 | static void |
@@ -48,8 +50,8 @@ probe_workqueue_insertion(struct task_struct *wq_thread, | |||
48 | struct cpu_workqueue_stats *node, *next; | 50 | struct cpu_workqueue_stats *node, *next; |
49 | unsigned long flags; | 51 | unsigned long flags; |
50 | 52 | ||
51 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 53 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
52 | list_for_each_entry_safe(node, next, &all_workqueue_stat[cpu].list, | 54 | list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, |
53 | list) { | 55 | list) { |
54 | if (node->pid == wq_thread->pid) { | 56 | if (node->pid == wq_thread->pid) { |
55 | atomic_inc(&node->inserted); | 57 | atomic_inc(&node->inserted); |
@@ -58,7 +60,7 @@ probe_workqueue_insertion(struct task_struct *wq_thread, | |||
58 | } | 60 | } |
59 | pr_debug("trace_workqueue: entry not found\n"); | 61 | pr_debug("trace_workqueue: entry not found\n"); |
60 | found: | 62 | found: |
61 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 63 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
62 | } | 64 | } |
63 | 65 | ||
64 | /* Execution of a work */ | 66 | /* Execution of a work */ |
@@ -70,8 +72,8 @@ probe_workqueue_execution(struct task_struct *wq_thread, | |||
70 | struct cpu_workqueue_stats *node, *next; | 72 | struct cpu_workqueue_stats *node, *next; |
71 | unsigned long flags; | 73 | unsigned long flags; |
72 | 74 | ||
73 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 75 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
74 | list_for_each_entry_safe(node, next, &all_workqueue_stat[cpu].list, | 76 | list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, |
75 | list) { | 77 | list) { |
76 | if (node->pid == wq_thread->pid) { | 78 | if (node->pid == wq_thread->pid) { |
77 | node->executed++; | 79 | node->executed++; |
@@ -80,7 +82,7 @@ probe_workqueue_execution(struct task_struct *wq_thread, | |||
80 | } | 82 | } |
81 | pr_debug("trace_workqueue: entry not found\n"); | 83 | pr_debug("trace_workqueue: entry not found\n"); |
82 | found: | 84 | found: |
83 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 85 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
84 | } | 86 | } |
85 | 87 | ||
86 | /* Creation of a cpu workqueue thread */ | 88 | /* Creation of a cpu workqueue thread */ |
@@ -104,11 +106,11 @@ static void probe_workqueue_creation(struct task_struct *wq_thread, int cpu) | |||
104 | 106 | ||
105 | cws->pid = wq_thread->pid; | 107 | cws->pid = wq_thread->pid; |
106 | 108 | ||
107 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 109 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
108 | if (list_empty(&all_workqueue_stat[cpu].list)) | 110 | if (list_empty(&workqueue_cpu_stat(cpu)->list)) |
109 | cws->first_entry = true; | 111 | cws->first_entry = true; |
110 | list_add_tail(&cws->list, &all_workqueue_stat[cpu].list); | 112 | list_add_tail(&cws->list, &workqueue_cpu_stat(cpu)->list); |
111 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 113 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
112 | } | 114 | } |
113 | 115 | ||
114 | /* Destruction of a cpu workqueue thread */ | 116 | /* Destruction of a cpu workqueue thread */ |
@@ -119,8 +121,8 @@ static void probe_workqueue_destruction(struct task_struct *wq_thread) | |||
119 | struct cpu_workqueue_stats *node, *next; | 121 | struct cpu_workqueue_stats *node, *next; |
120 | unsigned long flags; | 122 | unsigned long flags; |
121 | 123 | ||
122 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 124 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
123 | list_for_each_entry_safe(node, next, &all_workqueue_stat[cpu].list, | 125 | list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, |
124 | list) { | 126 | list) { |
125 | if (node->pid == wq_thread->pid) { | 127 | if (node->pid == wq_thread->pid) { |
126 | list_del(&node->list); | 128 | list_del(&node->list); |
@@ -131,7 +133,7 @@ static void probe_workqueue_destruction(struct task_struct *wq_thread) | |||
131 | 133 | ||
132 | pr_debug("trace_workqueue: don't find workqueue to destroy\n"); | 134 | pr_debug("trace_workqueue: don't find workqueue to destroy\n"); |
133 | found: | 135 | found: |
134 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 136 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
135 | 137 | ||
136 | } | 138 | } |
137 | 139 | ||
@@ -141,13 +143,13 @@ static struct cpu_workqueue_stats *workqueue_stat_start_cpu(int cpu) | |||
141 | struct cpu_workqueue_stats *ret = NULL; | 143 | struct cpu_workqueue_stats *ret = NULL; |
142 | 144 | ||
143 | 145 | ||
144 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 146 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
145 | 147 | ||
146 | if (!list_empty(&all_workqueue_stat[cpu].list)) | 148 | if (!list_empty(&workqueue_cpu_stat(cpu)->list)) |
147 | ret = list_entry(all_workqueue_stat[cpu].list.next, | 149 | ret = list_entry(workqueue_cpu_stat(cpu)->list.next, |
148 | struct cpu_workqueue_stats, list); | 150 | struct cpu_workqueue_stats, list); |
149 | 151 | ||
150 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 152 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
151 | 153 | ||
152 | return ret; | 154 | return ret; |
153 | } | 155 | } |
@@ -172,9 +174,9 @@ static void *workqueue_stat_next(void *prev, int idx) | |||
172 | unsigned long flags; | 174 | unsigned long flags; |
173 | void *ret = NULL; | 175 | void *ret = NULL; |
174 | 176 | ||
175 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 177 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
176 | if (list_is_last(&prev_cws->list, &all_workqueue_stat[cpu].list)) { | 178 | if (list_is_last(&prev_cws->list, &workqueue_cpu_stat(cpu)->list)) { |
177 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 179 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
178 | for (++cpu ; cpu < num_possible_cpus(); cpu++) { | 180 | for (++cpu ; cpu < num_possible_cpus(); cpu++) { |
179 | ret = workqueue_stat_start_cpu(cpu); | 181 | ret = workqueue_stat_start_cpu(cpu); |
180 | if (ret) | 182 | if (ret) |
@@ -182,7 +184,7 @@ static void *workqueue_stat_next(void *prev, int idx) | |||
182 | } | 184 | } |
183 | return NULL; | 185 | return NULL; |
184 | } | 186 | } |
185 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 187 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
186 | 188 | ||
187 | return list_entry(prev_cws->list.next, struct cpu_workqueue_stats, | 189 | return list_entry(prev_cws->list.next, struct cpu_workqueue_stats, |
188 | list); | 190 | list); |
@@ -199,10 +201,10 @@ static int workqueue_stat_show(struct seq_file *s, void *p) | |||
199 | cws->executed, | 201 | cws->executed, |
200 | trace_find_cmdline(cws->pid)); | 202 | trace_find_cmdline(cws->pid)); |
201 | 203 | ||
202 | spin_lock_irqsave(&all_workqueue_stat[cpu].lock, flags); | 204 | spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); |
203 | if (&cws->list == all_workqueue_stat[cpu].list.next) | 205 | if (&cws->list == workqueue_cpu_stat(cpu)->list.next) |
204 | seq_printf(s, "\n"); | 206 | seq_printf(s, "\n"); |
205 | spin_unlock_irqrestore(&all_workqueue_stat[cpu].lock, flags); | 207 | spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); |
206 | 208 | ||
207 | return 0; | 209 | return 0; |
208 | } | 210 | } |
@@ -258,17 +260,9 @@ int __init trace_workqueue_early_init(void) | |||
258 | if (ret) | 260 | if (ret) |
259 | goto no_creation; | 261 | goto no_creation; |
260 | 262 | ||
261 | all_workqueue_stat = kmalloc(sizeof(struct workqueue_global_stats) | ||
262 | * num_possible_cpus(), GFP_KERNEL); | ||
263 | |||
264 | if (!all_workqueue_stat) { | ||
265 | pr_warning("trace_workqueue: not enough memory\n"); | ||
266 | goto no_creation; | ||
267 | } | ||
268 | |||
269 | for_each_possible_cpu(cpu) { | 263 | for_each_possible_cpu(cpu) { |
270 | spin_lock_init(&all_workqueue_stat[cpu].lock); | 264 | spin_lock_init(&workqueue_cpu_stat(cpu)->lock); |
271 | INIT_LIST_HEAD(&all_workqueue_stat[cpu].list); | 265 | INIT_LIST_HEAD(&workqueue_cpu_stat(cpu)->list); |
272 | } | 266 | } |
273 | 267 | ||
274 | return 0; | 268 | return 0; |