aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/arch
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2015-07-17 12:33:41 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2015-08-17 10:11:36 -0400
commit90e457f7be0870052724b2d9c2c106e5847f2c19 (patch)
tree180c73f2e01bbc4211ae386d3368a09a1456d3f5 /tools/perf/arch
parentf4aa081949e7b6b01e711229c5a47ee3482a169c (diff)
perf tools: Add Intel PT support
Add support for Intel Processor Trace. Intel PT support fits within the new auxtrace infrastructure. Recording is supporting by identifying the Intel PT PMU, parsing options and setting up events. Decoding is supported by queuing up trace data by cpu or thread and then decoding synchronously delivering synthesized event samples into the session processing for tools to consume. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/1437150840-31811-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/arch')
-rw-r--r--tools/perf/arch/x86/util/Build2
-rw-r--r--tools/perf/arch/x86/util/intel-pt.c752
2 files changed, 754 insertions, 0 deletions
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index cfbccc4e3187..139608878888 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -6,3 +6,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
6 6
7libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o 7libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
8libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o 8libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
9
10libperf-$(CONFIG_AUXTRACE) += intel-pt.o
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
new file mode 100644
index 000000000000..da7d2c15e611
--- /dev/null
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -0,0 +1,752 @@
1/*
2 * intel_pt.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 <stdbool.h>
17#include <linux/kernel.h>
18#include <linux/types.h>
19#include <linux/bitops.h>
20#include <linux/log2.h>
21
22#include "../../perf.h"
23#include "../../util/session.h"
24#include "../../util/event.h"
25#include "../../util/evlist.h"
26#include "../../util/evsel.h"
27#include "../../util/cpumap.h"
28#include "../../util/parse-options.h"
29#include "../../util/parse-events.h"
30#include "../../util/pmu.h"
31#include "../../util/debug.h"
32#include "../../util/auxtrace.h"
33#include "../../util/tsc.h"
34#include "../../util/intel-pt.h"
35
36#define KiB(x) ((x) * 1024)
37#define MiB(x) ((x) * 1024 * 1024)
38#define KiB_MASK(x) (KiB(x) - 1)
39#define MiB_MASK(x) (MiB(x) - 1)
40
41#define INTEL_PT_DEFAULT_SAMPLE_SIZE KiB(4)
42
43#define INTEL_PT_MAX_SAMPLE_SIZE KiB(60)
44
45#define INTEL_PT_PSB_PERIOD_NEAR 256
46
47struct intel_pt_snapshot_ref {
48 void *ref_buf;
49 size_t ref_offset;
50 bool wrapped;
51};
52
53struct intel_pt_recording {
54 struct auxtrace_record itr;
55 struct perf_pmu *intel_pt_pmu;
56 int have_sched_switch;
57 struct perf_evlist *evlist;
58 bool snapshot_mode;
59 bool snapshot_init_done;
60 size_t snapshot_size;
61 size_t snapshot_ref_buf_size;
62 int snapshot_ref_cnt;
63 struct intel_pt_snapshot_ref *snapshot_refs;
64};
65
66static int intel_pt_parse_terms_with_default(struct list_head *formats,
67 const char *str,
68 u64 *config)
69{
70 struct list_head *terms;
71 struct perf_event_attr attr = { .size = 0, };
72 int err;
73
74 terms = malloc(sizeof(struct list_head));
75 if (!terms)
76 return -ENOMEM;
77
78 INIT_LIST_HEAD(terms);
79
80 err = parse_events_terms(terms, str);
81 if (err)
82 goto out_free;
83
84 attr.config = *config;
85 err = perf_pmu__config_terms(formats, &attr, terms, true, NULL);
86 if (err)
87 goto out_free;
88
89 *config = attr.config;
90out_free:
91 parse_events__free_terms(terms);
92 return err;
93}
94
95static int intel_pt_parse_terms(struct list_head *formats, const char *str,
96 u64 *config)
97{
98 *config = 0;
99 return intel_pt_parse_terms_with_default(formats, str, config);
100}
101
102static size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu __maybe_unused,
103 struct perf_evlist *evlist __maybe_unused)
104{
105 return 256;
106}
107
108static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu)
109{
110 u64 config;
111
112 intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &config);
113 return config;
114}
115
116static int intel_pt_parse_snapshot_options(struct auxtrace_record *itr,
117 struct record_opts *opts,
118 const char *str)
119{
120 struct intel_pt_recording *ptr =
121 container_of(itr, struct intel_pt_recording, itr);
122 unsigned long long snapshot_size = 0;
123 char *endptr;
124
125 if (str) {
126 snapshot_size = strtoull(str, &endptr, 0);
127 if (*endptr || snapshot_size > SIZE_MAX)
128 return -1;
129 }
130
131 opts->auxtrace_snapshot_mode = true;
132 opts->auxtrace_snapshot_size = snapshot_size;
133
134 ptr->snapshot_size = snapshot_size;
135
136 return 0;
137}
138
139struct perf_event_attr *
140intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
141{
142 struct perf_event_attr *attr;
143
144 attr = zalloc(sizeof(struct perf_event_attr));
145 if (!attr)
146 return NULL;
147
148 attr->config = intel_pt_default_config(intel_pt_pmu);
149
150 intel_pt_pmu->selectable = true;
151
152 return attr;
153}
154
155static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
156{
157 return INTEL_PT_AUXTRACE_PRIV_SIZE;
158}
159
160static int intel_pt_info_fill(struct auxtrace_record *itr,
161 struct perf_session *session,
162 struct auxtrace_info_event *auxtrace_info,
163 size_t priv_size)
164{
165 struct intel_pt_recording *ptr =
166 container_of(itr, struct intel_pt_recording, itr);
167 struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu;
168 struct perf_event_mmap_page *pc;
169 struct perf_tsc_conversion tc = { .time_mult = 0, };
170 bool cap_user_time_zero = false, per_cpu_mmaps;
171 u64 tsc_bit, noretcomp_bit;
172 int err;
173
174 if (priv_size != INTEL_PT_AUXTRACE_PRIV_SIZE)
175 return -EINVAL;
176
177 intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit);
178 intel_pt_parse_terms(&intel_pt_pmu->format, "noretcomp",
179 &noretcomp_bit);
180
181 if (!session->evlist->nr_mmaps)
182 return -EINVAL;
183
184 pc = session->evlist->mmap[0].base;
185 if (pc) {
186 err = perf_read_tsc_conversion(pc, &tc);
187 if (err) {
188 if (err != -EOPNOTSUPP)
189 return err;
190 } else {
191 cap_user_time_zero = tc.time_mult != 0;
192 }
193 if (!cap_user_time_zero)
194 ui__warning("Intel Processor Trace: TSC not available\n");
195 }
196
197 per_cpu_mmaps = !cpu_map__empty(session->evlist->cpus);
198
199 auxtrace_info->type = PERF_AUXTRACE_INTEL_PT;
200 auxtrace_info->priv[INTEL_PT_PMU_TYPE] = intel_pt_pmu->type;
201 auxtrace_info->priv[INTEL_PT_TIME_SHIFT] = tc.time_shift;
202 auxtrace_info->priv[INTEL_PT_TIME_MULT] = tc.time_mult;
203 auxtrace_info->priv[INTEL_PT_TIME_ZERO] = tc.time_zero;
204 auxtrace_info->priv[INTEL_PT_CAP_USER_TIME_ZERO] = cap_user_time_zero;
205 auxtrace_info->priv[INTEL_PT_TSC_BIT] = tsc_bit;
206 auxtrace_info->priv[INTEL_PT_NORETCOMP_BIT] = noretcomp_bit;
207 auxtrace_info->priv[INTEL_PT_HAVE_SCHED_SWITCH] = ptr->have_sched_switch;
208 auxtrace_info->priv[INTEL_PT_SNAPSHOT_MODE] = ptr->snapshot_mode;
209 auxtrace_info->priv[INTEL_PT_PER_CPU_MMAPS] = per_cpu_mmaps;
210
211 return 0;
212}
213
214static int intel_pt_track_switches(struct perf_evlist *evlist)
215{
216 const char *sched_switch = "sched:sched_switch";
217 struct perf_evsel *evsel;
218 int err;
219
220 if (!perf_evlist__can_select_event(evlist, sched_switch))
221 return -EPERM;
222
223 err = parse_events(evlist, sched_switch, NULL);
224 if (err) {
225 pr_debug2("%s: failed to parse %s, error %d\n",
226 __func__, sched_switch, err);
227 return err;
228 }
229
230 evsel = perf_evlist__last(evlist);
231
232 perf_evsel__set_sample_bit(evsel, CPU);
233 perf_evsel__set_sample_bit(evsel, TIME);
234
235 evsel->system_wide = true;
236 evsel->no_aux_samples = true;
237 evsel->immediate = true;
238
239 return 0;
240}
241
242static int intel_pt_recording_options(struct auxtrace_record *itr,
243 struct perf_evlist *evlist,
244 struct record_opts *opts)
245{
246 struct intel_pt_recording *ptr =
247 container_of(itr, struct intel_pt_recording, itr);
248 struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu;
249 bool have_timing_info;
250 struct perf_evsel *evsel, *intel_pt_evsel = NULL;
251 const struct cpu_map *cpus = evlist->cpus;
252 bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
253 u64 tsc_bit;
254
255 ptr->evlist = evlist;
256 ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
257
258 evlist__for_each(evlist, evsel) {
259 if (evsel->attr.type == intel_pt_pmu->type) {
260 if (intel_pt_evsel) {
261 pr_err("There may be only one " INTEL_PT_PMU_NAME " event\n");
262 return -EINVAL;
263 }
264 evsel->attr.freq = 0;
265 evsel->attr.sample_period = 1;
266 intel_pt_evsel = evsel;
267 opts->full_auxtrace = true;
268 }
269 }
270
271 if (opts->auxtrace_snapshot_mode && !opts->full_auxtrace) {
272 pr_err("Snapshot mode (-S option) requires " INTEL_PT_PMU_NAME " PMU event (-e " INTEL_PT_PMU_NAME ")\n");
273 return -EINVAL;
274 }
275
276 if (opts->use_clockid) {
277 pr_err("Cannot use clockid (-k option) with " INTEL_PT_PMU_NAME "\n");
278 return -EINVAL;
279 }
280
281 if (!opts->full_auxtrace)
282 return 0;
283
284 /* Set default sizes for snapshot mode */
285 if (opts->auxtrace_snapshot_mode) {
286 size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist);
287
288 if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
289 if (privileged) {
290 opts->auxtrace_mmap_pages = MiB(4) / page_size;
291 } else {
292 opts->auxtrace_mmap_pages = KiB(128) / page_size;
293 if (opts->mmap_pages == UINT_MAX)
294 opts->mmap_pages = KiB(256) / page_size;
295 }
296 } else if (!opts->auxtrace_mmap_pages && !privileged &&
297 opts->mmap_pages == UINT_MAX) {
298 opts->mmap_pages = KiB(256) / page_size;
299 }
300 if (!opts->auxtrace_snapshot_size)
301 opts->auxtrace_snapshot_size =
302 opts->auxtrace_mmap_pages * (size_t)page_size;
303 if (!opts->auxtrace_mmap_pages) {
304 size_t sz = opts->auxtrace_snapshot_size;
305
306 sz = round_up(sz, page_size) / page_size;
307 opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
308 }
309 if (opts->auxtrace_snapshot_size >
310 opts->auxtrace_mmap_pages * (size_t)page_size) {
311 pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
312 opts->auxtrace_snapshot_size,
313 opts->auxtrace_mmap_pages * (size_t)page_size);
314 return -EINVAL;
315 }
316 if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
317 pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
318 return -EINVAL;
319 }
320 pr_debug2("Intel PT snapshot size: %zu\n",
321 opts->auxtrace_snapshot_size);
322 if (psb_period &&
323 opts->auxtrace_snapshot_size <= psb_period +
324 INTEL_PT_PSB_PERIOD_NEAR)
325 ui__warning("Intel PT snapshot size (%zu) may be too small for PSB period (%zu)\n",
326 opts->auxtrace_snapshot_size, psb_period);
327 }
328
329 /* Set default sizes for full trace mode */
330 if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
331 if (privileged) {
332 opts->auxtrace_mmap_pages = MiB(4) / page_size;
333 } else {
334 opts->auxtrace_mmap_pages = KiB(128) / page_size;
335 if (opts->mmap_pages == UINT_MAX)
336 opts->mmap_pages = KiB(256) / page_size;
337 }
338 }
339
340 /* Validate auxtrace_mmap_pages */
341 if (opts->auxtrace_mmap_pages) {
342 size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
343 size_t min_sz;
344
345 if (opts->auxtrace_snapshot_mode)
346 min_sz = KiB(4);
347 else
348 min_sz = KiB(8);
349
350 if (sz < min_sz || !is_power_of_2(sz)) {
351 pr_err("Invalid mmap size for Intel Processor Trace: must be at least %zuKiB and a power of 2\n",
352 min_sz / 1024);
353 return -EINVAL;
354 }
355 }
356
357 intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit);
358
359 if (opts->full_auxtrace && (intel_pt_evsel->attr.config & tsc_bit))
360 have_timing_info = true;
361 else
362 have_timing_info = false;
363
364 /*
365 * Per-cpu recording needs sched_switch events to distinguish different
366 * threads.
367 */
368 if (have_timing_info && !cpu_map__empty(cpus)) {
369 int err;
370
371 err = intel_pt_track_switches(evlist);
372 if (err == -EPERM)
373 pr_debug2("Unable to select sched:sched_switch\n");
374 else if (err)
375 return err;
376 else
377 ptr->have_sched_switch = 1;
378 }
379
380 if (intel_pt_evsel) {
381 /*
382 * To obtain the auxtrace buffer file descriptor, the auxtrace
383 * event must come first.
384 */
385 perf_evlist__to_front(evlist, intel_pt_evsel);
386 /*
387 * In the case of per-cpu mmaps, we need the CPU on the
388 * AUX event.
389 */
390 if (!cpu_map__empty(cpus))
391 perf_evsel__set_sample_bit(intel_pt_evsel, CPU);
392 }
393
394 /* Add dummy event to keep tracking */
395 if (opts->full_auxtrace) {
396 struct perf_evsel *tracking_evsel;
397 int err;
398
399 err = parse_events(evlist, "dummy:u", NULL);
400 if (err)
401 return err;
402
403 tracking_evsel = perf_evlist__last(evlist);
404
405 perf_evlist__set_tracking_event(evlist, tracking_evsel);
406
407 tracking_evsel->attr.freq = 0;
408 tracking_evsel->attr.sample_period = 1;
409
410 /* In per-cpu case, always need the time of mmap events etc */
411 if (!cpu_map__empty(cpus))
412 perf_evsel__set_sample_bit(tracking_evsel, TIME);
413 }
414
415 /*
416 * Warn the user when we do not have enough information to decode i.e.
417 * per-cpu with no sched_switch (except workload-only).
418 */
419 if (!ptr->have_sched_switch && !cpu_map__empty(cpus) &&
420 !target__none(&opts->target))
421 ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n");
422
423 return 0;
424}
425
426static int intel_pt_snapshot_start(struct auxtrace_record *itr)
427{
428 struct intel_pt_recording *ptr =
429 container_of(itr, struct intel_pt_recording, itr);
430 struct perf_evsel *evsel;
431
432 evlist__for_each(ptr->evlist, evsel) {
433 if (evsel->attr.type == ptr->intel_pt_pmu->type)
434 return perf_evlist__disable_event(ptr->evlist, evsel);
435 }
436 return -EINVAL;
437}
438
439static int intel_pt_snapshot_finish(struct auxtrace_record *itr)
440{
441 struct intel_pt_recording *ptr =
442 container_of(itr, struct intel_pt_recording, itr);
443 struct perf_evsel *evsel;
444
445 evlist__for_each(ptr->evlist, evsel) {
446 if (evsel->attr.type == ptr->intel_pt_pmu->type)
447 return perf_evlist__enable_event(ptr->evlist, evsel);
448 }
449 return -EINVAL;
450}
451
452static int intel_pt_alloc_snapshot_refs(struct intel_pt_recording *ptr, int idx)
453{
454 const size_t sz = sizeof(struct intel_pt_snapshot_ref);
455 int cnt = ptr->snapshot_ref_cnt, new_cnt = cnt * 2;
456 struct intel_pt_snapshot_ref *refs;
457
458 if (!new_cnt)
459 new_cnt = 16;
460
461 while (new_cnt <= idx)
462 new_cnt *= 2;
463
464 refs = calloc(new_cnt, sz);
465 if (!refs)
466 return -ENOMEM;
467
468 memcpy(refs, ptr->snapshot_refs, cnt * sz);
469
470 ptr->snapshot_refs = refs;
471 ptr->snapshot_ref_cnt = new_cnt;
472
473 return 0;
474}
475
476static void intel_pt_free_snapshot_refs(struct intel_pt_recording *ptr)
477{
478 int i;
479
480 for (i = 0; i < ptr->snapshot_ref_cnt; i++)
481 zfree(&ptr->snapshot_refs[i].ref_buf);
482 zfree(&ptr->snapshot_refs);
483}
484
485static void intel_pt_recording_free(struct auxtrace_record *itr)
486{
487 struct intel_pt_recording *ptr =
488 container_of(itr, struct intel_pt_recording, itr);
489
490 intel_pt_free_snapshot_refs(ptr);
491 free(ptr);
492}
493
494static int intel_pt_alloc_snapshot_ref(struct intel_pt_recording *ptr, int idx,
495 size_t snapshot_buf_size)
496{
497 size_t ref_buf_size = ptr->snapshot_ref_buf_size;
498 void *ref_buf;
499
500 ref_buf = zalloc(ref_buf_size);
501 if (!ref_buf)
502 return -ENOMEM;
503
504 ptr->snapshot_refs[idx].ref_buf = ref_buf;
505 ptr->snapshot_refs[idx].ref_offset = snapshot_buf_size - ref_buf_size;
506
507 return 0;
508}
509
510static size_t intel_pt_snapshot_ref_buf_size(struct intel_pt_recording *ptr,
511 size_t snapshot_buf_size)
512{
513 const size_t max_size = 256 * 1024;
514 size_t buf_size = 0, psb_period;
515
516 if (ptr->snapshot_size <= 64 * 1024)
517 return 0;
518
519 psb_period = intel_pt_psb_period(ptr->intel_pt_pmu, ptr->evlist);
520 if (psb_period)
521 buf_size = psb_period * 2;
522
523 if (!buf_size || buf_size > max_size)
524 buf_size = max_size;
525
526 if (buf_size >= snapshot_buf_size)
527 return 0;
528
529 if (buf_size >= ptr->snapshot_size / 2)
530 return 0;
531
532 return buf_size;
533}
534
535static int intel_pt_snapshot_init(struct intel_pt_recording *ptr,
536 size_t snapshot_buf_size)
537{
538 if (ptr->snapshot_init_done)
539 return 0;
540
541 ptr->snapshot_init_done = true;
542
543 ptr->snapshot_ref_buf_size = intel_pt_snapshot_ref_buf_size(ptr,
544 snapshot_buf_size);
545
546 return 0;
547}
548
549/**
550 * intel_pt_compare_buffers - compare bytes in a buffer to a circular buffer.
551 * @buf1: first buffer
552 * @compare_size: number of bytes to compare
553 * @buf2: second buffer (a circular buffer)
554 * @offs2: offset in second buffer
555 * @buf2_size: size of second buffer
556 *
557 * The comparison allows for the possibility that the bytes to compare in the
558 * circular buffer are not contiguous. It is assumed that @compare_size <=
559 * @buf2_size. This function returns %false if the bytes are identical, %true
560 * otherwise.
561 */
562static bool intel_pt_compare_buffers(void *buf1, size_t compare_size,
563 void *buf2, size_t offs2, size_t buf2_size)
564{
565 size_t end2 = offs2 + compare_size, part_size;
566
567 if (end2 <= buf2_size)
568 return memcmp(buf1, buf2 + offs2, compare_size);
569
570 part_size = end2 - buf2_size;
571 if (memcmp(buf1, buf2 + offs2, part_size))
572 return true;
573
574 compare_size -= part_size;
575
576 return memcmp(buf1 + part_size, buf2, compare_size);
577}
578
579static bool intel_pt_compare_ref(void *ref_buf, size_t ref_offset,
580 size_t ref_size, size_t buf_size,
581 void *data, size_t head)
582{
583 size_t ref_end = ref_offset + ref_size;
584
585 if (ref_end > buf_size) {
586 if (head > ref_offset || head < ref_end - buf_size)
587 return true;
588 } else if (head > ref_offset && head < ref_end) {
589 return true;
590 }
591
592 return intel_pt_compare_buffers(ref_buf, ref_size, data, ref_offset,
593 buf_size);
594}
595
596static void intel_pt_copy_ref(void *ref_buf, size_t ref_size, size_t buf_size,
597 void *data, size_t head)
598{
599 if (head >= ref_size) {
600 memcpy(ref_buf, data + head - ref_size, ref_size);
601 } else {
602 memcpy(ref_buf, data, head);
603 ref_size -= head;
604 memcpy(ref_buf + head, data + buf_size - ref_size, ref_size);
605 }
606}
607
608static bool intel_pt_wrapped(struct intel_pt_recording *ptr, int idx,
609 struct auxtrace_mmap *mm, unsigned char *data,
610 u64 head)
611{
612 struct intel_pt_snapshot_ref *ref = &ptr->snapshot_refs[idx];
613 bool wrapped;
614
615 wrapped = intel_pt_compare_ref(ref->ref_buf, ref->ref_offset,
616 ptr->snapshot_ref_buf_size, mm->len,
617 data, head);
618
619 intel_pt_copy_ref(ref->ref_buf, ptr->snapshot_ref_buf_size, mm->len,
620 data, head);
621
622 return wrapped;
623}
624
625static bool intel_pt_first_wrap(u64 *data, size_t buf_size)
626{
627 int i, a, b;
628
629 b = buf_size >> 3;
630 a = b - 512;
631 if (a < 0)
632 a = 0;
633
634 for (i = a; i < b; i++) {
635 if (data[i])
636 return true;
637 }
638
639 return false;
640}
641
642static int intel_pt_find_snapshot(struct auxtrace_record *itr, int idx,
643 struct auxtrace_mmap *mm, unsigned char *data,
644 u64 *head, u64 *old)
645{
646 struct intel_pt_recording *ptr =
647 container_of(itr, struct intel_pt_recording, itr);
648 bool wrapped;
649 int err;
650
651 pr_debug3("%s: mmap index %d old head %zu new head %zu\n",
652 __func__, idx, (size_t)*old, (size_t)*head);
653
654 err = intel_pt_snapshot_init(ptr, mm->len);
655 if (err)
656 goto out_err;
657
658 if (idx >= ptr->snapshot_ref_cnt) {
659 err = intel_pt_alloc_snapshot_refs(ptr, idx);
660 if (err)
661 goto out_err;
662 }
663
664 if (ptr->snapshot_ref_buf_size) {
665 if (!ptr->snapshot_refs[idx].ref_buf) {
666 err = intel_pt_alloc_snapshot_ref(ptr, idx, mm->len);
667 if (err)
668 goto out_err;
669 }
670 wrapped = intel_pt_wrapped(ptr, idx, mm, data, *head);
671 } else {
672 wrapped = ptr->snapshot_refs[idx].wrapped;
673 if (!wrapped && intel_pt_first_wrap((u64 *)data, mm->len)) {
674 ptr->snapshot_refs[idx].wrapped = true;
675 wrapped = true;
676 }
677 }
678
679 /*
680 * In full trace mode 'head' continually increases. However in snapshot
681 * mode 'head' is an offset within the buffer. Here 'old' and 'head'
682 * are adjusted to match the full trace case which expects that 'old' is
683 * always less than 'head'.
684 */
685 if (wrapped) {
686 *old = *head;
687 *head += mm->len;
688 } else {
689 if (mm->mask)
690 *old &= mm->mask;
691 else
692 *old %= mm->len;
693 if (*old > *head)
694 *head += mm->len;
695 }
696
697 pr_debug3("%s: wrap-around %sdetected, adjusted old head %zu adjusted new head %zu\n",
698 __func__, wrapped ? "" : "not ", (size_t)*old, (size_t)*head);
699
700 return 0;
701
702out_err:
703 pr_err("%s: failed, error %d\n", __func__, err);
704 return err;
705}
706
707static u64 intel_pt_reference(struct auxtrace_record *itr __maybe_unused)
708{
709 return rdtsc();
710}
711
712static int intel_pt_read_finish(struct auxtrace_record *itr, int idx)
713{
714 struct intel_pt_recording *ptr =
715 container_of(itr, struct intel_pt_recording, itr);
716 struct perf_evsel *evsel;
717
718 evlist__for_each(ptr->evlist, evsel) {
719 if (evsel->attr.type == ptr->intel_pt_pmu->type)
720 return perf_evlist__enable_event_idx(ptr->evlist, evsel,
721 idx);
722 }
723 return -EINVAL;
724}
725
726struct auxtrace_record *intel_pt_recording_init(int *err)
727{
728 struct perf_pmu *intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
729 struct intel_pt_recording *ptr;
730
731 if (!intel_pt_pmu)
732 return NULL;
733
734 ptr = zalloc(sizeof(struct intel_pt_recording));
735 if (!ptr) {
736 *err = -ENOMEM;
737 return NULL;
738 }
739
740 ptr->intel_pt_pmu = intel_pt_pmu;
741 ptr->itr.recording_options = intel_pt_recording_options;
742 ptr->itr.info_priv_size = intel_pt_info_priv_size;
743 ptr->itr.info_fill = intel_pt_info_fill;
744 ptr->itr.free = intel_pt_recording_free;
745 ptr->itr.snapshot_start = intel_pt_snapshot_start;
746 ptr->itr.snapshot_finish = intel_pt_snapshot_finish;
747 ptr->itr.find_snapshot = intel_pt_find_snapshot;
748 ptr->itr.parse_snapshot_options = intel_pt_parse_snapshot_options;
749 ptr->itr.reference = intel_pt_reference;
750 ptr->itr.read_finish = intel_pt_read_finish;
751 return &ptr->itr;
752}