#
# 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
#
#
#
#
# pginfo - tool for displaying Processor Group information
#
use warnings;
use strict;
use Errno;
#
# Constants
#
# It is possible that wnen trying to parse PG information, PG generation changes
# which will cause PG new method to fail with errno set to EAGAIN In this case
# we retry open up to RETRY_COUNT times pausing RETRY_DELAY seconds between each
# retry.
#
# When printing PGs we print them as a little tree with each PG shifted by
# LEVEL_OFFSET from each parent. For example:
#
# PG RELATIONSHIP CPUs
# 0 System 0-7
# 3 Socket 0 2 4 6
# 2 Cache 0 2 4 6
#
use constant {
VERSION => 1.1,
LEVEL_OFFSET => 1,
RETRY_COUNT => 4,
RETRY_DELAY => 0.25,
};
#
# Return codes
#
# 0 Successful completion.
#
# 1 An error occurred.
#
# 2 Invalid command-line options were specified.
#
use constant {
E_SUCCESS => 0,
E_ERROR => 1,
E_USAGE => 2,
};
# Set message locale
# Get script name for error messages
#
# Process options
#
my $do_cpulist; # -C - Show CPU IDs
my $do_cpus; # -c - Treat args as CPU IDs
my $do_physical; # -p - Show physical relationships
my $do_sharing_only; # -S - Only show sharing relationships
my $do_tree; # -T - Show ASCII tree
my $do_usage; # -h - Show usage
my $do_version; # -V - Show version
my $script_mode; # -I - Only show IDs
my @sharing_filter; # -r string,...
my @sharing_filter_neg; # -R string,...
# Exit code
# Parse options from the command line
"idlist|I" => \$script_mode,
"cpulist|C" => \$do_cpulist,
"physical|p" => \$do_physical,
"help|h|?" => \$do_usage,
"sharing|s" => \$do_sharing_only,
"relationship|r=s" => \@sharing_filter,
"norelationship|R=s" => \@sharing_filter_neg,
"tree|topology|T" => \$do_tree,
"version|V" => \$do_version,
"verbose+" => \$verbose,
"v+" => \$verbose,
# Print usage message when -h is given
if ($do_version) {
#
# Print version information and exit
#
exit(E_SUCCESS);
}
#
# Verify options compatibility
#
if ($script_mode && $do_cpulist) {
printf STDERR
gettext("%s: options -I and -C can not be used at the same time\n"),
$cmdname;
}
if (($script_mode || $do_cpulist) &&
($do_physical || $do_sharing_only ||
$do_tree)) {
printf STDERR
gettext("%s: options -C and -I can not be used with -p -s or -T\n"),
$cmdname;
}
if ($do_physical && $do_sharing_only) {
printf STDERR
}
if ($do_tree && $do_sharing_only) {
printf STDERR
gettext("%s: option -T can not be used with -s\n"),
$cmdname;
}
$do_tree = 1;
$do_physical = 1;
}
#
# Get PG information
#
-retry => RETRY_COUNT,
'-delay' => RETRY_DELAY);
if (!$p) {
printf STDERR
gettext("%s: can not obtain Processor Group information: $!\n"),
$cmdname;
exit(E_ERROR);
}
#
# Convert -[Rr] string1,string2,... into list (string1, string2, ...)
#
@sharing_filter = map { split /,/ } @sharing_filter;
@sharing_filter_neg = map { split /,/ } @sharing_filter_neg;
#
# Get list of all PGs in the system
#
my @all_pgs = $p->all_depth_first();
if (scalar(@all_pgs) == 0) {
printf STDERR
gettext("%s: this system does not have any Processor groups\n"),
$cmdname;
exit(E_ERROR);
}
#
# @pgs is the list of PGs we are going to work with after all the option
# processing
#
#
# get list of all CPUs in the system by looking at the root PG cpus
#
#
# If there are arguments in the command line, treat them as either PG IDs or as
# CPUs that should be converted to PG IDs.
# Arguments can be specified as x-y x,y,z and use special keyword 'all'
#
if (scalar @ARGV) {
#
# Convert 'all' in arguments to all CPUs or all PGs
#
my @args;
# Expand any x-y,z ranges
if ($do_cpus) {
# @bad_cpus is a list of invalid CPU IDs
if (scalar @bad_cpus) {
printf STDERR
gettext("%s: Invalid processor IDs %s\n"),
}
#
# List of PGs is the list of any PGs that contain specified CPUs
#
@pgs = grep {
} @all_pgs;
} else {
# @pgs is a list of valid CPUs in the arguments
# @bad_pgs is a list of invalid PG IDs
if (scalar @bad_pgs) {
printf STDERR
gettext("%s: Invalid PG IDs %s\n"),
}
}
}
#
# Now we have list of PGs to work with. Now apply filtering. First list only
# those matching -R
#
scalar @sharing_filter;
# Remove any that doesn't match -r
scalar @sharing_filter_neg;
# Do we have any PGs left?
if (scalar(@pgs) == 0) {
printf STDERR
gettext("%s: no processor groups matching command line arguments %s\n"),
$cmdname, "@ARGV";
exit(E_ERROR);
}
#
# Global list of PGs that should be excluded from the output - it is only used
# when tree mode is specified.
#
my @exclude_pgs;
if ($do_tree) {
@exclude_pgs = grep {
} @all_pgs;
#
# In tree mode add PGs that are in the lineage of given PGs
#
}
#
# -I is specified, print list of all PGs
#
if ($script_mode) {
if (scalar(@pgs)) {
print "@pgs\n";
} else {
print "none\n";
}
exit($rc);
}
#
# -C is specified, print list of all CPUs belonging to PGs
#
if ($do_cpulist) {
print "@cpu_list\n";
exit($rc);
}
# Mapping of relationships to list of PGs
my %pgs_by_relationship;
# Maximum length of all sharing names
my $max_sharename_len = length('RELATIONSHIP');
# Maximum length of PG ID
#
# For calculating proper offsets we need to know minimum and maximum level for
# all PGs
#
# Calculate maximum string length that should be used to represent PGs
if ($do_physical) {
}
}
if ($do_sharing_only) {
#
# -s - only print sharing relationships
# Get list of sharing relationships
if ($verbose) {
printf "%-${max_sharename_len}s %s\n",
'RELATIONSHIP', 'PGs';
foreach my $rel (@relationships) {
@pgs;
}
}
foreach my $rel (@relationships) {
printf "%-${max_sharename_len}s", $rel;
if ($verbose) {
print ' ', $pgs;
}
print "\n";
}
# we are done
exit($rc);
}
#
# Print PGs either in list form or tree form
#
if (!$do_tree) {
my $header;
$header = sprintf "%-${max_pg_len}s %-${max_sharename_len}s" .
" %s\n",
'PG', 'RELATIONSHIP', 'CPUs';
print $header;
} else {
#
# Construct a tree from PG hierarchy and prune any PGs that are
# specified with -R option
#
my $pg_tree = pg_make_tree($p);
# Find top-level PGs
my @top_level = grep {
} @pgs;
# Print each top-level node as ASCII tree
}
}
# We are done!
exit($rc);
######################################################################
# Internal functions
#
#
# pg_print(cookie, pg)
# print PG information in list mode
#
sub pg_print
{
my $p = shift;
my $pg = shift;
if ($do_physical) {
}
print " $cpus";
print "\n";
}
#
# pg_showcpus(cookie, pg)
# Print CPUs in the current PG
#
sub pg_showcpus
{
my $p = shift;
my $pg = shift;
return 0 unless $ncpus;
return (($ncpus == 1) ?
"CPU: $cpu_string":
"CPUs: $cpu_string");
}
#
# pg_print_node(cookie, pg)
# print PG as ASCII tree node
#
sub pg_print_node
{
my $p = shift;
my $pg = shift;
if ($do_physical) {
}
print "$pg ($sharing)";
print " $cpus";
print "\n";
}
#
# pg_print_tree(cookie, tree, pg, prefix, childprefix, npeers)
# print ASCII tree of PGs in the tree
# prefix should be used for the current node, childprefix for children nodes
# npeers is the number of peers of the current node
#
sub pg_print_tree
{
my $p = shift;
my $pg_tree = shift;
my $pg = shift;
return unless defined ($pg); # done!
my $prefix = shift;
my $childprefix = shift;
my $npeers = shift;
# Get list of my children
my $printprefix = "$childprefix";
pg_print_node ($p, $pg);
# Print the subtree with a new offset, starting from each child
map {
"$childprefix$new_prefix", --$nchildren)
} @children;
}
#
# list_match(arg, list)
# Return arg if argument matches any of the elements on the list
#
sub list_match
{
my $arg = shift;
}
#
# Make a version of PG parent-children relationships from cookie
#
sub pg_make_tree
{
my $p = shift;
my $pg_tree = ();
}
return ($pg_tree);
}
#
# pg_remove_from_tree(tree, pg)
# Prune PG from the tree
#
{
my $pg_tree = shift;
my $pg = shift;
return unless $node;
my $parent_node;
#
# Children have a new parent
#
#
# All children move to the parent (if there is one)
#
#
# Merge children from parent and @children list
#
#
# Remove myself from parent children
#
}
# Remove current node
}
#
# For a given list of PGs return the full lineage
#
sub pg_lineage
{
my $p = shift;
return unless scalar @_;
}
#
# Print usage information and exit with the return code specified
#
sub usage
{
my $rc = shift;
printf STDERR
gettext("Usage:\t%s [-T] [-p] [-v] [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"),
$cmdname;
printf STDERR
$cmdname;
exit($rc);
}