Mercurial > illumos > illumos-gate
changeset 13725:9a3ca91fb74e
2804 psrinfo should not depend on perl
Reviewed by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Gordon Ross <gordon.w.ross@gmail.com>
author | Garrett D'Amore <garrett@damore.org> |
---|---|
date | Thu, 14 Jun 2012 08:00:26 -0700 |
parents | 7740792727e0 |
children | 056b82d21d71 |
files | usr/src/cmd/psrinfo/Makefile usr/src/cmd/psrinfo/psrinfo.c usr/src/cmd/psrinfo/psrinfo.pl |
diffstat | 3 files changed, 663 insertions(+), 854 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/psrinfo/Makefile Wed Jun 13 22:54:43 2012 +0000 +++ b/usr/src/cmd/psrinfo/Makefile Thu Jun 14 08:00:26 2012 -0700 @@ -21,6 +21,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. +# # cmd/psrinfo/Makefile # @@ -28,6 +30,11 @@ include ../Makefile.cmd +LDLIBS += -lkstat +XGETFLAGS += +$(PROG).po := CPPFLAGS += -DXGETTEXT + + .KEEP_STATE: all: $(PROG) @@ -41,6 +48,6 @@ $(ROOTUSRSBINBINPROG): $(PROG) $(INS.file) -lint: +lint: lint_PROG include ../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/psrinfo/psrinfo.c Thu Jun 14 08:00:26 2012 -0700 @@ -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); +}
--- a/usr/src/cmd/psrinfo/psrinfo.pl Wed Jun 13 22:54:43 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,853 +0,0 @@ -#!/usr/perl5/bin/perl - -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# psrinfo: displays information about processors -# -# See detailed comment in the end of this file. -# - -use strict; -use warnings; -use locale; -use POSIX qw(locale_h strftime); -use File::Basename; -use Getopt::Long qw(:config no_ignore_case bundling auto_version); -use Sun::Solaris::Utils qw(textdomain gettext); -use Sun::Solaris::Kstat; - -# Set message locale -setlocale(LC_ALL, ""); -textdomain(TEXT_DOMAIN); - -###################################################################### -# Configuration variables -###################################################################### - -# Regexp describing cpu_info kstat fields describing CPU hierarchy. -my $valid_id_exp = qr{^(?:chip|core)_id$}; - -# Translation of kstat name to human-readable form -my %translations = ('chip_id' => gettext("The physical processor"), - 'core_id' => gettext("The core")); - -# Localized version of plural forms -my %pluralized_names = ('processor' => gettext("processor"), - 'processors' => gettext("processors"), - 'chip' => gettext("chip"), - 'chips' => gettext("chips"), - 'core' => gettext("core"), - 'cores' => gettext("cores")); - -# Localized CPU states -my %cpu_states = ('on-line' => gettext("on-line"), - 'off-line' => gettext("off-line"), - 'faulted' => gettext("faulted"), - 'powered-off' => gettext("powered-off"), - 'no-intr' => gettext("no-intr"), - 'spare' => gettext("spare"), - 'unknown' => gettext("unknown")); - -###################################################################### -# Global variables -###################################################################### - -# Hash with CPU ID as a key and specific per-cpu kstat hash as a value -our %cpu_list; - -# Command name without path and trailing .pl - used for error messages. -our $cmdname = basename($0, ".pl"); - -# Return value -our $errors = 0; - -###################################################################### -# Helper subroutines -###################################################################### - -# -# Print help string if specified or the standard help message and exit setting -# errno. -# -sub usage -{ - my (@msg) = @_; - print STDERR $cmdname, ": @msg\n" if (@msg); - print STDERR gettext("usage: \n" . - "\tpsrinfo [-v] [-p] [processor_id ...]\n" . - "\tpsrinfo -s [-p] processor_id\n"); - exit(2); -} - -# -# Return the input list with duplicates removed. -# Count how many times we've seen each element and remove elements seen more -# than once. -# -sub uniq -{ - my %seen; # Have we seen this element already? - return (grep { ++$seen{$_} == 1 } @_); -} - -# -# Return the intersection of two lists passed by reference -# Convert the first list to a hash with seen entries marked as 1-values -# Then grep only elements present in the first list from the second list. -# As a little optimization, use the shorter list to build a hash. -# -sub intersect -{ - my ($left, $right) = @_; - my %seen; # Set to 1 for everything in the first list - # Put the shortest list in $left - scalar @$left <= scalar @$right or ($right, $left) = ($left, $right); - - # Create a hash indexed by elements in @left with ones as a value. - map { $seen{$_} = 1 } @$left; - # Find members of @right present in @left - return (grep { $seen{$_} } @$right); -} - -# -# Return elements of the second list not present in the first list. Both lists -# are passed by reference. -# -sub set_subtract -{ - my ($left, $right) = @_; - my %seen; # Set to 1 for everything in the first list - # Create a hash indexed by elements in @left with ones as a value. - map { $seen{$_} = 1 } @$left; - # Find members of @right present in @left - return (grep { ! $seen{$_} } @$right); -} - -# -# Sort the list numerically -# Should be called in list context -# -sub nsort -{ - return (sort { $a <=> $b } @_); -} - -# -# Sort list numerically and remove duplicates -# Should be called in list context -# -sub uniqsort -{ - return (sort { $a <=> $b } uniq(@_)); -} - -# -# Return the maximum value of its arguments -# -sub max -{ - my $m = shift; - - foreach my $el (@_) { - $m = $el if $m < $el; - } - return ($m); -} - -# -# Pluralize name if there is more than one instance -# Arguments: name, ninstances -# -sub pluralize -{ - my ($name, $count) = @_; - # Remove trailing '_id' from the name. - $name =~ s/_id$//; - my $plural_name = $count > 1 ? "${name}s" : $name; - return ($pluralized_names{$plural_name} || $plural_name) -} - -# -# Translate id name into printable form -# Look at the %translations table and replace everything found there -# Remove trailing _id from the name if there is no translation -# -sub id_translate -{ - my $name = shift or return; - my $translated_name = $translations{$name}; - $name =~ s/_id$// unless $translated_name; - return ($translated_name || $name); -} - -# -# Consolidate consequtive CPU ids as start-end -# Input: list of CPUs -# Output: string with space-sepated cpu values with CPU ranges -# collapsed as x-y -# -sub collapse -{ - return ('') unless @_; - my @args = uniqsort(@_); - my $start = shift(@args); - my $result = ''; - my $end = $start; # Initial range consists of the first element - foreach my $el (@args) { - if ($el == ($end + 1)) { - # - # Got consecutive ID, so extend end of range without - # printing anything since the range may extend further - # - $end = $el; - } else { - # - # Next ID is not consecutive, so print IDs gotten so - # far. - # - if ($end > $start + 1) { # range - $result = "$result $start-$end"; - } elsif ($end > $start) { # different values - $result = "$result $start $end"; - } else { # same value - $result = "$result $start"; - } - - # Try finding consecutive range starting from this ID - $start = $end = $el; - } - } - - # Print last ID(s) - if ($end > $start + 1) { - $result = "$result $start-$end"; - } elsif ($end > $start) { - $result = "$result $start $end"; - } else { - $result = "$result $start"; - } - # Remove any spaces in the beginning - $result =~ s/^\s+//; - return ($result); -} - -# -# Expand start-end into the list of values -# Input: string containing a single numeric ID or x-y range -# Output: single value or a list of values -# Ranges with start being more than end are inverted -# -sub expand -{ - my $arg = shift; - - if ($arg =~ m/^\d+$/) { - # single number - return ($_); - } elsif ($arg =~ m/^(\d+)\-(\d+)$/) { - my ($start, $end) = ($1, $2); # $start-$end - # Reverse the interval if start > end - ($start, $end) = ($end, $start) if $start > $end; - return ($start .. $end); - } elsif ($arg =~ m/-/) { - printf STDERR - gettext("%s: invalid processor range %s\n"), - $cmdname, $_; - } else { - printf STDERR - gettext("%s: processor %s: Invalid argument\n"), - $cmdname, $_; - } - $errors = 2; - return (); -} - -# -# Functions for constructing CPU hierarchy. Only used with -vp option. -# - -# -# Return numerically sorted list of distinct values of a given cpu_info kstat -# field, spanning given CPU set. -# -# Arguments: -# Property name -# list of CPUs -# -# Treat undefined values as zeroes. -sub property_list -{ - my $prop_name = shift; - return (grep {$_ >= 0} uniqsort(map { $cpu_list{$_}->{$prop_name} || 0 } @_)); -} - -# -# Return subset of CPUs sharing specified value of a given cpu_info kstat field. -# Arguments: -# Property name -# Property value -# List of CPUs to select from -# -# Treat undefined values as zeroes. -sub cpus_by_prop -{ - my $prop_name = shift; - my $prop_val = shift; - - return (grep { ($cpu_list{$_}->{$prop_name} || 0) == $prop_val } @_); -} - -# -# Build component tree -# -# Arguments: -# Reference to the list of CPUs sharing the component -# Reference to the list of sub-components -# -sub build_component_tree -{ - my ($cpus, $comp_list) = @_; - # Get the first component and the rest - my ($comp_name, @comps) = @$comp_list; - my $tree = {}; - if (!$comp_name) { - $tree->{cpus} = $cpus; - return ($tree); - } - - # Get all possible component values - foreach my $v (property_list($comp_name, @$cpus)) { - my @comp_cpus = cpus_by_prop ($comp_name, $v, @$cpus); - $tree->{name} = $comp_name; - $tree->{cpus} = $cpus; - $tree->{values}->{$v} = build_component_tree(\@comp_cpus, - \@comps); - } - return ($tree); -} - -# -# Print the component tree -# Arguments: -# Reference to a tree -# indentation -# Output: maximum indentation -# -sub print_component_tree -{ - my ($tree, $ind) = @_; - my $spaces = ' ' x $ind; # indentation string - my $vals = $tree->{values}; - my $retval = $ind; - if ($vals) { - # This is not a leaf node - # Get node name and translate it to printable format - my $id_name = id_translate($tree->{name}); - # Examine each sub-node - foreach my $comp_val (nsort(keys %$vals)) { - my $child_tree = $vals->{$comp_val}; # Sub-tree - my $child_id = $child_tree->{name}; # Name of child node - my @cpus = @{$child_tree->{cpus}}; # CPUs for the child - my $ncpus = scalar @cpus; # Number of CPUs - my $cpuname = pluralize('processor', $ncpus); - my $cl = collapse(@cpus); # Printable CPU list - if (!$child_id) { - # Child is a leaf node - print $spaces; - printf gettext("%s has %d virtual %s"), - $id_name, $ncpus, $cpuname; - print " ($cl)\n"; - $retval = max($retval, $ind + 2); - } else { - # Child has several values. Let's see how many - my $grandchild_tree = $child_tree->{values}; - my $nvals = scalar(keys %$grandchild_tree); - my $child_id_name = pluralize($child_id, - $nvals); - print $spaces; - printf - gettext("%s has %d %s and %d virtual %s"), - $id_name, $nvals, $child_id_name, $ncpus, - $cpuname; - print " ($cl)\n"; - # Print the tree for the child - $retval = max($retval, - print_component_tree($child_tree, - $ind + 2)); - } - } - } - return ($retval); -} - - -############################ -# Main part of the program -############################ - -# -# Option processing -# -my ($opt_v, $opt_p, $opt_silent); - -GetOptions("p" => \$opt_p, - "v" => \$opt_v, - "s" => \$opt_silent) || usage(); - - -my $verbosity = 1; -my $phys_view; - -$verbosity |= 2 if $opt_v; -$verbosity &= ~1 if $opt_silent; -$phys_view = 1 if $opt_p; - -# Set $phys_verbose if -vp is specified -my $phys_verbose = $phys_view && ($verbosity > 1); - -# Verify options -usage(gettext("options -s and -v are mutually exclusive")) if $verbosity == 2; - -usage(gettext("must specify exactly one processor if -s used")) if - (($verbosity == 0) && scalar @ARGV != 1); - -# -# Read cpu_info kstats -# -my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or - (printf STDERR gettext("%s: kstat_open() failed: %s\n"), - $cmdname, $!), - exit(2); -my $cpu_info = $ks->{cpu_info} or - (printf STDERR gettext("%s: can not read cpu_info kstats\n"), - $cmdname), - exit(2); - -my ( - @all_cpus, # List of all CPUs in the system - @cpu_args, # CPUs to look at - @cpus, # List of CPUs to process - @id_list, # list of various xxx_id kstats representing CPU topology - %chips, # Hash with chip ID as a key and reference to the list of - # virtual CPU IDs, belonging to the chip as a value - @chip_list, # List of all chip_id values - $ctree, # The component tree - ); - -# -# Get information about each CPU. -# -# Collect list of all CPUs in @cpu_list array -# -# Construct %cpu_list hash keyed by CPU ID with cpu_info kstat hash as its -# value. -# -# Construct %chips hash keyed by chip ID. It has a 'cpus' entry, which is -# a reference to a list of CPU IDs within a chip. -# -foreach my $id (nsort(keys %$cpu_info)) { - # $id is CPU id - my $info = $cpu_info->{$id}; - - # - # The name part of the cpu_info kstat should always be a string - # cpu_info$id. - # - # The $ci hash reference holds all data for a specific CPU id. - # - my $ci = $info->{"cpu_info$id"} or next; - # Save CPU-specific information in cpu_list hash, indexed by CPU ID. - $cpu_list{$id} = $ci; - my $chip_id = $ci->{'chip_id'}; - # Collect CPUs within the chip. - # $chips{$chip_id} is a reference to a list of CPU IDs belonging to thie - # chip. It is automatically created when first referenced. - push (@{$chips{$chip_id}}, $id) if (defined($chip_id)); - # Collect list of CPU IDs in @cpus - push (@all_cpus, $id); -} - -# -# Figure out what CPUs to examine. -# Look at specific CPUs if any are specified on the command line or at all CPUs -# CPU ranges specified in the command line are expanded into lists of CPUs -# -if (scalar(@ARGV) == 0) { - @cpu_args = @all_cpus; -} else { - # Expand all x-y intervals in the argument list - @cpu_args = map { expand($_) } @ARGV; - - usage(gettext("must specify exactly one processor if -s used")) if - (($verbosity == 0) && scalar @cpu_args != 1); - - # Detect invalid CPUs in the arguments - my @bad_args = set_subtract(\@all_cpus, \@cpu_args); - my $nbadargs = scalar @bad_args; - - if ($nbadargs != 0) { - # Warn user about bad CPUs in the command line - my $argstr = collapse(@bad_args); - - if ($nbadargs > 1) { - printf STDERR gettext("%s: Invalid processors %s\n"), - $cmdname, $argstr; - } else { - printf STDERR - gettext("%s: processor %s: Invalid argument\n"), - $cmdname, $argstr; - } - $errors = 2; - } - - @cpu_args = uniqsort(intersect(\@all_cpus, \@cpu_args)); -} - -# -# In physical view, CPUs specified in the command line are only used to identify -# chips. The actual CPUs are all CPUs belonging to these chips. -# -if (! $phys_view) { - @cpus = @cpu_args; -} else { - # Get list of chips spanning all CPUs specified - @chip_list = property_list('chip_id', @cpu_args); - if (!scalar @chip_list && $errors == 0) { - printf STDERR - gettext("%s: Physical processor view not supported\n"), - $cmdname; - exit(1); - } - - # Get list of all CPUs within these chips - @cpus = uniqsort(map { @{$chips{$_}} } @chip_list); -} - - -if ($phys_verbose) { - # - # 1) Look at all possible xxx_id properties and remove those that have - # NCPU values or one value. Sort the rest. - # - # 2) Drop ids which have the same number of entries as number of CPUs or - # number of chips. - # - # 3) Build the component tree for the system - # - foreach my $id (keys %$cpu_info) { - my $info = $cpu_info->{$id}; - my $name = "cpu_info$id"; - my $ci = $info->{$name}; # cpu_info kstat for this CPU - - # Collect all statistic names matching $valid_id_exp - push @id_list, grep(/$valid_id_exp/, keys(%$ci)); - } - - # Remove duplicates - @id_list = uniq(@id_list); - - my $ncpus = scalar @cpus; - my %prop_nvals; # Number of instances of each property - my $nchips = scalar @chip_list; - - # - # Get list of properties which have more than ncpus and less than nchips - # instances. - # Also collect number of instances for each property. - # - @id_list = grep { - my @ids = property_list($_, @cpus); - my $nids = scalar @ids; - $prop_nvals{$_} = $nids; - ($_ eq "chip_id") || - (($nids > $nchips) && ($nids > 1) && ($nids < $ncpus)); - } @id_list; - - # Sort @id_list by number of instances for each property - @id_list = sort { $prop_nvals{$a} <=> $prop_nvals{$b} } @id_list; - - $ctree = build_component_tree(\@cpus, \@id_list); -} - - -# -# Walk all CPUs specified and print information about them. -# Do nothing for physical view - will do everything later. -# -foreach my $id (@cpus) { - last if $phys_view; # physical view is handled later - my $cpu = $cpu_list{$id} or next; - - # Get CPU state and its modification time - my $mtime = $cpu->{'state_begin'}; - my $mstring = strftime(gettext("%m/%d/%Y %T"), localtime($mtime)); - my $status = $cpu->{'state'} || gettext("unknown"); - # Get localized version of CPU status - $status = $cpu_states{$status} || $status; - - if ($verbosity == 0) { - # Print 1 if CPU is online, 0 if offline. - printf "%d\n", $status eq 'on-line'; - } elsif (! ($verbosity & 2)) { - printf gettext("%d\t%-8s since %s\n"), - $id, $status, $mstring; - } else { - printf gettext("Status of virtual processor %d as of: "), $id; - print strftime(gettext("%m/%d/%Y %T"), localtime()); - print "\n"; - printf gettext(" %s since %s.\n"), $status, $mstring; - my $clock_speed = $cpu->{'clock_MHz'}; - my $cpu_type = $cpu->{'cpu_type'}; - - # Display clock speed - if ($clock_speed ) { - printf - gettext(" The %s processor operates at %s MHz,\n"), - $cpu_type, $clock_speed; - } else { - printf - gettext(" the %s processor operates at an unknown frequency,\n"), - $cpu_type; - } - - # Display FPU type - my $fpu = $cpu->{'fpu_type'}; - if (! $fpu) { - print - gettext("\tand has no floating point processor.\n"); - } elsif ($fpu =~ m/^[aeiouy]/) { - printf - gettext("\tand has an %s floating point processor.\n"), - $fpu; - } else { - printf - gettext("\tand has a %s floating point processor.\n"), - $fpu; - } - } -} - -# -# Physical view print -# -if ($phys_view) { - if ($verbosity == 1) { - print scalar @chip_list, "\n"; - } elsif ($verbosity == 0) { - # Print 1 if all CPUs are online, 0 otherwise. - foreach my $chip_id (@chip_list) { - # Get CPUs on a chip - my @chip_cpus = uniqsort(@{$chips{$chip_id}}); - # List of all on-line CPUs on a chip - my @online_cpus = grep { - ($cpu_list{$_}->{state}) eq 'on-line' - } @chip_cpus; - - # - # Print 1 if number of online CPUs equals number of all - # CPUs - # - printf - "%d\n", scalar @online_cpus == scalar @chip_cpus; - } - } else { - # Walk the property tree and print everything in it. - my $tcores = $ctree->{values}; - my $cname = id_translate($ctree->{name}); - foreach my $chip (nsort(keys %$tcores)) { - my $chipref = $tcores->{$chip}; - my @chip_cpus = @{$chipref->{cpus}}; - my $ncpus = scalar @chip_cpus; - my $cpu_id = $chip_cpus[0]; - my $cpu = $cpu_list{$cpu_id}; - my $brand = $cpu->{brand} || gettext("(unknown)"); - my $impl = $cpu->{implementation} || - gettext("(unknown)"); - my $socket = $cpu->{socket_type}; - # - # Remove cpuid and chipid information from - # implementation string and print it. - # - $impl =~ s/(cpuid|chipid)\s*\w+\s+//; - $brand = '' if $impl && $impl =~ /^$brand/; - # List of CPUs on a chip - my $cpu_name = pluralize('processor', $ncpus); - # Collapse range of CPUs into a-b string - my $cl = collapse(@chip_cpus); - my $childname = $chipref->{name}; - if (! $childname) { - printf gettext("%s has %d virtual %s "), - $cname, $ncpus, $cpu_name; - print "($cl)\n"; - print " $impl\n" if $impl; - print "\t$brand" if $brand; - print "\t[ Socket: $socket ]" if $socket && - $socket ne "Unknown"; - print "\n"; - } else { - # Get child count - my $nchildren = - scalar(keys(%{$chipref->{values}})); - $childname = pluralize($childname, $nchildren); - printf - gettext("%s has %d %s and %d virtual %s "), - $cname, $nchildren, $childname, $ncpus, - $cpu_name; - print "($cl)\n"; - my $ident = print_component_tree ($chipref, 2); - my $spaces = ' ' x $ident; - print "$spaces$impl\n" if $impl; - print "$spaces $brand\n" if $brand; - } - } - } -} - -exit($errors); - -__END__ - -# The psrinfo command displays information about virtual and physical processors -# in a system. It gets all the information from the 'cpu_info' kstat. -# -# See detailed comment in the end of this file. -# -# -# -# This kstat -# has the following components: -# -# module: cpu_info -# instance: CPU ID -# name: cpu_infoID where ID is CPU ID -# class: misc -# -# The psrinfo command translates this information from kstat-specific -# representation to user-friendly format. -# -# The psrinfo command has several basic modes of operations: -# -# 1) Without options, it displays a line per CPU with CPU ID and its status and -# the time the status was last set in the following format: -# -# 0 on-line since MM/DD/YYYY HH:MM:SS -# 1 on-line since MM/DD/YYYY HH:MM:SS -# ... -# -# In this mode, the psrinfo command walks the list of CPUs (either from a -# command line or all CPUs) and prints the 'state' and 'state_begin' fields -# of cpu_info kstat structure for each CPU. The 'state_begin' is converted to -# local time. -# -# 2) With -s option and a single CPU ID as an argument, it displays 1 if the CPU -# is online and 0 otherwise. -# -# 3) With -p option, it displays the number of physical processors in a system. -# If any CPUs are specified in the command line, it displays the number of -# physical processors containing all virtual CPUs specified. The physical -# processor is identified by the 'chip_id' field of the cpu_info kstat. -# -# The code just walks over all CPUs specified and checks how many different -# core_id values they span. -# -# 4) With -v option, it displays several lines of information per virtual CPU, -# including its status, type, operating speed and FPU type. For example: -# -# Status of virtual processor 0 as of: MM/DD/YYYY HH:MM:SS -# on-line since MM/DD/YYYY HH:MM:SS. -# The i386 processor operates at XXXX MHz, -# and has an i387 compatible floating point processor. -# Status of virtual processor 1 as of: MM/DD/YYYY HH:MM:SS -# on-line since MM/DD/YYYY HH:MM:SS. -# The i386 processor operates at XXXX MHz, -# and has an i387 compatible floating point processor. -# -# This works in the same way as 1), just more kstat fields are massaged in the -# output. -# -# 5) With -vp option, it reports additional information about each physical -# processor. This information includes information about sub-components of -# each physical processor and virtual CPUs in each sub-component. For -# example: -# -# The physical processor has 2 cores and 4 virtual processors (0-3) -# The core has 2 virtual processors (0 1) -# The core has 2 virtual processors (2 3) -# x86 (GenuineIntel family 15 model 4 step 4 clock 3211 MHz) -# Intel(r) Pentium(r) D CPU 3.20GHz -# -# The implementation does not know anything about physical CPU components -# such as cores. Instead it looks at various cpu_info kstat statistics that -# look like xxx_id and tries to reconstruct the CPU hierarchy based on these -# fields. This works as follows: -# -# a) All kstats statistic names matching the $valid_id_exp regular expression -# are examined and each kstat statistic name is associated with the number -# of distinct entries in it. -# -# b) The resulting list of kstat statistic names is sorted according to the -# number of distinct entries, matching each name. For example, there are -# fewer chip_id values than core_id values. This implies that the core is -# a sub-component of a chip. -# -# c) All kstat names that have the same number of values as the number of -# physical processors ('chip_id' values) or the number of virtual -# processors are removed from the list. -# -# d) The resulting list represents the CPU hierarchy of the machine. It is -# translated into a tree showing the hardware hierarchy. Each level of the -# hierarchy contains the name, reference to a list of CPUs at this level -# and subcomponents, indexed by the value of each component. -# The example system above is represented by the following tree: -# -# $tree = -# { -# 'name' => 'chip_id', -# 'cpus' => [ '0', '1', '2', '3' ] -# 'values' => -# { -# '0' => -# { -# 'name' => 'core_id', -# 'cpus' => [ '0', '1', '2', '3' ] -# 'values' => -# { -# '0' => { 'cpus' => [ '0', '1' ] } -# '1' => { 'cpus' => [ '2', '3' ] }, -# }, -# } -# }, -# }; -# -# Each node contains reference to a list of virtual CPUs at this level of -# hierarchy - one list for a system as a whole, one for chip 0 and one two -# for each cores. node. Non-leaf nodes also contain the symbolic name of -# the component as represented in the cpu_info kstat and a hash of -# subnodes, indexed by the value of the component. The tree is built by -# the build_component_tree() function. -# -# e) The resulting tree is pretty-printed showing the number of -# sub-components and virtual CPUs in each sub-component. The tree is -# printed by the print_component_tree() function. -#