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