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 void 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 121 static const char * const tlb_access[] = { 122 "N/A", 123 "HIT", 124 "MISS", 125 "L1", 126 "L2", 127 "Walker", 128 "Fault", 129 }; 130 131 int 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 168 static 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 185 static 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 193 int 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 251 static const char * const snoop_access[] = { 252 "N/A", 253 "None", 254 "Hit", 255 "Miss", 256 "HitM", 257 }; 258 259 int 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 294 int 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 312 int 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 327 int 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) \ 343 do { \ 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 438 void 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