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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #define __EXTENSIONS__ /* header bug! strtok_r is overly hidden */
27 #include <string.h>
28 #include <strings.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <libintl.h>
33
34 #include <libcpc.h>
35
36 #include "cpucmds.h"
37
38 struct args {
39 FILE *fp;
40 int colnum;
41 int margin;
42 };
43
44 struct evlist {
45 char *list;
46 int size;
47 };
48
49 #define MAX_RHS_COLUMN 76
50 #define EVENT_MARGIN 17
51 #define ATTR_MARGIN 20
52
53 /*ARGSUSED*/
54 static void
list_cap(void * arg,uint_t regno,const char * name)55 list_cap(void *arg, uint_t regno, const char *name)
56 {
57 struct args *args = arg;
58 int i;
59
60 if ((args->colnum + strlen(name) + 1) > MAX_RHS_COLUMN) {
61 (void) fprintf(args->fp, "\n");
62 for (i = 0; i < args->margin; i++)
63 (void) fprintf(args->fp, " ");
64 args->colnum = args->margin;
65 }
66 args->colnum += fprintf(args->fp, "%s ", name);
67 }
68
69 static void
list_attr(void * arg,const char * name)70 list_attr(void *arg, const char *name)
71 {
72 /*
73 * The following attributes are used by the commands but should not be
74 * reported to the user, since they may not be specified directly.
75 */
76 if (strncmp(name, "picnum", 7) == 0 ||
77 strncmp(name, "count_sibling_usr", 18) == 0 ||
78 strncmp(name, "count_sibling_sys", 18) == 0)
79 return;
80
81 list_cap(arg, 0, name);
82 }
83
84 static void *
emalloc(size_t size)85 emalloc(size_t size)
86 {
87 void *ptr;
88
89 if ((ptr = malloc(size)) == NULL) {
90 (void) fprintf(stderr, gettext("no memory available\n"));
91 exit(1);
92 }
93
94 return (ptr);
95 }
96
97 /*
98 * Used by allpics_equal().
99 */
100 /*ARGSUSED*/
101 static void
cap_walker(void * arg,uint_t regno,const char * name)102 cap_walker(void *arg, uint_t regno, const char *name)
103 {
104 struct evlist *list = arg;
105
106 list->size += strlen(name);
107 if ((list->list = realloc(list->list, list->size + 1)) == NULL) {
108 (void) fprintf(stderr, gettext("no memory available\n"));
109 exit(1);
110 }
111
112 (void) strcat(list->list, name);
113 }
114
115 /*
116 * Returns 1 if all counters on this chip can count all possible events.
117 */
118 static int
allpics_equal(cpc_t * cpc)119 allpics_equal(cpc_t *cpc)
120 {
121 int npics = cpc_npic(cpc);
122 int i;
123 struct evlist **lists;
124 int ret = 1;
125
126 lists = emalloc(npics * sizeof (struct evlist *));
127
128 for (i = 0; i < npics; i++) {
129 lists[i] = emalloc(sizeof (struct evlist));
130 lists[i]->size = 0;
131 lists[i]->list = emalloc(1);
132 lists[i]->list[0] = '\0';
133 cpc_walk_events_pic(cpc, i, lists[i], cap_walker);
134 }
135
136 for (i = 1; i < npics; i++)
137 if (lists[i]->size != lists[0]->size ||
138 strncmp(lists[i]->list, lists[0]->list,
139 lists[0]->size) != 0) {
140 ret = 0;
141 break;
142 }
143
144 for (i = 0; i < npics; i++) {
145 free(lists[i]->list);
146 free(lists[i]);
147 }
148 free(lists);
149
150 return (ret);
151 }
152
153 int
capabilities(cpc_t * cpc,FILE * fp)154 capabilities(cpc_t *cpc, FILE *fp)
155 {
156 struct args _args, *args = &_args;
157 char *text, *tok, *cp;
158 const char *ccp;
159 int npic = cpc_npic(cpc);
160 int i, pics_equal = allpics_equal(cpc);
161
162 args->fp = fp;
163
164 if ((ccp = cpc_cciname(cpc)) == NULL)
165 ccp = "No information available";
166 (void) fprintf(args->fp, "\t%s: %s\n\n",
167 gettext("CPU performance counter interface"), ccp);
168
169 (void) fprintf(args->fp, gettext("\tevent specification syntax:\n"));
170
171 (void) fprintf(args->fp, "\t[picn=]<eventn>[,attr[n][=<val>]]"
172 "[,[picn=]<eventn>[,attr[n][=<val>]],...]\n");
173
174 (void) fprintf(args->fp, gettext("\n\tGeneric Events:\n"));
175
176 if (pics_equal) {
177 args->margin = args->colnum = EVENT_MARGIN;
178 (void) fprintf(args->fp, "\n\tevent[0-%d]: ", npic - 1);
179 cpc_walk_generic_events_pic(cpc, 0, args, list_cap);
180 (void) fprintf(args->fp, "\n");
181 } else {
182 args->margin = EVENT_MARGIN;
183 for (i = 0; i < npic; i++) {
184 (void) fprintf(args->fp, "\n\tevent%d: ", i);
185 if (i < 10) (void) fprintf(args->fp, " ");
186 args->colnum = EVENT_MARGIN;
187 cpc_walk_generic_events_pic(cpc, i, args, list_cap);
188 (void) fprintf(args->fp, "\n");
189 }
190 }
191
192 (void) fprintf(args->fp, gettext("\n\tSee generic_events(3CPC) for"
193 " descriptions of these events\n\n"));
194
195 (void) fprintf(args->fp, gettext("\tPlatform Specific Events:\n"));
196
197 if (pics_equal) {
198 args->margin = args->colnum = EVENT_MARGIN;
199 (void) fprintf(args->fp, "\n\tevent[0-%d]: ", npic - 1);
200 cpc_walk_events_pic(cpc, 0, args, list_cap);
201 (void) fprintf(args->fp, "\n");
202 } else {
203 args->margin = EVENT_MARGIN;
204 for (i = 0; i < npic; i++) {
205 (void) fprintf(args->fp, "\n\tevent%d: ", i);
206 if (i < 10) (void) fprintf(args->fp, " ");
207 args->colnum = EVENT_MARGIN;
208 cpc_walk_events_pic(cpc, i, args, list_cap);
209 (void) fprintf(args->fp, "\n");
210 }
211 }
212
213 (void) fprintf(args->fp, "\n\tattributes: ");
214 args->colnum = args->margin = ATTR_MARGIN;
215 cpc_walk_attrs(cpc, args, list_attr);
216 /*
217 * In addition to the attributes published by the kernel, we allow the
218 * user to specify two additional tokens on all platforms. List them
219 * here.
220 */
221 list_cap(args, 0, "nouser");
222 list_cap(args, 0, "sys");
223 (void) fprintf(args->fp, "\n\n\t");
224 args->colnum = 8;
225
226 if ((ccp = cpc_cpuref(cpc)) == NULL)
227 ccp = "No information available";
228 if ((text = strdup(ccp)) == NULL) {
229 (void) fprintf(stderr, gettext("no memory available.\n"));
230 exit(1);
231 }
232 for (cp = strtok_r(text, " ", &tok);
233 cp != NULL; cp = strtok_r(NULL, " ", &tok)) {
234 if ((args->colnum + strlen(cp) + 1) > MAX_RHS_COLUMN) {
235 (void) fprintf(args->fp, "\n\t");
236 args->colnum = 8;
237 }
238 args->colnum += fprintf(args->fp, "%s ", cp);
239 }
240 (void) fprintf(args->fp, "\n");
241 free(text);
242
243 return (0);
244 }
245
246 /*
247 * Returns 1 on SMT processors which do not have full CPC hardware for each
248 * logical processor.
249 */
250 int
smt_limited_cpc_hw(cpc_t * cpc)251 smt_limited_cpc_hw(cpc_t *cpc)
252 {
253 if (strcmp(cpc_cciname(cpc), "Pentium 4 with HyperThreading") == 0)
254 return (1);
255 return (0);
256 }
257