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