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 15 unsigned int perf_mem_events__loads_ldlat = 30; 16 17 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s } 18 19 struct 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 27 static char mem_loads_name[100]; 28 static bool mem_loads_name__init; 29 30 char *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 45 int 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 81 int 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 105 static const char * const tlb_access[] = { 106 "N/A", 107 "HIT", 108 "MISS", 109 "L1", 110 "L2", 111 "Walker", 112 "Fault", 113 }; 114 115 int 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 152 static 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 169 static const char * const mem_lvlnum[] = { 170 [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache", 171 [PERF_MEM_LVLNUM_LFB] = "LFB", 172 [PERF_MEM_LVLNUM_RAM] = "RAM", 173 [PERF_MEM_LVLNUM_PMEM] = "PMEM", 174 [PERF_MEM_LVLNUM_NA] = "N/A", 175 }; 176 177 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 178 { 179 size_t i, l = 0; 180 u64 m = PERF_MEM_LVL_NA; 181 u64 hit, miss; 182 int printed; 183 184 if (mem_info) 185 m = mem_info->data_src.mem_lvl; 186 187 sz -= 1; /* -1 for null termination */ 188 out[0] = '\0'; 189 190 hit = m & PERF_MEM_LVL_HIT; 191 miss = m & PERF_MEM_LVL_MISS; 192 193 /* already taken care of */ 194 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); 195 196 197 if (mem_info && mem_info->data_src.mem_remote) { 198 strcat(out, "Remote "); 199 l += 7; 200 } 201 202 printed = 0; 203 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { 204 if (!(m & 0x1)) 205 continue; 206 if (printed++) { 207 strcat(out, " or "); 208 l += 4; 209 } 210 l += scnprintf(out + l, sz - l, mem_lvl[i]); 211 } 212 213 if (mem_info && mem_info->data_src.mem_lvl_num) { 214 int lvl = mem_info->data_src.mem_lvl_num; 215 if (printed++) { 216 strcat(out, " or "); 217 l += 4; 218 } 219 if (mem_lvlnum[lvl]) 220 l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]); 221 else 222 l += scnprintf(out + l, sz - l, "L%d", lvl); 223 } 224 225 if (l == 0) 226 l += scnprintf(out + l, sz - l, "N/A"); 227 if (hit) 228 l += scnprintf(out + l, sz - l, " hit"); 229 if (miss) 230 l += scnprintf(out + l, sz - l, " miss"); 231 232 return l; 233 } 234 235 static const char * const snoop_access[] = { 236 "N/A", 237 "None", 238 "Hit", 239 "Miss", 240 "HitM", 241 }; 242 243 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 244 { 245 size_t i, l = 0; 246 u64 m = PERF_MEM_SNOOP_NA; 247 248 sz -= 1; /* -1 for null termination */ 249 out[0] = '\0'; 250 251 if (mem_info) 252 m = mem_info->data_src.mem_snoop; 253 254 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) { 255 if (!(m & 0x1)) 256 continue; 257 if (l) { 258 strcat(out, " or "); 259 l += 4; 260 } 261 l += scnprintf(out + l, sz - l, snoop_access[i]); 262 } 263 if (mem_info && 264 (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) { 265 if (l) { 266 strcat(out, " or "); 267 l += 4; 268 } 269 l += scnprintf(out + l, sz - l, "Fwd"); 270 } 271 272 if (*out == '\0') 273 l += scnprintf(out, sz - l, "N/A"); 274 275 return l; 276 } 277 278 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 279 { 280 u64 mask = PERF_MEM_LOCK_NA; 281 int l; 282 283 if (mem_info) 284 mask = mem_info->data_src.mem_lock; 285 286 if (mask & PERF_MEM_LOCK_NA) 287 l = scnprintf(out, sz, "N/A"); 288 else if (mask & PERF_MEM_LOCK_LOCKED) 289 l = scnprintf(out, sz, "Yes"); 290 else 291 l = scnprintf(out, sz, "No"); 292 293 return l; 294 } 295 296 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 297 { 298 int i = 0; 299 300 i += perf_mem__lvl_scnprintf(out, sz, mem_info); 301 i += scnprintf(out + i, sz - i, "|SNP "); 302 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info); 303 i += scnprintf(out + i, sz - i, "|TLB "); 304 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info); 305 i += scnprintf(out + i, sz - i, "|LCK "); 306 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info); 307 308 return i; 309 } 310 311 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi) 312 { 313 union perf_mem_data_src *data_src = &mi->data_src; 314 u64 daddr = mi->daddr.addr; 315 u64 op = data_src->mem_op; 316 u64 lvl = data_src->mem_lvl; 317 u64 snoop = data_src->mem_snoop; 318 u64 lock = data_src->mem_lock; 319 /* 320 * Skylake might report unknown remote level via this 321 * bit, consider it when evaluating remote HITMs. 322 */ 323 bool mrem = data_src->mem_remote; 324 int err = 0; 325 326 #define HITM_INC(__f) \ 327 do { \ 328 stats->__f++; \ 329 stats->tot_hitm++; \ 330 } while (0) 331 332 #define P(a, b) PERF_MEM_##a##_##b 333 334 stats->nr_entries++; 335 336 if (lock & P(LOCK, LOCKED)) stats->locks++; 337 338 if (op & P(OP, LOAD)) { 339 /* load */ 340 stats->load++; 341 342 if (!daddr) { 343 stats->ld_noadrs++; 344 return -1; 345 } 346 347 if (lvl & P(LVL, HIT)) { 348 if (lvl & P(LVL, UNC)) stats->ld_uncache++; 349 if (lvl & P(LVL, IO)) stats->ld_io++; 350 if (lvl & P(LVL, LFB)) stats->ld_fbhit++; 351 if (lvl & P(LVL, L1 )) stats->ld_l1hit++; 352 if (lvl & P(LVL, L2 )) stats->ld_l2hit++; 353 if (lvl & P(LVL, L3 )) { 354 if (snoop & P(SNOOP, HITM)) 355 HITM_INC(lcl_hitm); 356 else 357 stats->ld_llchit++; 358 } 359 360 if (lvl & P(LVL, LOC_RAM)) { 361 stats->lcl_dram++; 362 if (snoop & P(SNOOP, HIT)) 363 stats->ld_shared++; 364 else 365 stats->ld_excl++; 366 } 367 368 if ((lvl & P(LVL, REM_RAM1)) || 369 (lvl & P(LVL, REM_RAM2)) || 370 mrem) { 371 stats->rmt_dram++; 372 if (snoop & P(SNOOP, HIT)) 373 stats->ld_shared++; 374 else 375 stats->ld_excl++; 376 } 377 } 378 379 if ((lvl & P(LVL, REM_CCE1)) || 380 (lvl & P(LVL, REM_CCE2)) || 381 mrem) { 382 if (snoop & P(SNOOP, HIT)) 383 stats->rmt_hit++; 384 else if (snoop & P(SNOOP, HITM)) 385 HITM_INC(rmt_hitm); 386 } 387 388 if ((lvl & P(LVL, MISS))) 389 stats->ld_miss++; 390 391 } else if (op & P(OP, STORE)) { 392 /* store */ 393 stats->store++; 394 395 if (!daddr) { 396 stats->st_noadrs++; 397 return -1; 398 } 399 400 if (lvl & P(LVL, HIT)) { 401 if (lvl & P(LVL, UNC)) stats->st_uncache++; 402 if (lvl & P(LVL, L1 )) stats->st_l1hit++; 403 } 404 if (lvl & P(LVL, MISS)) 405 if (lvl & P(LVL, L1)) stats->st_l1miss++; 406 } else { 407 /* unparsable data_src? */ 408 stats->noparse++; 409 return -1; 410 } 411 412 if (!mi->daddr.map || !mi->iaddr.map) { 413 stats->nomap++; 414 return -1; 415 } 416 417 #undef P 418 #undef HITM_INC 419 return err; 420 } 421 422 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add) 423 { 424 stats->nr_entries += add->nr_entries; 425 426 stats->locks += add->locks; 427 stats->store += add->store; 428 stats->st_uncache += add->st_uncache; 429 stats->st_noadrs += add->st_noadrs; 430 stats->st_l1hit += add->st_l1hit; 431 stats->st_l1miss += add->st_l1miss; 432 stats->load += add->load; 433 stats->ld_excl += add->ld_excl; 434 stats->ld_shared += add->ld_shared; 435 stats->ld_uncache += add->ld_uncache; 436 stats->ld_io += add->ld_io; 437 stats->ld_miss += add->ld_miss; 438 stats->ld_noadrs += add->ld_noadrs; 439 stats->ld_fbhit += add->ld_fbhit; 440 stats->ld_l1hit += add->ld_l1hit; 441 stats->ld_l2hit += add->ld_l2hit; 442 stats->ld_llchit += add->ld_llchit; 443 stats->lcl_hitm += add->lcl_hitm; 444 stats->rmt_hitm += add->rmt_hitm; 445 stats->tot_hitm += add->tot_hitm; 446 stats->rmt_hit += add->rmt_hit; 447 stats->lcl_dram += add->lcl_dram; 448 stats->rmt_dram += add->rmt_dram; 449 stats->nomap += add->nomap; 450 stats->noparse += add->noparse; 451 } 452