1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018, Matthew Macy 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 * 29 */ 30 31 #include <sys/types.h> 32 #include <sys/errno.h> 33 #include <sys/sysctl.h> 34 #include <stddef.h> 35 #include <stdlib.h> 36 #include <limits.h> 37 #include <string.h> 38 #include <pmc.h> 39 #include <pmclog.h> 40 #include <libpmcstat.h> 41 #include "pmu-events/pmu-events.h" 42 43 #if defined(__amd64__) 44 struct pmu_alias { 45 const char *pa_alias; 46 const char *pa_name; 47 }; 48 static struct pmu_alias pmu_alias_table[] = { 49 { "UNHALTED_CORE_CYCLES", "CPU_CLK_UNHALTED.THREAD_P_ANY"}, 50 { "UNHALTED-CORE-CYCLES", "CPU_CLK_UNHALTED.THREAD_P_ANY"}, 51 { "LLC_MISSES", "LONGEST_LAT_CACHE.MISS"}, 52 { "LLC-MISSES", "LONGEST_LAT_CACHE.MISS"}, 53 { "LLC_REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"}, 54 { "LLC-REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"}, 55 { "LLC_MISS_RHITM", "mem_load_l3_miss_retired.remote_hitm"}, 56 { "LLC-MISS-RHITM", "mem_load_l3_miss_retired.remote_hitm"}, 57 { "RESOURCE_STALL", "RESOURCE_STALLS.ANY"}, 58 { "RESOURCE_STALLS_ANY", "RESOURCE_STALLS.ANY"}, 59 { "BRANCH_INSTRUCTION_RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"}, 60 { "BRANCH-INSTRUCTION-RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"}, 61 { "BRANCH_MISSES_RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"}, 62 { "BRANCH-MISSES-RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"}, 63 { NULL, NULL }, 64 }; 65 66 static const char *fixed_mode_cntrs[] = { 67 "inst_retired.any", 68 "cpu_clk_unhalted.thread", 69 "cpu_clk_unhalted.thread_any", 70 "cpu_clk_unhalted.ref_tsc", 71 NULL 72 }; 73 74 static const char * 75 pmu_alias_get(const char *name) 76 { 77 struct pmu_alias *pa; 78 79 for (pa = pmu_alias_table; pa->pa_alias != NULL; pa++) 80 if (strcasecmp(name, pa->pa_alias) == 0) 81 return (pa->pa_name); 82 return (name); 83 } 84 85 struct pmu_event_desc { 86 uint64_t ped_period; 87 uint64_t ped_offcore_rsp; 88 uint32_t ped_event; 89 uint32_t ped_frontend; 90 uint32_t ped_ldlat; 91 uint32_t ped_config1; 92 uint8_t ped_umask; 93 uint8_t ped_cmask; 94 uint8_t ped_any; 95 uint8_t ped_inv; 96 uint8_t ped_edge; 97 uint8_t ped_fc_mask; 98 uint8_t ped_ch_mask; 99 }; 100 101 static const struct pmu_events_map * 102 pmu_events_map_get(void) 103 { 104 size_t s; 105 char buf[64]; 106 const struct pmu_events_map *pme; 107 108 if (sysctlbyname("kern.hwpmc.cpuid", (void *)NULL, &s, 109 (void *)NULL, 0) == -1) 110 return (NULL); 111 if (sysctlbyname("kern.hwpmc.cpuid", buf, &s, 112 (void *)NULL, 0) == -1) 113 return (NULL); 114 for (pme = pmu_events_map; pme->cpuid != NULL; pme++) 115 if (strcmp(buf, pme->cpuid) == 0) 116 return (pme); 117 return (NULL); 118 } 119 120 static const struct pmu_event * 121 pmu_event_get(const char *event_name, int *idx) 122 { 123 const struct pmu_events_map *pme; 124 const struct pmu_event *pe; 125 int i; 126 127 if ((pme = pmu_events_map_get()) == NULL) 128 return (NULL); 129 for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) { 130 if (pe->name == NULL) 131 continue; 132 if (strcasecmp(pe->name, event_name) == 0) { 133 if (idx) 134 *idx = i; 135 return (pe); 136 } 137 } 138 return (NULL); 139 } 140 141 const char * 142 pmc_pmu_event_get_by_idx(int idx) 143 { 144 const struct pmu_events_map *pme; 145 const struct pmu_event *pe; 146 int i; 147 148 if ((pme = pmu_events_map_get()) == NULL) 149 return (NULL); 150 for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++) 151 ; 152 return (pe->name); 153 } 154 155 static int 156 pmu_parse_event(struct pmu_event_desc *ped, const char *eventin) 157 { 158 char *event; 159 char *kvp, *key, *value, *r; 160 char *debug; 161 162 if ((event = strdup(eventin)) == NULL) 163 return (ENOMEM); 164 r = event; 165 bzero(ped, sizeof(*ped)); 166 while ((kvp = strsep(&event, ",")) != NULL) { 167 key = strsep(&kvp, "="); 168 if (key == NULL) 169 abort(); 170 value = kvp; 171 if (strcmp(key, "umask") == 0) 172 ped->ped_umask = strtol(value, NULL, 16); 173 else if (strcmp(key, "event") == 0) 174 ped->ped_event = strtol(value, NULL, 16); 175 else if (strcmp(key, "period") == 0) 176 ped->ped_period = strtol(value, NULL, 10); 177 else if (strcmp(key, "offcore_rsp") == 0) 178 ped->ped_offcore_rsp = strtol(value, NULL, 16); 179 else if (strcmp(key, "any") == 0) 180 ped->ped_any = strtol(value, NULL, 10); 181 else if (strcmp(key, "cmask") == 0) 182 ped->ped_cmask = strtol(value, NULL, 10); 183 else if (strcmp(key, "inv") == 0) 184 ped->ped_inv = strtol(value, NULL, 10); 185 else if (strcmp(key, "edge") == 0) 186 ped->ped_edge = strtol(value, NULL, 10); 187 else if (strcmp(key, "frontend") == 0) 188 ped->ped_frontend = strtol(value, NULL, 16); 189 else if (strcmp(key, "ldlat") == 0) 190 ped->ped_ldlat = strtol(value, NULL, 16); 191 else if (strcmp(key, "fc_mask") == 0) 192 ped->ped_fc_mask = strtol(value, NULL, 16); 193 else if (strcmp(key, "ch_mask") == 0) 194 ped->ped_ch_mask = strtol(value, NULL, 16); 195 else if (strcmp(key, "config1") == 0) 196 ped->ped_config1 = strtol(value, NULL, 16); 197 else { 198 debug = getenv("PMUDEBUG"); 199 if (debug != NULL && strcmp(debug, "true") == 0 && value != NULL) 200 printf("unrecognized kvpair: %s:%s\n", key, value); 201 } 202 } 203 free(r); 204 return (0); 205 } 206 207 uint64_t 208 pmc_pmu_sample_rate_get(const char *event_name) 209 { 210 const struct pmu_event *pe; 211 struct pmu_event_desc ped; 212 213 event_name = pmu_alias_get(event_name); 214 if ((pe = pmu_event_get(event_name, NULL)) == NULL) 215 return (DEFAULT_SAMPLE_COUNT); 216 if (pe->alias && (pe = pmu_event_get(pe->alias, NULL)) == NULL) 217 return (DEFAULT_SAMPLE_COUNT); 218 if (pe->event == NULL) 219 return (DEFAULT_SAMPLE_COUNT); 220 if (pmu_parse_event(&ped, pe->event)) 221 return (DEFAULT_SAMPLE_COUNT); 222 return (ped.ped_period); 223 } 224 225 int 226 pmc_pmu_enabled(void) 227 { 228 229 return (pmu_events_map_get() != NULL); 230 } 231 232 void 233 pmc_pmu_print_counters(void) 234 { 235 const struct pmu_events_map *pme; 236 const struct pmu_event *pe; 237 struct pmu_event_desc ped; 238 char *debug; 239 int do_debug; 240 241 debug = getenv("PMUDEBUG"); 242 do_debug = 0; 243 244 if (debug != NULL && strcmp(debug, "true") == 0) 245 do_debug = 1; 246 if ((pme = pmu_events_map_get()) == NULL) 247 return; 248 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 249 if (pe->name == NULL) 250 continue; 251 printf("\t%s\n", pe->name); 252 if (do_debug) 253 pmu_parse_event(&ped, pe->event); 254 } 255 } 256 257 void 258 pmc_pmu_print_counter_desc(const char *ev) 259 { 260 const struct pmu_events_map *pme; 261 const struct pmu_event *pe; 262 263 if ((pme = pmu_events_map_get()) == NULL) 264 return; 265 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 266 if (pe->name == NULL) 267 continue; 268 if (strcasestr(pe->name, ev) != NULL && 269 pe->desc != NULL) 270 printf("%s:\t%s\n", pe->name, pe->desc); 271 } 272 } 273 274 void 275 pmc_pmu_print_counter_desc_long(const char *ev) 276 { 277 const struct pmu_events_map *pme; 278 const struct pmu_event *pe; 279 280 if ((pme = pmu_events_map_get()) == NULL) 281 return; 282 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 283 if (pe->name == NULL) 284 continue; 285 if (strcasestr(pe->name, ev) != NULL) { 286 if (pe->long_desc != NULL) 287 printf("%s:\n%s\n", pe->name, pe->long_desc); 288 else if (pe->desc != NULL) 289 printf("%s:\t%s\n", pe->name, pe->desc); 290 } 291 } 292 } 293 294 int 295 pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm) 296 { 297 const struct pmu_event *pe; 298 struct pmu_event_desc ped; 299 struct pmc_md_iap_op_pmcallocate *iap; 300 struct pmc_md_iaf_op_pmcallocate *iaf; 301 int idx, isfixed; 302 303 iap = &pm->pm_md.pm_iap; 304 iaf = &pm->pm_md.pm_iaf; 305 isfixed = 0; 306 bzero(iap, sizeof(*iap)); 307 event_name = pmu_alias_get(event_name); 308 pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE); 309 if ((pe = pmu_event_get(event_name, &idx)) == NULL) 310 return (ENOENT); 311 if (pe->alias && (pe = pmu_event_get(pe->alias, &idx)) == NULL) 312 return (ENOENT); 313 if (pe->event == NULL) 314 return (ENOENT); 315 if (pmu_parse_event(&ped, pe->event)) 316 return (ENOENT); 317 318 for (idx = 0; fixed_mode_cntrs[idx] != NULL; idx++) 319 if (strcmp(fixed_mode_cntrs[idx], event_name) == 0) 320 isfixed = 1; 321 322 if (isfixed) { 323 if (strcasestr(pe->desc, "retired") != NULL) 324 pm->pm_ev = PMC_EV_IAF_INSTR_RETIRED_ANY; 325 else if (strcasestr(pe->desc, "core") != NULL || 326 strcasestr(pe->desc, "unhalted")) 327 pm->pm_ev = PMC_EV_IAF_CPU_CLK_UNHALTED_CORE; 328 else if (strcasestr(pe->desc, "ref") != NULL) 329 pm->pm_ev = PMC_EV_IAF_CPU_CLK_UNHALTED_REF; 330 iaf->pm_iaf_flags |= (IAF_USR | IAF_OS); 331 if (ped.ped_any) 332 iaf->pm_iaf_flags |= IAF_ANY; 333 if (pm->pm_caps & PMC_CAP_INTERRUPT) 334 iaf->pm_iaf_flags |= IAF_PMI; 335 pm->pm_class = PMC_CLASS_IAF; 336 return (0); 337 } 338 pm->pm_caps |= PMC_CAP_QUALIFIER; 339 pm->pm_class = PMC_CLASS_IAP; 340 pm->pm_ev = idx; 341 iap->pm_iap_config |= IAP_EVSEL(ped.ped_event); 342 iap->pm_iap_config |= IAP_UMASK(ped.ped_umask); 343 iap->pm_iap_config |= IAP_CMASK(ped.ped_cmask); 344 iap->pm_iap_rsp = ped.ped_offcore_rsp; 345 346 iap->pm_iap_config |= (IAP_USR | IAP_OS); 347 if (ped.ped_edge) 348 iap->pm_iap_config |= IAP_EDGE; 349 if (ped.ped_any) 350 iap->pm_iap_config |= IAP_ANY; 351 if (ped.ped_inv) 352 iap->pm_iap_config |= IAP_EDGE; 353 if (pm->pm_caps & PMC_CAP_INTERRUPT) 354 iap->pm_iap_config |= IAP_INT; 355 return (0); 356 } 357 358 /* 359 * Ultimately rely on AMD calling theirs the same 360 */ 361 static const char *stat_mode_cntrs[] = { 362 "cpu_clk_unhalted.thread_any", 363 "inst_retired.any", 364 "br_inst_retired.all_branches", 365 "br_misp_retired.all_branches", 366 "longest_lat_cache.reference", 367 "longest_lat_cache.miss", 368 }; 369 370 int 371 pmc_pmu_stat_mode(const char ***cntrs) 372 { 373 if (pmc_pmu_enabled()) { 374 *cntrs = stat_mode_cntrs; 375 return (0); 376 } 377 return (EOPNOTSUPP); 378 } 379 380 #else 381 uint64_t pmc_pmu_sample_rate_get(const char *event_name __unused) { return (DEFAULT_SAMPLE_COUNT); } 382 void pmc_pmu_print_counters(void) {} 383 void pmc_pmu_print_counter_desc(const char *e __unused) {} 384 void pmc_pmu_print_counter_desc_long(const char *e __unused) {} 385 int pmc_pmu_enabled(void) { return (0); } 386 int pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unused) { return (EOPNOTSUPP); } 387 const char *pmc_pmu_event_get_by_idx(int idx __unused) { return (NULL); } 388 int pmc_pmu_stat_mode(const char ***a __unused) { return (EOPNOTSUPP); } 389 390 #endif 391