/*
* 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);
}