diff options
Diffstat (limited to 'kernel/trace/trace_hw_branches.c')
-rw-r--r-- | kernel/trace/trace_hw_branches.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c new file mode 100644 index 000000000000..649df22d435f --- /dev/null +++ b/kernel/trace/trace_hw_branches.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * h/w branch tracer for x86 based on bts | ||
3 | * | ||
4 | * Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/fs.h> | ||
10 | #include <linux/debugfs.h> | ||
11 | #include <linux/ftrace.h> | ||
12 | #include <linux/kallsyms.h> | ||
13 | |||
14 | #include <asm/ds.h> | ||
15 | |||
16 | #include "trace.h" | ||
17 | |||
18 | |||
19 | #define SIZEOF_BTS (1 << 13) | ||
20 | |||
21 | static DEFINE_PER_CPU(struct bts_tracer *, tracer); | ||
22 | static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer); | ||
23 | |||
24 | #define this_tracer per_cpu(tracer, smp_processor_id()) | ||
25 | #define this_buffer per_cpu(buffer, smp_processor_id()) | ||
26 | |||
27 | |||
28 | static void bts_trace_start_cpu(void *arg) | ||
29 | { | ||
30 | if (this_tracer) | ||
31 | ds_release_bts(this_tracer); | ||
32 | |||
33 | this_tracer = | ||
34 | ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS, | ||
35 | /* ovfl = */ NULL, /* th = */ (size_t)-1, | ||
36 | BTS_KERNEL); | ||
37 | if (IS_ERR(this_tracer)) { | ||
38 | this_tracer = NULL; | ||
39 | return; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | static void bts_trace_start(struct trace_array *tr) | ||
44 | { | ||
45 | int cpu; | ||
46 | |||
47 | tracing_reset_online_cpus(tr); | ||
48 | |||
49 | for_each_cpu(cpu, cpu_possible_mask) | ||
50 | smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1); | ||
51 | } | ||
52 | |||
53 | static void bts_trace_stop_cpu(void *arg) | ||
54 | { | ||
55 | if (this_tracer) { | ||
56 | ds_release_bts(this_tracer); | ||
57 | this_tracer = NULL; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | static void bts_trace_stop(struct trace_array *tr) | ||
62 | { | ||
63 | int cpu; | ||
64 | |||
65 | for_each_cpu(cpu, cpu_possible_mask) | ||
66 | smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1); | ||
67 | } | ||
68 | |||
69 | static int bts_trace_init(struct trace_array *tr) | ||
70 | { | ||
71 | tracing_reset_online_cpus(tr); | ||
72 | bts_trace_start(tr); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static void bts_trace_print_header(struct seq_file *m) | ||
78 | { | ||
79 | seq_puts(m, | ||
80 | "# CPU# FROM TO FUNCTION\n"); | ||
81 | seq_puts(m, | ||
82 | "# | | | |\n"); | ||
83 | } | ||
84 | |||
85 | static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | ||
86 | { | ||
87 | struct trace_entry *entry = iter->ent; | ||
88 | struct trace_seq *seq = &iter->seq; | ||
89 | struct hw_branch_entry *it; | ||
90 | |||
91 | trace_assign_type(it, entry); | ||
92 | |||
93 | if (entry->type == TRACE_HW_BRANCHES) { | ||
94 | if (trace_seq_printf(seq, "%4d ", entry->cpu) && | ||
95 | trace_seq_printf(seq, "0x%016llx -> 0x%016llx ", | ||
96 | it->from, it->to) && | ||
97 | (!it->from || | ||
98 | seq_print_ip_sym(seq, it->from, /* sym_flags = */ 0)) && | ||
99 | trace_seq_printf(seq, "\n")) | ||
100 | return TRACE_TYPE_HANDLED; | ||
101 | return TRACE_TYPE_PARTIAL_LINE;; | ||
102 | } | ||
103 | return TRACE_TYPE_UNHANDLED; | ||
104 | } | ||
105 | |||
106 | void trace_hw_branch(struct trace_array *tr, u64 from, u64 to) | ||
107 | { | ||
108 | struct ring_buffer_event *event; | ||
109 | struct hw_branch_entry *entry; | ||
110 | unsigned long irq; | ||
111 | |||
112 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq); | ||
113 | if (!event) | ||
114 | return; | ||
115 | entry = ring_buffer_event_data(event); | ||
116 | tracing_generic_entry_update(&entry->ent, 0, from); | ||
117 | entry->ent.type = TRACE_HW_BRANCHES; | ||
118 | entry->ent.cpu = smp_processor_id(); | ||
119 | entry->from = from; | ||
120 | entry->to = to; | ||
121 | ring_buffer_unlock_commit(tr->buffer, event, irq); | ||
122 | } | ||
123 | |||
124 | static void trace_bts_at(struct trace_array *tr, | ||
125 | const struct bts_trace *trace, void *at) | ||
126 | { | ||
127 | struct bts_struct bts; | ||
128 | int err = 0; | ||
129 | |||
130 | WARN_ON_ONCE(!trace->read); | ||
131 | if (!trace->read) | ||
132 | return; | ||
133 | |||
134 | err = trace->read(this_tracer, at, &bts); | ||
135 | if (err < 0) | ||
136 | return; | ||
137 | |||
138 | switch (bts.qualifier) { | ||
139 | case BTS_BRANCH: | ||
140 | trace_hw_branch(tr, bts.variant.lbr.from, bts.variant.lbr.to); | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static void trace_bts_cpu(void *arg) | ||
146 | { | ||
147 | struct trace_array *tr = (struct trace_array *) arg; | ||
148 | const struct bts_trace *trace; | ||
149 | unsigned char *at; | ||
150 | |||
151 | if (!this_tracer) | ||
152 | return; | ||
153 | |||
154 | ds_suspend_bts(this_tracer); | ||
155 | trace = ds_read_bts(this_tracer); | ||
156 | if (!trace) | ||
157 | goto out; | ||
158 | |||
159 | for (at = trace->ds.top; (void *)at < trace->ds.end; | ||
160 | at += trace->ds.size) | ||
161 | trace_bts_at(tr, trace, at); | ||
162 | |||
163 | for (at = trace->ds.begin; (void *)at < trace->ds.top; | ||
164 | at += trace->ds.size) | ||
165 | trace_bts_at(tr, trace, at); | ||
166 | |||
167 | out: | ||
168 | ds_resume_bts(this_tracer); | ||
169 | } | ||
170 | |||
171 | static void trace_bts_prepare(struct trace_iterator *iter) | ||
172 | { | ||
173 | int cpu; | ||
174 | |||
175 | for_each_cpu(cpu, cpu_possible_mask) | ||
176 | smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1); | ||
177 | } | ||
178 | |||
179 | struct tracer bts_tracer __read_mostly = | ||
180 | { | ||
181 | .name = "hw-branch-tracer", | ||
182 | .init = bts_trace_init, | ||
183 | .reset = bts_trace_stop, | ||
184 | .print_header = bts_trace_print_header, | ||
185 | .print_line = bts_trace_print_line, | ||
186 | .start = bts_trace_start, | ||
187 | .stop = bts_trace_stop, | ||
188 | .open = trace_bts_prepare | ||
189 | }; | ||
190 | |||
191 | __init static int init_bts_trace(void) | ||
192 | { | ||
193 | return register_tracer(&bts_tracer); | ||
194 | } | ||
195 | device_initcall(init_bts_trace); | ||