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__) || defined(__i386__) 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 {"cycles", "tsc-tsc"}, 64 {"instructions", "inst-retired.any_p"}, 65 {"branch-mispredicts", "br_misp_retired.all_branches" }, 66 {"branches", "br_inst_retired.all_branches" }, 67 {"interrupts", "hw_interrupts.received"}, 68 {"ic-misses", "frontend_retired.l1i_miss"}, 69 {NULL, NULL}, 70 }; 71 72 static const char *fixed_mode_cntrs[] = { 73 "inst_retired.any", 74 "cpu_clk_unhalted.thread", 75 "cpu_clk_unhalted.thread_any", 76 "cpu_clk_unhalted.ref_tsc", 77 NULL 78 }; 79 80 static const char * 81 pmu_alias_get(const char *name) 82 { 83 struct pmu_alias *pa; 84 85 for (pa = pmu_alias_table; pa->pa_alias != NULL; pa++) 86 if (strcasecmp(name, pa->pa_alias) == 0) 87 return (pa->pa_name); 88 return (name); 89 } 90 91 struct pmu_event_desc { 92 uint64_t ped_period; 93 uint64_t ped_offcore_rsp; 94 uint32_t ped_event; 95 uint32_t ped_frontend; 96 uint32_t ped_ldlat; 97 uint32_t ped_config1; 98 uint8_t ped_umask; 99 uint8_t ped_cmask; 100 uint8_t ped_any; 101 uint8_t ped_inv; 102 uint8_t ped_edge; 103 uint8_t ped_fc_mask; 104 uint8_t ped_ch_mask; 105 }; 106 107 static const struct pmu_events_map * 108 pmu_events_map_get(void) 109 { 110 size_t s; 111 char buf[64]; 112 const struct pmu_events_map *pme; 113 114 if (sysctlbyname("kern.hwpmc.cpuid", (void *)NULL, &s, 115 (void *)NULL, 0) == -1) 116 return (NULL); 117 if (sysctlbyname("kern.hwpmc.cpuid", buf, &s, 118 (void *)NULL, 0) == -1) 119 return (NULL); 120 for (pme = pmu_events_map; pme->cpuid != NULL; pme++) 121 if (strcmp(buf, pme->cpuid) == 0) 122 return (pme); 123 return (NULL); 124 } 125 126 static const struct pmu_event * 127 pmu_event_get(const char *event_name, int *idx) 128 { 129 const struct pmu_events_map *pme; 130 const struct pmu_event *pe; 131 int i; 132 133 if ((pme = pmu_events_map_get()) == NULL) 134 return (NULL); 135 for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) { 136 if (pe->name == NULL) 137 continue; 138 if (strcasecmp(pe->name, event_name) == 0) { 139 if (idx) 140 *idx = i; 141 return (pe); 142 } 143 } 144 return (NULL); 145 } 146 147 const char * 148 pmc_pmu_event_get_by_idx(int idx) 149 { 150 const struct pmu_events_map *pme; 151 const struct pmu_event *pe; 152 int i; 153 154 if ((pme = pmu_events_map_get()) == NULL) 155 return (NULL); 156 for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++); 157 return (pe->name); 158 } 159 160 static int 161 pmu_parse_event(struct pmu_event_desc *ped, const char *eventin) 162 { 163 char *event; 164 char *kvp, *key, *value, *r; 165 char *debug; 166 167 if ((event = strdup(eventin)) == NULL) 168 return (ENOMEM); 169 r = event; 170 bzero(ped, sizeof(*ped)); 171 while ((kvp = strsep(&event, ",")) != NULL) { 172 key = strsep(&kvp, "="); 173 if (key == NULL) 174 abort(); 175 value = kvp; 176 if (strcmp(key, "umask") == 0) 177 ped->ped_umask = strtol(value, NULL, 16); 178 else if (strcmp(key, "event") == 0) 179 ped->ped_event = strtol(value, NULL, 16); 180 else if (strcmp(key, "period") == 0) 181 ped->ped_period = strtol(value, NULL, 10); 182 else if (strcmp(key, "offcore_rsp") == 0) 183 ped->ped_offcore_rsp = strtol(value, NULL, 16); 184 else if (strcmp(key, "any") == 0) 185 ped->ped_any = strtol(value, NULL, 10); 186 else if (strcmp(key, "cmask") == 0) 187 ped->ped_cmask = strtol(value, NULL, 10); 188 else if (strcmp(key, "inv") == 0) 189 ped->ped_inv = strtol(value, NULL, 10); 190 else if (strcmp(key, "edge") == 0) 191 ped->ped_edge = strtol(value, NULL, 10); 192 else if (strcmp(key, "frontend") == 0) 193 ped->ped_frontend = strtol(value, NULL, 16); 194 else if (strcmp(key, "ldlat") == 0) 195 ped->ped_ldlat = strtol(value, NULL, 16); 196 else if (strcmp(key, "fc_mask") == 0) 197 ped->ped_fc_mask = strtol(value, NULL, 16); 198 else if (strcmp(key, "ch_mask") == 0) 199 ped->ped_ch_mask = strtol(value, NULL, 16); 200 else if (strcmp(key, "config1") == 0) 201 ped->ped_config1 = strtol(value, NULL, 16); 202 else { 203 debug = getenv("PMUDEBUG"); 204 if (debug != NULL && strcmp(debug, "true") == 0 && value != NULL) 205 printf("unrecognized kvpair: %s:%s\n", key, value); 206 } 207 } 208 free(r); 209 return (0); 210 } 211 212 uint64_t 213 pmc_pmu_sample_rate_get(const char *event_name) 214 { 215 const struct pmu_event *pe; 216 struct pmu_event_desc ped; 217 218 event_name = pmu_alias_get(event_name); 219 if ((pe = pmu_event_get(event_name, NULL)) == NULL) 220 return (DEFAULT_SAMPLE_COUNT); 221 if (pe->alias && (pe = pmu_event_get(pe->alias, NULL)) == NULL) 222 return (DEFAULT_SAMPLE_COUNT); 223 if (pe->event == NULL) 224 return (DEFAULT_SAMPLE_COUNT); 225 if (pmu_parse_event(&ped, pe->event)) 226 return (DEFAULT_SAMPLE_COUNT); 227 return (ped.ped_period); 228 } 229 230 int 231 pmc_pmu_enabled(void) 232 { 233 234 return (pmu_events_map_get() != NULL); 235 } 236 237 void 238 pmc_pmu_print_counters(const char *event_name) 239 { 240 const struct pmu_events_map *pme; 241 const struct pmu_event *pe; 242 struct pmu_event_desc ped; 243 char *debug; 244 int do_debug; 245 246 debug = getenv("PMUDEBUG"); 247 do_debug = 0; 248 249 if (debug != NULL && strcmp(debug, "true") == 0) 250 do_debug = 1; 251 if ((pme = pmu_events_map_get()) == NULL) 252 return; 253 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 254 if (pe->name == NULL) 255 continue; 256 if (event_name != NULL && strcasestr(pe->name, event_name) == NULL) 257 continue; 258 printf("\t%s\n", pe->name); 259 if (do_debug) 260 pmu_parse_event(&ped, pe->event); 261 } 262 } 263 264 void 265 pmc_pmu_print_counter_desc(const char *ev) 266 { 267 const struct pmu_events_map *pme; 268 const struct pmu_event *pe; 269 270 if ((pme = pmu_events_map_get()) == NULL) 271 return; 272 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 273 if (pe->name == NULL) 274 continue; 275 if (strcasestr(pe->name, ev) != NULL && 276 pe->desc != NULL) 277 printf("%s:\t%s\n", pe->name, pe->desc); 278 } 279 } 280 281 void 282 pmc_pmu_print_counter_desc_long(const char *ev) 283 { 284 const struct pmu_events_map *pme; 285 const struct pmu_event *pe; 286 287 if ((pme = pmu_events_map_get()) == NULL) 288 return; 289 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 290 if (pe->name == NULL) 291 continue; 292 if (strcasestr(pe->name, ev) != NULL) { 293 if (pe->long_desc != NULL) 294 printf("%s:\n%s\n", pe->name, pe->long_desc); 295 else if (pe->desc != NULL) 296 printf("%s:\t%s\n", pe->name, pe->desc); 297 } 298 } 299 } 300 301 void 302 pmc_pmu_print_counter_full(const char *ev) 303 { 304 const struct pmu_events_map *pme; 305 const struct pmu_event *pe; 306 307 if ((pme = pmu_events_map_get()) == NULL) 308 return; 309 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 310 if (pe->name == NULL) 311 continue; 312 if (strcasestr(pe->name, ev) == NULL) 313 continue; 314 printf("name: %s\n", pe->name); 315 if (pe->long_desc != NULL) 316 printf("desc: %s\n", pe->long_desc); 317 else if (pe->desc != NULL) 318 printf("desc: %s\n", pe->desc); 319 if (pe->event != NULL) 320 printf("event: %s\n", pe->event); 321 if (pe->topic != NULL) 322 printf("topic: %s\n", pe->topic); 323 if (pe->pmu != NULL) 324 printf("pmu: %s\n", pe->pmu); 325 if (pe->unit != NULL) 326 printf("unit: %s\n", pe->unit); 327 if (pe->perpkg != NULL) 328 printf("perpkg: %s\n", pe->perpkg); 329 if (pe->metric_expr != NULL) 330 printf("metric_expr: %s\n", pe->metric_expr); 331 if (pe->metric_name != NULL) 332 printf("metric_name: %s\n", pe->metric_name); 333 if (pe->metric_group != NULL) 334 printf("metric_group: %s\n", pe->metric_group); 335 } 336 } 337 338 int 339 pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm) 340 { 341 const struct pmu_event *pe; 342 struct pmu_event_desc ped; 343 struct pmc_md_iap_op_pmcallocate *iap; 344 struct pmc_md_iaf_op_pmcallocate *iaf; 345 int idx, isfixed; 346 347 iap = &pm->pm_md.pm_iap; 348 isfixed = 0; 349 bzero(iap, sizeof(*iap)); 350 event_name = pmu_alias_get(event_name); 351 pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE); 352 if ((pe = pmu_event_get(event_name, &idx)) == NULL) 353 return (ENOENT); 354 if (pe->alias && (pe = pmu_event_get(pe->alias, &idx)) == NULL) 355 return (ENOENT); 356 if (pe->event == NULL) 357 return (ENOENT); 358 if (pmu_parse_event(&ped, pe->event)) 359 return (ENOENT); 360 361 for (idx = 0; fixed_mode_cntrs[idx] != NULL; idx++) 362 if (strcmp(fixed_mode_cntrs[idx], event_name) == 0) 363 isfixed = 1; 364 if (isfixed) { 365 iaf = &pm->pm_md.pm_iaf; 366 pm->pm_class = PMC_CLASS_IAF; 367 if (strcasestr(pe->desc, "retired") != NULL) 368 pm->pm_ev = PMC_EV_IAF_INSTR_RETIRED_ANY; 369 else if (strcasestr(pe->desc, "core") != NULL || 370 strcasestr(pe->desc, "unhalted")) 371 pm->pm_ev = PMC_EV_IAF_CPU_CLK_UNHALTED_CORE; 372 else if (strcasestr(pe->desc, "ref") != NULL) 373 pm->pm_ev = PMC_EV_IAF_CPU_CLK_UNHALTED_REF; 374 iaf->pm_iaf_flags |= (IAF_USR | IAF_OS); 375 if (ped.ped_any) 376 iaf->pm_iaf_flags |= IAF_ANY; 377 if (pm->pm_caps & PMC_CAP_INTERRUPT) 378 iaf->pm_iaf_flags |= IAF_PMI; 379 return (0); 380 } else if (strcasestr(event_name, "UNC_") == event_name || 381 strcasestr(event_name, "uncore") != NULL) { 382 pm->pm_class = PMC_CLASS_UCP; 383 } else { 384 pm->pm_caps |= PMC_CAP_QUALIFIER; 385 pm->pm_class = PMC_CLASS_IAP; 386 } 387 pm->pm_ev = idx; 388 iap->pm_iap_config |= IAP_EVSEL(ped.ped_event); 389 iap->pm_iap_config |= IAP_UMASK(ped.ped_umask); 390 iap->pm_iap_config |= IAP_CMASK(ped.ped_cmask); 391 iap->pm_iap_rsp = ped.ped_offcore_rsp; 392 393 iap->pm_iap_config |= (IAP_USR | IAP_OS); 394 if (ped.ped_edge) 395 iap->pm_iap_config |= IAP_EDGE; 396 if (ped.ped_any) 397 iap->pm_iap_config |= IAP_ANY; 398 if (ped.ped_inv) 399 iap->pm_iap_config |= IAP_EDGE; 400 if (pm->pm_caps & PMC_CAP_INTERRUPT) 401 iap->pm_iap_config |= IAP_INT; 402 return (0); 403 } 404 405 /* 406 * Ultimately rely on AMD calling theirs the same 407 */ 408 static const char *stat_mode_cntrs[] = { 409 "cpu_clk_unhalted.thread_any", 410 "inst_retired.any", 411 "br_inst_retired.all_branches", 412 "br_misp_retired.all_branches", 413 "longest_lat_cache.reference", 414 "longest_lat_cache.miss", 415 }; 416 417 int 418 pmc_pmu_stat_mode(const char ***cntrs) 419 { 420 if (pmc_pmu_enabled()) { 421 *cntrs = stat_mode_cntrs; 422 return (0); 423 } 424 return (EOPNOTSUPP); 425 } 426 427 #else 428 429 uint64_t 430 pmc_pmu_sample_rate_get(const char *event_name __unused) 431 { 432 return (DEFAULT_SAMPLE_COUNT); 433 } 434 435 void 436 pmc_pmu_print_counters(const char *event_name __unused) 437 { 438 } 439 440 void 441 pmc_pmu_print_counter_desc(const char *e __unused) 442 { 443 } 444 445 void 446 pmc_pmu_print_counter_desc_long(const char *e __unused) 447 { 448 } 449 450 void 451 pmc_pmu_print_counter_full(const char *e __unused) 452 { 453 454 } 455 456 int 457 pmc_pmu_enabled(void) 458 { 459 return (0); 460 } 461 462 int 463 pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unused) 464 { 465 return (EOPNOTSUPP); 466 } 467 468 const char * 469 pmc_pmu_event_get_by_idx(int idx __unused) 470 { 471 return (NULL); 472 } 473 int 474 pmc_pmu_stat_mode(const char ***a __unused) 475 { 476 return (EOPNOTSUPP); 477 } 478 479 #endif 480