diff options
Diffstat (limited to 'usr/src/cmd/psrinfo/psrinfo.c')
| -rw-r--r-- | usr/src/cmd/psrinfo/psrinfo.c | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/usr/src/cmd/psrinfo/psrinfo.c b/usr/src/cmd/psrinfo/psrinfo.c new file mode 100644 index 0000000000..7488933901 --- /dev/null +++ b/usr/src/cmd/psrinfo/psrinfo.c @@ -0,0 +1,655 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. + */ + +/* + * This implements psrinfo(1M), a utility to report various information + * about processors, cores, and threads (virtual cpus). This is mostly + * intended for human consumption - this utility doesn't do much more than + * simply process kstats for human readability. + * + * All the relevant kstats are in the cpu_info kstat module. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <kstat.h> +#include <libintl.h> +#include <locale.h> +#include <libgen.h> +#include <ctype.h> +#include <errno.h> + +#define _(x) gettext(x) +#if XGETTEXT +/* These CPU states are here for benefit of xgettext */ +_("on-line") +_("off-line") +_("faulted") +_("powered-off") +_("no-intr") +_("spare") +_("unknown") +#endif + +/* + * We deal with sorted linked lists, where the sort key is usually the + * cpu id, core id, or chip id. We generalize this with simple node. + */ +struct link { + long l_id; + struct link *l_next; + void *l_ptr; +}; + +/* + * A physical chip. A chip can contain multiple cores and virtual cpus. + */ +struct pchip { + struct link p_link; + int p_ncore; + int p_nvcpu; + struct link *p_cores; + struct link *p_vcpus; + int p_doit; +}; + +struct core { + struct link c_link; + struct link c_link_pchip; + + int c_nvcpu; + int c_doit; + + struct pchip *c_pchip; + struct link *c_vcpus; +}; + +struct vcpu { + struct link v_link; + + struct link v_link_core; + struct link v_link_pchip; + + int v_doit; + + struct pchip *v_pchip; + struct core *v_core; + + char *v_state; + long v_state_begin; + char *v_cpu_type; + char *v_fpu_type; + long v_clock_mhz; + long v_pchip_id; /* 1 per socket */ + char *v_impl; + char *v_brand; + long v_core_id; /* n per chip_id */ +}; + +static struct link *pchips = NULL; +static struct link *cores = NULL; +static struct link *vcpus = NULL; + +static const char *cmdname; + +static void +usage(char *msg) +{ + if (msg != NULL) + (void) fprintf(stderr, "%s: %s\n", cmdname, msg); + (void) fprintf(stderr, _("usage: \n" \ + "\t%s [-v] [-p] [processor_id ...]\n" \ + "\t%s -s [-p] processor_id\n"), cmdname, cmdname); + exit(2); +} + +/* like perror, but includes the command name */ +static void +die(const char *msg) +{ + (void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno)); + exit(2); +} + +static char * +mystrdup(const char *src) +{ + char *dst; + + if ((dst = strdup(src)) == NULL) + die(_("strdup() failed")); + return (dst); +} + +static void * +zalloc(size_t size) +{ + void *ptr; + + if ((ptr = calloc(1, size)) == NULL) + die(_("calloc() failed")); + return (ptr); +} + +/* + * Insert a new node on a list, at the insertion point given. + */ +static void +ins_link(struct link **ins, struct link *item) +{ + item->l_next = *ins; + *ins = item; +} + +/* + * Find an id on a sorted list. If the requested id is not found, + * then the insertpt will be set (if not null) to the location where + * a new node should be inserted with ins_link (see above). + */ +static void * +find_link(void *list, int id, struct link ***insertpt) +{ + struct link **ins = list; + struct link *l; + + while ((l = *ins) != NULL) { + if (l->l_id == id) + return (l->l_ptr); + if (l->l_id > id) + break; + ins = &l->l_next; + } + if (insertpt != NULL) + *insertpt = ins; + return (NULL); +} + +/* + * Print the linked list of ids in parens, taking care to collapse + * ranges, so instead of (0 1 2 3) it should print (0-3). + */ +static void +print_links(struct link *l) +{ + int start = -1; + int end = 0; + + (void) printf(" ("); + while (l != NULL) { + if (start < 0) { + start = l->l_id; + } + end = l->l_id; + if ((l->l_next == NULL) || + (l->l_next->l_id > (l->l_id + 1))) { + /* end of the contiguous group */ + if (start == end) { + (void) printf("%d", start); + } else { + (void) printf("%d-%d", start, end); + } + if (l->l_next) + (void) printf(" "); + start = -1; + } + l = l->l_next; + } + (void) printf(")"); +} + +static const char * +timestr(long t) +{ + static char buffer[256]; + (void) strftime(buffer, sizeof (buffer), _("%m/%d/%Y %T"), + localtime(&t)); + return (buffer); +} + +static void +print_vp(int nspec) +{ + struct pchip *chip; + struct core *core; + struct vcpu *vcpu; + struct link *l1, *l2; + int len; + for (l1 = pchips; l1; l1 = l1->l_next) { + + chip = l1->l_ptr; + + if ((nspec != 0) && (chip->p_doit == 0)) + continue; + + vcpu = chip->p_vcpus->l_ptr; + + /* + * Note that some of the way these strings are broken up are + * to accommodate the legacy translations so that we won't + * have to retranslate for this utility. + */ + if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) { + (void) printf(_("%s has %d virtual %s"), + _("The physical processor"), + chip->p_nvcpu, + chip->p_nvcpu > 1 ? + _("processors") : + _("processor")); + } else { + (void) printf(_("%s has %d %s and %d virtual %s"), + _("The physical processor"), + chip->p_ncore, _("cores"), + chip->p_nvcpu, + chip->p_nvcpu > 1 ? + _("processors") : _("processor")); + } + + print_links(chip->p_vcpus); + (void) putchar('\n'); + + if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) { + if (strlen(vcpu->v_impl)) { + (void) printf(" %s\n", vcpu->v_impl); + } + if (((len = strlen(vcpu->v_brand)) != 0) && + (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0)) + (void) printf("\t%s", vcpu->v_brand); + (void) putchar('\n'); + } else { + for (l2 = chip->p_cores; l2; l2 = l2->l_next) { + core = l2->l_ptr; + (void) printf(_(" %s has %d virtual %s"), + _("The core"), + core->c_nvcpu, + chip->p_nvcpu > 1 ? + _("processors") : _("processor")); + print_links(core->c_vcpus); + (void) putchar('\n'); + } + if (strlen(vcpu->v_impl)) { + (void) printf(" %s\n", vcpu->v_impl); + } + if (((len = strlen(vcpu->v_brand)) != 0) && + (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0)) + (void) printf(" %s\n", vcpu->v_brand); + } + } +} + +static void +print_ps(void) +{ + int online = 1; + struct pchip *p; + struct vcpu *v; + struct link *l; + + /* + * Report "1" if all cpus colocated on the same chip are online. + */ + for (l = pchips; l != NULL; l = l->l_next) { + p = l->l_ptr; + if (p->p_doit) + break; + } + if (p == NULL) + return; /* should never happen! */ + for (l = p->p_vcpus; l != NULL; l = l->l_next) { + v = l->l_ptr; + if (strcmp(v->v_state, "on-line") != 0) { + online = 0; + break; + } + } + + (void) printf("%d\n", online); +} + +static void +print_s(void) +{ + struct link *l; + + /* + * Find the processor (there will be only one) that we selected, + * and report whether or not it is online. + */ + for (l = vcpus; l != NULL; l = l->l_next) { + struct vcpu *v = l->l_ptr; + if (v->v_doit) { + (void) printf("%d\n", + strcmp(v->v_state, "on-line") == 0 ? 1 : 0); + return; + } + } +} + +static void +print_p(int nspec) +{ + struct link *l1, *l2; + int online = 0; + + /* + * Print the number of physical packages with at least one processor + * online. + */ + for (l1 = pchips; l1 != NULL; l1 = l1->l_next) { + struct pchip *p = l1->l_ptr; + if ((nspec == 0) || (p->p_doit)) { + + for (l2 = p->p_vcpus; l2 != NULL; l2 = l2->l_next) { + struct vcpu *v = l2->l_ptr; + if (strcmp(v->v_state, "on-line") == 0) { + online++; + break; + } + } + } + } + (void) printf("%d\n", online); +} + +static void +print_v(int nspec) +{ + struct link *l; + + for (l = vcpus; l != NULL; l = l->l_next) { + struct vcpu *v = l->l_ptr; + + if ((nspec != 0) && (!v->v_doit)) + continue; + (void) printf(_("Status of virtual processor %d as of: "), + l->l_id); + (void) printf("%s\n", timestr(time(NULL))); + (void) printf(_(" %s since %s.\n"), + _(v->v_state), timestr(v->v_state_begin)); + if (v->v_clock_mhz) { + (void) printf( + _(" The %s processor operates at %llu MHz,\n"), + v->v_cpu_type, (unsigned long long)v->v_clock_mhz); + } else { + (void) printf( + _(" The %s processor operates at " \ + "an unknown frequency,\n"), v->v_cpu_type); + } + switch (*v->v_fpu_type) { + case '\0': + (void) printf( + _("\tand has no floating point processor.\n")); + break; + case 'a': case 'A': + case 'e': case 'E': + case 'i': case 'I': + case 'o': case 'O': + case 'u': case 'U': + case 'y': case 'Y': + (void) printf( + _("\tand has an %s floating point processor.\n"), + v->v_fpu_type); + break; + default: + (void) printf( + _("\tand has a %s floating point processor.\n"), + v->v_fpu_type); + break; + } + } +} + +static void +print_normal(int nspec) +{ + struct link *l; + struct vcpu *v; + + for (l = vcpus; l != NULL; l = l->l_next) { + v = l->l_ptr; + if ((nspec == 0) || (v->v_doit)) { + (void) printf(_("%d\t%-8s since %s\n"), + l->l_id, _(v->v_state), timestr(v->v_state_begin)); + } + } +} + +int +main(int argc, char **argv) +{ + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + struct vcpu *vc; + struct core *core; + struct pchip *chip; + struct link **ins; + char *s; + int nspec; + int optc; + int opt_s = 0; + int opt_p = 0; + int opt_v = 0; + int ex = 0; + + cmdname = basename(argv[0]); + + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + (void) textdomain(TEXT_DOMAIN); + + /* collect the kstats */ + if ((kc = kstat_open()) == NULL) + die(_("kstat_open() failed")); + + if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL) + die(_("kstat_lookup() failed")); + + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { + + if (strcmp(ksp->ks_module, "cpu_info") != 0) + continue; + if (kstat_read(kc, ksp, NULL) == NULL) + die(_("kstat_read() failed")); + + vc = find_link(&vcpus, ksp->ks_instance, &ins); + if (vc == NULL) { + vc = zalloc(sizeof (struct vcpu)); + vc->v_link.l_id = ksp->ks_instance; + vc->v_link_core.l_id = ksp->ks_instance; + vc->v_link_pchip.l_id = ksp->ks_instance; + vc->v_link.l_ptr = vc; + vc->v_link_core.l_ptr = vc; + vc->v_link_pchip.l_ptr = vc; + ins_link(ins, &vc->v_link); + } + + if ((knp = kstat_data_lookup(ksp, "state")) != NULL) { + vc->v_state = mystrdup(knp->value.c); + } else { + vc->v_state = "unknown"; + } + + if ((knp = kstat_data_lookup(ksp, "cpu_type")) != NULL) { + vc->v_cpu_type = mystrdup(knp->value.c); + } + if ((knp = kstat_data_lookup(ksp, "fpu_type")) != NULL) { + vc->v_fpu_type = mystrdup(knp->value.c); + } + + if ((knp = kstat_data_lookup(ksp, "state_begin")) != NULL) { + vc->v_state_begin = knp->value.l; + } + + if ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL) { + vc->v_clock_mhz = knp->value.l; + } + + if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) { + vc->v_brand = _("(unknown)"); + } else { + vc->v_brand = mystrdup(knp->value.str.addr.ptr); + } + + if ((knp = kstat_data_lookup(ksp, "implementation")) == NULL) { + vc->v_impl = _("(unknown)"); + } else { + vc->v_impl = mystrdup(knp->value.str.addr.ptr); + } + /* + * Legacy code removed the chipid and cpuid fields... we + * do the same for compatibility. Note that the original + * pattern is a bit strange, and we have to emulate this because + * on SPARC we *do* emit these. The original pattern we are + * emulating is: $impl =~ s/(cpuid|chipid)\s*\w+\s+//; + */ + if ((s = strstr(vc->v_impl, "chipid")) != NULL) { + char *x = s + strlen("chipid"); + while (isspace(*x)) + x++; + if ((!isalnum(*x)) && (*x != '_')) + goto nochipid; + while (isalnum(*x) || (*x == '_')) + x++; + if (!isspace(*x)) + goto nochipid; + while (isspace(*x)) + x++; + (void) strcpy(s, x); + } +nochipid: + if ((s = strstr(vc->v_impl, "cpuid")) != NULL) { + char *x = s + strlen("cpuid"); + while (isspace(*x)) + x++; + if ((!isalnum(*x)) && (*x != '_')) + goto nocpuid; + while (isalnum(*x) || (*x == '_')) + x++; + if (!isspace(*x)) + goto nocpuid; + while (isspace(*x)) + x++; + (void) strcpy(s, x); + } +nocpuid: + + if ((knp = kstat_data_lookup(ksp, "chip_id")) != NULL) + vc->v_pchip_id = knp->value.l; + chip = find_link(&pchips, vc->v_pchip_id, &ins); + if (chip == NULL) { + chip = zalloc(sizeof (struct pchip)); + chip->p_link.l_id = vc->v_pchip_id; + chip->p_link.l_ptr = chip; + ins_link(ins, &chip->p_link); + } + vc->v_pchip = chip; + + if ((knp = kstat_data_lookup(ksp, "core_id")) != NULL) + vc->v_core_id = knp->value.l; + core = find_link(&cores, vc->v_core_id, &ins); + if (core == NULL) { + core = zalloc(sizeof (struct core)); + core->c_link.l_id = vc->v_core_id; + core->c_link.l_ptr = core; + core->c_link_pchip.l_id = vc->v_core_id; + core->c_link_pchip.l_ptr = core; + core->c_pchip = chip; + ins_link(ins, &core->c_link); + chip->p_ncore++; + (void) find_link(&chip->p_cores, core->c_link.l_id, + &ins); + ins_link(ins, &core->c_link_pchip); + } + vc->v_core = core; + + + + /* now put other linkages in place */ + (void) find_link(&chip->p_vcpus, vc->v_link.l_id, &ins); + ins_link(ins, &vc->v_link_pchip); + chip->p_nvcpu++; + + (void) find_link(&core->c_vcpus, vc->v_link.l_id, &ins); + ins_link(ins, &vc->v_link_core); + core->c_nvcpu++; + } + + (void) kstat_close(kc); + + nspec = 0; + + while ((optc = getopt(argc, argv, "pvs")) != EOF) { + switch (optc) { + case 's': + opt_s = 1; + break; + case 'p': + opt_p = 1; + break; + case 'v': + opt_v = 1; + break; + default: + usage(NULL); + } + } + + while (optind < argc) { + long id; + char *eptr; + struct link *l; + id = strtol(argv[optind], &eptr, 10); + l = find_link(&vcpus, id, NULL); + if ((*eptr != '\0') || (l == NULL)) { + (void) fprintf(stderr, + _("%s: processor %s: Invalid argument\n"), + cmdname, argv[optind]); + ex = 2; + } else { + ((struct vcpu *)l->l_ptr)->v_doit = 1; + ((struct vcpu *)l->l_ptr)->v_pchip->p_doit = 1; + ((struct vcpu *)l->l_ptr)->v_core->c_doit = 1; + } + nspec++; + optind++; + } + + if (opt_s && opt_v) { + usage(_("options -s and -v are mutually exclusive")); + } + if (opt_s && nspec != 1) { + usage(_("must specify exactly one processor if -s used")); + } + if (opt_v && opt_p) { + print_vp(nspec); + } else if (opt_s && opt_p) { + print_ps(); + } else if (opt_p) { + print_p(nspec); + } else if (opt_v) { + print_v(nspec); + } else if (opt_s) { + print_s(); + } else { + print_normal(nspec); + } + + return (ex); +} |
