root/tools/perf/util/mem-events.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. perf_mem_events__name
  2. perf_mem_events__parse
  3. perf_mem_events__init
  4. perf_mem__tlb_scnprintf
  5. perf_mem__lvl_scnprintf
  6. perf_mem__snp_scnprintf
  7. perf_mem__lck_scnprintf
  8. perf_script__meminfo_scnprintf
  9. c2c_decode_stats
  10. c2c_add_stats

   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 
  16 unsigned int perf_mem_events__loads_ldlat = 30;
  17 
  18 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
  19 
  20 struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
  21         E("ldlat-loads",        "cpu/mem-loads,ldlat=%u/P",     "mem-loads"),
  22         E("ldlat-stores",       "cpu/mem-stores/P",             "mem-stores"),
  23 };
  24 #undef E
  25 
  26 #undef E
  27 
  28 static char mem_loads_name[100];
  29 static bool mem_loads_name__init;
  30 
  31 char * __weak perf_mem_events__name(int i)
  32 {
  33         if (i == PERF_MEM_EVENTS__LOAD) {
  34                 if (!mem_loads_name__init) {
  35                         mem_loads_name__init = true;
  36                         scnprintf(mem_loads_name, sizeof(mem_loads_name),
  37                                   perf_mem_events[i].name,
  38                                   perf_mem_events__loads_ldlat);
  39                 }
  40                 return mem_loads_name;
  41         }
  42 
  43         return (char *)perf_mem_events[i].name;
  44 }
  45 
  46 int perf_mem_events__parse(const char *str)
  47 {
  48         char *tok, *saveptr = NULL;
  49         bool found = false;
  50         char *buf;
  51         int j;
  52 
  53         /* We need buffer that we know we can write to. */
  54         buf = malloc(strlen(str) + 1);
  55         if (!buf)
  56                 return -ENOMEM;
  57 
  58         strcpy(buf, str);
  59 
  60         tok = strtok_r((char *)buf, ",", &saveptr);
  61 
  62         while (tok) {
  63                 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
  64                         struct perf_mem_event *e = &perf_mem_events[j];
  65 
  66                         if (strstr(e->tag, tok))
  67                                 e->record = found = true;
  68                 }
  69 
  70                 tok = strtok_r(NULL, ",", &saveptr);
  71         }
  72 
  73         free(buf);
  74 
  75         if (found)
  76                 return 0;
  77 
  78         pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
  79         return -1;
  80 }
  81 
  82 int perf_mem_events__init(void)
  83 {
  84         const char *mnt = sysfs__mount();
  85         bool found = false;
  86         int j;
  87 
  88         if (!mnt)
  89                 return -ENOENT;
  90 
  91         for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
  92                 char path[PATH_MAX];
  93                 struct perf_mem_event *e = &perf_mem_events[j];
  94                 struct stat st;
  95 
  96                 scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
  97                           mnt, e->sysfs_name);
  98 
  99                 if (!stat(path, &st))
 100                         e->supported = found = true;
 101         }
 102 
 103         return found ? 0 : -ENOENT;
 104 }
 105 
 106 static const char * const tlb_access[] = {
 107         "N/A",
 108         "HIT",
 109         "MISS",
 110         "L1",
 111         "L2",
 112         "Walker",
 113         "Fault",
 114 };
 115 
 116 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 117 {
 118         size_t l = 0, i;
 119         u64 m = PERF_MEM_TLB_NA;
 120         u64 hit, miss;
 121 
 122         sz -= 1; /* -1 for null termination */
 123         out[0] = '\0';
 124 
 125         if (mem_info)
 126                 m = mem_info->data_src.mem_dtlb;
 127 
 128         hit = m & PERF_MEM_TLB_HIT;
 129         miss = m & PERF_MEM_TLB_MISS;
 130 
 131         /* already taken care of */
 132         m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
 133 
 134         for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
 135                 if (!(m & 0x1))
 136                         continue;
 137                 if (l) {
 138                         strcat(out, " or ");
 139                         l += 4;
 140                 }
 141                 l += scnprintf(out + l, sz - l, tlb_access[i]);
 142         }
 143         if (*out == '\0')
 144                 l += scnprintf(out, sz - l, "N/A");
 145         if (hit)
 146                 l += scnprintf(out + l, sz - l, " hit");
 147         if (miss)
 148                 l += scnprintf(out + l, sz - l, " miss");
 149 
 150         return l;
 151 }
 152 
 153 static const char * const mem_lvl[] = {
 154         "N/A",
 155         "HIT",
 156         "MISS",
 157         "L1",
 158         "LFB",
 159         "L2",
 160         "L3",
 161         "Local RAM",
 162         "Remote RAM (1 hop)",
 163         "Remote RAM (2 hops)",
 164         "Remote Cache (1 hop)",
 165         "Remote Cache (2 hops)",
 166         "I/O",
 167         "Uncached",
 168 };
 169 
 170 static const char * const mem_lvlnum[] = {
 171         [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
 172         [PERF_MEM_LVLNUM_LFB] = "LFB",
 173         [PERF_MEM_LVLNUM_RAM] = "RAM",
 174         [PERF_MEM_LVLNUM_PMEM] = "PMEM",
 175         [PERF_MEM_LVLNUM_NA] = "N/A",
 176 };
 177 
 178 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 179 {
 180         size_t i, l = 0;
 181         u64 m =  PERF_MEM_LVL_NA;
 182         u64 hit, miss;
 183         int printed;
 184 
 185         if (mem_info)
 186                 m  = mem_info->data_src.mem_lvl;
 187 
 188         sz -= 1; /* -1 for null termination */
 189         out[0] = '\0';
 190 
 191         hit = m & PERF_MEM_LVL_HIT;
 192         miss = m & PERF_MEM_LVL_MISS;
 193 
 194         /* already taken care of */
 195         m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
 196 
 197 
 198         if (mem_info && mem_info->data_src.mem_remote) {
 199                 strcat(out, "Remote ");
 200                 l += 7;
 201         }
 202 
 203         printed = 0;
 204         for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
 205                 if (!(m & 0x1))
 206                         continue;
 207                 if (printed++) {
 208                         strcat(out, " or ");
 209                         l += 4;
 210                 }
 211                 l += scnprintf(out + l, sz - l, mem_lvl[i]);
 212         }
 213 
 214         if (mem_info && mem_info->data_src.mem_lvl_num) {
 215                 int lvl = mem_info->data_src.mem_lvl_num;
 216                 if (printed++) {
 217                         strcat(out, " or ");
 218                         l += 4;
 219                 }
 220                 if (mem_lvlnum[lvl])
 221                         l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
 222                 else
 223                         l += scnprintf(out + l, sz - l, "L%d", lvl);
 224         }
 225 
 226         if (l == 0)
 227                 l += scnprintf(out + l, sz - l, "N/A");
 228         if (hit)
 229                 l += scnprintf(out + l, sz - l, " hit");
 230         if (miss)
 231                 l += scnprintf(out + l, sz - l, " miss");
 232 
 233         return l;
 234 }
 235 
 236 static const char * const snoop_access[] = {
 237         "N/A",
 238         "None",
 239         "Hit",
 240         "Miss",
 241         "HitM",
 242 };
 243 
 244 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 245 {
 246         size_t i, l = 0;
 247         u64 m = PERF_MEM_SNOOP_NA;
 248 
 249         sz -= 1; /* -1 for null termination */
 250         out[0] = '\0';
 251 
 252         if (mem_info)
 253                 m = mem_info->data_src.mem_snoop;
 254 
 255         for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
 256                 if (!(m & 0x1))
 257                         continue;
 258                 if (l) {
 259                         strcat(out, " or ");
 260                         l += 4;
 261                 }
 262                 l += scnprintf(out + l, sz - l, snoop_access[i]);
 263         }
 264         if (mem_info &&
 265              (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
 266                 if (l) {
 267                         strcat(out, " or ");
 268                         l += 4;
 269                 }
 270                 l += scnprintf(out + l, sz - l, "Fwd");
 271         }
 272 
 273         if (*out == '\0')
 274                 l += scnprintf(out, sz - l, "N/A");
 275 
 276         return l;
 277 }
 278 
 279 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 280 {
 281         u64 mask = PERF_MEM_LOCK_NA;
 282         int l;
 283 
 284         if (mem_info)
 285                 mask = mem_info->data_src.mem_lock;
 286 
 287         if (mask & PERF_MEM_LOCK_NA)
 288                 l = scnprintf(out, sz, "N/A");
 289         else if (mask & PERF_MEM_LOCK_LOCKED)
 290                 l = scnprintf(out, sz, "Yes");
 291         else
 292                 l = scnprintf(out, sz, "No");
 293 
 294         return l;
 295 }
 296 
 297 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 298 {
 299         int i = 0;
 300 
 301         i += perf_mem__lvl_scnprintf(out, sz, mem_info);
 302         i += scnprintf(out + i, sz - i, "|SNP ");
 303         i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
 304         i += scnprintf(out + i, sz - i, "|TLB ");
 305         i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
 306         i += scnprintf(out + i, sz - i, "|LCK ");
 307         i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
 308 
 309         return i;
 310 }
 311 
 312 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
 313 {
 314         union perf_mem_data_src *data_src = &mi->data_src;
 315         u64 daddr  = mi->daddr.addr;
 316         u64 op     = data_src->mem_op;
 317         u64 lvl    = data_src->mem_lvl;
 318         u64 snoop  = data_src->mem_snoop;
 319         u64 lock   = data_src->mem_lock;
 320         /*
 321          * Skylake might report unknown remote level via this
 322          * bit, consider it when evaluating remote HITMs.
 323          */
 324         bool mrem  = data_src->mem_remote;
 325         int err = 0;
 326 
 327 #define HITM_INC(__f)           \
 328 do {                            \
 329         stats->__f++;           \
 330         stats->tot_hitm++;      \
 331 } while (0)
 332 
 333 #define P(a, b) PERF_MEM_##a##_##b
 334 
 335         stats->nr_entries++;
 336 
 337         if (lock & P(LOCK, LOCKED)) stats->locks++;
 338 
 339         if (op & P(OP, LOAD)) {
 340                 /* load */
 341                 stats->load++;
 342 
 343                 if (!daddr) {
 344                         stats->ld_noadrs++;
 345                         return -1;
 346                 }
 347 
 348                 if (lvl & P(LVL, HIT)) {
 349                         if (lvl & P(LVL, UNC)) stats->ld_uncache++;
 350                         if (lvl & P(LVL, IO))  stats->ld_io++;
 351                         if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
 352                         if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
 353                         if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
 354                         if (lvl & P(LVL, L3 )) {
 355                                 if (snoop & P(SNOOP, HITM))
 356                                         HITM_INC(lcl_hitm);
 357                                 else
 358                                         stats->ld_llchit++;
 359                         }
 360 
 361                         if (lvl & P(LVL, LOC_RAM)) {
 362                                 stats->lcl_dram++;
 363                                 if (snoop & P(SNOOP, HIT))
 364                                         stats->ld_shared++;
 365                                 else
 366                                         stats->ld_excl++;
 367                         }
 368 
 369                         if ((lvl & P(LVL, REM_RAM1)) ||
 370                             (lvl & P(LVL, REM_RAM2)) ||
 371                              mrem) {
 372                                 stats->rmt_dram++;
 373                                 if (snoop & P(SNOOP, HIT))
 374                                         stats->ld_shared++;
 375                                 else
 376                                         stats->ld_excl++;
 377                         }
 378                 }
 379 
 380                 if ((lvl & P(LVL, REM_CCE1)) ||
 381                     (lvl & P(LVL, REM_CCE2)) ||
 382                      mrem) {
 383                         if (snoop & P(SNOOP, HIT))
 384                                 stats->rmt_hit++;
 385                         else if (snoop & P(SNOOP, HITM))
 386                                 HITM_INC(rmt_hitm);
 387                 }
 388 
 389                 if ((lvl & P(LVL, MISS)))
 390                         stats->ld_miss++;
 391 
 392         } else if (op & P(OP, STORE)) {
 393                 /* store */
 394                 stats->store++;
 395 
 396                 if (!daddr) {
 397                         stats->st_noadrs++;
 398                         return -1;
 399                 }
 400 
 401                 if (lvl & P(LVL, HIT)) {
 402                         if (lvl & P(LVL, UNC)) stats->st_uncache++;
 403                         if (lvl & P(LVL, L1 )) stats->st_l1hit++;
 404                 }
 405                 if (lvl & P(LVL, MISS))
 406                         if (lvl & P(LVL, L1)) stats->st_l1miss++;
 407         } else {
 408                 /* unparsable data_src? */
 409                 stats->noparse++;
 410                 return -1;
 411         }
 412 
 413         if (!mi->daddr.map || !mi->iaddr.map) {
 414                 stats->nomap++;
 415                 return -1;
 416         }
 417 
 418 #undef P
 419 #undef HITM_INC
 420         return err;
 421 }
 422 
 423 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
 424 {
 425         stats->nr_entries       += add->nr_entries;
 426 
 427         stats->locks            += add->locks;
 428         stats->store            += add->store;
 429         stats->st_uncache       += add->st_uncache;
 430         stats->st_noadrs        += add->st_noadrs;
 431         stats->st_l1hit         += add->st_l1hit;
 432         stats->st_l1miss        += add->st_l1miss;
 433         stats->load             += add->load;
 434         stats->ld_excl          += add->ld_excl;
 435         stats->ld_shared        += add->ld_shared;
 436         stats->ld_uncache       += add->ld_uncache;
 437         stats->ld_io            += add->ld_io;
 438         stats->ld_miss          += add->ld_miss;
 439         stats->ld_noadrs        += add->ld_noadrs;
 440         stats->ld_fbhit         += add->ld_fbhit;
 441         stats->ld_l1hit         += add->ld_l1hit;
 442         stats->ld_l2hit         += add->ld_l2hit;
 443         stats->ld_llchit        += add->ld_llchit;
 444         stats->lcl_hitm         += add->lcl_hitm;
 445         stats->rmt_hitm         += add->rmt_hitm;
 446         stats->tot_hitm         += add->tot_hitm;
 447         stats->rmt_hit          += add->rmt_hit;
 448         stats->lcl_dram         += add->lcl_dram;
 449         stats->rmt_dram         += add->rmt_dram;
 450         stats->nomap            += add->nomap;
 451         stats->noparse          += add->noparse;
 452 }

/* [<][>][^][v][top][bottom][index][help] */