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