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 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 170 { 171 size_t i, l = 0; 172 u64 m = PERF_MEM_LVL_NA; 173 u64 hit, miss; 174 175 if (mem_info) 176 m = mem_info->data_src.mem_lvl; 177 178 sz -= 1; /* -1 for null termination */ 179 out[0] = '\0'; 180 181 hit = m & PERF_MEM_LVL_HIT; 182 miss = m & PERF_MEM_LVL_MISS; 183 184 /* already taken care of */ 185 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); 186 187 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { 188 if (!(m & 0x1)) 189 continue; 190 if (l) { 191 strcat(out, " or "); 192 l += 4; 193 } 194 l += scnprintf(out + l, sz - l, mem_lvl[i]); 195 } 196 if (*out == '\0') 197 l += scnprintf(out, sz - l, "N/A"); 198 if (hit) 199 l += scnprintf(out + l, sz - l, " hit"); 200 if (miss) 201 l += scnprintf(out + l, sz - l, " miss"); 202 203 return l; 204 } 205 206 static const char * const snoop_access[] = { 207 "N/A", 208 "None", 209 "Miss", 210 "Hit", 211 "HitM", 212 }; 213 214 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 215 { 216 size_t i, l = 0; 217 u64 m = PERF_MEM_SNOOP_NA; 218 219 sz -= 1; /* -1 for null termination */ 220 out[0] = '\0'; 221 222 if (mem_info) 223 m = mem_info->data_src.mem_snoop; 224 225 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) { 226 if (!(m & 0x1)) 227 continue; 228 if (l) { 229 strcat(out, " or "); 230 l += 4; 231 } 232 l += scnprintf(out + l, sz - l, snoop_access[i]); 233 } 234 235 if (*out == '\0') 236 l += scnprintf(out, sz - l, "N/A"); 237 238 return l; 239 } 240 241 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 242 { 243 u64 mask = PERF_MEM_LOCK_NA; 244 int l; 245 246 if (mem_info) 247 mask = mem_info->data_src.mem_lock; 248 249 if (mask & PERF_MEM_LOCK_NA) 250 l = scnprintf(out, sz, "N/A"); 251 else if (mask & PERF_MEM_LOCK_LOCKED) 252 l = scnprintf(out, sz, "Yes"); 253 else 254 l = scnprintf(out, sz, "No"); 255 256 return l; 257 } 258 259 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 260 { 261 int i = 0; 262 263 i += perf_mem__lvl_scnprintf(out, sz, mem_info); 264 i += scnprintf(out + i, sz - i, "|SNP "); 265 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info); 266 i += scnprintf(out + i, sz - i, "|TLB "); 267 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info); 268 i += scnprintf(out + i, sz - i, "|LCK "); 269 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info); 270 271 return i; 272 } 273 274 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi) 275 { 276 union perf_mem_data_src *data_src = &mi->data_src; 277 u64 daddr = mi->daddr.addr; 278 u64 op = data_src->mem_op; 279 u64 lvl = data_src->mem_lvl; 280 u64 snoop = data_src->mem_snoop; 281 u64 lock = data_src->mem_lock; 282 int err = 0; 283 284 #define HITM_INC(__f) \ 285 do { \ 286 stats->__f++; \ 287 stats->tot_hitm++; \ 288 } while (0) 289 290 #define P(a, b) PERF_MEM_##a##_##b 291 292 stats->nr_entries++; 293 294 if (lock & P(LOCK, LOCKED)) stats->locks++; 295 296 if (op & P(OP, LOAD)) { 297 /* load */ 298 stats->load++; 299 300 if (!daddr) { 301 stats->ld_noadrs++; 302 return -1; 303 } 304 305 if (lvl & P(LVL, HIT)) { 306 if (lvl & P(LVL, UNC)) stats->ld_uncache++; 307 if (lvl & P(LVL, IO)) stats->ld_io++; 308 if (lvl & P(LVL, LFB)) stats->ld_fbhit++; 309 if (lvl & P(LVL, L1 )) stats->ld_l1hit++; 310 if (lvl & P(LVL, L2 )) stats->ld_l2hit++; 311 if (lvl & P(LVL, L3 )) { 312 if (snoop & P(SNOOP, HITM)) 313 HITM_INC(lcl_hitm); 314 else 315 stats->ld_llchit++; 316 } 317 318 if (lvl & P(LVL, LOC_RAM)) { 319 stats->lcl_dram++; 320 if (snoop & P(SNOOP, HIT)) 321 stats->ld_shared++; 322 else 323 stats->ld_excl++; 324 } 325 326 if ((lvl & P(LVL, REM_RAM1)) || 327 (lvl & P(LVL, REM_RAM2))) { 328 stats->rmt_dram++; 329 if (snoop & P(SNOOP, HIT)) 330 stats->ld_shared++; 331 else 332 stats->ld_excl++; 333 } 334 } 335 336 if ((lvl & P(LVL, REM_CCE1)) || 337 (lvl & P(LVL, REM_CCE2))) { 338 if (snoop & P(SNOOP, HIT)) 339 stats->rmt_hit++; 340 else if (snoop & P(SNOOP, HITM)) 341 HITM_INC(rmt_hitm); 342 } 343 344 if ((lvl & P(LVL, MISS))) 345 stats->ld_miss++; 346 347 } else if (op & P(OP, STORE)) { 348 /* store */ 349 stats->store++; 350 351 if (!daddr) { 352 stats->st_noadrs++; 353 return -1; 354 } 355 356 if (lvl & P(LVL, HIT)) { 357 if (lvl & P(LVL, UNC)) stats->st_uncache++; 358 if (lvl & P(LVL, L1 )) stats->st_l1hit++; 359 } 360 if (lvl & P(LVL, MISS)) 361 if (lvl & P(LVL, L1)) stats->st_l1miss++; 362 } else { 363 /* unparsable data_src? */ 364 stats->noparse++; 365 return -1; 366 } 367 368 if (!mi->daddr.map || !mi->iaddr.map) { 369 stats->nomap++; 370 return -1; 371 } 372 373 #undef P 374 #undef HITM_INC 375 return err; 376 } 377 378 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add) 379 { 380 stats->nr_entries += add->nr_entries; 381 382 stats->locks += add->locks; 383 stats->store += add->store; 384 stats->st_uncache += add->st_uncache; 385 stats->st_noadrs += add->st_noadrs; 386 stats->st_l1hit += add->st_l1hit; 387 stats->st_l1miss += add->st_l1miss; 388 stats->load += add->load; 389 stats->ld_excl += add->ld_excl; 390 stats->ld_shared += add->ld_shared; 391 stats->ld_uncache += add->ld_uncache; 392 stats->ld_io += add->ld_io; 393 stats->ld_miss += add->ld_miss; 394 stats->ld_noadrs += add->ld_noadrs; 395 stats->ld_fbhit += add->ld_fbhit; 396 stats->ld_l1hit += add->ld_l1hit; 397 stats->ld_l2hit += add->ld_l2hit; 398 stats->ld_llchit += add->ld_llchit; 399 stats->lcl_hitm += add->lcl_hitm; 400 stats->rmt_hitm += add->rmt_hitm; 401 stats->tot_hitm += add->tot_hitm; 402 stats->rmt_hit += add->rmt_hit; 403 stats->lcl_dram += add->lcl_dram; 404 stats->rmt_dram += add->rmt_dram; 405 stats->nomap += add->nomap; 406 stats->noparse += add->noparse; 407 } 408