psrinfo.pl revision 59ac0c1669407488b67ae9e273667a340dccc611
#
# 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
# 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 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
#
# psrinfo: displays information about processors
#
# See detailed comment in the end of this file.
#
use strict;
use warnings;
use locale;
# Set message locale
######################################################################
# Configuration variables
######################################################################
# Regexp describing cpu_info kstat fields describing CPU hierarchy.
# Translation of kstat name to human-readable form
# Localized version of plural forms
# Localized CPU states
######################################################################
# 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.
# 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) = @_;
"\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 %seen; # Set to 1 for everything in the first list
# Put the shortest list in $left
# Create a hash indexed by elements in @left with ones as a value.
# Find members of @right present in @left
}
#
# Return elements of the second list not present in the first list. Both lists
# are passed by reference.
#
sub set_subtract
{
my %seen; # Set to 1 for everything in the first list
# Create a hash indexed by elements in @left with ones as a value.
# Find members of @right present in @left
}
#
# 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 (@_) {
}
return ($m);
}
#
# Pluralize name if there is more than one instance
# Arguments: name, ninstances
#
sub pluralize
{
# Remove trailing '_id' from the 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;
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 $result = '';
#
# Got consecutive ID, so extend end of range without
# printing anything since the range may extend further
#
} else {
#
# Next ID is not consecutive, so print IDs gotten so
# far.
#
$result = "$result $start-$end";
$result = "$result $start $end";
} else { # same value
$result = "$result $start";
}
# Try finding consecutive range starting from this ID
}
}
# Print last ID(s)
$result = "$result $start-$end";
$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+)$/) {
# Reverse the interval if 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 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;
}
#
# Build component tree
#
# Arguments:
# Reference to the list of CPUs sharing the component
# Reference to the list of sub-components
#
{
# Get the first component and the rest
my $tree = {};
if (!$comp_name) {
return ($tree);
}
# Get all possible component values
\@comps);
}
return ($tree);
}
#
# Print the component tree
# Arguments:
# Reference to a tree
# indentation
# Output: maximum indentation
#
{
if ($vals) {
# This is not a leaf node
# Get node name and translate it to printable format
# Examine each sub-node
if (!$child_id) {
# Child is a leaf node
print $spaces;
printf gettext("%s has %d virtual %s"),
print " ($cl)\n";
} else {
# Child has several values. Let's see how many
my $grandchild_tree = $child_tree->{values};
my $nvals = scalar(keys %$grandchild_tree);
$nvals);
print $spaces;
printf
gettext("%s has %d %s and %d virtual %s"),
$cpuname;
print " ($cl)\n";
# Print the tree for the child
$ind + 2));
}
}
}
return ($retval);
}
############################
# Main part of the program
############################
#
# Option processing
#
"v" => \$opt_v,
my $verbosity = 1;
my $phys_view;
# Set $phys_verbose if -vp is specified
# Verify options
#
# Read cpu_info kstats
#
$cmdname, $!),
exit(2);
$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.
#
# $id is CPU 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.
#
# Save CPU-specific information in cpu_list hash, indexed by CPU 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.
# Collect list of CPU IDs in @cpus
}
#
# 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) {
} else {
# Expand all x-y intervals in the argument list
# Detect invalid CPUs in the arguments
if ($nbadargs != 0) {
# Warn user about bad CPUs in the command line
if ($nbadargs > 1) {
} else {
printf STDERR
gettext("%s: processor %s: Invalid argument\n"),
}
$errors = 2;
}
}
#
# 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) {
} else {
# Get list of chips spanning all CPUs specified
printf STDERR
gettext("%s: Physical processor view not supported\n"),
$cmdname;
exit(1);
}
# Get list of all CPUs within these chips
}
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
#
my $name = "cpu_info$id";
# Collect all statistic names matching $valid_id_exp
}
# Remove duplicates
my %prop_nvals; # Number of instances of each property
#
# 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 {
$prop_nvals{$_} = $nids;
($_ eq "chip_id") ||
} @id_list;
# Sort @id_list by number of instances for each property
}
#
# Walk all CPUs specified and print information about them.
# Do nothing for physical view - will do everything later.
#
last if $phys_view; # physical view is handled later
# Get CPU state and its modification time
# Get localized version of CPU status
if ($verbosity == 0) {
# Print 1 if CPU is online, 0 if offline.
} elsif (! ($verbosity & 2)) {
printf gettext("%d\t%-8s since %s\n"),
} else {
print "\n";
# 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
if (! $fpu) {
print
gettext("\tand has no floating point processor.\n");
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.
# Get CPUs on a chip
# List of all on-line CPUs on a chip
my @online_cpus = grep {
} @chip_cpus;
#
# Print 1 if number of online CPUs equals number of all
# CPUs
#
printf
}
} else {
# Walk the property tree and print everything in it.
gettext("(unknown)");
#
# Remove cpuid and chipid information from
# implementation string and print it.
#
# List of CPUs on a chip
# Collapse range of CPUs into a-b string
if (! $childname) {
printf gettext("%s has %d virtual %s "),
print "($cl)\n";
print " $impl\n" if $impl;
print "\t$brand\n" if $brand;
} else {
# Get child count
my $nchildren =
scalar(keys(%{$chipref->{values}}));
printf
gettext("%s has %d %s and %d virtual %s "),
$cpu_name;
print "($cl)\n";
print "$spaces$impl\n" if $impl;
print "$spaces $brand\n" if $brand;
}
}
}
}
exit($errors);
# 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:
#
# ...
#
# 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:
#
# The i386 processor operates at XXXX MHz,
# and has an i387 compatible floating point processor.
# 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.
#