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
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.
13 * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
14 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
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.
23 * All the relevant kstats are in the cpu_info kstat module.
37 #define _(x) gettext(x)
39 /* These CPU states are here for benefit of xgettext */
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.
60 * A physical chip. A chip can contain multiple cores and virtual cpus.
73 struct link c_link_pchip
;
78 struct pchip
*c_pchip
;
85 struct link v_link_core
;
86 struct link v_link_pchip
;
90 struct pchip
*v_pchip
;
98 long v_pchip_id
; /* 1 per socket */
102 long v_core_id
; /* n per chip_id */
105 static struct link
*pchips
= NULL
;
106 static struct link
*cores
= NULL
;
107 static struct link
*vcpus
= NULL
;
109 static const char *cmdname
;
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
);
122 /* like perror, but includes the command name */
126 (void) fprintf(stderr
, "%s: %s: %s\n", cmdname
, msg
, strerror(errno
));
131 mystrdup(const char *src
)
135 if ((dst
= strdup(src
)) == NULL
)
136 die(_("strdup() failed"));
145 if ((ptr
= calloc(1, size
)) == NULL
)
146 die(_("calloc() failed"));
151 * Insert a new node on a list, at the insertion point given.
154 ins_link(struct link
**ins
, struct link
*item
)
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).
166 find_link(void *list
, int id
, struct link
***insertpt
)
168 struct link
**ins
= list
;
171 while ((l
= *ins
) != NULL
) {
178 if (insertpt
!= NULL
)
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).
188 print_links(struct link
*l
)
199 if ((l
->l_next
== NULL
) ||
200 (l
->l_next
->l_id
> (l
->l_id
+ 1))) {
201 /* end of the contiguous group */
203 (void) printf("%d", start
);
205 (void) printf("%d-%d", start
, end
);
219 static char buffer
[256];
220 (void) strftime(buffer
, sizeof (buffer
), _("%m/%d/%Y %T"),
231 struct link
*l1
, *l2
;
233 for (l1
= pchips
; l1
; l1
= l1
->l_next
) {
237 if ((nspec
!= 0) && (chip
->p_doit
== 0))
240 vcpu
= chip
->p_vcpus
->l_ptr
;
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.
247 if ((chip
->p_ncore
== 1) || (chip
->p_ncore
== chip
->p_nvcpu
)) {
248 (void) printf(_("%s has %d virtual %s"),
249 _("The physical processor"),
255 (void) printf(_("%s has %d %s and %d virtual %s"),
256 _("The physical processor"),
257 chip
->p_ncore
, _("cores"),
260 _("processors") : _("processor"));
263 print_links(chip
->p_vcpus
);
264 (void) putchar('\n');
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
);
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"),
276 (void) putchar('\n');
278 for (l2
= chip
->p_cores
; l2
; l2
= l2
->l_next
) {
280 (void) printf(_(" %s has %d virtual %s"),
284 _("processors") : _("processor"));
285 print_links(core
->c_vcpus
);
286 (void) putchar('\n');
288 if (strlen(vcpu
->v_impl
)) {
289 (void) printf(" %s\n", vcpu
->v_impl
);
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
);
307 * Report "1" if all cpus colocated on the same chip are online.
309 for (l
= pchips
; l
!= NULL
; l
= l
->l_next
) {
315 return; /* should never happen! */
316 for (l
= p
->p_vcpus
; l
!= NULL
; l
= l
->l_next
) {
318 if (strcmp(v
->v_state
, "on-line") != 0) {
324 (void) printf("%d\n", online
);
333 * Find the processor (there will be only one) that we selected,
334 * and report whether or not it is online.
336 for (l
= vcpus
; l
!= NULL
; l
= l
->l_next
) {
337 struct vcpu
*v
= l
->l_ptr
;
339 (void) printf("%d\n",
340 strcmp(v
->v_state
, "on-line") == 0 ? 1 : 0);
349 struct link
*l1
, *l2
;
353 * Print the number of physical packages with at least one processor
356 for (l1
= pchips
; l1
!= NULL
; l1
= l1
->l_next
) {
357 struct pchip
*p
= l1
->l_ptr
;
358 if ((nspec
== 0) || (p
->p_doit
)) {
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) {
369 (void) printf("%d\n", online
);
377 for (l
= vcpus
; l
!= NULL
; l
= l
->l_next
) {
378 struct vcpu
*v
= l
->l_ptr
;
380 if ((nspec
!= 0) && (!v
->v_doit
))
382 (void) printf(_("Status of virtual processor %d as of: "),
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
) {
389 _(" The %s processor operates at %llu MHz,\n"),
390 v
->v_cpu_type
, (unsigned long long)v
->v_clock_mhz
);
393 _(" The %s processor operates at " \
394 "an unknown frequency,\n"), v
->v_cpu_type
);
396 switch (*v
->v_fpu_type
) {
399 _("\tand has no floating point processor.\n"));
408 _("\tand has an %s floating point processor.\n"),
413 _("\tand has a %s floating point processor.\n"),
421 print_normal(int nspec
)
426 for (l
= vcpus
; l
!= NULL
; l
= l
->l_next
) {
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
));
436 main(int argc
, char **argv
)
453 cmdname
= basename(argv
[0]);
456 (void) setlocale(LC_ALL
, "");
457 #if !defined(TEXT_DOMAIN)
458 #define TEXT_DOMAIN "SYS_TEST"
460 (void) textdomain(TEXT_DOMAIN
);
462 /* collect the kstats */
463 if ((kc
= kstat_open()) == NULL
)
464 die(_("kstat_open() failed"));
466 if ((ksp
= kstat_lookup(kc
, "cpu_info", -1, NULL
)) == NULL
)
467 die(_("kstat_lookup() failed"));
469 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
471 if (strcmp(ksp
->ks_module
, "cpu_info") != 0)
473 if (kstat_read(kc
, ksp
, NULL
) == 0)
474 die(_("kstat_read() failed"));
476 vc
= find_link(&vcpus
, ksp
->ks_instance
, &ins
);
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
);
488 if ((knp
= kstat_data_lookup(ksp
, "state")) != NULL
) {
489 vc
->v_state
= mystrdup(knp
->value
.c
);
491 vc
->v_state
= "unknown";
494 if ((knp
= kstat_data_lookup(ksp
, "cpu_type")) != NULL
) {
495 vc
->v_cpu_type
= mystrdup(knp
->value
.c
);
497 if ((knp
= kstat_data_lookup(ksp
, "fpu_type")) != NULL
) {
498 vc
->v_fpu_type
= mystrdup(knp
->value
.c
);
501 if ((knp
= kstat_data_lookup(ksp
, "state_begin")) != NULL
) {
502 vc
->v_state_begin
= knp
->value
.l
;
505 if ((knp
= kstat_data_lookup(ksp
, "clock_MHz")) != NULL
) {
506 vc
->v_clock_mhz
= knp
->value
.l
;
509 if ((knp
= kstat_data_lookup(ksp
, "brand")) == NULL
) {
510 vc
->v_brand
= _("(unknown)");
512 vc
->v_brand
= mystrdup(knp
->value
.str
.addr
.ptr
);
515 if ((knp
= kstat_data_lookup(ksp
, "socket_type")) == NULL
) {
516 vc
->v_socket
= "Unknown";
518 vc
->v_socket
= mystrdup(knp
->value
.str
.addr
.ptr
);
521 if ((knp
= kstat_data_lookup(ksp
, "implementation")) == NULL
) {
522 vc
->v_impl
= _("(unknown)");
524 vc
->v_impl
= mystrdup(knp
->value
.str
.addr
.ptr
);
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+//;
533 if ((s
= strstr(vc
->v_impl
, "chipid")) != NULL
) {
534 char *x
= s
+ strlen("chipid");
537 if ((!isalnum(*x
)) && (*x
!= '_'))
539 while (isalnum(*x
) || (*x
== '_'))
548 if ((s
= strstr(vc
->v_impl
, "cpuid")) != NULL
) {
549 char *x
= s
+ strlen("cpuid");
552 if ((!isalnum(*x
)) && (*x
!= '_'))
554 while (isalnum(*x
) || (*x
== '_'))
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
);
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
);
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
);
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
);
587 (void) find_link(&chip
->p_cores
, core
->c_link
.l_id
,
589 ins_link(ins
, &core
->c_link_pchip
);
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
);
600 (void) find_link(&core
->c_vcpus
, vc
->v_link
.l_id
, &ins
);
601 ins_link(ins
, &vc
->v_link_core
);
605 (void) kstat_close(kc
);
609 while ((optc
= getopt(argc
, argv
, "pvs")) != EOF
) {
625 while (optind
< argc
) {
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
]);
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;
645 if (opt_s
&& opt_v
) {
646 usage(_("options -s and -v are mutually exclusive"));
648 if (opt_s
&& nspec
!= 1) {
649 usage(_("must specify exactly one processor if -s used"));
651 if (opt_v
&& opt_p
) {
653 } else if (opt_s
&& opt_p
) {