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