Home | History | Annotate | Download | only in psrinfo
      1  1843      akolb #!/usr/perl5/bin/perl
      2     0     stevel 
      3  1843      akolb #
      4  1843      akolb # CDDL HEADER START
      5  1843      akolb #
      6  1843      akolb # The contents of this file are subject to the terms of the
      7  1843      akolb # Common Development and Distribution License (the "License").
      8  1843      akolb # You may not use this file except in compliance with the License.
      9  1843      akolb #
     10  1843      akolb # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     11  1843      akolb # or http://www.opensolaris.org/os/licensing.
     12  1843      akolb # See the License for the specific language governing permissions
     13  1843      akolb # and limitations under the License.
     14  1843      akolb #
     15  1843      akolb # When distributing Covered Code, include this CDDL HEADER in each
     16  1843      akolb # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     17  1843      akolb # If applicable, add the following below this CDDL HEADER, with the
     18  1843      akolb # fields enclosed by brackets "[]" replaced with your own identifying
     19  1843      akolb # information: Portions Copyright [yyyy] [name of copyright owner]
     20  1843      akolb #
     21  1843      akolb # CDDL HEADER END
     22  1843      akolb #
     23  9482  Kuriakose # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  1843      akolb # Use is subject to license terms.
     25  1843      akolb #
     26  1843      akolb # psrinfo: displays information about processors
     27  1843      akolb #
     28  1843      akolb # See detailed comment in the end of this file.
     29  1843      akolb #
     30     0     stevel 
     31  1843      akolb use strict;
     32  1843      akolb use warnings;
     33  1843      akolb use locale;
     34  1843      akolb use POSIX qw(locale_h strftime);
     35  1843      akolb use File::Basename;
     36  1843      akolb use Getopt::Long qw(:config no_ignore_case bundling auto_version);
     37  1843      akolb use Sun::Solaris::Utils qw(textdomain gettext);
     38  1843      akolb use Sun::Solaris::Kstat;
     39  2520      akolb 
     40  2520      akolb # Set message locale
     41  2520      akolb setlocale(LC_ALL, "");
     42  2520      akolb textdomain(TEXT_DOMAIN);
     43     0     stevel 
     44  1843      akolb ######################################################################
     45  1843      akolb # Configuration variables
     46  1843      akolb ######################################################################
     47     0     stevel 
     48  1843      akolb # Regexp describing cpu_info kstat fields describing CPU hierarchy.
     49  1843      akolb my $valid_id_exp = qr{^(?:chip|core)_id$};
     50     0     stevel 
     51  1843      akolb # Translation of kstat name to human-readable form
     52  1843      akolb my %translations = ('chip_id' => gettext("The physical processor"),
     53  1843      akolb 		    'core_id' => gettext("The core"));
     54     0     stevel 
     55  1843      akolb # Localized version of plural forms
     56  1843      akolb my %pluralized_names = ('processor'	=> gettext("processor"),
     57  1843      akolb 			'processors'	=> gettext("processors"),
     58  1843      akolb 			'chip'		=> gettext("chip"),
     59  1843      akolb 			'chips'		=> gettext("chips"),
     60  1843      akolb 			'core'		=> gettext("core"),
     61  1843      akolb 			'cores'		=> gettext("cores"));
     62     0     stevel 
     63  1843      akolb # Localized CPU states
     64  1843      akolb my %cpu_states = ('on-line'	=> gettext("on-line"),
     65  1843      akolb 		  'off-line'	=> gettext("off-line"),
     66  1843      akolb 		  'faulted'	=> gettext("faulted"),
     67  1843      akolb 		  'powered-off' => gettext("powered-off"),
     68  1843      akolb 		  'no-intr'	=> gettext("no-intr"),
     69  1843      akolb 		  'spare'	=> gettext("spare"),
     70  1843      akolb 		  'unknown'	=> gettext("unknown"));
     71     0     stevel 
     72  1843      akolb ######################################################################
     73  1843      akolb # Global variables
     74  1843      akolb ######################################################################
     75  1469        hyw 
     76  1843      akolb # Hash with CPU ID as a key and specific per-cpu kstat hash as a value
     77  1843      akolb our %cpu_list;
     78     0     stevel 
     79  1843      akolb # Command name without path and trailing .pl - used for error messages.
     80  1843      akolb our $cmdname = basename($0, ".pl");
     81  1469        hyw 
     82  1843      akolb # Return value
     83  1843      akolb our $errors = 0;
     84     0     stevel 
     85  1843      akolb ######################################################################
     86  1843      akolb # Helper subroutines
     87  1843      akolb ######################################################################
     88  1843      akolb 
     89  1843      akolb #
     90  1843      akolb # Print help string if specified or the standard help message and exit setting
     91  1843      akolb # errno.
     92  1843      akolb #
     93  1843      akolb sub usage
     94     0     stevel {
     95  1843      akolb 	my (@msg) = @_;
     96  1843      akolb 	print STDERR $cmdname, ": @msg\n" if (@msg);
     97  1843      akolb 	print STDERR gettext("usage: \n" .
     98  1843      akolb 			 "\tpsrinfo [-v] [-p] [processor_id ...]\n" .
     99  1843      akolb 			 "\tpsrinfo -s [-p] processor_id\n");
    100     0     stevel 	exit(2);
    101     0     stevel }
    102     0     stevel 
    103  1843      akolb #
    104  1843      akolb # Return the input list with duplicates removed.
    105  1843      akolb # Count how many times we've seen each element and remove elements seen more
    106  1843      akolb # than once.
    107  1843      akolb #
    108  1843      akolb sub uniq
    109     0     stevel {
    110  1843      akolb 	my %seen;	# Have we seen this element already?
    111  1843      akolb 	return (grep { ++$seen{$_} == 1 } @_);
    112  1843      akolb }
    113     0     stevel 
    114  1843      akolb #
    115  1843      akolb # Return the intersection of two lists passed by reference
    116  1843      akolb # Convert the first list to a hash with seen entries marked as 1-values
    117  1843      akolb # Then grep only elements present in the first list from the second list.
    118  1843      akolb # As a little optimization, use the shorter list to build a hash.
    119  1843      akolb #
    120  1843      akolb sub intersect
    121  1843      akolb {
    122  1843      akolb 	my ($left, $right) = @_;
    123  1843      akolb 	my %seen;	# Set to 1 for everything in the first list
    124  1843      akolb 	# Put the shortest list in $left
    125  1843      akolb 	scalar @$left <= scalar @$right or ($right, $left) = ($left, $right);
    126     0     stevel 
    127  1843      akolb 	# Create a hash indexed by elements in @left with ones as a value.
    128  1843      akolb 	map { $seen{$_} = 1 } @$left;
    129  1843      akolb 	# Find members of @right present in @left
    130  1843      akolb 	return (grep { $seen{$_} } @$right);
    131  1843      akolb }
    132  1843      akolb 
    133  1843      akolb #
    134  1843      akolb # Return elements of the second list not present in the first list. Both lists
    135  1843      akolb # are passed by reference.
    136  1843      akolb #
    137  1843      akolb sub set_subtract
    138  1843      akolb {
    139  1843      akolb 	my ($left, $right) = @_;
    140  1843      akolb 	my %seen;	# Set to 1 for everything in the first list
    141  1843      akolb 	# Create a hash indexed by elements in @left with ones as a value.
    142  1843      akolb 	map { $seen{$_} = 1 } @$left;
    143  1843      akolb 	# Find members of @right present in @left
    144  1843      akolb 	return (grep { ! $seen{$_} } @$right);
    145  1843      akolb }
    146  1843      akolb 
    147  1843      akolb #
    148  1843      akolb # Sort the list numerically
    149  1843      akolb # Should be called in list context
    150  1843      akolb #
    151  1843      akolb sub nsort
    152  1843      akolb {
    153  1843      akolb 	return (sort { $a <=> $b } @_);
    154  1843      akolb }
    155  1843      akolb 
    156  1843      akolb #
    157  1843      akolb # Sort list numerically and remove duplicates
    158  1843      akolb # Should be called in list context
    159  1843      akolb #
    160  1843      akolb sub uniqsort
    161  1843      akolb {
    162  1843      akolb 	return (sort { $a <=> $b } uniq(@_));
    163  1843      akolb }
    164  1843      akolb 
    165  1843      akolb #
    166  1843      akolb # Return the maximum value of its arguments
    167  1843      akolb #
    168  1843      akolb sub max
    169  1843      akolb {
    170  1843      akolb 	my $m = shift;
    171  1843      akolb 
    172  1843      akolb 	foreach my $el (@_) {
    173  1843      akolb 		$m = $el if $m < $el;
    174  1843      akolb 	}
    175  1843      akolb 	return ($m);
    176  1843      akolb }
    177  1843      akolb 
    178  1843      akolb #
    179  1843      akolb # Pluralize name if there is more than one instance
    180  1843      akolb # Arguments: name, ninstances
    181  1843      akolb #
    182  1843      akolb sub pluralize
    183  1843      akolb {
    184  1843      akolb 	my ($name, $count) = @_;
    185  1843      akolb 	# Remove trailing '_id' from the name.
    186  1843      akolb 	$name =~ s/_id$//;
    187  1843      akolb 	my $plural_name = $count > 1 ? "${name}s" : $name;
    188  1843      akolb 	return ($pluralized_names{$plural_name} || $plural_name)
    189  1843      akolb }
    190  1843      akolb 
    191  1843      akolb #
    192  1843      akolb # Translate id name into printable form
    193  1843      akolb # Look at the %translations table and replace everything found there
    194  1843      akolb # Remove trailing _id from the name if there is no translation
    195  1843      akolb #
    196  1843      akolb sub id_translate
    197  1843      akolb {
    198  1843      akolb 	my $name = shift or return;
    199  1843      akolb 	my $translated_name = $translations{$name};
    200  1843      akolb 	$name =~ s/_id$// unless $translated_name;
    201  1843      akolb 	return ($translated_name || $name);
    202  1843      akolb }
    203  1843      akolb 
    204  1843      akolb #
    205  1843      akolb # Consolidate consequtive CPU ids as start-end
    206  1843      akolb # Input: list of CPUs
    207  2520      akolb # Output: string with space-sepated cpu values with CPU ranges
    208  2520      akolb #   collapsed as x-y
    209  1843      akolb #
    210  1843      akolb sub collapse
    211  1843      akolb {
    212  1843      akolb 	return ('') unless @_;
    213  1843      akolb 	my @args = uniqsort(@_);
    214  1843      akolb 	my $start = shift(@args);
    215  1843      akolb 	my $result = '';
    216  2520      akolb 	my $end = $start;	# Initial range consists of the first element
    217  1843      akolb 	foreach my $el (@args) {
    218  1843      akolb 		if ($el == ($end + 1)) {
    219  2520      akolb 			#
    220  2520      akolb 			# Got consecutive ID, so extend end of range without
    221  2520      akolb 			# printing anything since the range may extend further
    222  2520      akolb 			#
    223  1843      akolb 			$end = $el;
    224  1843      akolb 		} else {
    225  2520      akolb 			#
    226  2520      akolb 			# Next ID is not consecutive, so print IDs gotten so
    227  2520      akolb 			# far.
    228  2520      akolb 			#
    229  2520      akolb 			if ($end > $start + 1) {	# range
    230  1843      akolb 				$result = "$result $start-$end";
    231  2520      akolb 			} elsif ($end > $start) {	# different values
    232  2520      akolb 				$result = "$result $start $end";
    233  2520      akolb 			} else {	# same value
    234  1843      akolb 				$result = "$result $start";
    235  1843      akolb 			}
    236  2520      akolb 
    237  2520      akolb 			# Try finding consecutive range starting from this ID
    238  1843      akolb 			$start = $end = $el;
    239     0     stevel 		}
    240     0     stevel 	}
    241  2520      akolb 
    242  2520      akolb 	# Print last ID(s)
    243  1843      akolb 	if ($end > $start + 1) {
    244  1843      akolb 		$result = "$result $start-$end";
    245  1843      akolb 	} elsif ($end > $start) {
    246  1843      akolb 		$result = "$result $start $end";
    247  1843      akolb 	} else {
    248  1843      akolb 		$result = "$result $start";
    249  1843      akolb 	}
    250  1843      akolb 	# Remove any spaces in the beginning
    251  1843      akolb 	$result =~ s/^\s+//;
    252  1843      akolb 	return ($result);
    253  1843      akolb }
    254     0     stevel 
    255  1843      akolb #
    256  1843      akolb # Expand start-end into the list of values
    257  1843      akolb # Input: string containing a single numeric ID or x-y range
    258  1843      akolb # Output: single value or a list of values
    259  1843      akolb # Ranges with start being more than end are inverted
    260  1843      akolb #
    261  1843      akolb sub expand
    262  1843      akolb {
    263  1843      akolb 	my $arg = shift;
    264     0     stevel 
    265  1843      akolb 	if ($arg =~ m/^\d+$/) {
    266  1843      akolb 		# single number
    267  1843      akolb 		return ($_);
    268  1843      akolb 	} elsif ($arg =~ m/^(\d+)\-(\d+)$/) {
    269  1843      akolb 		my ($start, $end) = ($1, $2);	# $start-$end
    270  1843      akolb 		# Reverse the interval if start > end
    271  1843      akolb 		($start, $end) = ($end, $start) if $start > $end;
    272  1843      akolb 		return ($start .. $end);
    273  1843      akolb 	} elsif ($arg =~ m/-/) {
    274  1843      akolb 		printf STDERR
    275  1843      akolb 		  gettext("%s: invalid processor range %s\n"),
    276  1843      akolb 		    $cmdname, $_;
    277  1843      akolb 	} else {
    278  1843      akolb 		printf STDERR
    279  1843      akolb 		  gettext("%s: processor %s: Invalid argument\n"),
    280  1843      akolb 		    $cmdname, $_;
    281  1843      akolb 	}
    282  1843      akolb 	$errors = 2;
    283  1843      akolb 	return ();
    284  1843      akolb }
    285     0     stevel 
    286  1843      akolb #
    287  1843      akolb # Functions for constructing CPU hierarchy. Only used with -vp option.
    288  1843      akolb #
    289     0     stevel 
    290  1843      akolb #
    291  1843      akolb # Return numerically sorted list of distinct values of a given cpu_info kstat
    292  1843      akolb # field, spanning given CPU set.
    293  1843      akolb #
    294  1843      akolb # Arguments:
    295  1843      akolb #   Property name
    296  1843      akolb #   list of CPUs
    297  1843      akolb #
    298  1843      akolb # Treat undefined values as zeroes.
    299  1843      akolb sub property_list
    300  1843      akolb {
    301  1843      akolb 	my $prop_name = shift;
    302  4732     davemq 	return (grep {$_ >= 0} uniqsort(map { $cpu_list{$_}->{$prop_name} || 0 } @_));
    303  1843      akolb }
    304  1843      akolb 
    305  1843      akolb #
    306  1843      akolb # Return subset of CPUs sharing specified value of a given cpu_info kstat field.
    307  1843      akolb # Arguments:
    308  1843      akolb #   Property name
    309  1843      akolb #   Property value
    310  1843      akolb #   List of CPUs to select from
    311  1843      akolb #
    312  1843      akolb # Treat undefined values as zeroes.
    313  1843      akolb sub cpus_by_prop
    314  1843      akolb {
    315  1843      akolb 	my $prop_name = shift;
    316  1843      akolb 	my $prop_val = shift;
    317  1843      akolb 
    318  1843      akolb 	return (grep { ($cpu_list{$_}->{$prop_name} || 0) == $prop_val } @_);
    319  1843      akolb }
    320  1843      akolb 
    321  1843      akolb #
    322  1843      akolb # Build component tree
    323  1843      akolb #
    324  1843      akolb # Arguments:
    325  1843      akolb #    Reference to the list of CPUs sharing the component
    326  1843      akolb #    Reference to the list of sub-components
    327  1843      akolb #
    328  1843      akolb sub build_component_tree
    329  1843      akolb {
    330  1843      akolb 	my ($cpus, $comp_list) = @_;
    331  1843      akolb 	# Get the first component and the rest
    332  1843      akolb 	my ($comp_name, @comps) = @$comp_list;
    333  1843      akolb 	my $tree = {};
    334  1843      akolb 	if (!$comp_name) {
    335  1843      akolb 		$tree->{cpus} = $cpus;
    336  1843      akolb 		return ($tree);
    337  1843      akolb 	}
    338  1843      akolb 
    339  1843      akolb 	# Get all possible component values
    340  1843      akolb 	foreach my $v (property_list($comp_name, @$cpus)) {
    341  1843      akolb 		my @comp_cpus = cpus_by_prop ($comp_name, $v, @$cpus);
    342  1843      akolb 		$tree->{name} = $comp_name;
    343  1843      akolb 		$tree->{cpus} = $cpus;
    344  1843      akolb 		$tree->{values}->{$v} = build_component_tree(\@comp_cpus,
    345  1843      akolb 							     \@comps);
    346  1843      akolb 	}
    347  1843      akolb 	return ($tree);
    348  1843      akolb }
    349  1843      akolb 
    350  1843      akolb #
    351  1843      akolb # Print the component tree
    352  1843      akolb # Arguments:
    353  1843      akolb #   Reference to a tree
    354  1843      akolb #   indentation
    355  1843      akolb # Output: maximum indentation
    356  1843      akolb #
    357  1843      akolb sub print_component_tree
    358  1843      akolb {
    359  1843      akolb 	my ($tree, $ind) = @_;
    360  1843      akolb 	my $spaces = ' ' x $ind; # indentation string
    361  1843      akolb 	my $vals = $tree->{values};
    362  1843      akolb 	my $retval = $ind;
    363  1843      akolb 	if ($vals) {
    364  1843      akolb 		# This is not a leaf node
    365  1843      akolb 		# Get node name and translate it to printable format
    366  1843      akolb 		my $id_name = id_translate($tree->{name});
    367  1843      akolb 		# Examine each sub-node
    368  1843      akolb 		foreach my $comp_val (nsort(keys %$vals)) {
    369  1843      akolb 			my $child_tree = $vals->{$comp_val}; # Sub-tree
    370  1843      akolb 			my $child_id = $child_tree->{name}; # Name of child node
    371  1843      akolb 			my @cpus = @{$child_tree->{cpus}}; # CPUs for the child
    372  1843      akolb 			my $ncpus = scalar @cpus; # Number of CPUs
    373  1843      akolb 			my $cpuname = pluralize('processor', $ncpus);
    374  1843      akolb 			my $cl = collapse(@cpus); # Printable CPU list
    375  1843      akolb 			if (!$child_id) {
    376  1843      akolb 				# Child is a leaf node
    377  1843      akolb 				print $spaces;
    378  1843      akolb 				printf gettext("%s has %d virtual %s"),
    379  1843      akolb 				       $id_name, $ncpus, $cpuname;
    380  1843      akolb 				print " ($cl)\n";
    381  1843      akolb 				$retval = max($retval, $ind + 2);
    382  1843      akolb 			} else {
    383  1843      akolb 				# Child has several values. Let's see how many
    384  1843      akolb 				my $grandchild_tree = $child_tree->{values};
    385  1843      akolb 				my $nvals = scalar(keys %$grandchild_tree);
    386  1843      akolb 				my $child_id_name = pluralize($child_id,
    387  1843      akolb 							      $nvals);
    388  1843      akolb 				print $spaces;
    389  1843      akolb 				printf
    390  1843      akolb 				  gettext("%s has %d %s and %d virtual %s"),
    391  1843      akolb 				    $id_name, $nvals, $child_id_name, $ncpus,
    392  1843      akolb 				      $cpuname;
    393  1843      akolb 				print " ($cl)\n";
    394  1843      akolb 				# Print the tree for the child
    395  1843      akolb 				$retval = max($retval,
    396  1843      akolb 					      print_component_tree($child_tree,
    397  1843      akolb 								   $ind + 2));
    398  1843      akolb 			}
    399  1843      akolb 		}
    400  1843      akolb 	}
    401  1843      akolb 	return ($retval);
    402  1843      akolb }
    403  1843      akolb 
    404  1843      akolb 
    405  1843      akolb ############################
    406  1843      akolb # Main part of the program
    407  1843      akolb ############################
    408  1843      akolb 
    409  1843      akolb #
    410  1843      akolb # Option processing
    411  1843      akolb #
    412  1843      akolb my ($opt_v, $opt_p, $opt_silent);
    413  1843      akolb 
    414  1843      akolb GetOptions("p" => \$opt_p,
    415  1843      akolb  	   "v" => \$opt_v,
    416  1843      akolb  	   "s" => \$opt_silent) || usage();
    417  1843      akolb 
    418  1843      akolb 
    419  1843      akolb my $verbosity = 1;
    420  1843      akolb my $phys_view;
    421  1843      akolb 
    422  1843      akolb $verbosity |= 2 if $opt_v;
    423  1843      akolb $verbosity &= ~1 if $opt_silent;
    424  1843      akolb $phys_view = 1 if $opt_p;
    425  1843      akolb 
    426  1843      akolb # Set $phys_verbose if -vp is specified
    427  1843      akolb my $phys_verbose = $phys_view && ($verbosity > 1);
    428  1843      akolb 
    429  1843      akolb # Verify options
    430  1843      akolb usage(gettext("options -s and -v are mutually exclusive")) if $verbosity == 2;
    431  1843      akolb 
    432  1843      akolb usage(gettext("must specify exactly one processor if -s used")) if
    433  1843      akolb   (($verbosity == 0) && scalar @ARGV != 1);
    434  1843      akolb 
    435  1843      akolb #
    436  1843      akolb # Read cpu_info kstats
    437  1843      akolb #
    438  1843      akolb my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or
    439  1843      akolb   (printf STDERR gettext("%s: kstat_open() failed: %s\n"),
    440  1843      akolb    $cmdname, $!),
    441  1843      akolb     exit(2);
    442  1843      akolb my $cpu_info = $ks->{cpu_info} or
    443  1843      akolb   (printf STDERR gettext("%s: can not read cpu_info kstats\n"),
    444  1843      akolb    $cmdname),
    445  1843      akolb     exit(2);
    446  1843      akolb 
    447  1843      akolb my (
    448  1843      akolb     @all_cpus,	# List of all CPUs in the system
    449  1843      akolb     @cpu_args,	# CPUs to look at
    450  1843      akolb     @cpus,	# List of CPUs to process
    451  1843      akolb     @id_list,	# list of various xxx_id kstats representing CPU topology
    452  1843      akolb     %chips,	# Hash with chip ID as a key and reference to the list of
    453  1843      akolb 		# virtual CPU IDs, belonging to the chip as a value
    454  1843      akolb     @chip_list,	# List of all chip_id values
    455  1843      akolb     $ctree,	# The component tree
    456  1843      akolb    );
    457  1843      akolb 
    458  1843      akolb #
    459  1843      akolb # Get information about each CPU.
    460  1843      akolb #
    461  1843      akolb #   Collect list of all CPUs in @cpu_list array
    462  1843      akolb #
    463  1843      akolb #   Construct %cpu_list hash keyed by CPU ID with cpu_info kstat hash as its
    464  1843      akolb #   value.
    465  1843      akolb #
    466  1843      akolb #   Construct %chips hash keyed by chip ID. It has a 'cpus' entry, which is
    467  1843      akolb #   a reference to a list of CPU IDs within a chip.
    468  1843      akolb #
    469  1843      akolb foreach my $id (nsort(keys %$cpu_info)) {
    470  1843      akolb 	# $id is CPU id
    471  1843      akolb 	my $info = $cpu_info->{$id};
    472  1843      akolb 
    473  1843      akolb 	#
    474  1843      akolb 	# The name part of the cpu_info kstat should always be a string
    475  1843      akolb 	# cpu_info$id.
    476  1843      akolb 	#
    477  1843      akolb 	# The $ci hash reference holds all data for a specific CPU id.
    478  1843      akolb 	#
    479  1843      akolb 	my $ci = $info->{"cpu_info$id"} or next;
    480  1843      akolb 	# Save CPU-specific information in cpu_list hash, indexed by CPU ID.
    481  1843      akolb 	$cpu_list{$id} = $ci;
    482  1843      akolb 	my $chip_id = $ci->{'chip_id'};
    483  1843      akolb 	# Collect CPUs within the chip.
    484  1843      akolb 	# $chips{$chip_id} is a reference to a list of CPU IDs belonging to thie
    485  1843      akolb 	# chip. It is automatically created when first referenced.
    486  1843      akolb 	push (@{$chips{$chip_id}}, $id) if (defined($chip_id));
    487  1843      akolb 	# Collect list of CPU IDs in @cpus
    488  1843      akolb 	push (@all_cpus, $id);
    489  1843      akolb }
    490  1843      akolb 
    491  1843      akolb #
    492  1843      akolb # Figure out what CPUs to examine.
    493  1843      akolb # Look at specific CPUs if any are specified on the command line or at all CPUs
    494  1843      akolb # CPU ranges specified in the command line are expanded into lists of CPUs
    495  1843      akolb #
    496  1843      akolb if (scalar(@ARGV) == 0) {
    497  1843      akolb 	@cpu_args = @all_cpus;
    498  1843      akolb } else {
    499  1843      akolb 	# Expand all x-y intervals in the argument list
    500  1843      akolb 	@cpu_args = map { expand($_) } @ARGV;
    501  1843      akolb 
    502  1843      akolb 	usage(gettext("must specify exactly one processor if -s used")) if
    503  1843      akolb 	    (($verbosity == 0) && scalar @cpu_args != 1);
    504  1843      akolb 
    505  1843      akolb 	# Detect invalid CPUs in the arguments
    506  1843      akolb 	my @bad_args = set_subtract(\@all_cpus, \@cpu_args);
    507  1843      akolb 	my $nbadargs = scalar @bad_args;
    508  1843      akolb 
    509  1843      akolb 	if ($nbadargs != 0) {
    510  1843      akolb 		# Warn user about bad CPUs in the command line
    511  1843      akolb 		my $argstr = collapse(@bad_args);
    512  1843      akolb 
    513  1843      akolb 		if ($nbadargs > 1) {
    514  1843      akolb 			printf STDERR gettext("%s: Invalid processors %s\n"),
    515  1843      akolb 			  $cmdname, $argstr;
    516  1843      akolb 		} else {
    517  1843      akolb 			printf STDERR
    518  1843      akolb 			  gettext("%s: processor %s: Invalid argument\n"),
    519  1843      akolb 			  $cmdname, $argstr;
    520  1843      akolb 		}
    521  1843      akolb 		$errors = 2;
    522  1843      akolb 	}
    523  1843      akolb 
    524  1843      akolb 	@cpu_args = uniqsort(intersect(\@all_cpus, \@cpu_args));
    525  1843      akolb }
    526  1843      akolb 
    527  1843      akolb #
    528  1843      akolb # In physical view, CPUs specified in the command line are only used to identify
    529  1843      akolb # chips. The actual CPUs are all CPUs belonging to these chips.
    530  1843      akolb #
    531  1843      akolb if (! $phys_view) {
    532  1843      akolb 	@cpus = @cpu_args;
    533  1843      akolb } else {
    534  1843      akolb 	# Get list of chips spanning all CPUs specified
    535  1843      akolb 	@chip_list = property_list('chip_id', @cpu_args);
    536  1843      akolb 	if (!scalar @chip_list && $errors == 0) {
    537  1843      akolb 		printf STDERR
    538  1843      akolb 		  gettext("%s: Physical processor view not supported\n"),
    539  1843      akolb 		    $cmdname;
    540     0     stevel 		exit(1);
    541     0     stevel 	}
    542     0     stevel 
    543  1843      akolb 	# Get list of all CPUs within these chips
    544  1843      akolb 	@cpus = uniqsort(map { @{$chips{$_}} } @chip_list);
    545  1843      akolb }
    546     0     stevel 
    547     0     stevel 
    548  1843      akolb if ($phys_verbose) {
    549  1843      akolb 	#
    550  1843      akolb 	# 1) Look at all possible xxx_id properties and remove those that have
    551  1843      akolb 	#    NCPU values or one value. Sort the rest.
    552  1843      akolb 	#
    553  1843      akolb 	# 2) Drop ids which have the same number of entries as number of CPUs or
    554  1843      akolb 	#    number of chips.
    555  1843      akolb 	#
    556  1843      akolb 	# 3) Build the component tree for the system
    557  1843      akolb 	#
    558  1843      akolb 	foreach my $id (keys %$cpu_info) {
    559  1843      akolb 		my $info = $cpu_info->{$id};
    560  1843      akolb 		my $name = "cpu_info$id";
    561  1843      akolb 		my $ci = $info->{$name}; # cpu_info kstat for this CPU
    562     0     stevel 
    563  1843      akolb 		# Collect all statistic names matching $valid_id_exp
    564  1843      akolb 		push @id_list, grep(/$valid_id_exp/, keys(%$ci));
    565     0     stevel 	}
    566     0     stevel 
    567  1843      akolb 	# Remove duplicates
    568  1843      akolb 	@id_list = uniq(@id_list);
    569     0     stevel 
    570  1843      akolb 	my $ncpus = scalar @cpus;
    571  1843      akolb 	my %prop_nvals;		# Number of instances of each property
    572  1843      akolb 	my $nchips = scalar @chip_list;
    573  1843      akolb 
    574  1843      akolb 	#
    575  1843      akolb 	# Get list of properties which have more than ncpus and less than nchips
    576  1843      akolb 	# instances.
    577  1843      akolb 	# Also collect number of instances for each property.
    578  1843      akolb 	#
    579  1843      akolb 	@id_list = grep {
    580  1843      akolb 		my @ids = property_list($_, @cpus);
    581  1843      akolb 		my $nids = scalar @ids;
    582  1843      akolb 		$prop_nvals{$_} = $nids;
    583  1843      akolb 		($_ eq "chip_id") ||
    584  1843      akolb 		  (($nids > $nchips) && ($nids > 1) && ($nids < $ncpus));
    585  1843      akolb 	} @id_list;
    586  1843      akolb 
    587  1843      akolb 	# Sort @id_list by number of instances for each property
    588  1843      akolb 	@id_list = sort { $prop_nvals{$a} <=> $prop_nvals{$b} } @id_list;
    589  1843      akolb 
    590  1843      akolb 	$ctree = build_component_tree(\@cpus, \@id_list);
    591  1843      akolb }
    592  1843      akolb 
    593  1843      akolb 
    594  1843      akolb #
    595  1843      akolb # Walk all CPUs specified and print information about them.
    596  1843      akolb # Do nothing for physical view - will do everything later.
    597  1843      akolb #
    598  1843      akolb foreach my $id (@cpus) {
    599  1843      akolb 	last if $phys_view;	# physical view is handled later
    600  1843      akolb 	my $cpu = $cpu_list{$id} or next;
    601  1843      akolb 
    602  1843      akolb 	# Get CPU state and its modification time
    603  1843      akolb 	my $mtime = $cpu->{'state_begin'};
    604  1843      akolb 	my $mstring = strftime(gettext("%m/%d/%Y %T"), localtime($mtime));
    605  1843      akolb 	my $status = $cpu->{'state'} || gettext("unknown");
    606  1843      akolb 	# Get localized version of CPU status
    607  1843      akolb 	$status = $cpu_states{$status} || $status;
    608  1843      akolb 
    609  1843      akolb 	if ($verbosity == 0) {
    610  1843      akolb 		# Print 1 if CPU is online, 0 if offline.
    611  1843      akolb 		printf "%d\n", $status eq 'on-line';
    612  1843      akolb 	} elsif (! ($verbosity & 2)) {
    613  1843      akolb 		printf gettext("%d\t%-8s  since %s\n"),
    614  1843      akolb 			$id, $status, $mstring;
    615  1843      akolb 	} else {
    616  1843      akolb 		printf gettext("Status of virtual processor %d as of: "), $id;
    617  1843      akolb 		print strftime(gettext("%m/%d/%Y %T"), localtime());
    618  1843      akolb 		print "\n";
    619  1843      akolb 		printf gettext("  %s since %s.\n"), $status, $mstring;
    620  1843      akolb 		my $clock_speed =  $cpu->{'clock_MHz'};
    621  1843      akolb 		my $cpu_type = $cpu->{'cpu_type'};
    622  1843      akolb 
    623  1843      akolb 		# Display clock speed
    624  1843      akolb 		if ($clock_speed ) {
    625  1843      akolb 			printf
    626  1843      akolb 			  gettext("  The %s processor operates at %s MHz,\n"),
    627  1843      akolb 			       $cpu_type, $clock_speed;
    628  1843      akolb 		} else {
    629  1843      akolb 			printf
    630  1843      akolb 	      gettext("  the %s processor operates at an unknown frequency,\n"),
    631  1843      akolb 			$cpu_type;
    632  1843      akolb 		}
    633  1843      akolb 
    634  1843      akolb 		# Display FPU type
    635  1843      akolb 		my $fpu = $cpu->{'fpu_type'};
    636  1843      akolb 		if (! $fpu) {
    637  1843      akolb 			print
    638  1843      akolb 			  gettext("\tand has no floating point processor.\n");
    639  1843      akolb 		} elsif ($fpu =~ m/^[aeiouy]/) {
    640  1843      akolb 			printf
    641  1843      akolb 			 gettext("\tand has an %s floating point processor.\n"),
    642  1843      akolb 			   $fpu;
    643  1843      akolb 		} else {
    644  1843      akolb 			printf
    645  1843      akolb 			  gettext("\tand has a %s floating point processor.\n"),
    646  1843      akolb 			    $fpu;
    647  1843      akolb 		}
    648  1843      akolb 	}
    649  1843      akolb }
    650  1843      akolb 
    651  1843      akolb #
    652  1843      akolb # Physical view print
    653  1843      akolb #
    654  1843      akolb if ($phys_view) {
    655  1843      akolb 	if ($verbosity == 1) {
    656  1843      akolb 		print scalar @chip_list, "\n";
    657  1843      akolb 	} elsif ($verbosity == 0) {
    658  1843      akolb 		# Print 1 if all CPUs are online, 0 otherwise.
    659  1843      akolb 		foreach my $chip_id (@chip_list) {
    660  1843      akolb 			# Get CPUs on a chip
    661  1843      akolb 			my @chip_cpus = uniqsort(@{$chips{$chip_id}});
    662  1843      akolb 			# List of all on-line CPUs on a chip
    663  1843      akolb 			my @online_cpus = grep { 
    664  1843      akolb 				($cpu_list{$_}->{state}) eq 'on-line'
    665  1843      akolb 			} @chip_cpus;
    666  1843      akolb 
    667  1843      akolb 			#
    668  1843      akolb 			# Print 1 if number of online CPUs equals number of all
    669  1843      akolb 			# CPUs
    670  1843      akolb 			#
    671  1843      akolb 			printf
    672  1843      akolb 			  "%d\n", scalar @online_cpus == scalar @chip_cpus;
    673  1843      akolb 		}
    674  1843      akolb 	} else {
    675  1843      akolb 		# Walk the property tree and print everything in it.
    676  1843      akolb 		my $tcores = $ctree->{values};
    677  1843      akolb 		my $cname = id_translate($ctree->{name});
    678  1843      akolb 		foreach my $chip (nsort(keys %$tcores)) {
    679  1843      akolb 			my $chipref = $tcores->{$chip};
    680  1843      akolb 			my @chip_cpus = @{$chipref->{cpus}};
    681  1843      akolb 			my $ncpus = scalar @chip_cpus;
    682  1843      akolb 			my $cpu_id = $chip_cpus[0];
    683  1843      akolb 			my $cpu = $cpu_list{$cpu_id};
    684  1843      akolb 			my $brand = $cpu->{brand} ||  gettext("(unknown)");
    685  1843      akolb 			my $impl = $cpu->{implementation} ||
    686  1843      akolb 			  gettext("(unknown)");
    687  9482  Kuriakose 			my $socket = $cpu->{socket_type};
    688  1843      akolb 			#
    689  1843      akolb 			# Remove cpuid and chipid information from
    690  1843      akolb 			# implementation string and print it.
    691  1843      akolb 			#
    692  1843      akolb 			$impl =~ s/(cpuid|chipid)\s*\w+\s+//;
    693  1843      akolb 			$brand = '' if $impl && $impl =~ /^$brand/;
    694  1843      akolb 			# List of CPUs on a chip
    695  1843      akolb 			my $cpu_name = pluralize('processor', $ncpus);
    696  1843      akolb 			# Collapse range of CPUs into a-b string
    697  1843      akolb 			my $cl = collapse(@chip_cpus);
    698  1843      akolb 			my $childname = $chipref->{name};
    699  1843      akolb 			if (! $childname) {
    700  1843      akolb 				printf gettext("%s has %d virtual %s "),
    701  1843      akolb 				       $cname, $ncpus, $cpu_name;
    702  1843      akolb 				print "($cl)\n";
    703  1843      akolb 				print "  $impl\n" if $impl;
    704  9482  Kuriakose 				print "\t$brand" if $brand;
    705  9482  Kuriakose 				print "\t[ Socket: $socket ]" if $socket &&
    706  9482  Kuriakose 				  $socket ne "Unknown";
    707  9482  Kuriakose 				print "\n";
    708     0     stevel 			} else {
    709  1843      akolb 				# Get child count
    710  1843      akolb 				my $nchildren =
    711  1843      akolb 				  scalar(keys(%{$chipref->{values}}));
    712  1843      akolb 				$childname = pluralize($childname, $nchildren);
    713  1843      akolb 				printf
    714  1843      akolb 				  gettext("%s has %d %s and %d virtual %s "),
    715  1843      akolb 				       $cname, $nchildren, $childname, $ncpus,
    716  1843      akolb 				       $cpu_name;
    717  1843      akolb 				print "($cl)\n";
    718  1843      akolb 				my $ident = print_component_tree ($chipref, 2);
    719  1843      akolb 				my $spaces = ' ' x $ident;
    720  1843      akolb 				print "$spaces$impl\n" if $impl;
    721  1843      akolb 				print "$spaces  $brand\n" if $brand;
    722     0     stevel 			}
    723     0     stevel 		}
    724     0     stevel 	}
    725     0     stevel }
    726     0     stevel 
    727  1843      akolb exit($errors);
    728     0     stevel 
    729  1843      akolb __END__
    730     0     stevel 
    731  1843      akolb # The psrinfo command displays information about virtual and physical processors
    732  1843      akolb # in a system. It gets all the information from the 'cpu_info' kstat.
    733  1843      akolb #
    734  1843      akolb # See detailed comment in the end of this file.
    735  1843      akolb #
    736  1843      akolb #
    737  1843      akolb #
    738  1843      akolb # This kstat
    739  1843      akolb # has the following components:
    740  1843      akolb #
    741  1843      akolb # module:	cpu_info
    742  1843      akolb # instance:	CPU ID
    743  1843      akolb # name:		cpu_infoID where ID is CPU ID
    744  1843      akolb # class:	misc
    745  1843      akolb #
    746  1843      akolb # The psrinfo command translates this information from kstat-specific
    747  1843      akolb # representation to user-friendly format.
    748  1843      akolb #
    749  1843      akolb # The psrinfo command has several basic modes of operations:
    750  1843      akolb #
    751  1843      akolb # 1) Without options, it displays a line per CPU with CPU ID and its status and
    752  1843      akolb #    the time the status was last set in the following format:
    753  1843      akolb #
    754  1843      akolb #	0       on-line  since MM/DD/YYYY HH:MM:SS
    755  1843      akolb #	1	on-line  since MM/DD/YYYY HH:MM:SS
    756  1843      akolb #	...
    757  1843      akolb #
    758  1843      akolb #    In this mode, the psrinfo command walks the list of CPUs (either from a
    759  1843      akolb #    command line or all CPUs) and prints the 'state' and 'state_begin' fields
    760  1843      akolb #    of cpu_info kstat structure for each CPU. The 'state_begin' is converted to
    761  1843      akolb #    local time.
    762  1843      akolb #
    763  1843      akolb # 2) With -s option and a single CPU ID as an argument, it displays 1 if the CPU
    764  1843      akolb #    is online and 0 otherwise.
    765  1843      akolb #
    766  1843      akolb # 3) With -p option, it displays the number of physical processors in a system.
    767  1843      akolb #    If any CPUs are specified in the command line, it displays the number of
    768  1843      akolb #    physical processors containing all virtual CPUs specified. The physical
    769  1843      akolb #    processor is identified by the 'chip_id' field of the cpu_info kstat.
    770  1843      akolb #
    771  1843      akolb #    The code just walks over all CPUs specified and checks how many different
    772  1843      akolb #    core_id values they span.
    773  1843      akolb #
    774  1843      akolb # 4) With -v option, it displays several lines of information per virtual CPU,
    775  1843      akolb #    including its status, type, operating speed and FPU type. For example:
    776  1843      akolb #
    777  1843      akolb #	Status of virtual processor 0 as of: MM/DD/YYYY HH:MM:SS
    778  1843      akolb #	  on-line since MM/DD/YYYY HH:MM:SS.
    779  1843      akolb #	  The i386 processor operates at XXXX MHz,
    780  1843      akolb #	        and has an i387 compatible floating point processor.
    781  1843      akolb #	Status of virtual processor 1 as of: MM/DD/YYYY HH:MM:SS
    782  1843      akolb #	  on-line since MM/DD/YYYY HH:MM:SS.
    783  1843      akolb #	  The i386 processor operates at XXXX MHz,
    784  1843      akolb #	        and has an i387 compatible floating point processor.
    785  1843      akolb #
    786  1843      akolb # This works in the same way as 1), just more kstat fields are massaged in the
    787  1843      akolb # output.
    788  1843      akolb #
    789  1843      akolb # 5) With -vp option, it reports additional information about each physical
    790  1843      akolb #    processor. This information includes information about sub-components of
    791  1843      akolb #    each physical processor and virtual CPUs in each sub-component. For
    792  1843      akolb #    example:
    793  1843      akolb #
    794  1843      akolb #	The physical processor has 2 cores and 4 virtual processors (0-3)
    795  1843      akolb #	  The core has 2 virtual processors (0 1)
    796  1843      akolb #	  The core has 2 virtual processors (2 3)
    797  1843      akolb #	    x86 (GenuineIntel family 15 model 4 step 4 clock 3211 MHz)
    798  1843      akolb #	      Intel(r) Pentium(r) D CPU 3.20GHz
    799  1843      akolb #
    800  1843      akolb #    The implementation does not know anything about physical CPU components
    801  1843      akolb #    such as cores. Instead it looks at various cpu_info kstat statistics that
    802  1843      akolb #    look like xxx_id and tries to reconstruct the CPU hierarchy based on these
    803  1843      akolb #    fields. This works as follows:
    804  1843      akolb #
    805  1843      akolb #    a) All kstats statistic names matching the $valid_id_exp regular expression
    806  1843      akolb #       are examined and each kstat statistic name is associated with the number
    807  1843      akolb #       of distinct entries in it.
    808  1843      akolb #
    809  1843      akolb #    b) The resulting list of kstat statistic names is sorted according to the
    810  1843      akolb #       number of distinct entries, matching each name. For example, there are
    811  1843      akolb #       fewer chip_id values than core_id values. This implies that the core is
    812  1843      akolb #	a sub-component of a chip.
    813  1843      akolb #
    814  1843      akolb #    c) All kstat names that have the same number of values as the number of
    815  1843      akolb #       physical processors ('chip_id' values) or the number of virtual
    816  1843      akolb #       processors are removed from the list.
    817  1843      akolb #
    818  1843      akolb #    d) The resulting list represents the CPU hierarchy of the machine. It is
    819  1843      akolb #       translated into a tree showing the hardware hierarchy. Each level of the
    820  1843      akolb #       hierarchy contains the name, reference to a list of CPUs at this level
    821  1843      akolb #       and subcomponents, indexed by the value of each component.
    822  1843      akolb #       The example system above is represented by the following tree:
    823  1843      akolb #
    824  1843      akolb #	$tree =
    825  1843      akolb #	{
    826  1843      akolb #	 'name' => 'chip_id',
    827  1843      akolb #	 'cpus' => [ '0', '1', '2', '3' ]
    828  1843      akolb #	 'values' =>
    829  1843      akolb #	 {
    830  1843      akolb #	  '0' =>
    831  1843      akolb #	  {
    832  1843      akolb #	   'name' => 'core_id',
    833  1843      akolb #	   'cpus' => [ '0', '1', '2', '3' ]
    834  1843      akolb #	   'values' =>
    835  1843      akolb #	   {
    836  1843      akolb #	    '0' => { 'cpus' => [ '0', '1' ] }
    837  1843      akolb #	    '1' => { 'cpus' => [ '2', '3' ] },
    838  1843      akolb #	   },
    839  1843      akolb #	  }
    840  1843      akolb #	 },
    841  1843      akolb #	};
    842  1843      akolb #
    843  1843      akolb #       Each node contains reference to a list of virtual CPUs at this level of
    844  1843      akolb #       hierarchy - one list for a system as a whole, one for chip 0 and one two
    845  1843      akolb #       for each cores. node. Non-leaf nodes also contain the symbolic name of
    846  1843      akolb #       the component as represented in the cpu_info kstat and a hash of
    847  1843      akolb #       subnodes, indexed by the value of the component. The tree is built by
    848  1843      akolb #       the build_component_tree() function.
    849  1843      akolb #
    850  1843      akolb #    e) The resulting tree is pretty-printed showing the number of
    851  1843      akolb #       sub-components and virtual CPUs in each sub-component. The tree is
    852  1843      akolb #       printed by the print_component_tree() function.
    853  1843      akolb #
    854