xref: /illumos-gate/usr/src/cmd/psrinfo/psrinfo.c (revision a07094369b21309434206d9b3601d162693466fc)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <time.h>
34 #include <kstat.h>
35 #include <sys/processor.h>	/* for processorid_t */
36 
37 #include <libintl.h>
38 #include <locale.h>
39 
40 #ifndef	TEXT_DOMAIN
41 #define	TEXT_DOMAIN	"SYS_TEST"
42 #endif
43 
44 #define	NCPUSTATES	6	/* on-line, off-line, no-intr, faulted, */
45 				/* spare, power-off */
46 
47 /*
48  * Possible states that a cpu may be in, and their corresponding
49  * localized versions.
50  */
51 static struct {
52 	const char *state;	/* State returned in kstat. */
53 	const char *lstate;	/* Localized version of the state. */
54 } cpu_states[NCPUSTATES];
55 
56 static const char cmdname[] = "psrinfo";
57 
58 #define	CPUS_PER_CHIP_MAX 256	/* ABEN (Arbitrarily big-enough number) */
59 static int chip_count = 0;
60 
61 typedef struct chip {
62 	int chip_id;
63 	int visible;
64 	int online;
65 	int cpu_count;
66 	char impl[128];
67 	char brand[128];
68 	struct chip *next;
69 	processorid_t cpus[CPUS_PER_CHIP_MAX];
70 } chipdata;
71 static chipdata *chiplist = NULL;
72 
73 static void cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity,
74 	int phys_view, int visible);
75 static void print_cpuid_list(processorid_t *ids, int count);
76 
77 static chipdata *get_chipdata(int chip_id);
78 
79 static void
80 usage(char *msg)
81 {
82 	if (msg != NULL)
83 		(void) fprintf(stderr, "%s: %s\n", cmdname, msg);
84 	(void) fprintf(stderr,
85 	    gettext("usage: \n\t%s [-v] [-p] [processor_id ...]\n"
86 	    "\t%s -s [-p] processor_id\n"), cmdname, cmdname);
87 	exit(2);
88 }
89 
90 int
91 main(int argc, char *argv[])
92 {
93 	kstat_ctl_t *kc;
94 	kstat_t	*ksp;
95 	int c;
96 	processorid_t cpu;
97 	int verbosity = 1;	/* 0 = silent, 1 = normal, 3 = verbose */
98 	int phys_view = 0;
99 	int errors = 0;
100 	char *errptr;
101 
102 	(void) setlocale(LC_ALL, "");
103 	(void) textdomain(TEXT_DOMAIN);
104 
105 	while ((c = getopt(argc, argv, "psv")) != EOF) {
106 		switch (c) {
107 		case 'v':
108 			verbosity |= 2;
109 			break;
110 		case 's':
111 			verbosity &= ~1;
112 			break;
113 		case 'p':
114 			phys_view = 1;
115 			break;
116 		default:
117 			usage(NULL);
118 		}
119 	}
120 
121 	argc -= optind;
122 	argv += optind;
123 
124 	if (verbosity == 2)
125 		usage(gettext("options -s and -v are mutually exclusive"));
126 
127 	if (verbosity == 0 && argc != 1)
128 		usage(gettext("must specify exactly one processor if -s used"));
129 
130 	if ((kc = kstat_open()) == NULL) {
131 		(void) fprintf(stderr, gettext("%s: kstat_open() failed: %s\n"),
132 		    cmdname, strerror(errno));
133 		exit(1);
134 	}
135 
136 	/*
137 	 * Build localized cpu state table.
138 	 */
139 	cpu_states[0].state = PS_ONLINE;
140 	cpu_states[0].lstate = gettext(PS_ONLINE);
141 	cpu_states[1].state = PS_POWEROFF;
142 	cpu_states[1].lstate = gettext(PS_POWEROFF);
143 	cpu_states[2].state = PS_NOINTR;
144 	cpu_states[2].lstate = gettext(PS_NOINTR);
145 	cpu_states[3].state = PS_FAULTED;
146 	cpu_states[3].lstate = gettext(PS_FAULTED);
147 	cpu_states[4].state = PS_SPARE;
148 	cpu_states[4].lstate = gettext(PS_SPARE);
149 	cpu_states[5].state = PS_OFFLINE;
150 	cpu_states[5].lstate = gettext(PS_OFFLINE);
151 
152 	/*
153 	 * In the physical view, we want to display all the cpu info or
154 	 * none, even when the user specifies a range of CPUIDs.  So for
155 	 * "psrinfo -pv <range>" or "psrinfo -ps <range>", we inventory
156 	 * every cpu_info kstat, and *then* we go through the user
157 	 * specified processors merely flipping on their "visible"
158 	 * flags.
159 	 */
160 
161 	if (argc == 0 || (phys_view && (verbosity != 1))) {
162 		/*
163 		 * No processors specified.  Report on all of them.
164 		 * Or do a complete inventory in preparation for a
165 		 * specified list of physical processors.  See note
166 		 * immediately above.
167 		 */
168 		processorid_t maxcpu = -1;
169 
170 		for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next)
171 			if (strcmp(ksp->ks_module, "cpu_info") == 0 &&
172 			    ksp->ks_instance > maxcpu)
173 				maxcpu = ksp->ks_instance;
174 
175 		/*
176 		 * N.B. We're assuming that instance number == cpuid.
177 		 * A number of things break if this isn't true.  In
178 		 * particular, print_cpuid_list() assumes that c->cpus[]
179 		 * is numerically sorted, which is true only because we
180 		 * add each virtual processor in cpuid order here.
181 		 */
182 		for (cpu = 0; cpu <= maxcpu; cpu++)
183 			if (ksp = kstat_lookup(kc, "cpu_info", cpu, NULL))
184 				cpu_info(kc, ksp, verbosity, phys_view,
185 				    argc == 0);
186 	}
187 
188 	if (argc != 0) {
189 		/*
190 		 * Report on specified processors.
191 		 */
192 		for (; argc > 0; argv++, argc--) {
193 			if (strchr(*argv, '-') == NULL) {
194 				/* individual processor id */
195 				char cpubuf[20];
196 
197 				(void) sprintf(cpubuf, "cpu_info%.10s", *argv);
198 				if (ksp = kstat_lookup(kc, "cpu_info", -1,
199 				    cpubuf))
200 					cpu_info(kc, ksp, verbosity,
201 					    phys_view, 1);
202 				else {
203 					(void) fprintf(stderr,
204 					    gettext("%s: processor %s: %s\n"),
205 					    cmdname, *argv, strerror(EINVAL));
206 					errors = 2;
207 				}
208 			} else {
209 				/* range of processors */
210 				int first, last;
211 				int found = 0;
212 
213 				if (verbosity == 0) {
214 					usage(gettext("must specify exactly "
215 					    "one processor if -s used"));
216 				}
217 				first = (int)strtol(*argv, &errptr, 10);
218 				if (*errptr++ != '-') {
219 					(void) fprintf(stderr,
220 					    gettext("%s: invalid processor "
221 					    "range %s\n"), cmdname, *argv);
222 					errors = 2;
223 					continue;
224 				}
225 				last = (int)strtol(errptr, &errptr, 10);
226 				if ((errptr != NULL && *errptr != '\0') ||
227 				    last < first || first < 0) {
228 					(void) fprintf(stderr,
229 					    gettext("%s: invalid processor "
230 					    "range %s\n"), cmdname, *argv);
231 					errors = 2;
232 					continue;
233 				}
234 				for (cpu = first; cpu <= last; cpu++) {
235 					if (ksp = kstat_lookup(kc, "cpu_info",
236 					    cpu, NULL)) {
237 						found = 1;
238 						cpu_info(kc, ksp, verbosity,
239 						    phys_view, 1);
240 					}
241 				}
242 				if (!found) {
243 					(void) fprintf(stderr,
244 					    gettext("%s: no processors in "
245 					    "range %d-%d\n"), cmdname,
246 					    first, last);
247 				}
248 			}
249 		}
250 	}
251 
252 	if (phys_view) {
253 		chipdata *c;
254 
255 		switch (verbosity) {
256 		case 0:
257 			/*
258 			 * Print "1" if all the cpus on this chip are
259 			 * online.  "0" otherwise.
260 			 */
261 			for (c = chiplist; c != NULL; c = c->next) {
262 
263 				if (!c->visible)
264 					continue;
265 				(void) printf("%d\n",
266 				    c->online == c->cpu_count);
267 				exit(0);
268 			}
269 			break;
270 		case 1:
271 			/*
272 			 * Print the number of unique chips represented by
273 			 * all the cpus specified on the command line
274 			 * (or, with no args, all the cpus in the system).
275 			 */
276 			(void) printf("%d\n", chip_count);
277 			break;
278 		case 3:
279 			/*
280 			 * Print a report on each chip.
281 			 */
282 			for (c = chiplist; c != NULL; c = c->next) {
283 				if (!c->visible)
284 					continue;
285 
286 				(void) printf(gettext("The physical "
287 				    "processor has %d virtual %s ("),
288 				    c->cpu_count,
289 				    c->cpu_count == 1 ?
290 					gettext("processor") :
291 					gettext("processors"));
292 				print_cpuid_list(c->cpus, c->cpu_count);
293 				(void) printf(")\n");
294 
295 				(void) printf("  %s\n", c->impl);
296 
297 				/*
298 				 * If the "brand" has already been embedded
299 				 * at the front of the "impl" string, don't
300 				 * print it out again .. otherwise give it
301 				 * a fresh line to bask upon ..
302 				 */
303 				if (strncmp(c->impl, c->brand,
304 				    strlen(c->brand)) != 0)
305 					(void) printf("\t%s\n", c->brand);
306 			}
307 			break;
308 		}
309 	}
310 
311 	return (errors);
312 }
313 
314 #define	GETLONG(name)	((kstat_named_t *)kstat_data_lookup(ksp, name))->value.l
315 #define	GETSTR(name)	((kstat_named_t *)kstat_data_lookup(ksp, name))->value.c
316 #define	GETLONGSTR(name) \
317 	KSTAT_NAMED_STR_PTR((kstat_named_t *)kstat_data_lookup(ksp, name))
318 
319 /*
320  * Utility function to retrieve the localized version of the cpu state string.
321  */
322 static const char *
323 get_cpu_state(const char *state)
324 {
325 	int i;
326 
327 	for (i = 0; i < NCPUSTATES; i++)
328 		if (strcmp(cpu_states[i].state, state) == 0)
329 			return (cpu_states[i].lstate);
330 	return (gettext("(unknown)"));
331 }
332 
333 static void
334 cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, int phys_view,
335     int visible)
336 {
337 	char	curtime[40], start[40];
338 	processorid_t cpu_id = ksp->ks_instance;
339 	time_t	now = time(NULL);
340 
341 	if (kstat_read(kc, ksp, NULL) == -1) {
342 		(void) fprintf(stderr,
343 		    gettext("%s: kstat_read() failed for cpu %d: %s\n"),
344 		    cmdname, cpu_id, strerror(errno));
345 		exit(1);
346 	}
347 
348 	if (phys_view) {
349 		kstat_named_t *k =
350 		    (kstat_named_t *)kstat_data_lookup(ksp, "chip_id");
351 		chipdata *c;
352 
353 		if (k == NULL) {
354 			(void) fprintf(stderr,
355 			    gettext("%s: Physical processor view "
356 			    "not supported\n"),
357 			    cmdname);
358 			exit(1);
359 		}
360 
361 		c = get_chipdata(k->value.i32);
362 
363 		if (visible && c->cpu_count != c->visible) {
364 			/*
365 			 * We've already inventoried this chip.  And the user
366 			 * specified a range of CPUIDs.  So, now we just
367 			 * need to note that this is one of the chips to
368 			 * display.
369 			 */
370 			c->visible++;
371 			return;
372 		}
373 
374 		if (c->cpu_count == 0) {
375 			char *str;
376 
377 			str = GETLONGSTR("implementation");
378 			(void) strlcpy(c->impl, str ? str : "(unknown)",
379 			    sizeof (c->impl));
380 
381 			str = GETLONGSTR("brand");
382 			(void) strlcpy(c->brand, str ? str : "(unknown)",
383 			    sizeof (c->brand));
384 
385 			chip_count++;
386 		}
387 
388 		c->cpus[c->cpu_count] = cpu_id;
389 		c->cpu_count++;
390 		c->online += strcmp(GETSTR("state"), "on-line") == 0;
391 
392 		if (visible)
393 			c->visible++;
394 		return;
395 	}
396 
397 	(void) strftime(start, sizeof (start), gettext("%m/%d/%Y %T"),
398 	    localtime((time_t *)&GETLONG("state_begin")));
399 	(void) strftime(curtime, sizeof (curtime), gettext("%m/%d/%Y %T"),
400 	    localtime(&now));
401 
402 	if (verbosity == 0) {
403 		(void) printf("%d\n", strcmp(GETSTR("state"), "on-line") == 0);
404 		return;
405 	}
406 	if (verbosity == 1) {
407 		(void) printf(gettext("%d\t%-8s  since %s\n"), cpu_id,
408 		    get_cpu_state(GETSTR("state")), start);
409 		return;
410 	}
411 
412 	(void) printf(gettext("Status of virtual processor %d as of: %s\n"),
413 	    cpu_id, curtime);
414 	(void) printf(gettext("  %s since %s.\n"),
415 	    get_cpu_state(GETSTR("state")), start);
416 
417 	if (GETLONG("clock_MHz") != 0)
418 		(void) printf(gettext("  The %s processor operates at %d MHz"),
419 		    GETSTR("cpu_type"), GETLONG("clock_MHz"));
420 	else
421 		(void) printf(gettext("  The %s processor operates at an "
422 		    "unknown frequency"), GETSTR("cpu_type"));
423 
424 	if (GETSTR("fpu_type")[0] == '\0')
425 		(void) printf(gettext(",\n\tand has no floating point "
426 		    "processor.\n"));
427 	else if (strchr("aeiouy", GETSTR("fpu_type")[0]))
428 		(void) printf(gettext(",\n\tand has an %s floating point "
429 		    "processor.\n"), GETSTR("fpu_type"));
430 	else
431 		(void) printf(gettext(",\n\tand has a %s floating point "
432 		    "processor.\n"), GETSTR("fpu_type"));
433 }
434 
435 
436 
437 static void
438 print_cpuid_list(processorid_t *ids, int count)
439 {
440 	int i;
441 	char *separator = "";
442 
443 	for (i = 0; i < count; ++i) {
444 		/*
445 		 * if we're not at the ends, and the two adjacent numbers
446 		 * are sequential, then don't print this number.
447 		 */
448 		if (i > 0 && i < count-1 &&
449 		    ids[i]-1 == ids[i-1] &&
450 		    ids[i]+1 == ids[i+1]) {
451 			separator = "-";
452 			continue;
453 		}
454 
455 		(void) printf("%s%d", separator, ids[i]);
456 		separator = " ";
457 	}
458 }
459 
460 
461 /*
462  * get_chipdata(chip_id)
463  * Search for the chipdata structure (if allocated previously) that
464  * matches the chip_id argument and return a pointer to it.
465  * Allocate and return a pointer to a new chipdata if none
466  * is found matching the chip_id. The new chipdata will be inserted
467  * into the chiplist in ascending chip_id order.
468  * This routine allows us to support chip id (portid/deviceid)
469  * space which could be disjoint or/and larger from the logical cpuid space.
470  * Note that the number of chips visible to psrinfo is bounded by the
471  * number of (virtual) cpus. It is not possible to see more chips
472  * than (virtual) cpus.
473  */
474 static chipdata *
475 get_chipdata(int chip_id)
476 {
477 	chipdata *chip_ptr;
478 	chipdata *newchip_ptr;
479 	chipdata *prevchip_ptr = NULL;
480 
481 	if (chiplist == NULL) {
482 		/*
483 		 * first time - allocate the chipdata structure for
484 		 * this new chip.
485 		 */
486 		if ((chiplist = calloc(1, sizeof (chipdata))) == NULL) {
487 			(void) fprintf(stderr,
488 			    gettext("%s: Calloc failed\n"), cmdname);
489 			exit(1);
490 		}
491 		chiplist->chip_id = chip_id;
492 		chiplist->next = NULL;
493 		return (chiplist);
494 	} else {
495 		/*
496 		 * look for the chipdata if it already
497 		 * been allocated.
498 		 */
499 		for (chip_ptr = chiplist; chip_ptr != NULL;
500 		    chip_ptr = chip_ptr->next) {
501 			if (chip_ptr->chip_id == chip_id) {
502 				/* found the allocated chipdata */
503 				return (chip_ptr);
504 			} else if (chip_ptr->chip_id > chip_id) {
505 				/*
506 				 * End search here since chiplist
507 				 * is sorted in ascending order
508 				 */
509 				break;
510 			}
511 			prevchip_ptr = chip_ptr;
512 		}
513 
514 		/*
515 		 * Not found. Need to allocate a new chipdata
516 		 * structure and insert it into the chiplist in
517 		 * ascending chip_id order
518 		 */
519 		if ((newchip_ptr = calloc(1, sizeof (chipdata))) == NULL) {
520 			(void) fprintf(stderr,
521 			    gettext("%s: Calloc failed\n"), cmdname);
522 			exit(1);
523 		}
524 		newchip_ptr->chip_id = chip_id;
525 
526 		/*
527 		 * See if it needs to go in front of the list
528 		 * by looking at the result of the previous
529 		 * search, prevchip_ptr being NULL.
530 		 */
531 		if (prevchip_ptr == NULL) {
532 			/* put it in front of the list */
533 			newchip_ptr->next = chiplist;
534 			chiplist = newchip_ptr;
535 		} else {
536 			/*
537 			 * Insert the new chipdata in the list
538 			 * after the one pointed by prevchip_ptr
539 			 */
540 			newchip_ptr->next = prevchip_ptr->next;
541 			prevchip_ptr->next = newchip_ptr;
542 		}
543 		return (newchip_ptr);
544 	}
545 }
546