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 }