cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mem-events.c (14568B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <stddef.h>
      3#include <stdlib.h>
      4#include <string.h>
      5#include <errno.h>
      6#include <sys/types.h>
      7#include <sys/stat.h>
      8#include <unistd.h>
      9#include <api/fs/fs.h>
     10#include <linux/kernel.h>
     11#include "map_symbol.h"
     12#include "mem-events.h"
     13#include "debug.h"
     14#include "symbol.h"
     15#include "pmu.h"
     16#include "pmu-hybrid.h"
     17
     18unsigned int perf_mem_events__loads_ldlat = 30;
     19
     20#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
     21
     22static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
     23	E("ldlat-loads",	"cpu/mem-loads,ldlat=%u/P",	"cpu/events/mem-loads"),
     24	E("ldlat-stores",	"cpu/mem-stores/P",		"cpu/events/mem-stores"),
     25	E(NULL,			NULL,				NULL),
     26};
     27#undef E
     28
     29static char mem_loads_name[100];
     30static bool mem_loads_name__init;
     31
     32struct perf_mem_event * __weak perf_mem_events__ptr(int i)
     33{
     34	if (i >= PERF_MEM_EVENTS__MAX)
     35		return NULL;
     36
     37	return &perf_mem_events[i];
     38}
     39
     40char * __weak perf_mem_events__name(int i, char *pmu_name  __maybe_unused)
     41{
     42	struct perf_mem_event *e = perf_mem_events__ptr(i);
     43
     44	if (!e)
     45		return NULL;
     46
     47	if (i == PERF_MEM_EVENTS__LOAD) {
     48		if (!mem_loads_name__init) {
     49			mem_loads_name__init = true;
     50			scnprintf(mem_loads_name, sizeof(mem_loads_name),
     51				  e->name, perf_mem_events__loads_ldlat);
     52		}
     53		return mem_loads_name;
     54	}
     55
     56	return (char *)e->name;
     57}
     58
     59__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
     60{
     61	return false;
     62}
     63
     64int perf_mem_events__parse(const char *str)
     65{
     66	char *tok, *saveptr = NULL;
     67	bool found = false;
     68	char *buf;
     69	int j;
     70
     71	/* We need buffer that we know we can write to. */
     72	buf = malloc(strlen(str) + 1);
     73	if (!buf)
     74		return -ENOMEM;
     75
     76	strcpy(buf, str);
     77
     78	tok = strtok_r((char *)buf, ",", &saveptr);
     79
     80	while (tok) {
     81		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
     82			struct perf_mem_event *e = perf_mem_events__ptr(j);
     83
     84			if (!e->tag)
     85				continue;
     86
     87			if (strstr(e->tag, tok))
     88				e->record = found = true;
     89		}
     90
     91		tok = strtok_r(NULL, ",", &saveptr);
     92	}
     93
     94	free(buf);
     95
     96	if (found)
     97		return 0;
     98
     99	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
    100	return -1;
    101}
    102
    103static bool perf_mem_event__supported(const char *mnt, char *sysfs_name)
    104{
    105	char path[PATH_MAX];
    106	struct stat st;
    107
    108	scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
    109	return !stat(path, &st);
    110}
    111
    112int perf_mem_events__init(void)
    113{
    114	const char *mnt = sysfs__mount();
    115	bool found = false;
    116	int j;
    117
    118	if (!mnt)
    119		return -ENOENT;
    120
    121	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
    122		struct perf_mem_event *e = perf_mem_events__ptr(j);
    123		struct perf_pmu *pmu;
    124		char sysfs_name[100];
    125
    126		/*
    127		 * If the event entry isn't valid, skip initialization
    128		 * and "e->supported" will keep false.
    129		 */
    130		if (!e->tag)
    131			continue;
    132
    133		if (!perf_pmu__has_hybrid()) {
    134			scnprintf(sysfs_name, sizeof(sysfs_name),
    135				  e->sysfs_name, "cpu");
    136			e->supported = perf_mem_event__supported(mnt, sysfs_name);
    137		} else {
    138			perf_pmu__for_each_hybrid_pmu(pmu) {
    139				scnprintf(sysfs_name, sizeof(sysfs_name),
    140					  e->sysfs_name, pmu->name);
    141				e->supported |= perf_mem_event__supported(mnt, sysfs_name);
    142			}
    143		}
    144
    145		if (e->supported)
    146			found = true;
    147	}
    148
    149	return found ? 0 : -ENOENT;
    150}
    151
    152void perf_mem_events__list(void)
    153{
    154	int j;
    155
    156	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
    157		struct perf_mem_event *e = perf_mem_events__ptr(j);
    158
    159		fprintf(stderr, "%-13s%-*s%s\n",
    160			e->tag ?: "",
    161			verbose > 0 ? 25 : 0,
    162			verbose > 0 ? perf_mem_events__name(j, NULL) : "",
    163			e->supported ? ": available" : "");
    164	}
    165}
    166
    167static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
    168						    int idx)
    169{
    170	const char *mnt = sysfs__mount();
    171	char sysfs_name[100];
    172	struct perf_pmu *pmu;
    173
    174	perf_pmu__for_each_hybrid_pmu(pmu) {
    175		scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name,
    176			  pmu->name);
    177		if (!perf_mem_event__supported(mnt, sysfs_name)) {
    178			pr_err("failed: event '%s' not supported\n",
    179			       perf_mem_events__name(idx, pmu->name));
    180		}
    181	}
    182}
    183
    184int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
    185				 char **rec_tmp, int *tmp_nr)
    186{
    187	int i = *argv_nr, k = 0;
    188	struct perf_mem_event *e;
    189	struct perf_pmu *pmu;
    190	char *s;
    191
    192	for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
    193		e = perf_mem_events__ptr(j);
    194		if (!e->record)
    195			continue;
    196
    197		if (!perf_pmu__has_hybrid()) {
    198			if (!e->supported) {
    199				pr_err("failed: event '%s' not supported\n",
    200				       perf_mem_events__name(j, NULL));
    201				return -1;
    202			}
    203
    204			rec_argv[i++] = "-e";
    205			rec_argv[i++] = perf_mem_events__name(j, NULL);
    206		} else {
    207			if (!e->supported) {
    208				perf_mem_events__print_unsupport_hybrid(e, j);
    209				return -1;
    210			}
    211
    212			perf_pmu__for_each_hybrid_pmu(pmu) {
    213				rec_argv[i++] = "-e";
    214				s = perf_mem_events__name(j, pmu->name);
    215				if (s) {
    216					s = strdup(s);
    217					if (!s)
    218						return -1;
    219
    220					rec_argv[i++] = s;
    221					rec_tmp[k++] = s;
    222				}
    223			}
    224		}
    225	}
    226
    227	*argv_nr = i;
    228	*tmp_nr = k;
    229	return 0;
    230}
    231
    232static const char * const tlb_access[] = {
    233	"N/A",
    234	"HIT",
    235	"MISS",
    236	"L1",
    237	"L2",
    238	"Walker",
    239	"Fault",
    240};
    241
    242int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    243{
    244	size_t l = 0, i;
    245	u64 m = PERF_MEM_TLB_NA;
    246	u64 hit, miss;
    247
    248	sz -= 1; /* -1 for null termination */
    249	out[0] = '\0';
    250
    251	if (mem_info)
    252		m = mem_info->data_src.mem_dtlb;
    253
    254	hit = m & PERF_MEM_TLB_HIT;
    255	miss = m & PERF_MEM_TLB_MISS;
    256
    257	/* already taken care of */
    258	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
    259
    260	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
    261		if (!(m & 0x1))
    262			continue;
    263		if (l) {
    264			strcat(out, " or ");
    265			l += 4;
    266		}
    267		l += scnprintf(out + l, sz - l, tlb_access[i]);
    268	}
    269	if (*out == '\0')
    270		l += scnprintf(out, sz - l, "N/A");
    271	if (hit)
    272		l += scnprintf(out + l, sz - l, " hit");
    273	if (miss)
    274		l += scnprintf(out + l, sz - l, " miss");
    275
    276	return l;
    277}
    278
    279static const char * const mem_lvl[] = {
    280	"N/A",
    281	"HIT",
    282	"MISS",
    283	"L1",
    284	"LFB",
    285	"L2",
    286	"L3",
    287	"Local RAM",
    288	"Remote RAM (1 hop)",
    289	"Remote RAM (2 hops)",
    290	"Remote Cache (1 hop)",
    291	"Remote Cache (2 hops)",
    292	"I/O",
    293	"Uncached",
    294};
    295
    296static const char * const mem_lvlnum[] = {
    297	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
    298	[PERF_MEM_LVLNUM_LFB] = "LFB",
    299	[PERF_MEM_LVLNUM_RAM] = "RAM",
    300	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
    301	[PERF_MEM_LVLNUM_NA] = "N/A",
    302};
    303
    304static const char * const mem_hops[] = {
    305	"N/A",
    306	/*
    307	 * While printing, 'Remote' will be added to represent
    308	 * 'Remote core, same node' accesses as remote field need
    309	 * to be set with mem_hops field.
    310	 */
    311	"core, same node",
    312	"node, same socket",
    313	"socket, same board",
    314	"board",
    315};
    316
    317static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    318{
    319	u64 op = PERF_MEM_LOCK_NA;
    320	int l;
    321
    322	if (mem_info)
    323		op = mem_info->data_src.mem_op;
    324
    325	if (op & PERF_MEM_OP_NA)
    326		l = scnprintf(out, sz, "N/A");
    327	else if (op & PERF_MEM_OP_LOAD)
    328		l = scnprintf(out, sz, "LOAD");
    329	else if (op & PERF_MEM_OP_STORE)
    330		l = scnprintf(out, sz, "STORE");
    331	else if (op & PERF_MEM_OP_PFETCH)
    332		l = scnprintf(out, sz, "PFETCH");
    333	else if (op & PERF_MEM_OP_EXEC)
    334		l = scnprintf(out, sz, "EXEC");
    335	else
    336		l = scnprintf(out, sz, "No");
    337
    338	return l;
    339}
    340
    341int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    342{
    343	size_t i, l = 0;
    344	u64 m =  PERF_MEM_LVL_NA;
    345	u64 hit, miss;
    346	int printed = 0;
    347
    348	if (mem_info)
    349		m  = mem_info->data_src.mem_lvl;
    350
    351	sz -= 1; /* -1 for null termination */
    352	out[0] = '\0';
    353
    354	hit = m & PERF_MEM_LVL_HIT;
    355	miss = m & PERF_MEM_LVL_MISS;
    356
    357	/* already taken care of */
    358	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
    359
    360	if (mem_info && mem_info->data_src.mem_remote) {
    361		strcat(out, "Remote ");
    362		l += 7;
    363	}
    364
    365	/*
    366	 * Incase mem_hops field is set, we can skip printing data source via
    367	 * PERF_MEM_LVL namespace.
    368	 */
    369	if (mem_info && mem_info->data_src.mem_hops) {
    370		l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
    371	} else {
    372		for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
    373			if (!(m & 0x1))
    374				continue;
    375			if (printed++) {
    376				strcat(out, " or ");
    377				l += 4;
    378			}
    379			l += scnprintf(out + l, sz - l, mem_lvl[i]);
    380		}
    381	}
    382
    383	if (mem_info && mem_info->data_src.mem_lvl_num) {
    384		int lvl = mem_info->data_src.mem_lvl_num;
    385		if (printed++) {
    386			strcat(out, " or ");
    387			l += 4;
    388		}
    389		if (mem_lvlnum[lvl])
    390			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
    391		else
    392			l += scnprintf(out + l, sz - l, "L%d", lvl);
    393	}
    394
    395	if (l == 0)
    396		l += scnprintf(out + l, sz - l, "N/A");
    397	if (hit)
    398		l += scnprintf(out + l, sz - l, " hit");
    399	if (miss)
    400		l += scnprintf(out + l, sz - l, " miss");
    401
    402	return l;
    403}
    404
    405static const char * const snoop_access[] = {
    406	"N/A",
    407	"None",
    408	"Hit",
    409	"Miss",
    410	"HitM",
    411};
    412
    413int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    414{
    415	size_t i, l = 0;
    416	u64 m = PERF_MEM_SNOOP_NA;
    417
    418	sz -= 1; /* -1 for null termination */
    419	out[0] = '\0';
    420
    421	if (mem_info)
    422		m = mem_info->data_src.mem_snoop;
    423
    424	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
    425		if (!(m & 0x1))
    426			continue;
    427		if (l) {
    428			strcat(out, " or ");
    429			l += 4;
    430		}
    431		l += scnprintf(out + l, sz - l, snoop_access[i]);
    432	}
    433	if (mem_info &&
    434	     (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
    435		if (l) {
    436			strcat(out, " or ");
    437			l += 4;
    438		}
    439		l += scnprintf(out + l, sz - l, "Fwd");
    440	}
    441
    442	if (*out == '\0')
    443		l += scnprintf(out, sz - l, "N/A");
    444
    445	return l;
    446}
    447
    448int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    449{
    450	u64 mask = PERF_MEM_LOCK_NA;
    451	int l;
    452
    453	if (mem_info)
    454		mask = mem_info->data_src.mem_lock;
    455
    456	if (mask & PERF_MEM_LOCK_NA)
    457		l = scnprintf(out, sz, "N/A");
    458	else if (mask & PERF_MEM_LOCK_LOCKED)
    459		l = scnprintf(out, sz, "Yes");
    460	else
    461		l = scnprintf(out, sz, "No");
    462
    463	return l;
    464}
    465
    466int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    467{
    468	size_t l = 0;
    469	u64 mask = PERF_MEM_BLK_NA;
    470
    471	sz -= 1; /* -1 for null termination */
    472	out[0] = '\0';
    473
    474	if (mem_info)
    475		mask = mem_info->data_src.mem_blk;
    476
    477	if (!mask || (mask & PERF_MEM_BLK_NA)) {
    478		l += scnprintf(out + l, sz - l, " N/A");
    479		return l;
    480	}
    481	if (mask & PERF_MEM_BLK_DATA)
    482		l += scnprintf(out + l, sz - l, " Data");
    483	if (mask & PERF_MEM_BLK_ADDR)
    484		l += scnprintf(out + l, sz - l, " Addr");
    485
    486	return l;
    487}
    488
    489int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
    490{
    491	int i = 0;
    492
    493	i += scnprintf(out, sz, "|OP ");
    494	i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
    495	i += scnprintf(out + i, sz - i, "|LVL ");
    496	i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
    497	i += scnprintf(out + i, sz - i, "|SNP ");
    498	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
    499	i += scnprintf(out + i, sz - i, "|TLB ");
    500	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
    501	i += scnprintf(out + i, sz - i, "|LCK ");
    502	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
    503	i += scnprintf(out + i, sz - i, "|BLK ");
    504	i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
    505
    506	return i;
    507}
    508
    509int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
    510{
    511	union perf_mem_data_src *data_src = &mi->data_src;
    512	u64 daddr  = mi->daddr.addr;
    513	u64 op     = data_src->mem_op;
    514	u64 lvl    = data_src->mem_lvl;
    515	u64 snoop  = data_src->mem_snoop;
    516	u64 lock   = data_src->mem_lock;
    517	u64 blk    = data_src->mem_blk;
    518	/*
    519	 * Skylake might report unknown remote level via this
    520	 * bit, consider it when evaluating remote HITMs.
    521	 *
    522	 * Incase of power, remote field can also be used to denote cache
    523	 * accesses from the another core of same node. Hence, setting
    524	 * mrem only when HOPS is zero along with set remote field.
    525	 */
    526	bool mrem  = (data_src->mem_remote && !data_src->mem_hops);
    527	int err = 0;
    528
    529#define HITM_INC(__f)		\
    530do {				\
    531	stats->__f++;		\
    532	stats->tot_hitm++;	\
    533} while (0)
    534
    535#define P(a, b) PERF_MEM_##a##_##b
    536
    537	stats->nr_entries++;
    538
    539	if (lock & P(LOCK, LOCKED)) stats->locks++;
    540
    541	if (blk & P(BLK, DATA)) stats->blk_data++;
    542	if (blk & P(BLK, ADDR)) stats->blk_addr++;
    543
    544	if (op & P(OP, LOAD)) {
    545		/* load */
    546		stats->load++;
    547
    548		if (!daddr) {
    549			stats->ld_noadrs++;
    550			return -1;
    551		}
    552
    553		if (lvl & P(LVL, HIT)) {
    554			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
    555			if (lvl & P(LVL, IO))  stats->ld_io++;
    556			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
    557			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
    558			if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
    559			if (lvl & P(LVL, L3 )) {
    560				if (snoop & P(SNOOP, HITM))
    561					HITM_INC(lcl_hitm);
    562				else
    563					stats->ld_llchit++;
    564			}
    565
    566			if (lvl & P(LVL, LOC_RAM)) {
    567				stats->lcl_dram++;
    568				if (snoop & P(SNOOP, HIT))
    569					stats->ld_shared++;
    570				else
    571					stats->ld_excl++;
    572			}
    573
    574			if ((lvl & P(LVL, REM_RAM1)) ||
    575			    (lvl & P(LVL, REM_RAM2)) ||
    576			     mrem) {
    577				stats->rmt_dram++;
    578				if (snoop & P(SNOOP, HIT))
    579					stats->ld_shared++;
    580				else
    581					stats->ld_excl++;
    582			}
    583		}
    584
    585		if ((lvl & P(LVL, REM_CCE1)) ||
    586		    (lvl & P(LVL, REM_CCE2)) ||
    587		     mrem) {
    588			if (snoop & P(SNOOP, HIT))
    589				stats->rmt_hit++;
    590			else if (snoop & P(SNOOP, HITM))
    591				HITM_INC(rmt_hitm);
    592		}
    593
    594		if ((lvl & P(LVL, MISS)))
    595			stats->ld_miss++;
    596
    597	} else if (op & P(OP, STORE)) {
    598		/* store */
    599		stats->store++;
    600
    601		if (!daddr) {
    602			stats->st_noadrs++;
    603			return -1;
    604		}
    605
    606		if (lvl & P(LVL, HIT)) {
    607			if (lvl & P(LVL, UNC)) stats->st_uncache++;
    608			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
    609		}
    610		if (lvl & P(LVL, MISS))
    611			if (lvl & P(LVL, L1)) stats->st_l1miss++;
    612		if (lvl & P(LVL, NA))
    613			stats->st_na++;
    614	} else {
    615		/* unparsable data_src? */
    616		stats->noparse++;
    617		return -1;
    618	}
    619
    620	if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
    621		stats->nomap++;
    622		return -1;
    623	}
    624
    625#undef P
    626#undef HITM_INC
    627	return err;
    628}
    629
    630void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
    631{
    632	stats->nr_entries	+= add->nr_entries;
    633
    634	stats->locks		+= add->locks;
    635	stats->store		+= add->store;
    636	stats->st_uncache	+= add->st_uncache;
    637	stats->st_noadrs	+= add->st_noadrs;
    638	stats->st_l1hit		+= add->st_l1hit;
    639	stats->st_l1miss	+= add->st_l1miss;
    640	stats->st_na		+= add->st_na;
    641	stats->load		+= add->load;
    642	stats->ld_excl		+= add->ld_excl;
    643	stats->ld_shared	+= add->ld_shared;
    644	stats->ld_uncache	+= add->ld_uncache;
    645	stats->ld_io		+= add->ld_io;
    646	stats->ld_miss		+= add->ld_miss;
    647	stats->ld_noadrs	+= add->ld_noadrs;
    648	stats->ld_fbhit		+= add->ld_fbhit;
    649	stats->ld_l1hit		+= add->ld_l1hit;
    650	stats->ld_l2hit		+= add->ld_l2hit;
    651	stats->ld_llchit	+= add->ld_llchit;
    652	stats->lcl_hitm		+= add->lcl_hitm;
    653	stats->rmt_hitm		+= add->rmt_hitm;
    654	stats->tot_hitm		+= add->tot_hitm;
    655	stats->rmt_hit		+= add->rmt_hit;
    656	stats->lcl_dram		+= add->lcl_dram;
    657	stats->rmt_dram		+= add->rmt_dram;
    658	stats->blk_data		+= add->blk_data;
    659	stats->blk_addr		+= add->blk_addr;
    660	stats->nomap		+= add->nomap;
    661	stats->noparse		+= add->noparse;
    662}