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