aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/arch
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2015-07-17 12:33:43 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2015-08-21 10:34:10 -0400
commitd0170af7004dce9cd90b749842c37e379476cbc8 (patch)
treee30cf4c15b6b565cfe969b1613e297343b5b2932 /tools/perf/arch
parent6f56e9cf581c6cedcaea3eb69444b169867ccf3d (diff)
perf tools: Add Intel BTS support
Intel BTS support fits within the new auxtrace infrastructure. Recording is supporting by identifying the Intel BTS PMU, parsing options and setting up events. Decoding is supported by queuing up trace data by thread and then decoding synchronously delivering synthesized event samples into the session processing for tools to consume. Committer note: E.g: [root@felicio ~]# perf record --per-thread -e intel_bts// ls anaconda-ks.cfg apctest.output bin kernel-rt-3.10.0-298.rt56.171.el7.x86_64.rpm libexec lock_page.bpf.c perf.data perf.data.old [ perf record: Woken up 3 times to write data ] [ perf record: Captured and wrote 4.367 MB perf.data ] [root@felicio ~]# perf evlist -v intel_bts//: type: 6, size: 112, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, enable_on_exec: 1, sample_id_all: 1, exclude_guest: 1 dummy:u: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1 [root@felicio ~]# perf script # the navigate in the pager to some interesting place: ls 1843 1 branches: ffffffff810a60cb flush_signal_handlers ([kernel.kallsyms]) => ffffffff8121a522 setup_new_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8121a529 setup_new_exec ([kernel.kallsyms]) => ffffffff8122fa30 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa5d do_close_on_exec ([kernel.kallsyms]) => ffffffff81767ae0 _raw_spin_lock ([kernel.kallsyms]) ls 1843 1 branches: ffffffff81767af4 _raw_spin_lock ([kernel.kallsyms]) => ffffffff8122fa62 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fac9 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fad2 do_close_on_exec ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8122fadd do_close_on_exec ([kernel.kallsyms]) => ffffffff8120fc80 filp_close ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8120fcaf filp_close ([kernel.kallsyms]) => ffffffff8120fcb6 filp_close ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8120fcc2 filp_close ([kernel.kallsyms]) => ffffffff812547f0 dnotify_flush ([kernel.kallsyms]) ls 1843 1 branches: ffffffff81254823 dnotify_flush ([kernel.kallsyms]) => ffffffff8120fcc7 filp_close ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8120fccd filp_close ([kernel.kallsyms]) => ffffffff81261790 locks_remove_posix ([kernel.kallsyms]) ls 1843 1 branches: ffffffff812617a3 locks_remove_posix ([kernel.kallsyms]) => ffffffff812617b9 locks_remove_posix ([kernel.kallsyms]) ls 1843 1 branches: ffffffff812617b9 locks_remove_posix ([kernel.kallsyms]) => ffffffff8120fcd2 filp_close ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8120fcd5 filp_close ([kernel.kallsyms]) => ffffffff812142c0 fput ([kernel.kallsyms]) ls 1843 1 branches: ffffffff812142d6 fput ([kernel.kallsyms]) => ffffffff812142df fput ([kernel.kallsyms]) ls 1843 1 branches: ffffffff8121430c fput ([kernel.kallsyms]) => ffffffff810b6580 task_work_add ([kernel.kallsyms]) ls 1843 1 branches: ffffffff810b65ad task_work_add ([kernel.kallsyms]) => ffffffff810b65b1 task_work_add ([kernel.kallsyms]) ls 1843 1 branches: ffffffff810b65c1 task_work_add ([kernel.kallsyms]) => ffffffff810bc710 kick_process ([kernel.kallsyms]) ls 1843 1 branches: ffffffff810bc725 kick_process ([kernel.kallsyms]) => ffffffff810bc742 kick_process ([kernel.kallsyms]) ls 1843 1 branches: ffffffff810bc742 kick_process ([kernel.kallsyms]) => ffffffff810b65c6 task_work_add ([kernel.kallsyms]) ls 1843 1 branches: ffffffff810b65c9 task_work_add ([kernel.kallsyms]) => ffffffff81214311 fput ([kernel.kallsyms]) Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/1437150840-31811-9-git-send-email-adrian.hunter@intel.com [ Merged sample->time fix for bug found after first round of testing on slightly older kernel ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/arch')
-rw-r--r--tools/perf/arch/x86/util/Build1
-rw-r--r--tools/perf/arch/x86/util/auxtrace.c49
-rw-r--r--tools/perf/arch/x86/util/intel-bts.c458
-rw-r--r--tools/perf/arch/x86/util/pmu.c3
4 files changed, 509 insertions, 2 deletions
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index a8be9f9d0462..2c55e1b336c5 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -10,3 +10,4 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
10 10
11libperf-$(CONFIG_AUXTRACE) += auxtrace.o 11libperf-$(CONFIG_AUXTRACE) += auxtrace.o
12libperf-$(CONFIG_AUXTRACE) += intel-pt.o 12libperf-$(CONFIG_AUXTRACE) += intel-pt.o
13libperf-$(CONFIG_AUXTRACE) += intel-bts.o
diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util/auxtrace.c
index e7654b506312..7a7805583e3f 100644
--- a/tools/perf/arch/x86/util/auxtrace.c
+++ b/tools/perf/arch/x86/util/auxtrace.c
@@ -13,11 +13,56 @@
13 * 13 *
14 */ 14 */
15 15
16#include <stdbool.h>
17
16#include "../../util/header.h" 18#include "../../util/header.h"
19#include "../../util/debug.h"
20#include "../../util/pmu.h"
17#include "../../util/auxtrace.h" 21#include "../../util/auxtrace.h"
18#include "../../util/intel-pt.h" 22#include "../../util/intel-pt.h"
23#include "../../util/intel-bts.h"
24#include "../../util/evlist.h"
25
26static
27struct auxtrace_record *auxtrace_record__init_intel(struct perf_evlist *evlist,
28 int *err)
29{
30 struct perf_pmu *intel_pt_pmu;
31 struct perf_pmu *intel_bts_pmu;
32 struct perf_evsel *evsel;
33 bool found_pt = false;
34 bool found_bts = false;
35
36 intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
37 intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
38
39 if (evlist) {
40 evlist__for_each(evlist, evsel) {
41 if (intel_pt_pmu &&
42 evsel->attr.type == intel_pt_pmu->type)
43 found_pt = true;
44 if (intel_bts_pmu &&
45 evsel->attr.type == intel_bts_pmu->type)
46 found_bts = true;
47 }
48 }
49
50 if (found_pt && found_bts) {
51 pr_err("intel_pt and intel_bts may not be used together\n");
52 *err = -EINVAL;
53 return NULL;
54 }
55
56 if (found_pt)
57 return intel_pt_recording_init(err);
58
59 if (found_bts)
60 return intel_bts_recording_init(err);
19 61
20struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, 62 return NULL;
63}
64
65struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
21 int *err) 66 int *err)
22{ 67{
23 char buffer[64]; 68 char buffer[64];
@@ -32,7 +77,7 @@ struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist __maybe
32 } 77 }
33 78
34 if (!strncmp(buffer, "GenuineIntel,", 13)) 79 if (!strncmp(buffer, "GenuineIntel,", 13))
35 return intel_pt_recording_init(err); 80 return auxtrace_record__init_intel(evlist, err);
36 81
37 return NULL; 82 return NULL;
38} 83}
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
new file mode 100644
index 000000000000..9b94ce520917
--- /dev/null
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -0,0 +1,458 @@
1/*
2 * intel-bts.c: Intel Processor Trace support
3 * Copyright (c) 2013-2015, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 */
15
16#include <linux/kernel.h>
17#include <linux/types.h>
18#include <linux/bitops.h>
19#include <linux/log2.h>
20
21#include "../../util/cpumap.h"
22#include "../../util/evsel.h"
23#include "../../util/evlist.h"
24#include "../../util/session.h"
25#include "../../util/util.h"
26#include "../../util/pmu.h"
27#include "../../util/debug.h"
28#include "../../util/tsc.h"
29#include "../../util/auxtrace.h"
30#include "../../util/intel-bts.h"
31
32#define KiB(x) ((x) * 1024)
33#define MiB(x) ((x) * 1024 * 1024)
34#define KiB_MASK(x) (KiB(x) - 1)
35#define MiB_MASK(x) (MiB(x) - 1)
36
37#define INTEL_BTS_DFLT_SAMPLE_SIZE KiB(4)
38
39#define INTEL_BTS_MAX_SAMPLE_SIZE KiB(60)
40
41struct intel_bts_snapshot_ref {
42 void *ref_buf;
43 size_t ref_offset;
44 bool wrapped;
45};
46
47struct intel_bts_recording {
48 struct auxtrace_record itr;
49 struct perf_pmu *intel_bts_pmu;
50 struct perf_evlist *evlist;
51 bool snapshot_mode;
52 size_t snapshot_size;
53 int snapshot_ref_cnt;
54 struct intel_bts_snapshot_ref *snapshot_refs;
55};
56
57struct branch {
58 u64 from;
59 u64 to;
60 u64 misc;
61};
62
63static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
64{
65 return INTEL_BTS_AUXTRACE_PRIV_SIZE;
66}
67
68static int intel_bts_info_fill(struct auxtrace_record *itr,
69 struct perf_session *session,
70 struct auxtrace_info_event *auxtrace_info,
71 size_t priv_size)
72{
73 struct intel_bts_recording *btsr =
74 container_of(itr, struct intel_bts_recording, itr);
75 struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
76 struct perf_event_mmap_page *pc;
77 struct perf_tsc_conversion tc = { .time_mult = 0, };
78 bool cap_user_time_zero = false;
79 int err;
80
81 if (priv_size != INTEL_BTS_AUXTRACE_PRIV_SIZE)
82 return -EINVAL;
83
84 if (!session->evlist->nr_mmaps)
85 return -EINVAL;
86
87 pc = session->evlist->mmap[0].base;
88 if (pc) {
89 err = perf_read_tsc_conversion(pc, &tc);
90 if (err) {
91 if (err != -EOPNOTSUPP)
92 return err;
93 } else {
94 cap_user_time_zero = tc.time_mult != 0;
95 }
96 if (!cap_user_time_zero)
97 ui__warning("Intel BTS: TSC not available\n");
98 }
99
100 auxtrace_info->type = PERF_AUXTRACE_INTEL_BTS;
101 auxtrace_info->priv[INTEL_BTS_PMU_TYPE] = intel_bts_pmu->type;
102 auxtrace_info->priv[INTEL_BTS_TIME_SHIFT] = tc.time_shift;
103 auxtrace_info->priv[INTEL_BTS_TIME_MULT] = tc.time_mult;
104 auxtrace_info->priv[INTEL_BTS_TIME_ZERO] = tc.time_zero;
105 auxtrace_info->priv[INTEL_BTS_CAP_USER_TIME_ZERO] = cap_user_time_zero;
106 auxtrace_info->priv[INTEL_BTS_SNAPSHOT_MODE] = btsr->snapshot_mode;
107
108 return 0;
109}
110
111static int intel_bts_recording_options(struct auxtrace_record *itr,
112 struct perf_evlist *evlist,
113 struct record_opts *opts)
114{
115 struct intel_bts_recording *btsr =
116 container_of(itr, struct intel_bts_recording, itr);
117 struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
118 struct perf_evsel *evsel, *intel_bts_evsel = NULL;
119 const struct cpu_map *cpus = evlist->cpus;
120 bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
121
122 btsr->evlist = evlist;
123 btsr->snapshot_mode = opts->auxtrace_snapshot_mode;
124
125 evlist__for_each(evlist, evsel) {
126 if (evsel->attr.type == intel_bts_pmu->type) {
127 if (intel_bts_evsel) {
128 pr_err("There may be only one " INTEL_BTS_PMU_NAME " event\n");
129 return -EINVAL;
130 }
131 evsel->attr.freq = 0;
132 evsel->attr.sample_period = 1;
133 intel_bts_evsel = evsel;
134 opts->full_auxtrace = true;
135 }
136 }
137
138 if (opts->auxtrace_snapshot_mode && !opts->full_auxtrace) {
139 pr_err("Snapshot mode (-S option) requires " INTEL_BTS_PMU_NAME " PMU event (-e " INTEL_BTS_PMU_NAME ")\n");
140 return -EINVAL;
141 }
142
143 if (!opts->full_auxtrace)
144 return 0;
145
146 if (opts->full_auxtrace && !cpu_map__empty(cpus)) {
147 pr_err(INTEL_BTS_PMU_NAME " does not support per-cpu recording\n");
148 return -EINVAL;
149 }
150
151 /* Set default sizes for snapshot mode */
152 if (opts->auxtrace_snapshot_mode) {
153 if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
154 if (privileged) {
155 opts->auxtrace_mmap_pages = MiB(4) / page_size;
156 } else {
157 opts->auxtrace_mmap_pages = KiB(128) / page_size;
158 if (opts->mmap_pages == UINT_MAX)
159 opts->mmap_pages = KiB(256) / page_size;
160 }
161 } else if (!opts->auxtrace_mmap_pages && !privileged &&
162 opts->mmap_pages == UINT_MAX) {
163 opts->mmap_pages = KiB(256) / page_size;
164 }
165 if (!opts->auxtrace_snapshot_size)
166 opts->auxtrace_snapshot_size =
167 opts->auxtrace_mmap_pages * (size_t)page_size;
168 if (!opts->auxtrace_mmap_pages) {
169 size_t sz = opts->auxtrace_snapshot_size;
170
171 sz = round_up(sz, page_size) / page_size;
172 opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
173 }
174 if (opts->auxtrace_snapshot_size >
175 opts->auxtrace_mmap_pages * (size_t)page_size) {
176 pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
177 opts->auxtrace_snapshot_size,
178 opts->auxtrace_mmap_pages * (size_t)page_size);
179 return -EINVAL;
180 }
181 if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
182 pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
183 return -EINVAL;
184 }
185 pr_debug2("Intel BTS snapshot size: %zu\n",
186 opts->auxtrace_snapshot_size);
187 }
188
189 /* Set default sizes for full trace mode */
190 if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
191 if (privileged) {
192 opts->auxtrace_mmap_pages = MiB(4) / page_size;
193 } else {
194 opts->auxtrace_mmap_pages = KiB(128) / page_size;
195 if (opts->mmap_pages == UINT_MAX)
196 opts->mmap_pages = KiB(256) / page_size;
197 }
198 }
199
200 /* Validate auxtrace_mmap_pages */
201 if (opts->auxtrace_mmap_pages) {
202 size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
203 size_t min_sz;
204
205 if (opts->auxtrace_snapshot_mode)
206 min_sz = KiB(4);
207 else
208 min_sz = KiB(8);
209
210 if (sz < min_sz || !is_power_of_2(sz)) {
211 pr_err("Invalid mmap size for Intel BTS: must be at least %zuKiB and a power of 2\n",
212 min_sz / 1024);
213 return -EINVAL;
214 }
215 }
216
217 if (intel_bts_evsel) {
218 /*
219 * To obtain the auxtrace buffer file descriptor, the auxtrace event
220 * must come first.
221 */
222 perf_evlist__to_front(evlist, intel_bts_evsel);
223 /*
224 * In the case of per-cpu mmaps, we need the CPU on the
225 * AUX event.
226 */
227 if (!cpu_map__empty(cpus))
228 perf_evsel__set_sample_bit(intel_bts_evsel, CPU);
229 }
230
231 /* Add dummy event to keep tracking */
232 if (opts->full_auxtrace) {
233 struct perf_evsel *tracking_evsel;
234 int err;
235
236 err = parse_events(evlist, "dummy:u", NULL);
237 if (err)
238 return err;
239
240 tracking_evsel = perf_evlist__last(evlist);
241
242 perf_evlist__set_tracking_event(evlist, tracking_evsel);
243
244 tracking_evsel->attr.freq = 0;
245 tracking_evsel->attr.sample_period = 1;
246 }
247
248 return 0;
249}
250
251static int intel_bts_parse_snapshot_options(struct auxtrace_record *itr,
252 struct record_opts *opts,
253 const char *str)
254{
255 struct intel_bts_recording *btsr =
256 container_of(itr, struct intel_bts_recording, itr);
257 unsigned long long snapshot_size = 0;
258 char *endptr;
259
260 if (str) {
261 snapshot_size = strtoull(str, &endptr, 0);
262 if (*endptr || snapshot_size > SIZE_MAX)
263 return -1;
264 }
265
266 opts->auxtrace_snapshot_mode = true;
267 opts->auxtrace_snapshot_size = snapshot_size;
268
269 btsr->snapshot_size = snapshot_size;
270
271 return 0;
272}
273
274static u64 intel_bts_reference(struct auxtrace_record *itr __maybe_unused)
275{
276 return rdtsc();
277}
278
279static int intel_bts_alloc_snapshot_refs(struct intel_bts_recording *btsr,
280 int idx)
281{
282 const size_t sz = sizeof(struct intel_bts_snapshot_ref);
283 int cnt = btsr->snapshot_ref_cnt, new_cnt = cnt * 2;
284 struct intel_bts_snapshot_ref *refs;
285
286 if (!new_cnt)
287 new_cnt = 16;
288
289 while (new_cnt <= idx)
290 new_cnt *= 2;
291
292 refs = calloc(new_cnt, sz);
293 if (!refs)
294 return -ENOMEM;
295
296 memcpy(refs, btsr->snapshot_refs, cnt * sz);
297
298 btsr->snapshot_refs = refs;
299 btsr->snapshot_ref_cnt = new_cnt;
300
301 return 0;
302}
303
304static void intel_bts_free_snapshot_refs(struct intel_bts_recording *btsr)
305{
306 int i;
307
308 for (i = 0; i < btsr->snapshot_ref_cnt; i++)
309 zfree(&btsr->snapshot_refs[i].ref_buf);
310 zfree(&btsr->snapshot_refs);
311}
312
313static void intel_bts_recording_free(struct auxtrace_record *itr)
314{
315 struct intel_bts_recording *btsr =
316 container_of(itr, struct intel_bts_recording, itr);
317
318 intel_bts_free_snapshot_refs(btsr);
319 free(btsr);
320}
321
322static int intel_bts_snapshot_start(struct auxtrace_record *itr)
323{
324 struct intel_bts_recording *btsr =
325 container_of(itr, struct intel_bts_recording, itr);
326 struct perf_evsel *evsel;
327
328 evlist__for_each(btsr->evlist, evsel) {
329 if (evsel->attr.type == btsr->intel_bts_pmu->type)
330 return perf_evlist__disable_event(btsr->evlist, evsel);
331 }
332 return -EINVAL;
333}
334
335static int intel_bts_snapshot_finish(struct auxtrace_record *itr)
336{
337 struct intel_bts_recording *btsr =
338 container_of(itr, struct intel_bts_recording, itr);
339 struct perf_evsel *evsel;
340
341 evlist__for_each(btsr->evlist, evsel) {
342 if (evsel->attr.type == btsr->intel_bts_pmu->type)
343 return perf_evlist__enable_event(btsr->evlist, evsel);
344 }
345 return -EINVAL;
346}
347
348static bool intel_bts_first_wrap(u64 *data, size_t buf_size)
349{
350 int i, a, b;
351
352 b = buf_size >> 3;
353 a = b - 512;
354 if (a < 0)
355 a = 0;
356
357 for (i = a; i < b; i++) {
358 if (data[i])
359 return true;
360 }
361
362 return false;
363}
364
365static int intel_bts_find_snapshot(struct auxtrace_record *itr, int idx,
366 struct auxtrace_mmap *mm, unsigned char *data,
367 u64 *head, u64 *old)
368{
369 struct intel_bts_recording *btsr =
370 container_of(itr, struct intel_bts_recording, itr);
371 bool wrapped;
372 int err;
373
374 pr_debug3("%s: mmap index %d old head %zu new head %zu\n",
375 __func__, idx, (size_t)*old, (size_t)*head);
376
377 if (idx >= btsr->snapshot_ref_cnt) {
378 err = intel_bts_alloc_snapshot_refs(btsr, idx);
379 if (err)
380 goto out_err;
381 }
382
383 wrapped = btsr->snapshot_refs[idx].wrapped;
384 if (!wrapped && intel_bts_first_wrap((u64 *)data, mm->len)) {
385 btsr->snapshot_refs[idx].wrapped = true;
386 wrapped = true;
387 }
388
389 /*
390 * In full trace mode 'head' continually increases. However in snapshot
391 * mode 'head' is an offset within the buffer. Here 'old' and 'head'
392 * are adjusted to match the full trace case which expects that 'old' is
393 * always less than 'head'.
394 */
395 if (wrapped) {
396 *old = *head;
397 *head += mm->len;
398 } else {
399 if (mm->mask)
400 *old &= mm->mask;
401 else
402 *old %= mm->len;
403 if (*old > *head)
404 *head += mm->len;
405 }
406
407 pr_debug3("%s: wrap-around %sdetected, adjusted old head %zu adjusted new head %zu\n",
408 __func__, wrapped ? "" : "not ", (size_t)*old, (size_t)*head);
409
410 return 0;
411
412out_err:
413 pr_err("%s: failed, error %d\n", __func__, err);
414 return err;
415}
416
417static int intel_bts_read_finish(struct auxtrace_record *itr, int idx)
418{
419 struct intel_bts_recording *btsr =
420 container_of(itr, struct intel_bts_recording, itr);
421 struct perf_evsel *evsel;
422
423 evlist__for_each(btsr->evlist, evsel) {
424 if (evsel->attr.type == btsr->intel_bts_pmu->type)
425 return perf_evlist__enable_event_idx(btsr->evlist,
426 evsel, idx);
427 }
428 return -EINVAL;
429}
430
431struct auxtrace_record *intel_bts_recording_init(int *err)
432{
433 struct perf_pmu *intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
434 struct intel_bts_recording *btsr;
435
436 if (!intel_bts_pmu)
437 return NULL;
438
439 btsr = zalloc(sizeof(struct intel_bts_recording));
440 if (!btsr) {
441 *err = -ENOMEM;
442 return NULL;
443 }
444
445 btsr->intel_bts_pmu = intel_bts_pmu;
446 btsr->itr.recording_options = intel_bts_recording_options;
447 btsr->itr.info_priv_size = intel_bts_info_priv_size;
448 btsr->itr.info_fill = intel_bts_info_fill;
449 btsr->itr.free = intel_bts_recording_free;
450 btsr->itr.snapshot_start = intel_bts_snapshot_start;
451 btsr->itr.snapshot_finish = intel_bts_snapshot_finish;
452 btsr->itr.find_snapshot = intel_bts_find_snapshot;
453 btsr->itr.parse_snapshot_options = intel_bts_parse_snapshot_options;
454 btsr->itr.reference = intel_bts_reference;
455 btsr->itr.read_finish = intel_bts_read_finish;
456 btsr->itr.alignment = sizeof(struct branch);
457 return &btsr->itr;
458}
diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c
index fd11cc3ce780..79fe07158d00 100644
--- a/tools/perf/arch/x86/util/pmu.c
+++ b/tools/perf/arch/x86/util/pmu.c
@@ -3,6 +3,7 @@
3#include <linux/perf_event.h> 3#include <linux/perf_event.h>
4 4
5#include "../../util/intel-pt.h" 5#include "../../util/intel-pt.h"
6#include "../../util/intel-bts.h"
6#include "../../util/pmu.h" 7#include "../../util/pmu.h"
7 8
8struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) 9struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
@@ -10,6 +11,8 @@ struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __mayb
10#ifdef HAVE_AUXTRACE_SUPPORT 11#ifdef HAVE_AUXTRACE_SUPPORT
11 if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) 12 if (!strcmp(pmu->name, INTEL_PT_PMU_NAME))
12 return intel_pt_pmu_default_config(pmu); 13 return intel_pt_pmu_default_config(pmu);
14 if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME))
15 pmu->selectable = true;
13#endif 16#endif
14 return NULL; 17 return NULL;
15} 18}