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 * 67 pmu_alias_get(const char *name) 68 { 69 struct pmu_alias *pa; 70 71 for (pa = pmu_alias_table; pa->pa_alias != NULL; pa++) 72 if (strcasecmp(name, pa->pa_alias) == 0) 73 return (pa->pa_name); 74 return (name); 75 } 76 77 struct pmu_event_desc { 78 uint64_t ped_period; 79 uint64_t ped_offcore_rsp; 80 uint32_t ped_event; 81 uint32_t ped_frontend; 82 uint32_t ped_ldlat; 83 uint32_t ped_config1; 84 uint8_t ped_umask; 85 uint8_t ped_cmask; 86 uint8_t ped_any; 87 uint8_t ped_inv; 88 uint8_t ped_edge; 89 uint8_t ped_fc_mask; 90 uint8_t ped_ch_mask; 91 }; 92 93 static const struct pmu_events_map * 94 pmu_events_map_get(void) 95 { 96 size_t s; 97 char buf[64]; 98 const struct pmu_events_map *pme; 99 100 if (sysctlbyname("kern.hwpmc.cpuid", (void *)NULL, &s, 101 (void *)NULL, 0) == -1) 102 return (NULL); 103 if (sysctlbyname("kern.hwpmc.cpuid", buf, &s, 104 (void *)NULL, 0) == -1) 105 return (NULL); 106 for (pme = pmu_events_map; pme->cpuid != NULL; pme++) 107 if (strcmp(buf, pme->cpuid) == 0) 108 return (pme); 109 return (NULL); 110 } 111 112 static const struct pmu_event * 113 pmu_event_get(const char *event_name, int *idx) 114 { 115 const struct pmu_events_map *pme; 116 const struct pmu_event *pe; 117 int i; 118 119 if ((pme = pmu_events_map_get()) == NULL) 120 return (NULL); 121 for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) { 122 if (pe->name == NULL) 123 continue; 124 if (strcasecmp(pe->name, event_name) == 0) { 125 if (idx) 126 *idx = i; 127 return (pe); 128 } 129 } 130 return (NULL); 131 } 132 133 const char * 134 pmu_event_get_by_idx(int idx) 135 { 136 const struct pmu_events_map *pme; 137 const struct pmu_event *pe; 138 int i; 139 140 if ((pme = pmu_events_map_get()) == NULL) 141 return (NULL); 142 for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++) 143 ; 144 return (pe->name); 145 } 146 147 static int 148 pmu_parse_event(struct pmu_event_desc *ped, const char *eventin) 149 { 150 char *event; 151 char *kvp, *key, *value; 152 char *debug; 153 154 if ((event = strdup(eventin)) == NULL) 155 return (ENOMEM); 156 bzero(ped, sizeof(*ped)); 157 while ((kvp = strsep(&event, ",")) != NULL) { 158 key = strsep(&kvp, "="); 159 if (key == NULL) 160 abort(); 161 value = kvp; 162 if (strcmp(key, "umask") == 0) 163 ped->ped_umask = strtol(value, NULL, 16); 164 else if (strcmp(key, "event") == 0) 165 ped->ped_event = strtol(value, NULL, 16); 166 else if (strcmp(key, "period") == 0) 167 ped->ped_period = strtol(value, NULL, 10); 168 else if (strcmp(key, "offcore_rsp") == 0) 169 ped->ped_offcore_rsp = strtol(value, NULL, 16); 170 else if (strcmp(key, "any") == 0) 171 ped->ped_any = strtol(value, NULL, 10); 172 else if (strcmp(key, "cmask") == 0) 173 ped->ped_cmask = strtol(value, NULL, 10); 174 else if (strcmp(key, "inv") == 0) 175 ped->ped_inv = strtol(value, NULL, 10); 176 else if (strcmp(key, "edge") == 0) 177 ped->ped_edge = strtol(value, NULL, 10); 178 else if (strcmp(key, "frontend") == 0) 179 ped->ped_frontend = strtol(value, NULL, 16); 180 else if (strcmp(key, "ldlat") == 0) 181 ped->ped_ldlat = strtol(value, NULL, 16); 182 else if (strcmp(key, "fc_mask") == 0) 183 ped->ped_fc_mask = strtol(value, NULL, 16); 184 else if (strcmp(key, "ch_mask") == 0) 185 ped->ped_ch_mask = strtol(value, NULL, 16); 186 else if (strcmp(key, "config1") == 0) 187 ped->ped_config1 = strtol(value, NULL, 16); 188 else { 189 debug = getenv("PMUDEBUG"); 190 if (debug != NULL && strcmp(debug, "true") == 0 && value != NULL) 191 printf("unrecognized kvpair: %s:%s\n", key, value); 192 } 193 } 194 free(event); 195 return (0); 196 } 197 198 uint64_t 199 pmc_pmu_sample_rate_get(const char *event_name) 200 { 201 const struct pmu_event *pe; 202 struct pmu_event_desc ped; 203 204 event_name = pmu_alias_get(event_name); 205 if ((pe = pmu_event_get(event_name, NULL)) == NULL) 206 return (DEFAULT_SAMPLE_COUNT); 207 if (pe->alias && (pe = pmu_event_get(pe->alias, NULL)) == NULL) 208 return (DEFAULT_SAMPLE_COUNT); 209 if (pe->event == NULL) 210 return (DEFAULT_SAMPLE_COUNT); 211 if (pmu_parse_event(&ped, pe->event)) 212 return (DEFAULT_SAMPLE_COUNT); 213 return (ped.ped_period); 214 } 215 216 int 217 pmc_pmu_enabled(void) 218 { 219 220 return (pmu_events_map_get() != NULL); 221 } 222 223 void 224 pmc_pmu_print_counters(void) 225 { 226 const struct pmu_events_map *pme; 227 const struct pmu_event *pe; 228 struct pmu_event_desc ped; 229 char *debug; 230 int do_debug; 231 232 debug = getenv("PMUDEBUG"); 233 do_debug = 0; 234 235 if (debug != NULL && strcmp(debug, "true") == 0) 236 do_debug = 1; 237 if ((pme = pmu_events_map_get()) == NULL) 238 return; 239 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 240 if (pe->name == NULL) 241 continue; 242 printf("\t%s\n", pe->name); 243 if (do_debug) 244 pmu_parse_event(&ped, pe->event); 245 } 246 } 247 248 void 249 pmc_pmu_print_counter_desc(const char *ev) 250 { 251 const struct pmu_events_map *pme; 252 const struct pmu_event *pe; 253 254 if ((pme = pmu_events_map_get()) == NULL) 255 return; 256 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 257 if (pe->name == NULL) 258 continue; 259 if (strcasestr(pe->name, ev) != NULL && 260 pe->desc != NULL) 261 printf("%s:\t%s\n", pe->name, pe->desc); 262 } 263 } 264 265 void 266 pmc_pmu_print_counter_desc_long(const char *ev) 267 { 268 const struct pmu_events_map *pme; 269 const struct pmu_event *pe; 270 271 if ((pme = pmu_events_map_get()) == NULL) 272 return; 273 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 274 if (pe->name == NULL) 275 continue; 276 if (strcasestr(pe->name, ev) != NULL) { 277 if (pe->long_desc != NULL) 278 printf("%s:\n%s\n", pe->name, pe->long_desc); 279 else if (pe->desc != NULL) 280 printf("%s:\t%s\n", pe->name, pe->desc); 281 } 282 } 283 } 284 285 int 286 pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm) 287 { 288 const struct pmu_event *pe; 289 struct pmu_event_desc ped; 290 struct pmc_md_iap_op_pmcallocate *iap; 291 int idx; 292 293 iap = &pm->pm_md.pm_iap; 294 bzero(iap, sizeof(*iap)); 295 event_name = pmu_alias_get(event_name); 296 if ((pe = pmu_event_get(event_name, &idx)) == NULL) 297 return (ENOENT); 298 if (pe->alias && (pe = pmu_event_get(pe->alias, &idx)) == NULL) 299 return (ENOENT); 300 if (pe->event == NULL) 301 return (ENOENT); 302 if (pmu_parse_event(&ped, pe->event)) 303 return (ENOENT); 304 305 pm->pm_class = PMC_CLASS_IAP; 306 pm->pm_ev = idx; 307 iap->pm_iap_config |= IAP_EVSEL(ped.ped_event); 308 iap->pm_iap_config |= IAP_UMASK(ped.ped_umask); 309 iap->pm_iap_config |= IAP_CMASK(ped.ped_cmask); 310 iap->pm_iap_rsp = ped.ped_offcore_rsp; 311 312 iap->pm_iap_config |= (IAP_USR | IAP_OS); 313 if (ped.ped_edge) 314 iap->pm_iap_config |= IAP_EDGE; 315 if (ped.ped_any) 316 iap->pm_iap_config |= IAP_ANY; 317 if (ped.ped_inv) 318 iap->pm_iap_config |= IAP_EDGE; 319 if (pm->pm_caps & PMC_CAP_INTERRUPT) 320 iap->pm_iap_config |= IAP_INT; 321 return (0); 322 } 323 324 #else 325 uint64_t pmc_pmu_sample_rate_get(const char *event_name __unused) { return (DEFAULT_SAMPLE_COUNT); } 326 void pmc_pmu_print_counters(void) {} 327 void pmc_pmu_print_counter_desc(const char *e __unused) {} 328 void pmc_pmu_print_counter_desc_long(const char *e __unused) {} 329 int pmc_pmu_enabled(void) { return (0); } 330 int pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unused) { return (EOPNOTSUPP); } 331 const char *pmu_event_get_by_idx(int idx __unused) { return (NULL); } 332 333 #endif 334