162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <stddef.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <string.h>
562306a36Sopenharmony_ci#include <errno.h>
662306a36Sopenharmony_ci#include <sys/types.h>
762306a36Sopenharmony_ci#include <sys/stat.h>
862306a36Sopenharmony_ci#include <unistd.h>
962306a36Sopenharmony_ci#include <api/fs/fs.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include "map_symbol.h"
1262306a36Sopenharmony_ci#include "mem-events.h"
1362306a36Sopenharmony_ci#include "debug.h"
1462306a36Sopenharmony_ci#include "symbol.h"
1562306a36Sopenharmony_ci#include "pmu.h"
1662306a36Sopenharmony_ci#include "pmus.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciunsigned int perf_mem_events__loads_ldlat = 30;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
2362306a36Sopenharmony_ci	E("ldlat-loads",	"cpu/mem-loads,ldlat=%u/P",	"cpu/events/mem-loads"),
2462306a36Sopenharmony_ci	E("ldlat-stores",	"cpu/mem-stores/P",		"cpu/events/mem-stores"),
2562306a36Sopenharmony_ci	E(NULL,			NULL,				NULL),
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci#undef E
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic char mem_loads_name[100];
3062306a36Sopenharmony_cistatic bool mem_loads_name__init;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct perf_mem_event * __weak perf_mem_events__ptr(int i)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	if (i >= PERF_MEM_EVENTS__MAX)
3562306a36Sopenharmony_ci		return NULL;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return &perf_mem_events[i];
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciconst char * __weak perf_mem_events__name(int i, const char *pmu_name  __maybe_unused)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct perf_mem_event *e = perf_mem_events__ptr(i);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!e)
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (i == PERF_MEM_EVENTS__LOAD) {
4862306a36Sopenharmony_ci		if (!mem_loads_name__init) {
4962306a36Sopenharmony_ci			mem_loads_name__init = true;
5062306a36Sopenharmony_ci			scnprintf(mem_loads_name, sizeof(mem_loads_name),
5162306a36Sopenharmony_ci				  e->name, perf_mem_events__loads_ldlat);
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci		return mem_loads_name;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return e->name;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return false;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciint perf_mem_events__parse(const char *str)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	char *tok, *saveptr = NULL;
6762306a36Sopenharmony_ci	bool found = false;
6862306a36Sopenharmony_ci	char *buf;
6962306a36Sopenharmony_ci	int j;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* We need buffer that we know we can write to. */
7262306a36Sopenharmony_ci	buf = malloc(strlen(str) + 1);
7362306a36Sopenharmony_ci	if (!buf)
7462306a36Sopenharmony_ci		return -ENOMEM;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	strcpy(buf, str);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	tok = strtok_r((char *)buf, ",", &saveptr);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	while (tok) {
8162306a36Sopenharmony_ci		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
8262306a36Sopenharmony_ci			struct perf_mem_event *e = perf_mem_events__ptr(j);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci			if (!e->tag)
8562306a36Sopenharmony_ci				continue;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci			if (strstr(e->tag, tok))
8862306a36Sopenharmony_ci				e->record = found = true;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		tok = strtok_r(NULL, ",", &saveptr);
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	free(buf);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (found)
9762306a36Sopenharmony_ci		return 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
10062306a36Sopenharmony_ci	return -1;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic bool perf_mem_event__supported(const char *mnt, struct perf_pmu *pmu,
10462306a36Sopenharmony_ci				      struct perf_mem_event *e)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	char sysfs_name[100];
10762306a36Sopenharmony_ci	char path[PATH_MAX];
10862306a36Sopenharmony_ci	struct stat st;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, pmu->name);
11162306a36Sopenharmony_ci	scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
11262306a36Sopenharmony_ci	return !stat(path, &st);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint perf_mem_events__init(void)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	const char *mnt = sysfs__mount();
11862306a36Sopenharmony_ci	bool found = false;
11962306a36Sopenharmony_ci	int j;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!mnt)
12262306a36Sopenharmony_ci		return -ENOENT;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
12562306a36Sopenharmony_ci		struct perf_mem_event *e = perf_mem_events__ptr(j);
12662306a36Sopenharmony_ci		struct perf_pmu *pmu = NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		/*
12962306a36Sopenharmony_ci		 * If the event entry isn't valid, skip initialization
13062306a36Sopenharmony_ci		 * and "e->supported" will keep false.
13162306a36Sopenharmony_ci		 */
13262306a36Sopenharmony_ci		if (!e->tag)
13362306a36Sopenharmony_ci			continue;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		/*
13662306a36Sopenharmony_ci		 * Scan all PMUs not just core ones, since perf mem/c2c on
13762306a36Sopenharmony_ci		 * platforms like AMD uses IBS OP PMU which is independent
13862306a36Sopenharmony_ci		 * of core PMU.
13962306a36Sopenharmony_ci		 */
14062306a36Sopenharmony_ci		while ((pmu = perf_pmus__scan(pmu)) != NULL) {
14162306a36Sopenharmony_ci			e->supported |= perf_mem_event__supported(mnt, pmu, e);
14262306a36Sopenharmony_ci			if (e->supported) {
14362306a36Sopenharmony_ci				found = true;
14462306a36Sopenharmony_ci				break;
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return found ? 0 : -ENOENT;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_civoid perf_mem_events__list(void)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int j;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
15762306a36Sopenharmony_ci		struct perf_mem_event *e = perf_mem_events__ptr(j);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		fprintf(stderr, "%-*s%-*s%s",
16062306a36Sopenharmony_ci			e->tag ? 13 : 0,
16162306a36Sopenharmony_ci			e->tag ? : "",
16262306a36Sopenharmony_ci			e->tag && verbose > 0 ? 25 : 0,
16362306a36Sopenharmony_ci			e->tag && verbose > 0 ? perf_mem_events__name(j, NULL) : "",
16462306a36Sopenharmony_ci			e->supported ? ": available\n" : "");
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
16962306a36Sopenharmony_ci						    int idx)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const char *mnt = sysfs__mount();
17262306a36Sopenharmony_ci	struct perf_pmu *pmu = NULL;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
17562306a36Sopenharmony_ci		if (!perf_mem_event__supported(mnt, pmu, e)) {
17662306a36Sopenharmony_ci			pr_err("failed: event '%s' not supported\n",
17762306a36Sopenharmony_ci			       perf_mem_events__name(idx, pmu->name));
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciint perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
18362306a36Sopenharmony_ci				 char **rec_tmp, int *tmp_nr)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	const char *mnt = sysfs__mount();
18662306a36Sopenharmony_ci	int i = *argv_nr, k = 0;
18762306a36Sopenharmony_ci	struct perf_mem_event *e;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
19062306a36Sopenharmony_ci		e = perf_mem_events__ptr(j);
19162306a36Sopenharmony_ci		if (!e->record)
19262306a36Sopenharmony_ci			continue;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (perf_pmus__num_mem_pmus() == 1) {
19562306a36Sopenharmony_ci			if (!e->supported) {
19662306a36Sopenharmony_ci				pr_err("failed: event '%s' not supported\n",
19762306a36Sopenharmony_ci				       perf_mem_events__name(j, NULL));
19862306a36Sopenharmony_ci				return -1;
19962306a36Sopenharmony_ci			}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci			rec_argv[i++] = "-e";
20262306a36Sopenharmony_ci			rec_argv[i++] = perf_mem_events__name(j, NULL);
20362306a36Sopenharmony_ci		} else {
20462306a36Sopenharmony_ci			struct perf_pmu *pmu = NULL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			if (!e->supported) {
20762306a36Sopenharmony_ci				perf_mem_events__print_unsupport_hybrid(e, j);
20862306a36Sopenharmony_ci				return -1;
20962306a36Sopenharmony_ci			}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci			while ((pmu = perf_pmus__scan(pmu)) != NULL) {
21262306a36Sopenharmony_ci				const char *s = perf_mem_events__name(j, pmu->name);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci				if (!perf_mem_event__supported(mnt, pmu, e))
21562306a36Sopenharmony_ci					continue;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci				rec_argv[i++] = "-e";
21862306a36Sopenharmony_ci				if (s) {
21962306a36Sopenharmony_ci					char *copy = strdup(s);
22062306a36Sopenharmony_ci					if (!copy)
22162306a36Sopenharmony_ci						return -1;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci					rec_argv[i++] = copy;
22462306a36Sopenharmony_ci					rec_tmp[k++] = copy;
22562306a36Sopenharmony_ci				}
22662306a36Sopenharmony_ci			}
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	*argv_nr = i;
23162306a36Sopenharmony_ci	*tmp_nr = k;
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic const char * const tlb_access[] = {
23662306a36Sopenharmony_ci	"N/A",
23762306a36Sopenharmony_ci	"HIT",
23862306a36Sopenharmony_ci	"MISS",
23962306a36Sopenharmony_ci	"L1",
24062306a36Sopenharmony_ci	"L2",
24162306a36Sopenharmony_ci	"Walker",
24262306a36Sopenharmony_ci	"Fault",
24362306a36Sopenharmony_ci};
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciint perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	size_t l = 0, i;
24862306a36Sopenharmony_ci	u64 m = PERF_MEM_TLB_NA;
24962306a36Sopenharmony_ci	u64 hit, miss;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	sz -= 1; /* -1 for null termination */
25262306a36Sopenharmony_ci	out[0] = '\0';
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (mem_info)
25562306a36Sopenharmony_ci		m = mem_info->data_src.mem_dtlb;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	hit = m & PERF_MEM_TLB_HIT;
25862306a36Sopenharmony_ci	miss = m & PERF_MEM_TLB_MISS;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* already taken care of */
26162306a36Sopenharmony_ci	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
26462306a36Sopenharmony_ci		if (!(m & 0x1))
26562306a36Sopenharmony_ci			continue;
26662306a36Sopenharmony_ci		if (l) {
26762306a36Sopenharmony_ci			strcat(out, " or ");
26862306a36Sopenharmony_ci			l += 4;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, tlb_access[i]);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	if (*out == '\0')
27362306a36Sopenharmony_ci		l += scnprintf(out, sz - l, "N/A");
27462306a36Sopenharmony_ci	if (hit)
27562306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " hit");
27662306a36Sopenharmony_ci	if (miss)
27762306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " miss");
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return l;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const char * const mem_lvl[] = {
28362306a36Sopenharmony_ci	"N/A",
28462306a36Sopenharmony_ci	"HIT",
28562306a36Sopenharmony_ci	"MISS",
28662306a36Sopenharmony_ci	"L1",
28762306a36Sopenharmony_ci	"LFB/MAB",
28862306a36Sopenharmony_ci	"L2",
28962306a36Sopenharmony_ci	"L3",
29062306a36Sopenharmony_ci	"Local RAM",
29162306a36Sopenharmony_ci	"Remote RAM (1 hop)",
29262306a36Sopenharmony_ci	"Remote RAM (2 hops)",
29362306a36Sopenharmony_ci	"Remote Cache (1 hop)",
29462306a36Sopenharmony_ci	"Remote Cache (2 hops)",
29562306a36Sopenharmony_ci	"I/O",
29662306a36Sopenharmony_ci	"Uncached",
29762306a36Sopenharmony_ci};
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic const char * const mem_lvlnum[] = {
30062306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_UNC] = "Uncached",
30162306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_CXL] = "CXL",
30262306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_IO] = "I/O",
30362306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
30462306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_LFB] = "LFB/MAB",
30562306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_RAM] = "RAM",
30662306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
30762306a36Sopenharmony_ci	[PERF_MEM_LVLNUM_NA] = "N/A",
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic const char * const mem_hops[] = {
31162306a36Sopenharmony_ci	"N/A",
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * While printing, 'Remote' will be added to represent
31462306a36Sopenharmony_ci	 * 'Remote core, same node' accesses as remote field need
31562306a36Sopenharmony_ci	 * to be set with mem_hops field.
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	"core, same node",
31862306a36Sopenharmony_ci	"node, same socket",
31962306a36Sopenharmony_ci	"socket, same board",
32062306a36Sopenharmony_ci	"board",
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	u64 op = PERF_MEM_LOCK_NA;
32662306a36Sopenharmony_ci	int l;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (mem_info)
32962306a36Sopenharmony_ci		op = mem_info->data_src.mem_op;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (op & PERF_MEM_OP_NA)
33262306a36Sopenharmony_ci		l = scnprintf(out, sz, "N/A");
33362306a36Sopenharmony_ci	else if (op & PERF_MEM_OP_LOAD)
33462306a36Sopenharmony_ci		l = scnprintf(out, sz, "LOAD");
33562306a36Sopenharmony_ci	else if (op & PERF_MEM_OP_STORE)
33662306a36Sopenharmony_ci		l = scnprintf(out, sz, "STORE");
33762306a36Sopenharmony_ci	else if (op & PERF_MEM_OP_PFETCH)
33862306a36Sopenharmony_ci		l = scnprintf(out, sz, "PFETCH");
33962306a36Sopenharmony_ci	else if (op & PERF_MEM_OP_EXEC)
34062306a36Sopenharmony_ci		l = scnprintf(out, sz, "EXEC");
34162306a36Sopenharmony_ci	else
34262306a36Sopenharmony_ci		l = scnprintf(out, sz, "No");
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return l;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciint perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	union perf_mem_data_src data_src;
35062306a36Sopenharmony_ci	int printed = 0;
35162306a36Sopenharmony_ci	size_t l = 0;
35262306a36Sopenharmony_ci	size_t i;
35362306a36Sopenharmony_ci	int lvl;
35462306a36Sopenharmony_ci	char hit_miss[5] = {0};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	sz -= 1; /* -1 for null termination */
35762306a36Sopenharmony_ci	out[0] = '\0';
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!mem_info)
36062306a36Sopenharmony_ci		goto na;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	data_src = mem_info->data_src;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (data_src.mem_lvl & PERF_MEM_LVL_HIT)
36562306a36Sopenharmony_ci		memcpy(hit_miss, "hit", 3);
36662306a36Sopenharmony_ci	else if (data_src.mem_lvl & PERF_MEM_LVL_MISS)
36762306a36Sopenharmony_ci		memcpy(hit_miss, "miss", 4);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	lvl = data_src.mem_lvl_num;
37062306a36Sopenharmony_ci	if (lvl && lvl != PERF_MEM_LVLNUM_NA) {
37162306a36Sopenharmony_ci		if (data_src.mem_remote) {
37262306a36Sopenharmony_ci			strcat(out, "Remote ");
37362306a36Sopenharmony_ci			l += 7;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		if (data_src.mem_hops)
37762306a36Sopenharmony_ci			l += scnprintf(out + l, sz - l, "%s ", mem_hops[data_src.mem_hops]);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		if (mem_lvlnum[lvl])
38062306a36Sopenharmony_ci			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
38162306a36Sopenharmony_ci		else
38262306a36Sopenharmony_ci			l += scnprintf(out + l, sz - l, "L%d", lvl);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " %s", hit_miss);
38562306a36Sopenharmony_ci		return l;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	lvl = data_src.mem_lvl;
38962306a36Sopenharmony_ci	if (!lvl)
39062306a36Sopenharmony_ci		goto na;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	lvl &= ~(PERF_MEM_LVL_NA | PERF_MEM_LVL_HIT | PERF_MEM_LVL_MISS);
39362306a36Sopenharmony_ci	if (!lvl)
39462306a36Sopenharmony_ci		goto na;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	for (i = 0; lvl && i < ARRAY_SIZE(mem_lvl); i++, lvl >>= 1) {
39762306a36Sopenharmony_ci		if (!(lvl & 0x1))
39862306a36Sopenharmony_ci			continue;
39962306a36Sopenharmony_ci		if (printed++) {
40062306a36Sopenharmony_ci			strcat(out, " or ");
40162306a36Sopenharmony_ci			l += 4;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, mem_lvl[i]);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (printed) {
40762306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " %s", hit_miss);
40862306a36Sopenharmony_ci		return l;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cina:
41262306a36Sopenharmony_ci	strcat(out, "N/A");
41362306a36Sopenharmony_ci	return 3;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic const char * const snoop_access[] = {
41762306a36Sopenharmony_ci	"N/A",
41862306a36Sopenharmony_ci	"None",
41962306a36Sopenharmony_ci	"Hit",
42062306a36Sopenharmony_ci	"Miss",
42162306a36Sopenharmony_ci	"HitM",
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic const char * const snoopx_access[] = {
42562306a36Sopenharmony_ci	"Fwd",
42662306a36Sopenharmony_ci	"Peer",
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ciint perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	size_t i, l = 0;
43262306a36Sopenharmony_ci	u64 m = PERF_MEM_SNOOP_NA;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	sz -= 1; /* -1 for null termination */
43562306a36Sopenharmony_ci	out[0] = '\0';
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (mem_info)
43862306a36Sopenharmony_ci		m = mem_info->data_src.mem_snoop;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
44162306a36Sopenharmony_ci		if (!(m & 0x1))
44262306a36Sopenharmony_ci			continue;
44362306a36Sopenharmony_ci		if (l) {
44462306a36Sopenharmony_ci			strcat(out, " or ");
44562306a36Sopenharmony_ci			l += 4;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, snoop_access[i]);
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	m = 0;
45162306a36Sopenharmony_ci	if (mem_info)
45262306a36Sopenharmony_ci		m = mem_info->data_src.mem_snoopx;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	for (i = 0; m && i < ARRAY_SIZE(snoopx_access); i++, m >>= 1) {
45562306a36Sopenharmony_ci		if (!(m & 0x1))
45662306a36Sopenharmony_ci			continue;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (l) {
45962306a36Sopenharmony_ci			strcat(out, " or ");
46062306a36Sopenharmony_ci			l += 4;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, snoopx_access[i]);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (*out == '\0')
46662306a36Sopenharmony_ci		l += scnprintf(out, sz - l, "N/A");
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return l;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	u64 mask = PERF_MEM_LOCK_NA;
47462306a36Sopenharmony_ci	int l;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (mem_info)
47762306a36Sopenharmony_ci		mask = mem_info->data_src.mem_lock;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (mask & PERF_MEM_LOCK_NA)
48062306a36Sopenharmony_ci		l = scnprintf(out, sz, "N/A");
48162306a36Sopenharmony_ci	else if (mask & PERF_MEM_LOCK_LOCKED)
48262306a36Sopenharmony_ci		l = scnprintf(out, sz, "Yes");
48362306a36Sopenharmony_ci	else
48462306a36Sopenharmony_ci		l = scnprintf(out, sz, "No");
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return l;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciint perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	size_t l = 0;
49262306a36Sopenharmony_ci	u64 mask = PERF_MEM_BLK_NA;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	sz -= 1; /* -1 for null termination */
49562306a36Sopenharmony_ci	out[0] = '\0';
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (mem_info)
49862306a36Sopenharmony_ci		mask = mem_info->data_src.mem_blk;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (!mask || (mask & PERF_MEM_BLK_NA)) {
50162306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " N/A");
50262306a36Sopenharmony_ci		return l;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	if (mask & PERF_MEM_BLK_DATA)
50562306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " Data");
50662306a36Sopenharmony_ci	if (mask & PERF_MEM_BLK_ADDR)
50762306a36Sopenharmony_ci		l += scnprintf(out + l, sz - l, " Addr");
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return l;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ciint perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	int i = 0;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	i += scnprintf(out, sz, "|OP ");
51762306a36Sopenharmony_ci	i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
51862306a36Sopenharmony_ci	i += scnprintf(out + i, sz - i, "|LVL ");
51962306a36Sopenharmony_ci	i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
52062306a36Sopenharmony_ci	i += scnprintf(out + i, sz - i, "|SNP ");
52162306a36Sopenharmony_ci	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
52262306a36Sopenharmony_ci	i += scnprintf(out + i, sz - i, "|TLB ");
52362306a36Sopenharmony_ci	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
52462306a36Sopenharmony_ci	i += scnprintf(out + i, sz - i, "|LCK ");
52562306a36Sopenharmony_ci	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
52662306a36Sopenharmony_ci	i += scnprintf(out + i, sz - i, "|BLK ");
52762306a36Sopenharmony_ci	i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return i;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ciint c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	union perf_mem_data_src *data_src = &mi->data_src;
53562306a36Sopenharmony_ci	u64 daddr  = mi->daddr.addr;
53662306a36Sopenharmony_ci	u64 op     = data_src->mem_op;
53762306a36Sopenharmony_ci	u64 lvl    = data_src->mem_lvl;
53862306a36Sopenharmony_ci	u64 snoop  = data_src->mem_snoop;
53962306a36Sopenharmony_ci	u64 snoopx = data_src->mem_snoopx;
54062306a36Sopenharmony_ci	u64 lock   = data_src->mem_lock;
54162306a36Sopenharmony_ci	u64 blk    = data_src->mem_blk;
54262306a36Sopenharmony_ci	/*
54362306a36Sopenharmony_ci	 * Skylake might report unknown remote level via this
54462306a36Sopenharmony_ci	 * bit, consider it when evaluating remote HITMs.
54562306a36Sopenharmony_ci	 *
54662306a36Sopenharmony_ci	 * Incase of power, remote field can also be used to denote cache
54762306a36Sopenharmony_ci	 * accesses from the another core of same node. Hence, setting
54862306a36Sopenharmony_ci	 * mrem only when HOPS is zero along with set remote field.
54962306a36Sopenharmony_ci	 */
55062306a36Sopenharmony_ci	bool mrem  = (data_src->mem_remote && !data_src->mem_hops);
55162306a36Sopenharmony_ci	int err = 0;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci#define HITM_INC(__f)		\
55462306a36Sopenharmony_cido {				\
55562306a36Sopenharmony_ci	stats->__f++;		\
55662306a36Sopenharmony_ci	stats->tot_hitm++;	\
55762306a36Sopenharmony_ci} while (0)
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci#define PEER_INC(__f)		\
56062306a36Sopenharmony_cido {				\
56162306a36Sopenharmony_ci	stats->__f++;		\
56262306a36Sopenharmony_ci	stats->tot_peer++;	\
56362306a36Sopenharmony_ci} while (0)
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci#define P(a, b) PERF_MEM_##a##_##b
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	stats->nr_entries++;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (lock & P(LOCK, LOCKED)) stats->locks++;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (blk & P(BLK, DATA)) stats->blk_data++;
57262306a36Sopenharmony_ci	if (blk & P(BLK, ADDR)) stats->blk_addr++;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (op & P(OP, LOAD)) {
57562306a36Sopenharmony_ci		/* load */
57662306a36Sopenharmony_ci		stats->load++;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		if (!daddr) {
57962306a36Sopenharmony_ci			stats->ld_noadrs++;
58062306a36Sopenharmony_ci			return -1;
58162306a36Sopenharmony_ci		}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (lvl & P(LVL, HIT)) {
58462306a36Sopenharmony_ci			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
58562306a36Sopenharmony_ci			if (lvl & P(LVL, IO))  stats->ld_io++;
58662306a36Sopenharmony_ci			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
58762306a36Sopenharmony_ci			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
58862306a36Sopenharmony_ci			if (lvl & P(LVL, L2)) {
58962306a36Sopenharmony_ci				stats->ld_l2hit++;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci				if (snoopx & P(SNOOPX, PEER))
59262306a36Sopenharmony_ci					PEER_INC(lcl_peer);
59362306a36Sopenharmony_ci			}
59462306a36Sopenharmony_ci			if (lvl & P(LVL, L3 )) {
59562306a36Sopenharmony_ci				if (snoop & P(SNOOP, HITM))
59662306a36Sopenharmony_ci					HITM_INC(lcl_hitm);
59762306a36Sopenharmony_ci				else
59862306a36Sopenharmony_ci					stats->ld_llchit++;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci				if (snoopx & P(SNOOPX, PEER))
60162306a36Sopenharmony_ci					PEER_INC(lcl_peer);
60262306a36Sopenharmony_ci			}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci			if (lvl & P(LVL, LOC_RAM)) {
60562306a36Sopenharmony_ci				stats->lcl_dram++;
60662306a36Sopenharmony_ci				if (snoop & P(SNOOP, HIT))
60762306a36Sopenharmony_ci					stats->ld_shared++;
60862306a36Sopenharmony_ci				else
60962306a36Sopenharmony_ci					stats->ld_excl++;
61062306a36Sopenharmony_ci			}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci			if ((lvl & P(LVL, REM_RAM1)) ||
61362306a36Sopenharmony_ci			    (lvl & P(LVL, REM_RAM2)) ||
61462306a36Sopenharmony_ci			     mrem) {
61562306a36Sopenharmony_ci				stats->rmt_dram++;
61662306a36Sopenharmony_ci				if (snoop & P(SNOOP, HIT))
61762306a36Sopenharmony_ci					stats->ld_shared++;
61862306a36Sopenharmony_ci				else
61962306a36Sopenharmony_ci					stats->ld_excl++;
62062306a36Sopenharmony_ci			}
62162306a36Sopenharmony_ci		}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		if ((lvl & P(LVL, REM_CCE1)) ||
62462306a36Sopenharmony_ci		    (lvl & P(LVL, REM_CCE2)) ||
62562306a36Sopenharmony_ci		     mrem) {
62662306a36Sopenharmony_ci			if (snoop & P(SNOOP, HIT)) {
62762306a36Sopenharmony_ci				stats->rmt_hit++;
62862306a36Sopenharmony_ci			} else if (snoop & P(SNOOP, HITM)) {
62962306a36Sopenharmony_ci				HITM_INC(rmt_hitm);
63062306a36Sopenharmony_ci			} else if (snoopx & P(SNOOPX, PEER)) {
63162306a36Sopenharmony_ci				stats->rmt_hit++;
63262306a36Sopenharmony_ci				PEER_INC(rmt_peer);
63362306a36Sopenharmony_ci			}
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		if ((lvl & P(LVL, MISS)))
63762306a36Sopenharmony_ci			stats->ld_miss++;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	} else if (op & P(OP, STORE)) {
64062306a36Sopenharmony_ci		/* store */
64162306a36Sopenharmony_ci		stats->store++;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		if (!daddr) {
64462306a36Sopenharmony_ci			stats->st_noadrs++;
64562306a36Sopenharmony_ci			return -1;
64662306a36Sopenharmony_ci		}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		if (lvl & P(LVL, HIT)) {
64962306a36Sopenharmony_ci			if (lvl & P(LVL, UNC)) stats->st_uncache++;
65062306a36Sopenharmony_ci			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci		if (lvl & P(LVL, MISS))
65362306a36Sopenharmony_ci			if (lvl & P(LVL, L1)) stats->st_l1miss++;
65462306a36Sopenharmony_ci		if (lvl & P(LVL, NA))
65562306a36Sopenharmony_ci			stats->st_na++;
65662306a36Sopenharmony_ci	} else {
65762306a36Sopenharmony_ci		/* unparsable data_src? */
65862306a36Sopenharmony_ci		stats->noparse++;
65962306a36Sopenharmony_ci		return -1;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
66362306a36Sopenharmony_ci		stats->nomap++;
66462306a36Sopenharmony_ci		return -1;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci#undef P
66862306a36Sopenharmony_ci#undef HITM_INC
66962306a36Sopenharmony_ci	return err;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_civoid c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	stats->nr_entries	+= add->nr_entries;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	stats->locks		+= add->locks;
67762306a36Sopenharmony_ci	stats->store		+= add->store;
67862306a36Sopenharmony_ci	stats->st_uncache	+= add->st_uncache;
67962306a36Sopenharmony_ci	stats->st_noadrs	+= add->st_noadrs;
68062306a36Sopenharmony_ci	stats->st_l1hit		+= add->st_l1hit;
68162306a36Sopenharmony_ci	stats->st_l1miss	+= add->st_l1miss;
68262306a36Sopenharmony_ci	stats->st_na		+= add->st_na;
68362306a36Sopenharmony_ci	stats->load		+= add->load;
68462306a36Sopenharmony_ci	stats->ld_excl		+= add->ld_excl;
68562306a36Sopenharmony_ci	stats->ld_shared	+= add->ld_shared;
68662306a36Sopenharmony_ci	stats->ld_uncache	+= add->ld_uncache;
68762306a36Sopenharmony_ci	stats->ld_io		+= add->ld_io;
68862306a36Sopenharmony_ci	stats->ld_miss		+= add->ld_miss;
68962306a36Sopenharmony_ci	stats->ld_noadrs	+= add->ld_noadrs;
69062306a36Sopenharmony_ci	stats->ld_fbhit		+= add->ld_fbhit;
69162306a36Sopenharmony_ci	stats->ld_l1hit		+= add->ld_l1hit;
69262306a36Sopenharmony_ci	stats->ld_l2hit		+= add->ld_l2hit;
69362306a36Sopenharmony_ci	stats->ld_llchit	+= add->ld_llchit;
69462306a36Sopenharmony_ci	stats->lcl_hitm		+= add->lcl_hitm;
69562306a36Sopenharmony_ci	stats->rmt_hitm		+= add->rmt_hitm;
69662306a36Sopenharmony_ci	stats->tot_hitm		+= add->tot_hitm;
69762306a36Sopenharmony_ci	stats->lcl_peer		+= add->lcl_peer;
69862306a36Sopenharmony_ci	stats->rmt_peer		+= add->rmt_peer;
69962306a36Sopenharmony_ci	stats->tot_peer		+= add->tot_peer;
70062306a36Sopenharmony_ci	stats->rmt_hit		+= add->rmt_hit;
70162306a36Sopenharmony_ci	stats->lcl_dram		+= add->lcl_dram;
70262306a36Sopenharmony_ci	stats->rmt_dram		+= add->rmt_dram;
70362306a36Sopenharmony_ci	stats->blk_data		+= add->blk_data;
70462306a36Sopenharmony_ci	stats->blk_addr		+= add->blk_addr;
70562306a36Sopenharmony_ci	stats->nomap		+= add->nomap;
70662306a36Sopenharmony_ci	stats->noparse		+= add->noparse;
70762306a36Sopenharmony_ci}
708