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