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