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