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