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 2004 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 <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <strings.h>
33#include <libintl.h>
34#include <sys/types.h>
35
36
37#include <libcpc.h>
38#include "cpucmds.h"
39
40#define	CHARS_PER_REQ 11	/* space required for printing column headers */
41
42/*
43 * These routines are solely used to manage a list of request sets.
44 */
45
46struct __cpc_setgrp {
47	struct setgrp_elem {
48		cpc_set_t	*set;
49		uint8_t		sysonly;	/* All reqs sys-mode only ? */
50		int		nreqs;
51		int		*picnums;	/* picnum used per req */
52		cpc_buf_t	*data1;
53		cpc_buf_t	*data2;
54		cpc_buf_t	*scratch;
55		char *name;
56		char *hdr;
57	} *sets;		/* array of events and names */
58	int nelem;		/* size of array */
59	int current;		/* currently bound event in eventset */
60	int smt;		/* Measures physical events on SMT CPU */
61	int has_sysonly_set;	/* Does this group have a system-only set? */
62	cpc_t *cpc;		/* library handle */
63};
64
65static void *emalloc(size_t n);
66
67cpc_setgrp_t *
68cpc_setgrp_new(cpc_t *cpc, int smt)
69{
70	cpc_setgrp_t *sgrp;
71
72	sgrp = emalloc(sizeof (*sgrp));
73	sgrp->current = -1;
74	sgrp->cpc = cpc;
75	sgrp->smt = smt;
76	sgrp->has_sysonly_set = 0;
77	return (sgrp);
78}
79
80/*
81 * Walker to count the number of requests in a set, and check if any requests
82 * count user-mode events.
83 */
84/*ARGSUSED*/
85static void
86cpc_setgrp_walker(void *arg, int index, const char *event, uint64_t preset,
87    uint_t flags, int nattrs, const cpc_attr_t *attrs)
88{
89	struct setgrp_elem *se = arg;
90
91	se->nreqs++;
92	if (flags & CPC_COUNT_USER)
93		se->sysonly = 0;
94}
95
96/*
97 * Walker to discover the picnums used by the requests in a set.
98 */
99/*ARGSUSED*/
100static void
101cpc_setgrp_picwalker(void *arg, int index, const char *event, uint64_t preset,
102    uint_t flags, int nattrs, const cpc_attr_t *attrs)
103{
104	int *picnums = arg;
105	int i;
106
107	for (i = 0; i < nattrs; i++) {
108		if (strncmp(attrs[i].ca_name, "picnum", 7) == 0)
109			break;
110	}
111	if (i == nattrs)
112		picnums[index] = -1;
113
114	picnums[index] = (int)attrs[i].ca_val;
115}
116
117cpc_setgrp_t *
118cpc_setgrp_newset(cpc_setgrp_t *sgrp, const char *spec, int *errcnt)
119{
120	cpc_set_t		*set;
121	struct setgrp_elem	*new;
122	char			hdr[CHARS_PER_REQ+1];
123	int			i;
124
125	if ((set = cpc_strtoset(sgrp->cpc, spec, sgrp->smt)) == NULL) {
126		*errcnt += 1;
127		return (NULL);
128	}
129
130	if ((new = realloc(sgrp->sets, (1 + sgrp->nelem) * sizeof (*new)))
131	    == NULL) {
132		(void) fprintf(stderr,
133		    gettext("cpc_setgrp: no re memory available\n"));
134		exit(0);
135	}
136
137	sgrp->sets = new;
138	sgrp->sets[sgrp->nelem].set = set;
139	/*
140	 * Count the number of requests in the set we just made. If any requests
141	 * in the set have CPC_COUNT_USER in the flags, the sysonly flag will
142	 * be cleared.
143	 */
144	sgrp->sets[sgrp->nelem].nreqs = 0;
145	sgrp->sets[sgrp->nelem].sysonly = 1;
146	cpc_walk_requests(sgrp->cpc, set, &(sgrp->sets[sgrp->nelem]),
147	    cpc_setgrp_walker);
148
149	if (sgrp->sets[sgrp->nelem].sysonly == 1)
150		sgrp->has_sysonly_set = 1;
151
152	sgrp->sets[sgrp->nelem].picnums = emalloc(sgrp->sets[sgrp->nelem].nreqs
153	    * sizeof (int));
154
155	sgrp->sets[sgrp->nelem].hdr = emalloc((sgrp->sets[sgrp->nelem].nreqs *
156	    CHARS_PER_REQ) + 1);
157
158	/*
159	 * Find out which picnums the requests are using.
160	 */
161	cpc_walk_requests(sgrp->cpc, set, sgrp->sets[sgrp->nelem].picnums,
162	    cpc_setgrp_picwalker);
163	/*
164	 * Use the picnums we discovered to build a printable header for this
165	 * set.
166	 */
167	sgrp->sets[sgrp->nelem].hdr[0] = '\0';
168	for (i = 0; i < sgrp->sets[sgrp->nelem].nreqs; i++) {
169		(void) snprintf(hdr, CHARS_PER_REQ, "%8s%-2d ", "pic",
170		    sgrp->sets[sgrp->nelem].picnums[i]);
171		(void) strncat(sgrp->sets[sgrp->nelem].hdr, hdr,
172		    sgrp->sets[sgrp->nelem].nreqs * CHARS_PER_REQ);
173	}
174	sgrp->sets[sgrp->nelem].hdr[strlen(sgrp->sets[sgrp->nelem].hdr)] = '\0';
175
176	if ((sgrp->sets[sgrp->nelem].name = strdup(spec)) == NULL) {
177		(void) fprintf(stderr,
178		    gettext("cpc_setgrp: no memory available\n"));
179		exit(0);
180	}
181
182	if ((sgrp->sets[sgrp->nelem].data1 = cpc_buf_create(sgrp->cpc, set))
183	    == NULL ||
184	    (sgrp->sets[sgrp->nelem].data2 = cpc_buf_create(sgrp->cpc, set))
185	    == NULL ||
186	    (sgrp->sets[sgrp->nelem].scratch = cpc_buf_create(sgrp->cpc, set))
187	    == NULL) {
188		(void) fprintf(stderr,
189		    gettext("cpc_setgrp: no memory available\n"));
190		exit(0);
191	}
192
193	if (sgrp->current < 0)
194		sgrp->current = 0;
195	sgrp->nelem++;
196	return (sgrp);
197}
198
199int
200cpc_setgrp_getbufs(cpc_setgrp_t *sgrp, cpc_buf_t ***data1, cpc_buf_t ***data2,
201    cpc_buf_t ***scratch)
202{
203	if ((uint_t)sgrp->current >= sgrp->nelem)
204		return (-1);
205
206	*data1   = &(sgrp->sets[sgrp->current].data1);
207	*data2   = &(sgrp->sets[sgrp->current].data2);
208	*scratch = &(sgrp->sets[sgrp->current].scratch);
209
210	return (sgrp->sets[sgrp->current].nreqs);
211}
212
213cpc_setgrp_t *
214cpc_setgrp_clone(cpc_setgrp_t *old)
215{
216	int			i;
217	cpc_setgrp_t		*new;
218	struct setgrp_elem	*newa;
219
220	new = emalloc(sizeof (*new));
221	newa = emalloc(old->nelem * sizeof (*newa));
222
223	new->nelem = old->nelem;
224	new->current = old->current;
225	new->cpc = old->cpc;
226	new->sets = newa;
227	new->smt = old->smt;
228	new->has_sysonly_set = old->has_sysonly_set;
229	for (i = 0; i < old->nelem; i++) {
230		if ((newa[i].set = cpc_strtoset(old->cpc, old->sets[i].name,
231		    old->smt)) == NULL) {
232			(void) fprintf(stderr,
233			    gettext("cpc_setgrp: cpc_strtoset() failed\n"));
234			exit(0);
235		}
236		if ((newa[i].name = strdup(old->sets[i].name)) == NULL) {
237			(void) fprintf(stderr,
238			    gettext("cpc_setgrp: no memory available\n"));
239			exit(0);
240		}
241		newa[i].sysonly = old->sets[i].sysonly;
242		newa[i].nreqs = old->sets[i].nreqs;
243		newa[i].data1 = cpc_buf_create(old->cpc, newa[i].set);
244		newa[i].data2 = cpc_buf_create(old->cpc, newa[i].set);
245		newa[i].scratch = cpc_buf_create(old->cpc, newa[i].set);
246		if (newa[i].data1 == NULL || newa[i].data2 == NULL ||
247		    newa[i].scratch == NULL) {
248			(void) fprintf(stderr,
249			    gettext("cpc_setgrp: no memory available\n"));
250			exit(0);
251		}
252		cpc_buf_copy(old->cpc, newa[i].data1, old->sets[i].data1);
253		cpc_buf_copy(old->cpc, newa[i].data2, old->sets[i].data2);
254		cpc_buf_copy(old->cpc, newa[i].scratch, old->sets[i].scratch);
255	}
256	return (new);
257}
258
259static void
260cpc_setgrp_delset(cpc_setgrp_t *sgrp)
261{
262	int l;
263
264	if ((uint_t)sgrp->current >= sgrp->nelem)
265		sgrp->current = sgrp->nelem - 1;
266	if (sgrp->current < 0)
267		return;
268	free(sgrp->sets[sgrp->current].name);
269	free(sgrp->sets[sgrp->current].hdr);
270	free(sgrp->sets[sgrp->current].picnums);
271	(void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].data1);
272	(void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].data2);
273	(void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].scratch);
274	for (l = sgrp->current; l < sgrp->nelem - 1; l++)
275		sgrp->sets[l] = sgrp->sets[l + 1];
276	sgrp->nelem--;
277}
278
279void
280cpc_setgrp_free(cpc_setgrp_t *sgrp)
281{
282	if (sgrp->sets) {
283		while (sgrp->nelem)
284			cpc_setgrp_delset(sgrp);
285		free(sgrp->sets);
286	}
287	free(sgrp);
288}
289
290cpc_set_t *
291cpc_setgrp_getset(cpc_setgrp_t *sgrp)
292{
293	if ((uint_t)sgrp->current >= sgrp->nelem)
294		return (NULL);
295	return (sgrp->sets[sgrp->current].set);
296}
297
298const char *
299cpc_setgrp_getname(cpc_setgrp_t *sgrp)
300{
301	if ((uint_t)sgrp->current >= sgrp->nelem)
302		return (NULL);
303	return (sgrp->sets[sgrp->current].name);
304}
305
306const char *
307cpc_setgrp_gethdr(cpc_setgrp_t *sgrp)
308{
309	if ((uint_t)sgrp->current >= sgrp->nelem)
310		return (NULL);
311	return (sgrp->sets[sgrp->current].hdr);
312}
313
314int
315cpc_setgrp_numsets(cpc_setgrp_t *sgrp)
316{
317	return (sgrp->nelem);
318}
319
320cpc_set_t *
321cpc_setgrp_nextset(cpc_setgrp_t *sgrp)
322{
323	if (sgrp->current < 0)
324		return (NULL);
325
326	if (++sgrp->current >= sgrp->nelem)
327		sgrp->current = 0;
328
329	return (cpc_setgrp_getset(sgrp));
330}
331
332/*
333 * Put the setgrp pointer back to the beginning of the set
334 */
335void
336cpc_setgrp_reset(cpc_setgrp_t *sgrp)
337{
338	if (sgrp->current > 0)
339		sgrp->current = 0;
340}
341
342/*
343 * Adds the data from the 'data1' buf into the accum setgrp.
344 */
345void
346cpc_setgrp_accum(cpc_setgrp_t *accum, cpc_setgrp_t *sgrp)
347{
348	int i;
349
350	cpc_setgrp_reset(accum);
351	cpc_setgrp_reset(sgrp);
352	if (accum->nelem != sgrp->nelem)
353		return;
354
355	for (i = 0; i < sgrp->nelem; i++) {
356		if (accum->sets[i].nreqs != sgrp->sets[i].nreqs)
357			return;
358		cpc_buf_add(sgrp->cpc, accum->sets[i].data1,
359		    accum->sets[i].data1, sgrp->sets[i].data1);
360	}
361}
362
363/*
364 * Returns 1 if all requests in the current set count only system-mode events.
365 */
366int
367cpc_setgrp_sysonly(cpc_setgrp_t *sgrp)
368{
369	return ((int)sgrp->sets[sgrp->current].sysonly);
370}
371
372/*
373 * Returns 1 if any set in the group is a system-mode-only set.
374 */
375int
376cpc_setgrp_has_sysonly(cpc_setgrp_t *sgrp)
377{
378	return (sgrp->has_sysonly_set);
379}
380
381/*
382 * If we ever fail to get memory, we print an error message and exit.
383 */
384static void *
385emalloc(size_t n)
386{
387	/*
388	 * Several callers of this routine need zero-filled buffers.
389	 */
390	void *p = calloc(1, n);
391
392	if (p == NULL) {
393		(void) fprintf(stderr,
394		    gettext("cpc_setgrp: no memory available\n"));
395		exit(0);
396	}
397
398	return (p);
399}
400