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