xref: /illumos-gate/usr/src/lib/libcpc/common/obsoleted.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/syscall.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <stdarg.h>
39 #include <signal.h>
40 #include <libintl.h>
41 #include <dirent.h>
42 #include <sys/cpc_impl.h>
43 
44 #include "libcpc.h"
45 #include "libcpc_impl.h"
46 
47 /*
48  * CPC library handle for use by CPCv1 implementation.
49  */
50 cpc_t *__cpc = NULL;
51 mutex_t __cpc_lock;		/* protects __cpc handle */
52 int __cpc_v1_cpuver;		/* CPU version in use by CPCv1 client */
53 
54 #ifdef __sparc
55 uint64_t __cpc_v1_pcr;		/* last bound %pcr value */
56 #else
57 uint32_t __cpc_v1_pes[2];	/* last bound %pes values */
58 #endif /* __sparc */
59 
60 int
61 __cpc_init(void)
62 {
63 	const char *fn = "__cpc_init";
64 	extern cpc_t *__cpc;	/* CPC handle for obsolete clients to share */
65 
66 	(void) mutex_lock(&__cpc_lock);
67 	if (__cpc == NULL && (__cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
68 		__cpc_error(fn, dgettext(TEXT_DOMAIN,
69 		    "Couldn't open CPC library handle\n"));
70 		(void) mutex_unlock(&__cpc_lock);
71 		return (-1);
72 	}
73 	(void) mutex_unlock(&__cpc_lock);
74 
75 	return (0);
76 }
77 
78 int
79 cpc_bind_event(cpc_event_t *this, int flags)
80 {
81 	cpc_set_t		*set;
82 	cpc_request_t		*rp;
83 	int			ret;
84 
85 	if (this == NULL) {
86 		(void) cpc_rele();
87 		return (0);
88 	}
89 
90 	if (__cpc_init() != 0) {
91 		errno = ENXIO;
92 		return (-1);
93 	}
94 
95 	/*
96 	 * The cpuver and control fields of the cpc_event_t must be saved off
97 	 * for later. The user may call cpc_take_sample(), expecting these to
98 	 * be copied into a different cpc_event_t struct by the kernel. We have
99 	 * to fake that behavior for CPCv1 clients.
100 	 */
101 	__cpc_v1_cpuver = this->ce_cpuver;
102 #ifdef __sparc
103 	__cpc_v1_pcr = this->ce_pcr;
104 #else
105 	__cpc_v1_pes[0] = this->ce_pes[0];
106 	__cpc_v1_pes[1] = this->ce_pes[1];
107 #endif /* __sparc */
108 
109 	if ((set = __cpc_eventtoset(__cpc, this, flags)) == NULL) {
110 		errno = EINVAL;
111 		return (-1);
112 	}
113 
114 	/*
115 	 * Convert flags to CPC2.
116 	 */
117 	if (flags & CPC_BIND_EMT_OVF) {
118 		for (rp = set->cs_request; rp != NULL; rp = rp->cr_next)
119 			rp->cr_flags |= CPC_OVF_NOTIFY_EMT;
120 		flags &= ~CPC_BIND_EMT_OVF;
121 	}
122 
123 	ret = cpc_bind_curlwp(__cpc, set, flags);
124 
125 	(void) cpc_set_destroy(__cpc, set);
126 
127 	return (ret);
128 }
129 
130 int
131 cpc_take_sample(cpc_event_t *this)
132 {
133 	this->ce_cpuver = __cpc_v1_cpuver;
134 #ifdef __sparc
135 	this->ce_pcr = __cpc_v1_pcr;
136 #else
137 	this->ce_pes[0] = __cpc_v1_pes[0];
138 	this->ce_pes[1] = __cpc_v1_pes[1];
139 #endif /* __sparc */
140 
141 	return (syscall(SYS_cpc, CPC_SAMPLE, -1, this->ce_pic, &this->ce_hrt,
142 	    &CPC_TICKREG(this), 0));
143 }
144 
145 int
146 cpc_count_usr_events(int enable)
147 {
148 	return (syscall(SYS_cpc, CPC_USR_EVENTS, -1, enable, 0));
149 }
150 
151 int
152 cpc_count_sys_events(int enable)
153 {
154 	return (syscall(SYS_cpc, CPC_SYS_EVENTS, -1, enable, 0));
155 }
156 
157 int
158 cpc_rele(void)
159 {
160 	return (syscall(SYS_cpc, CPC_RELE, -1, NULL, 0));
161 }
162 
163 /*
164  * See if the system call is working and installed.
165  *
166  * We invoke the system call with nonsense arguments - if it's
167  * there and working correctly, it will return EINVAL.
168  *
169  * (This avoids the user getting a SIGSYS core dump when they attempt
170  * to bind on older hardware)
171  */
172 int
173 cpc_access(void)
174 {
175 	void (*handler)(int);
176 	int error = 0;
177 	const char fn[] = "access";
178 
179 	handler = signal(SIGSYS, SIG_IGN);
180 	if (syscall(SYS_cpc, -1, -1, NULL, 0) == -1 &&
181 	    errno != EINVAL)
182 		error = errno;
183 	(void) signal(SIGSYS, handler);
184 
185 	switch (error) {
186 	case EAGAIN:
187 		__cpc_error(fn, dgettext(TEXT_DOMAIN, "Another process may be "
188 		    "sampling system-wide CPU statistics\n"));
189 		break;
190 	case ENOSYS:
191 		__cpc_error(fn,
192 		    dgettext(TEXT_DOMAIN, "CPU performance counters "
193 		    "are inaccessible on this machine\n"));
194 		break;
195 	default:
196 		__cpc_error(fn, "%s\n", strerror(errno));
197 		break;
198 	case 0:
199 		return (0);
200 	}
201 
202 	errno = error;
203 	return (-1);
204 }
205 
206 /*
207  * To look at the system-wide counters, we have to open the
208  * 'shared' device.  Once that device is open, no further contexts
209  * can be installed (though one open is needed per CPU)
210  */
211 int
212 cpc_shared_open(void)
213 {
214 	const char driver[] = CPUDRV_SHARED;
215 
216 	return (open(driver, O_RDWR));
217 }
218 
219 void
220 cpc_shared_close(int fd)
221 {
222 	(void) cpc_shared_rele(fd);
223 	(void) close(fd);
224 }
225 
226 int
227 cpc_shared_bind_event(int fd, cpc_event_t *this, int flags)
228 {
229 	extern cpc_t		*__cpc;
230 	cpc_set_t		*set;
231 	int			ret;
232 	char			*packed_set;
233 	size_t			packsize;
234 	int			subcode;
235 	__cpc_args_t		cpc_args;
236 
237 	if (this == NULL) {
238 		(void) cpc_shared_rele(fd);
239 		return (0);
240 	} else if (flags != 0) {
241 		errno = EINVAL;
242 		return (-1);
243 	}
244 
245 	if (__cpc_init() != 0) {
246 		errno = ENXIO;
247 		return (-1);
248 	}
249 
250 	if ((set = __cpc_eventtoset(__cpc, this, flags)) == NULL) {
251 		errno = EINVAL;
252 		return (-1);
253 	}
254 
255 	__cpc_v1_cpuver = this->ce_cpuver;
256 
257 	if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
258 		errno = ENOMEM;
259 		return (-1);
260 	}
261 
262 	cpc_args.udata1 = packed_set;
263 	cpc_args.udata2 = (void *)packsize;
264 	cpc_args.udata3 = (void *)&subcode;
265 
266 	ret = ioctl(fd, CPCIO_BIND, &cpc_args);
267 
268 	free(packed_set);
269 	(void) cpc_set_destroy(__cpc, set);
270 
271 	return (ret);
272 }
273 
274 int
275 cpc_shared_take_sample(int fd, cpc_event_t *this)
276 {
277 	__cpc_args_t args;
278 
279 	args.udata1 = this->ce_pic;
280 	args.udata2 = &this->ce_hrt;
281 	args.udata3 = &CPC_TICKREG(this);
282 
283 	this->ce_cpuver = __cpc_v1_cpuver;
284 
285 	return (ioctl(fd, CPCIO_SAMPLE, &args));
286 }
287 
288 int
289 cpc_shared_rele(int fd)
290 {
291 	return (ioctl(fd, CPCIO_RELE, 0));
292 }
293 
294 int
295 cpc_pctx_bind_event(pctx_t *pctx, id_t lwpid, cpc_event_t *event, int flags)
296 {
297 	cpc_set_t		*set;
298 	int			ret;
299 
300 	if (event == NULL)
301 		return (cpc_pctx_rele(pctx, lwpid));
302 
303 	if (__cpc_init() != 0) {
304 		errno = ENXIO;
305 		return (-1);
306 	}
307 
308 	else if (flags != 0) {
309 		errno = EINVAL;
310 		return (-1);
311 	}
312 
313 	if ((set = __cpc_eventtoset(__cpc, event, flags)) == NULL) {
314 		errno = EINVAL;
315 		return (-1);
316 	}
317 
318 	/*
319 	 * The cpuver and control fields of the cpc_event_t must be saved off
320 	 * for later. The user may call cpc_take_sample(), expecting these to
321 	 * be copied into a different cpc_event_t struct by the kernel. We have
322 	 * to fake that behavior for CPCv1 clients.
323 	 */
324 	__cpc_v1_cpuver = event->ce_cpuver;
325 
326 	ret = cpc_bind_pctx(__cpc, pctx, lwpid, set, 0);
327 
328 	(void) cpc_set_destroy(__cpc, set);
329 
330 	return (ret);
331 }
332 
333 int
334 cpc_pctx_take_sample(pctx_t *pctx, id_t lwpid, cpc_event_t *event)
335 {
336 	event->ce_cpuver = __cpc_v1_cpuver;
337 
338 	return (__pctx_cpc(pctx, __cpc, CPC_SAMPLE, lwpid, event->ce_pic,
339 	    &event->ce_hrt, &CPC_TICKREG(event), CPC1_BUFSIZE));
340 }
341 
342 /*
343  * Given a process context and an lwpid, mark the CPU performance
344  * counter context as invalid.
345  */
346 int
347 cpc_pctx_invalidate(pctx_t *pctx, id_t lwpid)
348 {
349 	return (__pctx_cpc(pctx, __cpc, CPC_INVALIDATE, lwpid, 0, 0, 0, 0));
350 }
351 
352 /*
353  * Given a process context and an lwpid, remove all our
354  * hardware context from it.
355  */
356 int
357 cpc_pctx_rele(pctx_t *pctx, id_t lwpid)
358 {
359 	return (__pctx_cpc(pctx, __cpc, CPC_RELE, lwpid, 0, 0, 0, 0));
360 }
361 
362 static cpc_errfn_t *__cpc_uerrfn;
363 
364 /*PRINTFLIKE2*/
365 void
366 __cpc_error(const char *fn, const char *fmt, ...)
367 {
368 	va_list ap;
369 
370 	va_start(ap, fmt);
371 	if (__cpc_uerrfn)
372 		__cpc_uerrfn(fn, fmt, ap);
373 	else {
374 		(void) fprintf(stderr, "libcpc: %s: ", fn);
375 		(void) vfprintf(stderr, fmt, ap);
376 	}
377 	va_end(ap);
378 }
379 
380 void
381 cpc_seterrfn(cpc_errfn_t *errfn)
382 {
383 	__cpc_uerrfn = errfn;
384 }
385 
386 /*
387  * cpc_version() is only for CPC1 clients.
388  */
389 uint_t __cpc_workver = CPC_VER_1;
390 
391 uint_t
392 cpc_version(uint_t ver)
393 {
394 	__cpc_workver = CPC_VER_1;
395 
396 	switch (ver) {
397 	case CPC_VER_NONE:
398 	case CPC_VER_CURRENT:
399 		return (CPC_VER_CURRENT);
400 	case CPC_VER_1:
401 		/*
402 		 * As long as the client is using cpc_version() at all, it is
403 		 * a CPCv1 client.  We still allow CPCv1 clients to compile on
404 		 * CPCv2 systems.
405 		 */
406 		return (CPC_VER_1);
407 	}
408 
409 	return (CPC_VER_NONE);
410 }
411