xref: /illumos-gate/usr/src/cmd/psrinfo/psrinfo.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
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 <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <time.h>
35 #include <kstat.h>
36 #include <sys/processor.h>	/* for processorid_t */
37 
38 #include <libintl.h>
39 #include <locale.h>
40 
41 #ifndef	TEXT_DOMAIN
42 #define	TEXT_DOMAIN	"SYS_TEST"
43 #endif
44 
45 #define	NCPUSTATES	6	/* on-line, off-line, no-intr, faulted, */
46 				/* spare, power-off */
47 
48 /*
49  * Possible states that a cpu may be in, and their corresponding
50  * localized versions.
51  */
52 static struct {
53 	const char *state;	/* State returned in kstat. */
54 	const char *lstate;	/* Localized version of the state. */
55 } cpu_states[NCPUSTATES];
56 
57 static const char cmdname[] = "psrinfo";
58 
59 #define	CORES_PER_CHIP_MAX 256	/* ABEN (Arbitrarily big-enough number) */
60 static int chip_count = 0;
61 static int max_chip_id;
62 static struct chip {
63 	int visible;
64 	int online;
65 	int core_count;
66 	char impl[128];
67 	char brand[128];
68 	processorid_t cores[CORES_PER_CHIP_MAX];
69 } *chips;
70 
71 static void cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity,
72 	int phys_view, int visible);
73 
74 static void
75 usage(char *msg)
76 {
77 	if (msg != NULL)
78 		(void) fprintf(stderr, "%s: %s\n", cmdname, msg);
79 	(void) fprintf(stderr,
80 	    gettext("usage: \n\t%s [-v] [-p] [processor_id ...]\n"
81 	    "\t%s -s [-p] processor_id\n"), cmdname, cmdname);
82 	exit(2);
83 }
84 
85 int
86 main(int argc, char *argv[])
87 {
88 	kstat_ctl_t *kc;
89 	kstat_t	*ksp;
90 	int c;
91 	processorid_t cpu;
92 	int verbosity = 1;	/* 0 = silent, 1 = normal, 3 = verbose */
93 	int phys_view = 0;
94 	int errors = 0;
95 	char *errptr;
96 
97 	(void) setlocale(LC_ALL, "");
98 	(void) textdomain(TEXT_DOMAIN);
99 
100 	while ((c = getopt(argc, argv, "psv")) != EOF) {
101 		switch (c) {
102 		case 'v':
103 			verbosity |= 2;
104 			break;
105 		case 's':
106 			verbosity &= ~1;
107 			break;
108 		case 'p':
109 			phys_view = 1;
110 			break;
111 		default:
112 			usage(NULL);
113 		}
114 	}
115 
116 	argc -= optind;
117 	argv += optind;
118 
119 	if (verbosity == 2)
120 		usage(gettext("options -s and -v are mutually exclusive"));
121 
122 	if (verbosity == 0 && argc != 1)
123 		usage(gettext("must specify exactly one processor if -s used"));
124 
125 	if ((kc = kstat_open()) == NULL) {
126 		(void) fprintf(stderr, gettext("%s: kstat_open() failed: %s\n"),
127 		    cmdname, strerror(errno));
128 		exit(1);
129 	}
130 
131 	/*
132 	 * Build localized cpu state table.
133 	 */
134 	cpu_states[0].state = PS_ONLINE;
135 	cpu_states[0].lstate = gettext(PS_ONLINE);
136 	cpu_states[1].state = PS_POWEROFF;
137 	cpu_states[1].lstate = gettext(PS_POWEROFF);
138 	cpu_states[2].state = PS_NOINTR;
139 	cpu_states[2].lstate = gettext(PS_NOINTR);
140 	cpu_states[3].state = PS_FAULTED;
141 	cpu_states[3].lstate = gettext(PS_FAULTED);
142 	cpu_states[4].state = PS_SPARE;
143 	cpu_states[4].lstate = gettext(PS_SPARE);
144 	cpu_states[5].state = PS_OFFLINE;
145 	cpu_states[5].lstate = gettext(PS_OFFLINE);
146 
147 	if (phys_view) {
148 		/*
149 		 * Note that we assume that MAX_CHIPID is <= MAX_CPUID.
150 		 * If this becomes untrue, a new sysconf() would be warranted.
151 		 */
152 		max_chip_id = sysconf(_SC_CPUID_MAX);
153 		chips = calloc(max_chip_id + 1, sizeof (struct chip));
154 		if (chips == NULL) {
155 			perror("calloc");
156 			exit(1);
157 		}
158 	}
159 
160 	/*
161 	 * In the physical view, we want to display all the core info or
162 	 * none, even when the user specifies a range of CPUIDs.  So for
163 	 * "psrinfo -pv <range>" or "psrinfo -ps <range>", we inventory
164 	 * every cpu_info kstat, and *then* we go through the user
165 	 * specified processors merely flipping on their "visible"
166 	 * flags.
167 	 */
168 
169 	if (argc == 0 || (phys_view && (verbosity != 1))) {
170 		/*
171 		 * No processors specified.  Report on all of them.
172 		 * Or do a complete inventory in preparation for a
173 		 * specified list of physical processors.  See note
174 		 * immediately above.
175 		 */
176 		processorid_t maxcpu = -1;
177 
178 		for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next)
179 			if (strcmp(ksp->ks_module, "cpu_info") == 0 &&
180 			    ksp->ks_instance > maxcpu)
181 				maxcpu = ksp->ks_instance;
182 
183 		for (cpu = 0; cpu <= maxcpu; cpu++)
184 			if (ksp = kstat_lookup(kc, "cpu_info", cpu, NULL))
185 				cpu_info(kc, ksp, verbosity, phys_view,
186 				    argc == 0);
187 	}
188 
189 	if (argc != 0) {
190 		/*
191 		 * Report on specified processors.
192 		 */
193 		for (; argc > 0; argv++, argc--) {
194 			if (strchr(*argv, '-') == NULL) {
195 				/* individual processor id */
196 				char cpubuf[20];
197 
198 				(void) sprintf(cpubuf, "cpu_info%.10s", *argv);
199 				if (ksp = kstat_lookup(kc, "cpu_info", -1,
200 				    cpubuf))
201 					cpu_info(kc, ksp, verbosity,
202 					    phys_view, 1);
203 				else {
204 					(void) fprintf(stderr,
205 					    gettext("%s: processor %s: %s\n"),
206 					    cmdname, *argv, strerror(EINVAL));
207 					errors = 2;
208 				}
209 			} else {
210 				/* range of processors */
211 				int first, last;
212 				int found = 0;
213 
214 				if (verbosity == 0) {
215 					usage(gettext("must specify exactly "
216 					    "one processor if -s used"));
217 				}
218 				first = (int)strtol(*argv, &errptr, 10);
219 				if (*errptr++ != '-') {
220 					(void) fprintf(stderr,
221 					    gettext("%s: invalid processor "
222 					    "range %s\n"), cmdname, *argv);
223 					errors = 2;
224 					continue;
225 				}
226 				last = (int)strtol(errptr, &errptr, 10);
227 				if ((errptr != NULL && *errptr != '\0') ||
228 				    last < first || first < 0) {
229 					(void) fprintf(stderr,
230 					    gettext("%s: invalid processor "
231 					    "range %s\n"), cmdname, *argv);
232 					errors = 2;
233 					continue;
234 				}
235 				for (cpu = first; cpu <= last; cpu++) {
236 					if (ksp = kstat_lookup(kc, "cpu_info",
237 					    cpu, NULL)) {
238 						found = 1;
239 						cpu_info(kc, ksp, verbosity,
240 						    phys_view, 1);
241 					}
242 				}
243 				if (!found) {
244 					(void) fprintf(stderr,
245 					    gettext("%s: no processors in "
246 					    "range %d-%d\n"), cmdname,
247 					    first, last);
248 				}
249 			}
250 		}
251 	}
252 
253 	if (phys_view) {
254 		int i;
255 
256 		switch (verbosity) {
257 		case 0:
258 			/*
259 			 * Print "1" if all the cores on this chip are
260 			 * online.  "0" otherwise.
261 			 */
262 			for (i = 0; i <= max_chip_id; i++) {
263 				struct chip *c = &chips[i];
264 
265 				if (!c->visible)
266 					continue;
267 				(void) printf("%d\n",
268 				    c->online == c->core_count);
269 				exit(0);
270 			}
271 			break;
272 		case 1:
273 			/*
274 			 * Print the number of unique chips represented by
275 			 * all the cores specified on the command line
276 			 * (or, with no args, all the cores in the system).
277 			 */
278 			(void) printf("%d\n", chip_count);
279 			break;
280 		case 3:
281 			/*
282 			 * Print a report on each chip.
283 			 */
284 			for (i = 0; i <= max_chip_id; i++) {
285 				int j;
286 				struct chip *c = &chips[i];
287 
288 				if (!c->visible)
289 					continue;
290 
291 				(void) printf(gettext("The physical "
292 				    "processor has %d virtual %s ("),
293 				    c->core_count,
294 				    c->core_count == 1 ?
295 					gettext("processor") :
296 					gettext("processors"));
297 				for (j = 0; j < c->core_count; j++) {
298 					if (j > 0)
299 						(void) printf(", ");
300 					(void) printf("%d", c->cores[j]);
301 				}
302 				(void) printf(")\n");
303 
304 				(void) printf("  %s\n", c->impl);
305 
306 				/*
307 				 * If the "brand" has already been embedded
308 				 * at the front of the "impl" string, don't
309 				 * print it out again .. otherwise give it
310 				 * a fresh line to bask upon ..
311 				 */
312 				if (strncmp(c->impl, c->brand,
313 				    strlen(c->brand)) != 0)
314 					(void) printf("\t%s\n", c->brand);
315 			}
316 			break;
317 		}
318 	}
319 
320 	return (errors);
321 }
322 
323 #define	GETLONG(name)	((kstat_named_t *)kstat_data_lookup(ksp, name))->value.l
324 #define	GETSTR(name)	((kstat_named_t *)kstat_data_lookup(ksp, name))->value.c
325 #define	GETLONGSTR(name) \
326 	KSTAT_NAMED_STR_PTR((kstat_named_t *)kstat_data_lookup(ksp, name))
327 
328 /*
329  * Utility function to retrieve the localized version of the cpu state string.
330  */
331 static const char *
332 get_cpu_state(const char *state)
333 {
334 	int i;
335 
336 	for (i = 0; i < NCPUSTATES; i++)
337 		if (strcmp(cpu_states[i].state, state) == 0)
338 			return (cpu_states[i].lstate);
339 	return (gettext("(unknown)"));
340 }
341 
342 static void
343 cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, int phys_view,
344     int visible)
345 {
346 	char	curtime[40], start[40];
347 	processorid_t cpu_id = ksp->ks_instance;
348 	time_t	now = time(NULL);
349 
350 	if (kstat_read(kc, ksp, NULL) == -1) {
351 		(void) fprintf(stderr,
352 		    gettext("%s: kstat_read() failed for cpu %d: %s\n"),
353 		    cmdname, cpu_id, strerror(errno));
354 		exit(1);
355 	}
356 
357 	if (phys_view) {
358 		kstat_named_t *k =
359 		    (kstat_named_t *)kstat_data_lookup(ksp, "chip_id");
360 		struct chip *c;
361 
362 		if (k == NULL) {
363 			(void) fprintf(stderr,
364 			    gettext("%s: Physical processor view "
365 			    "not supported\n"),
366 			    cmdname);
367 			exit(1);
368 		}
369 
370 		c = &chips[k->value.i32];
371 
372 		if (visible && c->core_count != c->visible) {
373 			/*
374 			 * We've already inventoried this chip.  And the user
375 			 * specified a range of CPUIDs.  So, now we just
376 			 * need to note that this is one of the chips to
377 			 * display.
378 			 */
379 			c->visible++;
380 			return;
381 		}
382 
383 		if (c->core_count == 0) {
384 			char *str;
385 
386 			str = GETLONGSTR("implementation");
387 			(void) strlcpy(c->impl, str ? str : "(unknown)",
388 			    sizeof (c->impl));
389 
390 			str = GETLONGSTR("brand");
391 			(void) strlcpy(c->brand, str ? str : "(unknown)",
392 			    sizeof (c->brand));
393 
394 			chip_count++;
395 		}
396 
397 		c->cores[c->core_count] = cpu_id;
398 		c->core_count++;
399 		c->online += strcmp(GETSTR("state"), "on-line") == 0;
400 
401 		if (visible)
402 			c->visible++;
403 		return;
404 	}
405 
406 	(void) strftime(start, sizeof (start), gettext("%m/%d/%Y %T"),
407 	    localtime((time_t *)&GETLONG("state_begin")));
408 	(void) strftime(curtime, sizeof (curtime), gettext("%m/%d/%Y %T"),
409 	    localtime(&now));
410 
411 	if (verbosity == 0) {
412 		(void) printf("%d\n", strcmp(GETSTR("state"), "on-line") == 0);
413 		return;
414 	}
415 	if (verbosity == 1) {
416 		(void) printf(gettext("%d\t%-8s  since %s\n"), cpu_id,
417 		    get_cpu_state(GETSTR("state")), start);
418 		return;
419 	}
420 
421 	(void) printf(gettext("Status of virtual processor %d as of: %s\n"),
422 	    cpu_id, curtime);
423 	(void) printf(gettext("  %s since %s.\n"),
424 	    get_cpu_state(GETSTR("state")), start);
425 
426 	if (GETLONG("clock_MHz") != 0)
427 		(void) printf(gettext("  The %s processor operates at %d MHz"),
428 		    GETSTR("cpu_type"), GETLONG("clock_MHz"));
429 	else
430 		(void) printf(gettext("  The %s processor operates at an "
431 		    "unknown frequency"), GETSTR("cpu_type"));
432 
433 	if (GETSTR("fpu_type")[0] == '\0')
434 		(void) printf(gettext(",\n\tand has no floating point "
435 		    "processor.\n"));
436 	else if (strchr("aeiouy", GETSTR("fpu_type")[0]))
437 		(void) printf(gettext(",\n\tand has an %s floating point "
438 		    "processor.\n"), GETSTR("fpu_type"));
439 	else
440 		(void) printf(gettext(",\n\tand has a %s floating point "
441 		    "processor.\n"), GETSTR("fpu_type"));
442 }
443