linux/tools/perf/util/mem-events.c
<<
>>
Prefs
   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
  16unsigned int perf_mem_events__loads_ldlat = 30;
  17
  18#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
  19
  20struct 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
  28static char mem_loads_name[100];
  29static bool mem_loads_name__init;
  30
  31char * __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
  46int 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
  82int 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
 106void perf_mem_events__list(void)
 107{
 108        int j;
 109
 110        for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 111                struct perf_mem_event *e = &perf_mem_events[j];
 112
 113                fprintf(stderr, "%-13s%-*s%s\n",
 114                        e->tag,
 115                        verbose > 0 ? 25 : 0,
 116                        verbose > 0 ? perf_mem_events__name(j) : "",
 117                        e->supported ? ": available" : "");
 118        }
 119}
 120
 121static const char * const tlb_access[] = {
 122        "N/A",
 123        "HIT",
 124        "MISS",
 125        "L1",
 126        "L2",
 127        "Walker",
 128        "Fault",
 129};
 130
 131int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 132{
 133        size_t l = 0, i;
 134        u64 m = PERF_MEM_TLB_NA;
 135        u64 hit, miss;
 136
 137        sz -= 1; /* -1 for null termination */
 138        out[0] = '\0';
 139
 140        if (mem_info)
 141                m = mem_info->data_src.mem_dtlb;
 142
 143        hit = m & PERF_MEM_TLB_HIT;
 144        miss = m & PERF_MEM_TLB_MISS;
 145
 146        /* already taken care of */
 147        m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
 148
 149        for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
 150                if (!(m & 0x1))
 151                        continue;
 152                if (l) {
 153                        strcat(out, " or ");
 154                        l += 4;
 155                }
 156                l += scnprintf(out + l, sz - l, tlb_access[i]);
 157        }
 158        if (*out == '\0')
 159                l += scnprintf(out, sz - l, "N/A");
 160        if (hit)
 161                l += scnprintf(out + l, sz - l, " hit");
 162        if (miss)
 163                l += scnprintf(out + l, sz - l, " miss");
 164
 165        return l;
 166}
 167
 168static const char * const mem_lvl[] = {
 169        "N/A",
 170        "HIT",
 171        "MISS",
 172        "L1",
 173        "LFB",
 174        "L2",
 175        "L3",
 176        "Local RAM",
 177        "Remote RAM (1 hop)",
 178        "Remote RAM (2 hops)",
 179        "Remote Cache (1 hop)",
 180        "Remote Cache (2 hops)",
 181        "I/O",
 182        "Uncached",
 183};
 184
 185static const char * const mem_lvlnum[] = {
 186        [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
 187        [PERF_MEM_LVLNUM_LFB] = "LFB",
 188        [PERF_MEM_LVLNUM_RAM] = "RAM",
 189        [PERF_MEM_LVLNUM_PMEM] = "PMEM",
 190        [PERF_MEM_LVLNUM_NA] = "N/A",
 191};
 192
 193int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 194{
 195        size_t i, l = 0;
 196        u64 m =  PERF_MEM_LVL_NA;
 197        u64 hit, miss;
 198        int printed;
 199
 200        if (mem_info)
 201                m  = mem_info->data_src.mem_lvl;
 202
 203        sz -= 1; /* -1 for null termination */
 204        out[0] = '\0';
 205
 206        hit = m & PERF_MEM_LVL_HIT;
 207        miss = m & PERF_MEM_LVL_MISS;
 208
 209        /* already taken care of */
 210        m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
 211
 212
 213        if (mem_info && mem_info->data_src.mem_remote) {
 214                strcat(out, "Remote ");
 215                l += 7;
 216        }
 217
 218        printed = 0;
 219        for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
 220                if (!(m & 0x1))
 221                        continue;
 222                if (printed++) {
 223                        strcat(out, " or ");
 224                        l += 4;
 225                }
 226                l += scnprintf(out + l, sz - l, mem_lvl[i]);
 227        }
 228
 229        if (mem_info && mem_info->data_src.mem_lvl_num) {
 230                int lvl = mem_info->data_src.mem_lvl_num;
 231                if (printed++) {
 232                        strcat(out, " or ");
 233                        l += 4;
 234                }
 235                if (mem_lvlnum[lvl])
 236                        l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
 237                else
 238                        l += scnprintf(out + l, sz - l, "L%d", lvl);
 239        }
 240
 241        if (l == 0)
 242                l += scnprintf(out + l, sz - l, "N/A");
 243        if (hit)
 244                l += scnprintf(out + l, sz - l, " hit");
 245        if (miss)
 246                l += scnprintf(out + l, sz - l, " miss");
 247
 248        return l;
 249}
 250
 251static const char * const snoop_access[] = {
 252        "N/A",
 253        "None",
 254        "Hit",
 255        "Miss",
 256        "HitM",
 257};
 258
 259int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 260{
 261        size_t i, l = 0;
 262        u64 m = PERF_MEM_SNOOP_NA;
 263
 264        sz -= 1; /* -1 for null termination */
 265        out[0] = '\0';
 266
 267        if (mem_info)
 268                m = mem_info->data_src.mem_snoop;
 269
 270        for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
 271                if (!(m & 0x1))
 272                        continue;
 273                if (l) {
 274                        strcat(out, " or ");
 275                        l += 4;
 276                }
 277                l += scnprintf(out + l, sz - l, snoop_access[i]);
 278        }
 279        if (mem_info &&
 280             (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
 281                if (l) {
 282                        strcat(out, " or ");
 283                        l += 4;
 284                }
 285                l += scnprintf(out + l, sz - l, "Fwd");
 286        }
 287
 288        if (*out == '\0')
 289                l += scnprintf(out, sz - l, "N/A");
 290
 291        return l;
 292}
 293
 294int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 295{
 296        u64 mask = PERF_MEM_LOCK_NA;
 297        int l;
 298
 299        if (mem_info)
 300                mask = mem_info->data_src.mem_lock;
 301
 302        if (mask & PERF_MEM_LOCK_NA)
 303                l = scnprintf(out, sz, "N/A");
 304        else if (mask & PERF_MEM_LOCK_LOCKED)
 305                l = scnprintf(out, sz, "Yes");
 306        else
 307                l = scnprintf(out, sz, "No");
 308
 309        return l;
 310}
 311
 312int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 313{
 314        int i = 0;
 315
 316        i += perf_mem__lvl_scnprintf(out, sz, mem_info);
 317        i += scnprintf(out + i, sz - i, "|SNP ");
 318        i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
 319        i += scnprintf(out + i, sz - i, "|TLB ");
 320        i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
 321        i += scnprintf(out + i, sz - i, "|LCK ");
 322        i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
 323
 324        return i;
 325}
 326
 327int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
 328{
 329        union perf_mem_data_src *data_src = &mi->data_src;
 330        u64 daddr  = mi->daddr.addr;
 331        u64 op     = data_src->mem_op;
 332        u64 lvl    = data_src->mem_lvl;
 333        u64 snoop  = data_src->mem_snoop;
 334        u64 lock   = data_src->mem_lock;
 335        /*
 336         * Skylake might report unknown remote level via this
 337         * bit, consider it when evaluating remote HITMs.
 338         */
 339        bool mrem  = data_src->mem_remote;
 340        int err = 0;
 341
 342#define HITM_INC(__f)           \
 343do {                            \
 344        stats->__f++;           \
 345        stats->tot_hitm++;      \
 346} while (0)
 347
 348#define P(a, b) PERF_MEM_##a##_##b
 349
 350        stats->nr_entries++;
 351
 352        if (lock & P(LOCK, LOCKED)) stats->locks++;
 353
 354        if (op & P(OP, LOAD)) {
 355                /* load */
 356                stats->load++;
 357
 358                if (!daddr) {
 359                        stats->ld_noadrs++;
 360                        return -1;
 361                }
 362
 363                if (lvl & P(LVL, HIT)) {
 364                        if (lvl & P(LVL, UNC)) stats->ld_uncache++;
 365                        if (lvl & P(LVL, IO))  stats->ld_io++;
 366                        if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
 367                        if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
 368                        if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
 369                        if (lvl & P(LVL, L3 )) {
 370                                if (snoop & P(SNOOP, HITM))
 371                                        HITM_INC(lcl_hitm);
 372                                else
 373                                        stats->ld_llchit++;
 374                        }
 375
 376                        if (lvl & P(LVL, LOC_RAM)) {
 377                                stats->lcl_dram++;
 378                                if (snoop & P(SNOOP, HIT))
 379                                        stats->ld_shared++;
 380                                else
 381                                        stats->ld_excl++;
 382                        }
 383
 384                        if ((lvl & P(LVL, REM_RAM1)) ||
 385                            (lvl & P(LVL, REM_RAM2)) ||
 386                             mrem) {
 387                                stats->rmt_dram++;
 388                                if (snoop & P(SNOOP, HIT))
 389                                        stats->ld_shared++;
 390                                else
 391                                        stats->ld_excl++;
 392                        }
 393                }
 394
 395                if ((lvl & P(LVL, REM_CCE1)) ||
 396                    (lvl & P(LVL, REM_CCE2)) ||
 397                     mrem) {
 398                        if (snoop & P(SNOOP, HIT))
 399                                stats->rmt_hit++;
 400                        else if (snoop & P(SNOOP, HITM))
 401                                HITM_INC(rmt_hitm);
 402                }
 403
 404                if ((lvl & P(LVL, MISS)))
 405                        stats->ld_miss++;
 406
 407        } else if (op & P(OP, STORE)) {
 408                /* store */
 409                stats->store++;
 410
 411                if (!daddr) {
 412                        stats->st_noadrs++;
 413                        return -1;
 414                }
 415
 416                if (lvl & P(LVL, HIT)) {
 417                        if (lvl & P(LVL, UNC)) stats->st_uncache++;
 418                        if (lvl & P(LVL, L1 )) stats->st_l1hit++;
 419                }
 420                if (lvl & P(LVL, MISS))
 421                        if (lvl & P(LVL, L1)) stats->st_l1miss++;
 422        } else {
 423                /* unparsable data_src? */
 424                stats->noparse++;
 425                return -1;
 426        }
 427
 428        if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
 429                stats->nomap++;
 430                return -1;
 431        }
 432
 433#undef P
 434#undef HITM_INC
 435        return err;
 436}
 437
 438void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
 439{
 440        stats->nr_entries       += add->nr_entries;
 441
 442        stats->locks            += add->locks;
 443        stats->store            += add->store;
 444        stats->st_uncache       += add->st_uncache;
 445        stats->st_noadrs        += add->st_noadrs;
 446        stats->st_l1hit         += add->st_l1hit;
 447        stats->st_l1miss        += add->st_l1miss;
 448        stats->load             += add->load;
 449        stats->ld_excl          += add->ld_excl;
 450        stats->ld_shared        += add->ld_shared;
 451        stats->ld_uncache       += add->ld_uncache;
 452        stats->ld_io            += add->ld_io;
 453        stats->ld_miss          += add->ld_miss;
 454        stats->ld_noadrs        += add->ld_noadrs;
 455        stats->ld_fbhit         += add->ld_fbhit;
 456        stats->ld_l1hit         += add->ld_l1hit;
 457        stats->ld_l2hit         += add->ld_l2hit;
 458        stats->ld_llchit        += add->ld_llchit;
 459        stats->lcl_hitm         += add->lcl_hitm;
 460        stats->rmt_hitm         += add->rmt_hitm;
 461        stats->tot_hitm         += add->tot_hitm;
 462        stats->rmt_hit          += add->rmt_hit;
 463        stats->lcl_dram         += add->lcl_dram;
 464        stats->rmt_dram         += add->rmt_dram;
 465        stats->nomap            += add->nomap;
 466        stats->noparse          += add->noparse;
 467}
 468