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 "mem-events.h"
  12#include "debug.h"
  13#include "symbol.h"
  14#include "sort.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
 106static const char * const tlb_access[] = {
 107        "N/A",
 108        "HIT",
 109        "MISS",
 110        "L1",
 111        "L2",
 112        "Walker",
 113        "Fault",
 114};
 115
 116int 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
 153static 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
 170static 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
 178int 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
 236static const char * const snoop_access[] = {
 237        "N/A",
 238        "None",
 239        "Hit",
 240        "Miss",
 241        "HitM",
 242};
 243
 244int 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
 279int 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
 297int 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
 312int 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)           \
 328do {                            \
 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
 423void 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}
 453