root/usr/src/cmd/cpc/common/caps.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#define  __EXTENSIONS__ /* header bug! strtok_r is overly hidden */
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libintl.h>

#include <libcpc.h>

#include "cpucmds.h"

struct args {
        FILE *fp;
        int colnum;
        int margin;
};

struct evlist {
        char *list;
        int size;
};

#define MAX_RHS_COLUMN  76
#define EVENT_MARGIN    17
#define ATTR_MARGIN     20

/*ARGSUSED*/
static void
list_cap(void *arg, uint_t regno, const char *name)
{
        struct args *args = arg;
        int i;

        if ((args->colnum + strlen(name) + 1) > MAX_RHS_COLUMN) {
                (void) fprintf(args->fp, "\n");
                for (i = 0; i < args->margin; i++)
                        (void) fprintf(args->fp, " ");
                args->colnum = args->margin;
        }
        args->colnum += fprintf(args->fp, "%s ", name);
}

static void
list_attr(void *arg, const char *name)
{
        /*
         * The following attributes are used by the commands but should not be
         * reported to the user, since they may not be specified directly.
         */
        if (strncmp(name, "picnum", 7) == 0 ||
            strncmp(name, "count_sibling_usr", 18) == 0 ||
            strncmp(name, "count_sibling_sys", 18) == 0)
                return;

        list_cap(arg, 0, name);
}

static void *
emalloc(size_t size)
{
        void *ptr;

        if ((ptr = malloc(size)) == NULL) {
                (void) fprintf(stderr, gettext("no memory available\n"));
                exit(1);
        }

        return (ptr);
}

/*
 * Used by allpics_equal().
 */
/*ARGSUSED*/
static void
cap_walker(void *arg, uint_t regno, const char *name)
{
        struct evlist *list = arg;

        list->size += strlen(name);
        if ((list->list = realloc(list->list, list->size + 1)) == NULL) {
                (void) fprintf(stderr, gettext("no memory available\n"));
                exit(1);
        }

        (void) strcat(list->list, name);
}

/*
 * Returns 1 if all counters on this chip can count all possible events.
 */
static int
allpics_equal(cpc_t *cpc)
{
        int     npics = cpc_npic(cpc);
        int     i;
        struct  evlist **lists;
        int     ret = 1;

        lists = emalloc(npics * sizeof (struct evlist *));

        for (i = 0; i < npics; i++) {
                lists[i] = emalloc(sizeof (struct evlist));
                lists[i]->size = 0;
                lists[i]->list = emalloc(1);
                lists[i]->list[0] = '\0';
                cpc_walk_events_pic(cpc, i, lists[i], cap_walker);
        }

        for (i = 1; i < npics; i++)
                if (lists[i]->size != lists[0]->size ||
                    strncmp(lists[i]->list, lists[0]->list,
                    lists[0]->size) != 0) {
                        ret = 0;
                        break;
                }

        for (i = 0; i < npics; i++) {
                free(lists[i]->list);
                free(lists[i]);
        }
        free(lists);

        return (ret);
}

int
capabilities(cpc_t *cpc, FILE *fp)
{
        struct args _args, *args = &_args;
        char *text, *tok, *cp;
        const char *ccp;
        int npic = cpc_npic(cpc);
        int i, pics_equal = allpics_equal(cpc);

        args->fp = fp;

        if ((ccp = cpc_cciname(cpc)) == NULL)
                ccp = "No information available";
        (void) fprintf(args->fp, "\t%s: %s\n\n",
            gettext("CPU performance counter interface"), ccp);

        (void) fprintf(args->fp, gettext("\tevent specification syntax:\n"));

        (void) fprintf(args->fp, "\t[picn=]<eventn>[,attr[n][=<val>]]"
            "[,[picn=]<eventn>[,attr[n][=<val>]],...]\n");

        (void) fprintf(args->fp, gettext("\n\tGeneric Events:\n"));

        if (pics_equal) {
                args->margin = args->colnum = EVENT_MARGIN;
                (void) fprintf(args->fp, "\n\tevent[0-%d]: ", npic - 1);
                cpc_walk_generic_events_pic(cpc, 0, args, list_cap);
                (void) fprintf(args->fp, "\n");
        } else {
                args->margin = EVENT_MARGIN;
                for (i = 0; i < npic; i++) {
                        (void) fprintf(args->fp, "\n\tevent%d: ", i);
                        if (i < 10) (void) fprintf(args->fp, " ");
                        args->colnum = EVENT_MARGIN;
                        cpc_walk_generic_events_pic(cpc, i, args, list_cap);
                        (void) fprintf(args->fp, "\n");
                }
        }

        (void) fprintf(args->fp, gettext("\n\tSee generic_events(3CPC) for"
            " descriptions of these events\n\n"));

        (void) fprintf(args->fp, gettext("\tPlatform Specific Events:\n"));

        if (pics_equal) {
                args->margin = args->colnum = EVENT_MARGIN;
                (void) fprintf(args->fp, "\n\tevent[0-%d]: ", npic - 1);
                cpc_walk_events_pic(cpc, 0, args, list_cap);
                (void) fprintf(args->fp, "\n");
        } else {
                args->margin = EVENT_MARGIN;
                for (i = 0; i < npic; i++) {
                        (void) fprintf(args->fp, "\n\tevent%d: ", i);
                        if (i < 10) (void) fprintf(args->fp, " ");
                        args->colnum = EVENT_MARGIN;
                        cpc_walk_events_pic(cpc, i, args, list_cap);
                        (void) fprintf(args->fp, "\n");
                }
        }

        (void) fprintf(args->fp, "\n\tattributes: ");
        args->colnum = args->margin = ATTR_MARGIN;
        cpc_walk_attrs(cpc, args, list_attr);
        /*
         * In addition to the attributes published by the kernel, we allow the
         * user to specify two additional tokens on all platforms. List them
         * here.
         */
        list_cap(args, 0, "nouser");
        list_cap(args, 0, "sys");
        (void) fprintf(args->fp, "\n\n\t");
        args->colnum = 8;

        if ((ccp = cpc_cpuref(cpc)) == NULL)
                ccp = "No information available";
        if ((text = strdup(ccp)) == NULL) {
                (void) fprintf(stderr, gettext("no memory available.\n"));
                exit(1);
        }
        for (cp = strtok_r(text, " ", &tok);
            cp != NULL; cp = strtok_r(NULL, " ", &tok)) {
                if ((args->colnum + strlen(cp) + 1) > MAX_RHS_COLUMN) {
                        (void) fprintf(args->fp, "\n\t");
                        args->colnum = 8;
                }
                args->colnum += fprintf(args->fp, "%s ", cp);
        }
        (void) fprintf(args->fp, "\n");
        free(text);

        return (0);
}

/*
 * Returns 1 on SMT processors which do not have full CPC hardware for each
 * logical processor.
 */
int
smt_limited_cpc_hw(cpc_t *cpc)
{
        if (strcmp(cpc_cciname(cpc), "Pentium 4 with HyperThreading") == 0)
                return (1);
        return (0);
}