diff options
author | Markus Metzger <markut.t.metzger@intel.com> | 2008-12-11 07:53:26 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-12 02:08:14 -0500 |
commit | a93751cab71d63126687551823ed3e70cd85854a (patch) | |
tree | 665e9bf274c457edd672ad44320fad962e6c16f5 /kernel/trace/trace_hw_branches.c | |
parent | c2724775ce57c98b8af9694857b941dc61056516 (diff) |
x86, bts, ftrace: adapt the hw-branch-tracer to the ds.c interface
Impact: restructure code, cleanup
Remove BTS bits from the hw-branch-tracer (renamed from bts-tracer) and
use the ds interface.
Signed-off-by: Markus Metzger <markut.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_hw_branches.c')
-rw-r--r-- | kernel/trace/trace_hw_branches.c | 205 |
1 files changed, 205 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..ee29e012aa97 --- /dev/null +++ b/kernel/trace/trace_hw_branches.c | |||
@@ -0,0 +1,205 @@ | |||
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_reset(struct trace_array *tr) | ||
29 | { | ||
30 | int cpu; | ||
31 | |||
32 | tr->time_start = ftrace_now(tr->cpu); | ||
33 | |||
34 | for_each_online_cpu(cpu) | ||
35 | tracing_reset(tr, cpu); | ||
36 | } | ||
37 | |||
38 | static void bts_trace_start_cpu(void *arg) | ||
39 | { | ||
40 | if (this_tracer) | ||
41 | ds_release_bts(this_tracer); | ||
42 | |||
43 | this_tracer = | ||
44 | ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS, | ||
45 | /* ovfl = */ NULL, /* th = */ (size_t)-1, | ||
46 | BTS_KERNEL); | ||
47 | if (IS_ERR(this_tracer)) { | ||
48 | this_tracer = NULL; | ||
49 | return; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | static void bts_trace_start(struct trace_array *tr) | ||
54 | { | ||
55 | int cpu; | ||
56 | |||
57 | bts_trace_reset(tr); | ||
58 | |||
59 | for_each_cpu_mask(cpu, cpu_possible_map) | ||
60 | smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1); | ||
61 | } | ||
62 | |||
63 | static void bts_trace_stop_cpu(void *arg) | ||
64 | { | ||
65 | if (this_tracer) { | ||
66 | ds_release_bts(this_tracer); | ||
67 | this_tracer = NULL; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static void bts_trace_stop(struct trace_array *tr) | ||
72 | { | ||
73 | int cpu; | ||
74 | |||
75 | for_each_cpu_mask(cpu, cpu_possible_map) | ||
76 | smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1); | ||
77 | } | ||
78 | |||
79 | static int bts_trace_init(struct trace_array *tr) | ||
80 | { | ||
81 | bts_trace_reset(tr); | ||
82 | bts_trace_start(tr); | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static void bts_trace_print_header(struct seq_file *m) | ||
88 | { | ||
89 | seq_puts(m, | ||
90 | "# CPU# FROM TO FUNCTION\n"); | ||
91 | seq_puts(m, | ||
92 | "# | | | |\n"); | ||
93 | } | ||
94 | |||
95 | static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | ||
96 | { | ||
97 | struct trace_entry *entry = iter->ent; | ||
98 | struct trace_seq *seq = &iter->seq; | ||
99 | struct hw_branch_entry *it; | ||
100 | |||
101 | trace_assign_type(it, entry); | ||
102 | |||
103 | if (entry->type == TRACE_HW_BRANCHES) { | ||
104 | if (trace_seq_printf(seq, "%4d ", entry->cpu) && | ||
105 | trace_seq_printf(seq, "0x%016llx -> 0x%016llx ", | ||
106 | it->from, it->to) && | ||
107 | (!it->from || | ||
108 | seq_print_ip_sym(seq, it->from, /* sym_flags = */ 0)) && | ||
109 | trace_seq_printf(seq, "\n")) | ||
110 | return TRACE_TYPE_HANDLED; | ||
111 | return TRACE_TYPE_PARTIAL_LINE;; | ||
112 | } | ||
113 | return TRACE_TYPE_UNHANDLED; | ||
114 | } | ||
115 | |||
116 | void trace_hw_branch(struct trace_array *tr, u64 from, u64 to) | ||
117 | { | ||
118 | struct ring_buffer_event *event; | ||
119 | struct hw_branch_entry *entry; | ||
120 | unsigned long irq; | ||
121 | |||
122 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq); | ||
123 | if (!event) | ||
124 | return; | ||
125 | entry = ring_buffer_event_data(event); | ||
126 | tracing_generic_entry_update(&entry->ent, 0, from); | ||
127 | entry->ent.type = TRACE_HW_BRANCHES; | ||
128 | entry->ent.cpu = smp_processor_id(); | ||
129 | entry->from = from; | ||
130 | entry->to = to; | ||
131 | ring_buffer_unlock_commit(tr->buffer, event, irq); | ||
132 | } | ||
133 | |||
134 | static void trace_bts_at(struct trace_array *tr, | ||
135 | const struct bts_trace *trace, void *at) | ||
136 | { | ||
137 | struct bts_struct bts; | ||
138 | int err = 0; | ||
139 | |||
140 | WARN_ON_ONCE(!trace->read); | ||
141 | if (!trace->read) | ||
142 | return; | ||
143 | |||
144 | err = trace->read(this_tracer, at, &bts); | ||
145 | if (err < 0) | ||
146 | return; | ||
147 | |||
148 | switch (bts.qualifier) { | ||
149 | case BTS_BRANCH: | ||
150 | trace_hw_branch(tr, bts.variant.lbr.from, bts.variant.lbr.to); | ||
151 | break; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static void trace_bts_cpu(void *arg) | ||
156 | { | ||
157 | struct trace_array *tr = (struct trace_array *) arg; | ||
158 | const struct bts_trace *trace; | ||
159 | unsigned char *at; | ||
160 | |||
161 | if (!this_tracer) | ||
162 | return; | ||
163 | |||
164 | ds_suspend_bts(this_tracer); | ||
165 | trace = ds_read_bts(this_tracer); | ||
166 | if (!trace) | ||
167 | goto out; | ||
168 | |||
169 | for (at = trace->ds.top; (void *)at < trace->ds.end; | ||
170 | at += trace->ds.size) | ||
171 | trace_bts_at(tr, trace, at); | ||
172 | |||
173 | for (at = trace->ds.begin; (void *)at < trace->ds.top; | ||
174 | at += trace->ds.size) | ||
175 | trace_bts_at(tr, trace, at); | ||
176 | |||
177 | out: | ||
178 | ds_resume_bts(this_tracer); | ||
179 | } | ||
180 | |||
181 | static void trace_bts_prepare(struct trace_iterator *iter) | ||
182 | { | ||
183 | int cpu; | ||
184 | |||
185 | for_each_cpu_mask(cpu, cpu_possible_map) | ||
186 | smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1); | ||
187 | } | ||
188 | |||
189 | struct tracer bts_tracer __read_mostly = | ||
190 | { | ||
191 | .name = "hw-branch-tracer", | ||
192 | .init = bts_trace_init, | ||
193 | .reset = bts_trace_stop, | ||
194 | .print_header = bts_trace_print_header, | ||
195 | .print_line = bts_trace_print_line, | ||
196 | .start = bts_trace_start, | ||
197 | .stop = bts_trace_stop, | ||
198 | .open = trace_bts_prepare | ||
199 | }; | ||
200 | |||
201 | __init static int init_bts_trace(void) | ||
202 | { | ||
203 | return register_tracer(&bts_tracer); | ||
204 | } | ||
205 | device_initcall(init_bts_trace); | ||