xref: /illumos-gate/usr/src/cmd/psrinfo/psrinfo.c (revision a3477ee4728af4a4c3c6869e248aa735d52cbefb)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * This implements psrinfo(1M), a utility to report various information
18  * about processors, cores, and threads (virtual cpus).  This is mostly
19  * intended for human consumption - this utility doesn't do much more than
20  * simply process kstats for human readability.
21  *
22  * All the relevant kstats are in the cpu_info kstat module.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <kstat.h>
30 #include <libintl.h>
31 #include <locale.h>
32 #include <libgen.h>
33 #include <ctype.h>
34 #include <errno.h>
35 
36 #define	_(x)	gettext(x)
37 #if XGETTEXT
38 /* These CPU states are here for benefit of xgettext */
39 _("on-line")
40 _("off-line")
41 _("faulted")
42 _("powered-off")
43 _("no-intr")
44 _("spare")
45 _("unknown")
46 #endif
47 
48 /*
49  * We deal with sorted linked lists, where the sort key is usually the
50  * cpu id, core id, or chip id.  We generalize this with simple node.
51  */
52 struct link {
53 	long		l_id;
54 	struct link	*l_next;
55 	void		*l_ptr;
56 };
57 
58 /*
59  * A physical chip.  A chip can contain multiple cores and virtual cpus.
60  */
61 struct pchip {
62 	struct link	p_link;
63 	int		p_ncore;
64 	int		p_nvcpu;
65 	struct link	*p_cores;
66 	struct link	*p_vcpus;
67 	int		p_doit;
68 };
69 
70 struct core {
71 	struct link	c_link;
72 	struct link	c_link_pchip;
73 
74 	int		c_nvcpu;
75 	int		c_doit;
76 
77 	struct pchip	*c_pchip;
78 	struct link	*c_vcpus;
79 };
80 
81 struct vcpu {
82 	struct link	v_link;
83 
84 	struct link	v_link_core;
85 	struct link	v_link_pchip;
86 
87 	int		v_doit;
88 
89 	struct pchip	*v_pchip;
90 	struct core	*v_core;
91 
92 	char		*v_state;
93 	long		v_state_begin;
94 	char		*v_cpu_type;
95 	char		*v_fpu_type;
96 	long		v_clock_mhz;
97 	long		v_pchip_id;	/* 1 per socket */
98 	char		*v_impl;
99 	char		*v_brand;
100 	long		v_core_id;	/* n per chip_id */
101 };
102 
103 static struct link *pchips = NULL;
104 static struct link *cores = NULL;
105 static struct link *vcpus = NULL;
106 
107 static const char *cmdname;
108 
109 static void
110 usage(char *msg)
111 {
112 	if (msg != NULL)
113 		(void) fprintf(stderr, "%s: %s\n", cmdname, msg);
114 	(void) fprintf(stderr, _("usage: \n" \
115 	    "\t%s [-v] [-p] [processor_id ...]\n" \
116 	    "\t%s -s [-p] processor_id\n"), cmdname, cmdname);
117 	exit(2);
118 }
119 
120 /* like perror, but includes the command name */
121 static void
122 die(const char *msg)
123 {
124 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno));
125 	exit(2);
126 }
127 
128 static char *
129 mystrdup(const char *src)
130 {
131 	char *dst;
132 
133 	if ((dst = strdup(src)) == NULL)
134 		die(_("strdup() failed"));
135 	return (dst);
136 }
137 
138 static void *
139 zalloc(size_t size)
140 {
141 	void *ptr;
142 
143 	if ((ptr = calloc(1, size)) == NULL)
144 		die(_("calloc() failed"));
145 	return (ptr);
146 }
147 
148 /*
149  * Insert a new node on a list, at the insertion point given.
150  */
151 static void
152 ins_link(struct link **ins, struct link *item)
153 {
154 	item->l_next = *ins;
155 	*ins = item;
156 }
157 
158 /*
159  * Find an id on a sorted list.  If the requested id is not found,
160  * then the insertpt will be set (if not null) to the location where
161  * a new node should be inserted with ins_link (see above).
162  */
163 static void *
164 find_link(void *list, int id, struct link ***insertpt)
165 {
166 	struct link **ins = list;
167 	struct link *l;
168 
169 	while ((l = *ins) != NULL) {
170 		if (l->l_id == id)
171 			return (l->l_ptr);
172 		if (l->l_id > id)
173 			break;
174 		ins = &l->l_next;
175 	}
176 	if (insertpt != NULL)
177 		*insertpt = ins;
178 	return (NULL);
179 }
180 
181 /*
182  * Print the linked list of ids in parens, taking care to collapse
183  * ranges, so instead of (0 1 2 3) it should print (0-3).
184  */
185 static void
186 print_links(struct link *l)
187 {
188 	int	start = -1;
189 	int	end = 0;
190 
191 	(void) printf(" (");
192 	while (l != NULL) {
193 		if (start < 0) {
194 			start = l->l_id;
195 		}
196 		end = l->l_id;
197 		if ((l->l_next == NULL) ||
198 		    (l->l_next->l_id > (l->l_id + 1))) {
199 			/* end of the contiguous group */
200 			if (start == end) {
201 				(void) printf("%d", start);
202 			} else {
203 				(void) printf("%d-%d", start, end);
204 			}
205 			if (l->l_next)
206 				(void) printf(" ");
207 			start = -1;
208 		}
209 		l = l->l_next;
210 	}
211 	(void) printf(")");
212 }
213 
214 static const char *
215 timestr(long t)
216 {
217 	static char buffer[256];
218 	(void) strftime(buffer, sizeof (buffer), _("%m/%d/%Y %T"),
219 	    localtime(&t));
220 	return (buffer);
221 }
222 
223 static void
224 print_vp(int nspec)
225 {
226 	struct pchip *chip;
227 	struct core *core;
228 	struct vcpu *vcpu;
229 	struct link *l1, *l2;
230 	int len;
231 	for (l1 = pchips; l1; l1 = l1->l_next) {
232 
233 		chip = l1->l_ptr;
234 
235 		if ((nspec != 0) && (chip->p_doit == 0))
236 			continue;
237 
238 		vcpu = chip->p_vcpus->l_ptr;
239 
240 		/*
241 		 * Note that some of the way these strings are broken up are
242 		 * to accommodate the legacy translations so that we won't
243 		 * have to retranslate for this utility.
244 		 */
245 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
246 			(void) printf(_("%s has %d virtual %s"),
247 			    _("The physical processor"),
248 			    chip->p_nvcpu,
249 			    chip->p_nvcpu > 1 ?
250 			    _("processors") :
251 			    _("processor"));
252 		} else {
253 			(void) printf(_("%s has %d %s and %d virtual %s"),
254 			    _("The physical processor"),
255 			    chip->p_ncore, _("cores"),
256 			    chip->p_nvcpu,
257 			    chip->p_nvcpu > 1 ?
258 			    _("processors") : _("processor"));
259 		}
260 
261 		print_links(chip->p_vcpus);
262 		(void) putchar('\n');
263 
264 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
265 			if (strlen(vcpu->v_impl)) {
266 				(void) printf("  %s\n", vcpu->v_impl);
267 			}
268 			if (((len = strlen(vcpu->v_brand)) != 0) &&
269 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
270 				(void) printf("\t%s", vcpu->v_brand);
271 			(void) putchar('\n');
272 		} else {
273 			for (l2 = chip->p_cores; l2; l2 = l2->l_next) {
274 				core = l2->l_ptr;
275 				(void) printf(_("  %s has %d virtual %s"),
276 				    _("The core"),
277 				    core->c_nvcpu,
278 				    chip->p_nvcpu > 1 ?
279 				    _("processors") : _("processor"));
280 				print_links(core->c_vcpus);
281 				(void) putchar('\n');
282 			}
283 			if (strlen(vcpu->v_impl)) {
284 				(void) printf("    %s\n", vcpu->v_impl);
285 			}
286 			if (((len = strlen(vcpu->v_brand)) != 0) &&
287 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
288 				(void) printf("      %s\n", vcpu->v_brand);
289 		}
290 	}
291 }
292 
293 static void
294 print_ps(void)
295 {
296 	int online = 1;
297 	struct pchip *p;
298 	struct vcpu *v;
299 	struct link *l;
300 
301 	/*
302 	 * Report "1" if all cpus colocated on the same chip are online.
303 	 */
304 	for (l = pchips; l != NULL; l = l->l_next) {
305 		p = l->l_ptr;
306 		if (p->p_doit)
307 			break;
308 	}
309 	if (p == NULL)
310 		return;	/* should never happen! */
311 	for (l = p->p_vcpus; l != NULL; l = l->l_next) {
312 		v = l->l_ptr;
313 		if (strcmp(v->v_state, "on-line") != 0) {
314 			online = 0;
315 			break;
316 		}
317 	}
318 
319 	(void) printf("%d\n", online);
320 }
321 
322 static void
323 print_s(void)
324 {
325 	struct link *l;
326 
327 	/*
328 	 * Find the processor (there will be only one) that we selected,
329 	 * and report whether or not it is online.
330 	 */
331 	for (l = vcpus; l != NULL; l = l->l_next) {
332 		struct vcpu *v = l->l_ptr;
333 		if (v->v_doit) {
334 			(void) printf("%d\n",
335 			    strcmp(v->v_state, "on-line") == 0 ? 1 : 0);
336 			return;
337 		}
338 	}
339 }
340 
341 static void
342 print_p(int nspec)
343 {
344 	struct		link *l1, *l2;
345 	int		online = 0;
346 
347 	/*
348 	 * Print the number of physical packages with at least one processor
349 	 * online.
350 	 */
351 	for (l1 = pchips; l1 != NULL; l1 = l1->l_next) {
352 		struct pchip *p = l1->l_ptr;
353 		if ((nspec == 0) || (p->p_doit)) {
354 
355 			for (l2 = p->p_vcpus; l2 != NULL; l2 = l2->l_next) {
356 				struct vcpu *v = l2->l_ptr;
357 				if (strcmp(v->v_state, "on-line") == 0) {
358 					online++;
359 					break;
360 				}
361 			}
362 		}
363 	}
364 	(void) printf("%d\n", online);
365 }
366 
367 static void
368 print_v(int nspec)
369 {
370 	struct link	*l;
371 
372 	for (l = vcpus; l != NULL; l = l->l_next) {
373 		struct vcpu *v = l->l_ptr;
374 
375 		if ((nspec != 0) && (!v->v_doit))
376 			continue;
377 		(void) printf(_("Status of virtual processor %d as of: "),
378 		    l->l_id);
379 		(void) printf("%s\n", timestr(time(NULL)));
380 		(void) printf(_("  %s since %s.\n"),
381 		    _(v->v_state), timestr(v->v_state_begin));
382 		if (v->v_clock_mhz) {
383 			(void) printf(
384 			    _("  The %s processor operates at %llu MHz,\n"),
385 			    v->v_cpu_type, (unsigned long long)v->v_clock_mhz);
386 		} else {
387 			(void) printf(
388 			    _("  The %s processor operates at " \
389 			    "an unknown frequency,\n"), v->v_cpu_type);
390 		}
391 		switch (*v->v_fpu_type) {
392 		case '\0':
393 			(void) printf(
394 			    _("\tand has no floating point processor.\n"));
395 			break;
396 		case 'a': case 'A':
397 		case 'e': case 'E':
398 		case 'i': case 'I':
399 		case 'o': case 'O':
400 		case 'u': case 'U':
401 		case 'y': case 'Y':
402 			(void) printf(
403 			    _("\tand has an %s floating point processor.\n"),
404 			    v->v_fpu_type);
405 			break;
406 		default:
407 			(void) printf(
408 			    _("\tand has a %s floating point processor.\n"),
409 			    v->v_fpu_type);
410 			break;
411 		}
412 	}
413 }
414 
415 static void
416 print_normal(int nspec)
417 {
418 	struct link	*l;
419 	struct vcpu	*v;
420 
421 	for (l = vcpus; l != NULL; l = l->l_next) {
422 		v = l->l_ptr;
423 		if ((nspec == 0) || (v->v_doit)) {
424 			(void) printf(_("%d\t%-8s  since %s\n"),
425 			    l->l_id, _(v->v_state), timestr(v->v_state_begin));
426 		}
427 	}
428 }
429 
430 int
431 main(int argc, char **argv)
432 {
433 	kstat_ctl_t	*kc;
434 	kstat_t		*ksp;
435 	kstat_named_t	*knp;
436 	struct vcpu	*vc;
437 	struct core	*core;
438 	struct pchip	*chip;
439 	struct link	**ins;
440 	char		*s;
441 	int		nspec;
442 	int		optc;
443 	int		opt_s = 0;
444 	int		opt_p = 0;
445 	int		opt_v = 0;
446 	int		ex = 0;
447 
448 	cmdname = basename(argv[0]);
449 
450 
451 	(void) setlocale(LC_ALL, "");
452 #if !defined(TEXT_DOMAIN)
453 #define	TEXT_DOMAIN	"SYS_TEST"
454 #endif
455 	(void) textdomain(TEXT_DOMAIN);
456 
457 	/* collect the kstats */
458 	if ((kc = kstat_open()) == NULL)
459 		die(_("kstat_open() failed"));
460 
461 	if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL)
462 		die(_("kstat_lookup() failed"));
463 
464 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
465 
466 		if (strcmp(ksp->ks_module, "cpu_info") != 0)
467 			continue;
468 		if (kstat_read(kc, ksp, NULL) == NULL)
469 			die(_("kstat_read() failed"));
470 
471 		vc = find_link(&vcpus, ksp->ks_instance, &ins);
472 		if (vc == NULL) {
473 			vc = zalloc(sizeof (struct vcpu));
474 			vc->v_link.l_id = ksp->ks_instance;
475 			vc->v_link_core.l_id = ksp->ks_instance;
476 			vc->v_link_pchip.l_id = ksp->ks_instance;
477 			vc->v_link.l_ptr = vc;
478 			vc->v_link_core.l_ptr = vc;
479 			vc->v_link_pchip.l_ptr = vc;
480 			ins_link(ins, &vc->v_link);
481 		}
482 
483 		if ((knp = kstat_data_lookup(ksp, "state")) != NULL) {
484 			vc->v_state = mystrdup(knp->value.c);
485 		} else {
486 			vc->v_state = "unknown";
487 		}
488 
489 		if ((knp = kstat_data_lookup(ksp, "cpu_type")) != NULL) {
490 			vc->v_cpu_type = mystrdup(knp->value.c);
491 		}
492 		if ((knp = kstat_data_lookup(ksp, "fpu_type")) != NULL) {
493 			vc->v_fpu_type = mystrdup(knp->value.c);
494 		}
495 
496 		if ((knp = kstat_data_lookup(ksp, "state_begin")) != NULL) {
497 			vc->v_state_begin = knp->value.l;
498 		}
499 
500 		if ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL) {
501 			vc->v_clock_mhz = knp->value.l;
502 		}
503 
504 		if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
505 			vc->v_brand = _("(unknown)");
506 		} else {
507 			vc->v_brand = mystrdup(knp->value.str.addr.ptr);
508 		}
509 
510 		if ((knp = kstat_data_lookup(ksp, "implementation")) == NULL) {
511 			vc->v_impl = _("(unknown)");
512 		} else {
513 			vc->v_impl = mystrdup(knp->value.str.addr.ptr);
514 		}
515 		/*
516 		 * Legacy code removed the chipid and cpuid fields... we
517 		 * do the same for compatibility.  Note that the original
518 		 * pattern is a bit strange, and we have to emulate this because
519 		 * on SPARC we *do* emit these.  The original pattern we are
520 		 * emulating is: $impl =~ s/(cpuid|chipid)\s*\w+\s+//;
521 		 */
522 		if ((s = strstr(vc->v_impl, "chipid")) != NULL) {
523 			char *x = s + strlen("chipid");
524 			while (isspace(*x))
525 				x++;
526 			if ((!isalnum(*x)) && (*x != '_'))
527 				goto nochipid;
528 			while (isalnum(*x) || (*x == '_'))
529 				x++;
530 			if (!isspace(*x))
531 				goto nochipid;
532 			while (isspace(*x))
533 				x++;
534 			(void) strcpy(s, x);
535 		}
536 nochipid:
537 		if ((s = strstr(vc->v_impl, "cpuid")) != NULL) {
538 			char *x = s + strlen("cpuid");
539 			while (isspace(*x))
540 				x++;
541 			if ((!isalnum(*x)) && (*x != '_'))
542 				goto nocpuid;
543 			while (isalnum(*x) || (*x == '_'))
544 				x++;
545 			if (!isspace(*x))
546 				goto nocpuid;
547 			while (isspace(*x))
548 				x++;
549 			(void) strcpy(s, x);
550 		}
551 nocpuid:
552 
553 		if ((knp = kstat_data_lookup(ksp, "chip_id")) != NULL)
554 			vc->v_pchip_id = knp->value.l;
555 		chip = find_link(&pchips, vc->v_pchip_id, &ins);
556 		if (chip == NULL) {
557 			chip = zalloc(sizeof (struct pchip));
558 			chip->p_link.l_id = vc->v_pchip_id;
559 			chip->p_link.l_ptr = chip;
560 			ins_link(ins, &chip->p_link);
561 		}
562 		vc->v_pchip = chip;
563 
564 		if ((knp = kstat_data_lookup(ksp, "core_id")) != NULL)
565 			vc->v_core_id = knp->value.l;
566 		core = find_link(&cores, vc->v_core_id, &ins);
567 		if (core == NULL) {
568 			core = zalloc(sizeof (struct core));
569 			core->c_link.l_id = vc->v_core_id;
570 			core->c_link.l_ptr = core;
571 			core->c_link_pchip.l_id = vc->v_core_id;
572 			core->c_link_pchip.l_ptr = core;
573 			core->c_pchip = chip;
574 			ins_link(ins, &core->c_link);
575 			chip->p_ncore++;
576 			(void) find_link(&chip->p_cores, core->c_link.l_id,
577 			    &ins);
578 			ins_link(ins, &core->c_link_pchip);
579 		}
580 		vc->v_core = core;
581 
582 
583 
584 		/* now put other linkages in place */
585 		(void) find_link(&chip->p_vcpus, vc->v_link.l_id, &ins);
586 		ins_link(ins, &vc->v_link_pchip);
587 		chip->p_nvcpu++;
588 
589 		(void) find_link(&core->c_vcpus, vc->v_link.l_id, &ins);
590 		ins_link(ins, &vc->v_link_core);
591 		core->c_nvcpu++;
592 	}
593 
594 	(void) kstat_close(kc);
595 
596 	nspec = 0;
597 
598 	while ((optc = getopt(argc, argv, "pvs")) != EOF) {
599 		switch (optc) {
600 		case 's':
601 			opt_s = 1;
602 			break;
603 		case 'p':
604 			opt_p = 1;
605 			break;
606 		case 'v':
607 			opt_v = 1;
608 			break;
609 		default:
610 			usage(NULL);
611 		}
612 	}
613 
614 	while (optind < argc) {
615 		long id;
616 		char *eptr;
617 		struct link *l;
618 		id = strtol(argv[optind], &eptr, 10);
619 		l = find_link(&vcpus, id, NULL);
620 		if ((*eptr != '\0') || (l == NULL)) {
621 			(void) fprintf(stderr,
622 			    _("%s: processor %s: Invalid argument\n"),
623 			    cmdname, argv[optind]);
624 			ex = 2;
625 		} else {
626 			((struct vcpu *)l->l_ptr)->v_doit = 1;
627 			((struct vcpu *)l->l_ptr)->v_pchip->p_doit = 1;
628 			((struct vcpu *)l->l_ptr)->v_core->c_doit = 1;
629 		}
630 		nspec++;
631 		optind++;
632 	}
633 
634 	if (opt_s && opt_v) {
635 		usage(_("options -s and -v are mutually exclusive"));
636 	}
637 	if (opt_s && nspec != 1) {
638 		usage(_("must specify exactly one processor if -s used"));
639 	}
640 	if (opt_v && opt_p) {
641 		print_vp(nspec);
642 	} else if (opt_s && opt_p) {
643 		print_ps();
644 	} else if (opt_p) {
645 		print_p(nspec);
646 	} else if (opt_v) {
647 		print_v(nspec);
648 	} else if (opt_s) {
649 		print_s();
650 	} else {
651 		print_normal(nspec);
652 	}
653 
654 	return (ex);
655 }
656