162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * intel-bts.c: Intel Processor Trace support
462306a36Sopenharmony_ci * Copyright (c) 2013-2015, Intel Corporation.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <errno.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/bitops.h>
1162306a36Sopenharmony_ci#include <linux/log2.h>
1262306a36Sopenharmony_ci#include <linux/zalloc.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "../../../util/cpumap.h"
1562306a36Sopenharmony_ci#include "../../../util/event.h"
1662306a36Sopenharmony_ci#include "../../../util/evsel.h"
1762306a36Sopenharmony_ci#include "../../../util/evlist.h"
1862306a36Sopenharmony_ci#include "../../../util/mmap.h"
1962306a36Sopenharmony_ci#include "../../../util/session.h"
2062306a36Sopenharmony_ci#include "../../../util/pmus.h"
2162306a36Sopenharmony_ci#include "../../../util/debug.h"
2262306a36Sopenharmony_ci#include "../../../util/record.h"
2362306a36Sopenharmony_ci#include "../../../util/tsc.h"
2462306a36Sopenharmony_ci#include "../../../util/auxtrace.h"
2562306a36Sopenharmony_ci#include "../../../util/intel-bts.h"
2662306a36Sopenharmony_ci#include <internal/lib.h> // page_size
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define KiB(x) ((x) * 1024)
2962306a36Sopenharmony_ci#define MiB(x) ((x) * 1024 * 1024)
3062306a36Sopenharmony_ci#define KiB_MASK(x) (KiB(x) - 1)
3162306a36Sopenharmony_ci#define MiB_MASK(x) (MiB(x) - 1)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct intel_bts_snapshot_ref {
3462306a36Sopenharmony_ci	void	*ref_buf;
3562306a36Sopenharmony_ci	size_t	ref_offset;
3662306a36Sopenharmony_ci	bool	wrapped;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct intel_bts_recording {
4062306a36Sopenharmony_ci	struct auxtrace_record		itr;
4162306a36Sopenharmony_ci	struct perf_pmu			*intel_bts_pmu;
4262306a36Sopenharmony_ci	struct evlist		*evlist;
4362306a36Sopenharmony_ci	bool				snapshot_mode;
4462306a36Sopenharmony_ci	size_t				snapshot_size;
4562306a36Sopenharmony_ci	int				snapshot_ref_cnt;
4662306a36Sopenharmony_ci	struct intel_bts_snapshot_ref	*snapshot_refs;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct branch {
5062306a36Sopenharmony_ci	u64 from;
5162306a36Sopenharmony_ci	u64 to;
5262306a36Sopenharmony_ci	u64 misc;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic size_t
5662306a36Sopenharmony_ciintel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
5762306a36Sopenharmony_ci			 struct evlist *evlist __maybe_unused)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	return INTEL_BTS_AUXTRACE_PRIV_SIZE;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int intel_bts_info_fill(struct auxtrace_record *itr,
6362306a36Sopenharmony_ci			       struct perf_session *session,
6462306a36Sopenharmony_ci			       struct perf_record_auxtrace_info *auxtrace_info,
6562306a36Sopenharmony_ci			       size_t priv_size)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
6862306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
6962306a36Sopenharmony_ci	struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
7062306a36Sopenharmony_ci	struct perf_event_mmap_page *pc;
7162306a36Sopenharmony_ci	struct perf_tsc_conversion tc = { .time_mult = 0, };
7262306a36Sopenharmony_ci	bool cap_user_time_zero = false;
7362306a36Sopenharmony_ci	int err;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (priv_size != INTEL_BTS_AUXTRACE_PRIV_SIZE)
7662306a36Sopenharmony_ci		return -EINVAL;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!session->evlist->core.nr_mmaps)
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	pc = session->evlist->mmap[0].core.base;
8262306a36Sopenharmony_ci	if (pc) {
8362306a36Sopenharmony_ci		err = perf_read_tsc_conversion(pc, &tc);
8462306a36Sopenharmony_ci		if (err) {
8562306a36Sopenharmony_ci			if (err != -EOPNOTSUPP)
8662306a36Sopenharmony_ci				return err;
8762306a36Sopenharmony_ci		} else {
8862306a36Sopenharmony_ci			cap_user_time_zero = tc.time_mult != 0;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		if (!cap_user_time_zero)
9162306a36Sopenharmony_ci			ui__warning("Intel BTS: TSC not available\n");
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	auxtrace_info->type = PERF_AUXTRACE_INTEL_BTS;
9562306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_PMU_TYPE] = intel_bts_pmu->type;
9662306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_TIME_SHIFT] = tc.time_shift;
9762306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_TIME_MULT] = tc.time_mult;
9862306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_TIME_ZERO] = tc.time_zero;
9962306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_CAP_USER_TIME_ZERO] = cap_user_time_zero;
10062306a36Sopenharmony_ci	auxtrace_info->priv[INTEL_BTS_SNAPSHOT_MODE] = btsr->snapshot_mode;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int intel_bts_recording_options(struct auxtrace_record *itr,
10662306a36Sopenharmony_ci				       struct evlist *evlist,
10762306a36Sopenharmony_ci				       struct record_opts *opts)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
11062306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
11162306a36Sopenharmony_ci	struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
11262306a36Sopenharmony_ci	struct evsel *evsel, *intel_bts_evsel = NULL;
11362306a36Sopenharmony_ci	const struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
11462306a36Sopenharmony_ci	bool privileged = perf_event_paranoid_check(-1);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (opts->auxtrace_sample_mode) {
11762306a36Sopenharmony_ci		pr_err("Intel BTS does not support AUX area sampling\n");
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	btsr->evlist = evlist;
12262306a36Sopenharmony_ci	btsr->snapshot_mode = opts->auxtrace_snapshot_mode;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	evlist__for_each_entry(evlist, evsel) {
12562306a36Sopenharmony_ci		if (evsel->core.attr.type == intel_bts_pmu->type) {
12662306a36Sopenharmony_ci			if (intel_bts_evsel) {
12762306a36Sopenharmony_ci				pr_err("There may be only one " INTEL_BTS_PMU_NAME " event\n");
12862306a36Sopenharmony_ci				return -EINVAL;
12962306a36Sopenharmony_ci			}
13062306a36Sopenharmony_ci			evsel->core.attr.freq = 0;
13162306a36Sopenharmony_ci			evsel->core.attr.sample_period = 1;
13262306a36Sopenharmony_ci			evsel->needs_auxtrace_mmap = true;
13362306a36Sopenharmony_ci			intel_bts_evsel = evsel;
13462306a36Sopenharmony_ci			opts->full_auxtrace = true;
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (opts->auxtrace_snapshot_mode && !opts->full_auxtrace) {
13962306a36Sopenharmony_ci		pr_err("Snapshot mode (-S option) requires " INTEL_BTS_PMU_NAME " PMU event (-e " INTEL_BTS_PMU_NAME ")\n");
14062306a36Sopenharmony_ci		return -EINVAL;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!opts->full_auxtrace)
14462306a36Sopenharmony_ci		return 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (opts->full_auxtrace && !perf_cpu_map__empty(cpus)) {
14762306a36Sopenharmony_ci		pr_err(INTEL_BTS_PMU_NAME " does not support per-cpu recording\n");
14862306a36Sopenharmony_ci		return -EINVAL;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Set default sizes for snapshot mode */
15262306a36Sopenharmony_ci	if (opts->auxtrace_snapshot_mode) {
15362306a36Sopenharmony_ci		if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
15462306a36Sopenharmony_ci			if (privileged) {
15562306a36Sopenharmony_ci				opts->auxtrace_mmap_pages = MiB(4) / page_size;
15662306a36Sopenharmony_ci			} else {
15762306a36Sopenharmony_ci				opts->auxtrace_mmap_pages = KiB(128) / page_size;
15862306a36Sopenharmony_ci				if (opts->mmap_pages == UINT_MAX)
15962306a36Sopenharmony_ci					opts->mmap_pages = KiB(256) / page_size;
16062306a36Sopenharmony_ci			}
16162306a36Sopenharmony_ci		} else if (!opts->auxtrace_mmap_pages && !privileged &&
16262306a36Sopenharmony_ci			   opts->mmap_pages == UINT_MAX) {
16362306a36Sopenharmony_ci			opts->mmap_pages = KiB(256) / page_size;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		if (!opts->auxtrace_snapshot_size)
16662306a36Sopenharmony_ci			opts->auxtrace_snapshot_size =
16762306a36Sopenharmony_ci				opts->auxtrace_mmap_pages * (size_t)page_size;
16862306a36Sopenharmony_ci		if (!opts->auxtrace_mmap_pages) {
16962306a36Sopenharmony_ci			size_t sz = opts->auxtrace_snapshot_size;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci			sz = round_up(sz, page_size) / page_size;
17262306a36Sopenharmony_ci			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		if (opts->auxtrace_snapshot_size >
17562306a36Sopenharmony_ci				opts->auxtrace_mmap_pages * (size_t)page_size) {
17662306a36Sopenharmony_ci			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
17762306a36Sopenharmony_ci			       opts->auxtrace_snapshot_size,
17862306a36Sopenharmony_ci			       opts->auxtrace_mmap_pages * (size_t)page_size);
17962306a36Sopenharmony_ci			return -EINVAL;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci		if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
18262306a36Sopenharmony_ci			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
18362306a36Sopenharmony_ci			return -EINVAL;
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci		pr_debug2("Intel BTS snapshot size: %zu\n",
18662306a36Sopenharmony_ci			  opts->auxtrace_snapshot_size);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Set default sizes for full trace mode */
19062306a36Sopenharmony_ci	if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
19162306a36Sopenharmony_ci		if (privileged) {
19262306a36Sopenharmony_ci			opts->auxtrace_mmap_pages = MiB(4) / page_size;
19362306a36Sopenharmony_ci		} else {
19462306a36Sopenharmony_ci			opts->auxtrace_mmap_pages = KiB(128) / page_size;
19562306a36Sopenharmony_ci			if (opts->mmap_pages == UINT_MAX)
19662306a36Sopenharmony_ci				opts->mmap_pages = KiB(256) / page_size;
19762306a36Sopenharmony_ci		}
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Validate auxtrace_mmap_pages */
20162306a36Sopenharmony_ci	if (opts->auxtrace_mmap_pages) {
20262306a36Sopenharmony_ci		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
20362306a36Sopenharmony_ci		size_t min_sz;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		if (opts->auxtrace_snapshot_mode)
20662306a36Sopenharmony_ci			min_sz = KiB(4);
20762306a36Sopenharmony_ci		else
20862306a36Sopenharmony_ci			min_sz = KiB(8);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if (sz < min_sz || !is_power_of_2(sz)) {
21162306a36Sopenharmony_ci			pr_err("Invalid mmap size for Intel BTS: must be at least %zuKiB and a power of 2\n",
21262306a36Sopenharmony_ci			       min_sz / 1024);
21362306a36Sopenharmony_ci			return -EINVAL;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (intel_bts_evsel) {
21862306a36Sopenharmony_ci		/*
21962306a36Sopenharmony_ci		 * To obtain the auxtrace buffer file descriptor, the auxtrace event
22062306a36Sopenharmony_ci		 * must come first.
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		evlist__to_front(evlist, intel_bts_evsel);
22362306a36Sopenharmony_ci		/*
22462306a36Sopenharmony_ci		 * In the case of per-cpu mmaps, we need the CPU on the
22562306a36Sopenharmony_ci		 * AUX event.
22662306a36Sopenharmony_ci		 */
22762306a36Sopenharmony_ci		if (!perf_cpu_map__empty(cpus))
22862306a36Sopenharmony_ci			evsel__set_sample_bit(intel_bts_evsel, CPU);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Add dummy event to keep tracking */
23262306a36Sopenharmony_ci	if (opts->full_auxtrace) {
23362306a36Sopenharmony_ci		struct evsel *tracking_evsel;
23462306a36Sopenharmony_ci		int err;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		err = parse_event(evlist, "dummy:u");
23762306a36Sopenharmony_ci		if (err)
23862306a36Sopenharmony_ci			return err;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		tracking_evsel = evlist__last(evlist);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		evlist__set_tracking_event(evlist, tracking_evsel);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		tracking_evsel->core.attr.freq = 0;
24562306a36Sopenharmony_ci		tracking_evsel->core.attr.sample_period = 1;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int intel_bts_parse_snapshot_options(struct auxtrace_record *itr,
25262306a36Sopenharmony_ci					    struct record_opts *opts,
25362306a36Sopenharmony_ci					    const char *str)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
25662306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
25762306a36Sopenharmony_ci	unsigned long long snapshot_size = 0;
25862306a36Sopenharmony_ci	char *endptr;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (str) {
26162306a36Sopenharmony_ci		snapshot_size = strtoull(str, &endptr, 0);
26262306a36Sopenharmony_ci		if (*endptr || snapshot_size > SIZE_MAX)
26362306a36Sopenharmony_ci			return -1;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	opts->auxtrace_snapshot_mode = true;
26762306a36Sopenharmony_ci	opts->auxtrace_snapshot_size = snapshot_size;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	btsr->snapshot_size = snapshot_size;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return 0;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic u64 intel_bts_reference(struct auxtrace_record *itr __maybe_unused)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	return rdtsc();
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int intel_bts_alloc_snapshot_refs(struct intel_bts_recording *btsr,
28062306a36Sopenharmony_ci					 int idx)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	const size_t sz = sizeof(struct intel_bts_snapshot_ref);
28362306a36Sopenharmony_ci	int cnt = btsr->snapshot_ref_cnt, new_cnt = cnt * 2;
28462306a36Sopenharmony_ci	struct intel_bts_snapshot_ref *refs;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!new_cnt)
28762306a36Sopenharmony_ci		new_cnt = 16;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	while (new_cnt <= idx)
29062306a36Sopenharmony_ci		new_cnt *= 2;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	refs = calloc(new_cnt, sz);
29362306a36Sopenharmony_ci	if (!refs)
29462306a36Sopenharmony_ci		return -ENOMEM;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	memcpy(refs, btsr->snapshot_refs, cnt * sz);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	btsr->snapshot_refs = refs;
29962306a36Sopenharmony_ci	btsr->snapshot_ref_cnt = new_cnt;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void intel_bts_free_snapshot_refs(struct intel_bts_recording *btsr)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	int i;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	for (i = 0; i < btsr->snapshot_ref_cnt; i++)
30962306a36Sopenharmony_ci		zfree(&btsr->snapshot_refs[i].ref_buf);
31062306a36Sopenharmony_ci	zfree(&btsr->snapshot_refs);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void intel_bts_recording_free(struct auxtrace_record *itr)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
31662306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	intel_bts_free_snapshot_refs(btsr);
31962306a36Sopenharmony_ci	free(btsr);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int intel_bts_snapshot_start(struct auxtrace_record *itr)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
32562306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
32662306a36Sopenharmony_ci	struct evsel *evsel;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	evlist__for_each_entry(btsr->evlist, evsel) {
32962306a36Sopenharmony_ci		if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
33062306a36Sopenharmony_ci			return evsel__disable(evsel);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	return -EINVAL;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int intel_bts_snapshot_finish(struct auxtrace_record *itr)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
33862306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
33962306a36Sopenharmony_ci	struct evsel *evsel;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	evlist__for_each_entry(btsr->evlist, evsel) {
34262306a36Sopenharmony_ci		if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
34362306a36Sopenharmony_ci			return evsel__enable(evsel);
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	return -EINVAL;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic bool intel_bts_first_wrap(u64 *data, size_t buf_size)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	int i, a, b;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	b = buf_size >> 3;
35362306a36Sopenharmony_ci	a = b - 512;
35462306a36Sopenharmony_ci	if (a < 0)
35562306a36Sopenharmony_ci		a = 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	for (i = a; i < b; i++) {
35862306a36Sopenharmony_ci		if (data[i])
35962306a36Sopenharmony_ci			return true;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return false;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int intel_bts_find_snapshot(struct auxtrace_record *itr, int idx,
36662306a36Sopenharmony_ci				   struct auxtrace_mmap *mm, unsigned char *data,
36762306a36Sopenharmony_ci				   u64 *head, u64 *old)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct intel_bts_recording *btsr =
37062306a36Sopenharmony_ci			container_of(itr, struct intel_bts_recording, itr);
37162306a36Sopenharmony_ci	bool wrapped;
37262306a36Sopenharmony_ci	int err;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	pr_debug3("%s: mmap index %d old head %zu new head %zu\n",
37562306a36Sopenharmony_ci		  __func__, idx, (size_t)*old, (size_t)*head);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (idx >= btsr->snapshot_ref_cnt) {
37862306a36Sopenharmony_ci		err = intel_bts_alloc_snapshot_refs(btsr, idx);
37962306a36Sopenharmony_ci		if (err)
38062306a36Sopenharmony_ci			goto out_err;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	wrapped = btsr->snapshot_refs[idx].wrapped;
38462306a36Sopenharmony_ci	if (!wrapped && intel_bts_first_wrap((u64 *)data, mm->len)) {
38562306a36Sopenharmony_ci		btsr->snapshot_refs[idx].wrapped = true;
38662306a36Sopenharmony_ci		wrapped = true;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * In full trace mode 'head' continually increases.  However in snapshot
39162306a36Sopenharmony_ci	 * mode 'head' is an offset within the buffer.  Here 'old' and 'head'
39262306a36Sopenharmony_ci	 * are adjusted to match the full trace case which expects that 'old' is
39362306a36Sopenharmony_ci	 * always less than 'head'.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	if (wrapped) {
39662306a36Sopenharmony_ci		*old = *head;
39762306a36Sopenharmony_ci		*head += mm->len;
39862306a36Sopenharmony_ci	} else {
39962306a36Sopenharmony_ci		if (mm->mask)
40062306a36Sopenharmony_ci			*old &= mm->mask;
40162306a36Sopenharmony_ci		else
40262306a36Sopenharmony_ci			*old %= mm->len;
40362306a36Sopenharmony_ci		if (*old > *head)
40462306a36Sopenharmony_ci			*head += mm->len;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	pr_debug3("%s: wrap-around %sdetected, adjusted old head %zu adjusted new head %zu\n",
40862306a36Sopenharmony_ci		  __func__, wrapped ? "" : "not ", (size_t)*old, (size_t)*head);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciout_err:
41362306a36Sopenharmony_ci	pr_err("%s: failed, error %d\n", __func__, err);
41462306a36Sopenharmony_ci	return err;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistruct auxtrace_record *intel_bts_recording_init(int *err)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct perf_pmu *intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME);
42062306a36Sopenharmony_ci	struct intel_bts_recording *btsr;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (!intel_bts_pmu)
42362306a36Sopenharmony_ci		return NULL;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
42662306a36Sopenharmony_ci		*err = -errno;
42762306a36Sopenharmony_ci		return NULL;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	btsr = zalloc(sizeof(struct intel_bts_recording));
43162306a36Sopenharmony_ci	if (!btsr) {
43262306a36Sopenharmony_ci		*err = -ENOMEM;
43362306a36Sopenharmony_ci		return NULL;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	btsr->intel_bts_pmu = intel_bts_pmu;
43762306a36Sopenharmony_ci	btsr->itr.pmu = intel_bts_pmu;
43862306a36Sopenharmony_ci	btsr->itr.recording_options = intel_bts_recording_options;
43962306a36Sopenharmony_ci	btsr->itr.info_priv_size = intel_bts_info_priv_size;
44062306a36Sopenharmony_ci	btsr->itr.info_fill = intel_bts_info_fill;
44162306a36Sopenharmony_ci	btsr->itr.free = intel_bts_recording_free;
44262306a36Sopenharmony_ci	btsr->itr.snapshot_start = intel_bts_snapshot_start;
44362306a36Sopenharmony_ci	btsr->itr.snapshot_finish = intel_bts_snapshot_finish;
44462306a36Sopenharmony_ci	btsr->itr.find_snapshot = intel_bts_find_snapshot;
44562306a36Sopenharmony_ci	btsr->itr.parse_snapshot_options = intel_bts_parse_snapshot_options;
44662306a36Sopenharmony_ci	btsr->itr.reference = intel_bts_reference;
44762306a36Sopenharmony_ci	btsr->itr.read_finish = auxtrace_record__read_finish;
44862306a36Sopenharmony_ci	btsr->itr.alignment = sizeof(struct branch);
44962306a36Sopenharmony_ci	return &btsr->itr;
45062306a36Sopenharmony_ci}
451