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 13 unsigned int perf_mem_events__loads_ldlat = 30; 14 15 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s } 16 17 struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = { 18 E("ldlat-loads", "cpu/mem-loads,ldlat=%u/P", "mem-loads"), 19 E("ldlat-stores", "cpu/mem-stores/P", "mem-stores"), 20 }; 21 #undef E 22 23 #undef E 24 25 static char mem_loads_name[100]; 26 static bool mem_loads_name__init; 27 28 char *perf_mem_events__name(int i) 29 { 30 if (i == PERF_MEM_EVENTS__LOAD) { 31 if (!mem_loads_name__init) { 32 mem_loads_name__init = true; 33 scnprintf(mem_loads_name, sizeof(mem_loads_name), 34 perf_mem_events[i].name, 35 perf_mem_events__loads_ldlat); 36 } 37 return mem_loads_name; 38 } 39 40 return (char *)perf_mem_events[i].name; 41 } 42 43 int perf_mem_events__parse(const char *str) 44 { 45 char *tok, *saveptr = NULL; 46 bool found = false; 47 char *buf; 48 int j; 49 50 /* We need buffer that we know we can write to. */ 51 buf = malloc(strlen(str) + 1); 52 if (!buf) 53 return -ENOMEM; 54 55 strcpy(buf, str); 56 57 tok = strtok_r((char *)buf, ",", &saveptr); 58 59 while (tok) { 60 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { 61 struct perf_mem_event *e = &perf_mem_events[j]; 62 63 if (strstr(e->tag, tok)) 64 e->record = found = true; 65 } 66 67 tok = strtok_r(NULL, ",", &saveptr); 68 } 69 70 free(buf); 71 72 if (found) 73 return 0; 74 75 pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str); 76 return -1; 77 } 78 79 int perf_mem_events__init(void) 80 { 81 const char *mnt = sysfs__mount(); 82 bool found = false; 83 int j; 84 85 if (!mnt) 86 return -ENOENT; 87 88 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { 89 char path[PATH_MAX]; 90 struct perf_mem_event *e = &perf_mem_events[j]; 91 struct stat st; 92 93 scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s", 94 mnt, e->sysfs_name); 95 96 if (!stat(path, &st)) 97 e->supported = found = true; 98 } 99 100 return found ? 0 : -ENOENT; 101 } 102 103 static const char * const tlb_access[] = { 104 "N/A", 105 "HIT", 106 "MISS", 107 "L1", 108 "L2", 109 "Walker", 110 "Fault", 111 }; 112 113 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 114 { 115 size_t l = 0, i; 116 u64 m = PERF_MEM_TLB_NA; 117 u64 hit, miss; 118 119 sz -= 1; /* -1 for null termination */ 120 out[0] = '\0'; 121 122 if (mem_info) 123 m = mem_info->data_src.mem_dtlb; 124 125 hit = m & PERF_MEM_TLB_HIT; 126 miss = m & PERF_MEM_TLB_MISS; 127 128 /* already taken care of */ 129 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); 130 131 for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) { 132 if (!(m & 0x1)) 133 continue; 134 if (l) { 135 strcat(out, " or "); 136 l += 4; 137 } 138 l += scnprintf(out + l, sz - l, tlb_access[i]); 139 } 140 if (*out == '\0') 141 l += scnprintf(out, sz - l, "N/A"); 142 if (hit) 143 l += scnprintf(out + l, sz - l, " hit"); 144 if (miss) 145 l += scnprintf(out + l, sz - l, " miss"); 146 147 return l; 148 } 149 150 static const char * const mem_lvl[] = { 151 "N/A", 152 "HIT", 153 "MISS", 154 "L1", 155 "LFB", 156 "L2", 157 "L3", 158 "Local RAM", 159 "Remote RAM (1 hop)", 160 "Remote RAM (2 hops)", 161 "Remote Cache (1 hop)", 162 "Remote Cache (2 hops)", 163 "I/O", 164 "Uncached", 165 }; 166 167 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 168 { 169 size_t i, l = 0; 170 u64 m = PERF_MEM_LVL_NA; 171 u64 hit, miss; 172 173 if (mem_info) 174 m = mem_info->data_src.mem_lvl; 175 176 sz -= 1; /* -1 for null termination */ 177 out[0] = '\0'; 178 179 hit = m & PERF_MEM_LVL_HIT; 180 miss = m & PERF_MEM_LVL_MISS; 181 182 /* already taken care of */ 183 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); 184 185 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { 186 if (!(m & 0x1)) 187 continue; 188 if (l) { 189 strcat(out, " or "); 190 l += 4; 191 } 192 l += scnprintf(out + l, sz - l, mem_lvl[i]); 193 } 194 if (*out == '\0') 195 l += scnprintf(out, sz - l, "N/A"); 196 if (hit) 197 l += scnprintf(out + l, sz - l, " hit"); 198 if (miss) 199 l += scnprintf(out + l, sz - l, " miss"); 200 201 return l; 202 } 203 204 static const char * const snoop_access[] = { 205 "N/A", 206 "None", 207 "Miss", 208 "Hit", 209 "HitM", 210 }; 211 212 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 213 { 214 size_t i, l = 0; 215 u64 m = PERF_MEM_SNOOP_NA; 216 217 sz -= 1; /* -1 for null termination */ 218 out[0] = '\0'; 219 220 if (mem_info) 221 m = mem_info->data_src.mem_snoop; 222 223 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) { 224 if (!(m & 0x1)) 225 continue; 226 if (l) { 227 strcat(out, " or "); 228 l += 4; 229 } 230 l += scnprintf(out + l, sz - l, snoop_access[i]); 231 } 232 233 if (*out == '\0') 234 l += scnprintf(out, sz - l, "N/A"); 235 236 return l; 237 } 238 239 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 240 { 241 u64 mask = PERF_MEM_LOCK_NA; 242 int l; 243 244 if (mem_info) 245 mask = mem_info->data_src.mem_lock; 246 247 if (mask & PERF_MEM_LOCK_NA) 248 l = scnprintf(out, sz, "N/A"); 249 else if (mask & PERF_MEM_LOCK_LOCKED) 250 l = scnprintf(out, sz, "Yes"); 251 else 252 l = scnprintf(out, sz, "No"); 253 254 return l; 255 } 256 257 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info) 258 { 259 int i = 0; 260 261 i += perf_mem__lvl_scnprintf(out, sz, mem_info); 262 i += scnprintf(out + i, sz - i, "|SNP "); 263 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info); 264 i += scnprintf(out + i, sz - i, "|TLB "); 265 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info); 266 i += scnprintf(out + i, sz - i, "|LCK "); 267 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info); 268 269 return i; 270 } 271