xref: /freebsd/lib/libpmc/libpmc_pmu_util.c (revision e08e9e999091f86081377b7cedc3fd2fe2ab70fc)
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