root/usr/src/cmd/cpc/common/setgrp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <libintl.h>
#include <sys/types.h>


#include <libcpc.h>
#include "cpucmds.h"

#define CHARS_PER_REQ 11        /* space required for printing column headers */

/*
 * These routines are solely used to manage a list of request sets.
 */

struct __cpc_setgrp {
        struct setgrp_elem {
                cpc_set_t       *set;
                uint8_t         sysonly;        /* All reqs sys-mode only ? */
                int             nreqs;
                int             *picnums;       /* picnum used per req */
                cpc_buf_t       *data1;
                cpc_buf_t       *data2;
                cpc_buf_t       *scratch;
                char *name;
                char *hdr;
        } *sets;                /* array of events and names */
        int nelem;              /* size of array */
        int current;            /* currently bound event in eventset */
        int smt;                /* Measures physical events on SMT CPU */
        int has_sysonly_set;    /* Does this group have a system-only set? */
        cpc_t *cpc;             /* library handle */
};

static void *emalloc(size_t n);

cpc_setgrp_t *
cpc_setgrp_new(cpc_t *cpc, int smt)
{
        cpc_setgrp_t *sgrp;

        sgrp = emalloc(sizeof (*sgrp));
        sgrp->current = -1;
        sgrp->cpc = cpc;
        sgrp->smt = smt;
        sgrp->has_sysonly_set = 0;
        return (sgrp);
}

/*
 * Walker to count the number of requests in a set, and check if any requests
 * count user-mode events.
 */
/*ARGSUSED*/
static void
cpc_setgrp_walker(void *arg, int index, const char *event, uint64_t preset,
    uint_t flags, int nattrs, const cpc_attr_t *attrs)
{
        struct setgrp_elem *se = arg;

        se->nreqs++;
        if (flags & CPC_COUNT_USER)
                se->sysonly = 0;
}

/*
 * Walker to discover the picnums used by the requests in a set.
 */
/*ARGSUSED*/
static void
cpc_setgrp_picwalker(void *arg, int index, const char *event, uint64_t preset,
    uint_t flags, int nattrs, const cpc_attr_t *attrs)
{
        int *picnums = arg;
        int i;

        for (i = 0; i < nattrs; i++) {
                if (strncmp(attrs[i].ca_name, "picnum", 7) == 0)
                        break;
        }
        if (i == nattrs)
                picnums[index] = -1;

        picnums[index] = (int)attrs[i].ca_val;
}

cpc_setgrp_t *
cpc_setgrp_newset(cpc_setgrp_t *sgrp, const char *spec, int *errcnt)
{
        cpc_set_t               *set;
        struct setgrp_elem      *new;
        char                    hdr[CHARS_PER_REQ+1];
        int                     i;

        if ((set = cpc_strtoset(sgrp->cpc, spec, sgrp->smt)) == NULL) {
                *errcnt += 1;
                return (NULL);
        }

        if ((new = realloc(sgrp->sets, (1 + sgrp->nelem) * sizeof (*new)))
            == NULL) {
                (void) fprintf(stderr,
                    gettext("cpc_setgrp: no re memory available\n"));
                exit(0);
        }

        sgrp->sets = new;
        sgrp->sets[sgrp->nelem].set = set;
        /*
         * Count the number of requests in the set we just made. If any requests
         * in the set have CPC_COUNT_USER in the flags, the sysonly flag will
         * be cleared.
         */
        sgrp->sets[sgrp->nelem].nreqs = 0;
        sgrp->sets[sgrp->nelem].sysonly = 1;
        cpc_walk_requests(sgrp->cpc, set, &(sgrp->sets[sgrp->nelem]),
            cpc_setgrp_walker);

        if (sgrp->sets[sgrp->nelem].sysonly == 1)
                sgrp->has_sysonly_set = 1;

        sgrp->sets[sgrp->nelem].picnums = emalloc(sgrp->sets[sgrp->nelem].nreqs
            * sizeof (int));

        sgrp->sets[sgrp->nelem].hdr = emalloc((sgrp->sets[sgrp->nelem].nreqs *
            CHARS_PER_REQ) + 1);

        /*
         * Find out which picnums the requests are using.
         */
        cpc_walk_requests(sgrp->cpc, set, sgrp->sets[sgrp->nelem].picnums,
            cpc_setgrp_picwalker);
        /*
         * Use the picnums we discovered to build a printable header for this
         * set.
         */
        sgrp->sets[sgrp->nelem].hdr[0] = '\0';
        for (i = 0; i < sgrp->sets[sgrp->nelem].nreqs; i++) {
                (void) snprintf(hdr, CHARS_PER_REQ, "%8s%-2d ", "pic",
                    sgrp->sets[sgrp->nelem].picnums[i]);
                (void) strncat(sgrp->sets[sgrp->nelem].hdr, hdr,
                    sgrp->sets[sgrp->nelem].nreqs * CHARS_PER_REQ);
        }
        sgrp->sets[sgrp->nelem].hdr[strlen(sgrp->sets[sgrp->nelem].hdr)] = '\0';

        if ((sgrp->sets[sgrp->nelem].name = strdup(spec)) == NULL) {
                (void) fprintf(stderr,
                    gettext("cpc_setgrp: no memory available\n"));
                exit(0);
        }

        if ((sgrp->sets[sgrp->nelem].data1 = cpc_buf_create(sgrp->cpc, set))
            == NULL ||
            (sgrp->sets[sgrp->nelem].data2 = cpc_buf_create(sgrp->cpc, set))
            == NULL ||
            (sgrp->sets[sgrp->nelem].scratch = cpc_buf_create(sgrp->cpc, set))
            == NULL) {
                (void) fprintf(stderr,
                    gettext("cpc_setgrp: no memory available\n"));
                exit(0);
        }

        if (sgrp->current < 0)
                sgrp->current = 0;
        sgrp->nelem++;
        return (sgrp);
}

int
cpc_setgrp_getbufs(cpc_setgrp_t *sgrp, cpc_buf_t ***data1, cpc_buf_t ***data2,
    cpc_buf_t ***scratch)
{
        if ((uint_t)sgrp->current >= sgrp->nelem)
                return (-1);

        *data1   = &(sgrp->sets[sgrp->current].data1);
        *data2   = &(sgrp->sets[sgrp->current].data2);
        *scratch = &(sgrp->sets[sgrp->current].scratch);

        return (sgrp->sets[sgrp->current].nreqs);
}

cpc_setgrp_t *
cpc_setgrp_clone(cpc_setgrp_t *old)
{
        int                     i;
        cpc_setgrp_t            *new;
        struct setgrp_elem      *newa;

        new = emalloc(sizeof (*new));
        newa = emalloc(old->nelem * sizeof (*newa));

        new->nelem = old->nelem;
        new->current = old->current;
        new->cpc = old->cpc;
        new->sets = newa;
        new->smt = old->smt;
        new->has_sysonly_set = old->has_sysonly_set;
        for (i = 0; i < old->nelem; i++) {
                if ((newa[i].set = cpc_strtoset(old->cpc, old->sets[i].name,
                    old->smt)) == NULL) {
                        (void) fprintf(stderr,
                            gettext("cpc_setgrp: cpc_strtoset() failed\n"));
                        exit(0);
                }
                if ((newa[i].name = strdup(old->sets[i].name)) == NULL) {
                        (void) fprintf(stderr,
                            gettext("cpc_setgrp: no memory available\n"));
                        exit(0);
                }
                newa[i].sysonly = old->sets[i].sysonly;
                newa[i].nreqs = old->sets[i].nreqs;
                newa[i].data1 = cpc_buf_create(old->cpc, newa[i].set);
                newa[i].data2 = cpc_buf_create(old->cpc, newa[i].set);
                newa[i].scratch = cpc_buf_create(old->cpc, newa[i].set);
                if (newa[i].data1 == NULL || newa[i].data2 == NULL ||
                    newa[i].scratch == NULL) {
                        (void) fprintf(stderr,
                            gettext("cpc_setgrp: no memory available\n"));
                        exit(0);
                }
                cpc_buf_copy(old->cpc, newa[i].data1, old->sets[i].data1);
                cpc_buf_copy(old->cpc, newa[i].data2, old->sets[i].data2);
                cpc_buf_copy(old->cpc, newa[i].scratch, old->sets[i].scratch);
        }
        return (new);
}

static void
cpc_setgrp_delset(cpc_setgrp_t *sgrp)
{
        int l;

        if ((uint_t)sgrp->current >= sgrp->nelem)
                sgrp->current = sgrp->nelem - 1;
        if (sgrp->current < 0)
                return;
        free(sgrp->sets[sgrp->current].name);
        free(sgrp->sets[sgrp->current].hdr);
        free(sgrp->sets[sgrp->current].picnums);
        (void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].data1);
        (void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].data2);
        (void) cpc_buf_destroy(sgrp->cpc, sgrp->sets[sgrp->current].scratch);
        for (l = sgrp->current; l < sgrp->nelem - 1; l++)
                sgrp->sets[l] = sgrp->sets[l + 1];
        sgrp->nelem--;
}

void
cpc_setgrp_free(cpc_setgrp_t *sgrp)
{
        if (sgrp->sets) {
                while (sgrp->nelem)
                        cpc_setgrp_delset(sgrp);
                free(sgrp->sets);
        }
        free(sgrp);
}

cpc_set_t *
cpc_setgrp_getset(cpc_setgrp_t *sgrp)
{
        if ((uint_t)sgrp->current >= sgrp->nelem)
                return (NULL);
        return (sgrp->sets[sgrp->current].set);
}

const char *
cpc_setgrp_getname(cpc_setgrp_t *sgrp)
{
        if ((uint_t)sgrp->current >= sgrp->nelem)
                return (NULL);
        return (sgrp->sets[sgrp->current].name);
}

const char *
cpc_setgrp_gethdr(cpc_setgrp_t *sgrp)
{
        if ((uint_t)sgrp->current >= sgrp->nelem)
                return (NULL);
        return (sgrp->sets[sgrp->current].hdr);
}

int
cpc_setgrp_numsets(cpc_setgrp_t *sgrp)
{
        return (sgrp->nelem);
}

cpc_set_t *
cpc_setgrp_nextset(cpc_setgrp_t *sgrp)
{
        if (sgrp->current < 0)
                return (NULL);

        if (++sgrp->current >= sgrp->nelem)
                sgrp->current = 0;

        return (cpc_setgrp_getset(sgrp));
}

/*
 * Put the setgrp pointer back to the beginning of the set
 */
void
cpc_setgrp_reset(cpc_setgrp_t *sgrp)
{
        if (sgrp->current > 0)
                sgrp->current = 0;
}

/*
 * Adds the data from the 'data1' buf into the accum setgrp.
 */
void
cpc_setgrp_accum(cpc_setgrp_t *accum, cpc_setgrp_t *sgrp)
{
        int i;

        cpc_setgrp_reset(accum);
        cpc_setgrp_reset(sgrp);
        if (accum->nelem != sgrp->nelem)
                return;

        for (i = 0; i < sgrp->nelem; i++) {
                if (accum->sets[i].nreqs != sgrp->sets[i].nreqs)
                        return;
                cpc_buf_add(sgrp->cpc, accum->sets[i].data1,
                    accum->sets[i].data1, sgrp->sets[i].data1);
        }
}

/*
 * Returns 1 if all requests in the current set count only system-mode events.
 */
int
cpc_setgrp_sysonly(cpc_setgrp_t *sgrp)
{
        return ((int)sgrp->sets[sgrp->current].sysonly);
}

/*
 * Returns 1 if any set in the group is a system-mode-only set.
 */
int
cpc_setgrp_has_sysonly(cpc_setgrp_t *sgrp)
{
        return (sgrp->has_sysonly_set);
}

/*
 * If we ever fail to get memory, we print an error message and exit.
 */
static void *
emalloc(size_t n)
{
        /*
         * Several callers of this routine need zero-filled buffers.
         */
        void *p = calloc(1, n);

        if (p == NULL) {
                (void) fprintf(stderr,
                    gettext("cpc_setgrp: no memory available\n"));
                exit(0);
        }

        return (p);
}