lgrpinfo.pl revision 7595fad9bab86562dbe8d6580c4b310bbad949b7
#
# 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 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
#
# lgrpinfo: display information about locality groups.
#
require 5.6.1;
use warnings;
use strict;
# Sun::Solaris::Kstat is used to extract per-lgroup load average.
#
# Amount of load contributed by a single thread. The value is exported by the
# kernel in the 'loadscale' variable of lgroup kstat, but in case it is missing
# we use the current default value as the best guess.
#
# Get script name
# Get liblgrp version
our $VERSION = "%I% (liblgrp version $version)";
# The $loads hash keeps per-lgroup load average.
our $loads = {};
########################################
# Main body
##
# Set message locale
# Parse command-line options
"c" => \$opt_c,
"C" => \$opt_C,
"e" => \$opt_e,
"G" => \$opt_G,
"h|?" => \$opt_h,
"l" => \$opt_l,
"L" => \$opt_L,
"I" => \$opt_I,
"m" => \$opt_m,
"r" => \$opt_r,
"t" => \$opt_t,
"T" => \$opt_T,
"u=s" => \$opt_u,
# Check for conflicting options
my $nfilters = 0;
if ($nfilters > 1) {
printf STDERR
gettext("%s: Options -C, -T and -P can not be used together\n"),
$cmdname;
usage(3);
}
printf STDERR
gettext("%s: Option -T can not be used with -I, -t\n"),
$cmdname;
usage(3);
}
printf STDERR
gettext("%s: Warning: with '-T' all lgroups on the command line "),
$cmdname;
}
$cmdname;
usage(3);
}
# Figure out what to do based on options
my $do_default = 1 unless
die(gettext("$cmdname: can not get lgroup information from the system\n"));
# Get list of all lgroups, the root and the list of intermediates
# Print everything if -a is specified or it is default without -T
# Print individual information if do_all or requested specific print
# Does the liblgrp(3LIB) has enough capabilities to support resource view?
if ($opt_r) {
printf STDERR
gettext("%s: sorry, your system does not support"),
$cmdname;
printf STDERR " lgrp_resources(3LGRP)\n";
}
$do_rsrc = 0;
}
# Get list of lgrps from arguments, expanding symbolic names like
# "root" and "leaves"
# Use all lgroups if none are specified on the command line
# Apply 'Parent' or 'Children' operations if requested
# Drop repeating elements and sort lgroups numerically.
# If both -L and -c are specified, just print list of CPUs.
@lgrp_list);
print "@cpus\n";
exit(0);
}
my $unit_str = "K";
# Convert units to canonical numeric and string formats.
if ($opt_u) {
if ($opt_u =~ /^b$/i) {
$units = 1;
$unit_str = "B";
} elsif ($opt_u =~ /^k$/i) {
$unit_str = "K";
} elsif ($opt_u =~ /^m$/i) {
$unit_str = "M";
} elsif ($opt_u =~ /^g$/i) {
$unit_str = "G";
} elsif ($opt_u =~ /^t$/i) {
$unit_str = "T";
} elsif ($opt_u =~ /^p$/i) {
$unit_str = "P";
} elsif ($opt_u =~ /^e$/i) {
$unit_str = "E";
} elsif (! ($opt_u =~ /^m$/i)) {
printf STDERR
gettext("%s: invalid unit '$opt_u', should be [b|k|m|g|t|p|e]"),
$cmdname;
$opt_u = 0;
}
}
# Collect load average data if requested.
# Get latency values for each lgroup.
my %self_latencies;
# If -T is specified, just print topology and return.
if ($opt_T) {
lgrp_prettyprint($l);
exit(0);
}
if (!scalar @lgrp_list) {
exit(2);
}
# Just print list of lgrps if doing just filtering
if ($do_something) {
# Walk through each requested lgrp and print whatever is requested.
if ($do_topo) {
# Get children of this lgrp.
gettext("Children: none") :
# Are there any parents for this lgrp?
"";
}
if ($do_cpu) {
}
if ($do_memory) {
}
if ($do_rsrc) {
}
# Print all the information about lgrp.
print "\n\t$children$parents" if $do_topo;
print "\n\t$rsrc" if $do_rsrc;
}
print "\n";
}
}
exit 0;
#
# usage(exit_status)
# print usage message and exit with the specified exit status.
#
sub usage
{
print STDERR " [-aceGlLmrt] [-u unit] [-C|-P] [lgrp] ...\n";
print STDERR " \t$cmdname -I [-c] [-G] [-C|-P] [lgrp] ...\n";
print STDERR " \t$cmdname -T [-aceGlLmr] [-u unit]\n";
print STDERR " \t$cmdname -h\n\n";
printf STDERR
gettext(" Display information about locality groups\n\n" .
"\t-a: Equivalent to \"%s\" without -T and to \"%s\" with -T\n"),
"-celLmrt", "-celLmr";
print STDERR
gettext("\t-c: Print CPU information\n"),
gettext("\t-C: Children of the specified lgroups\n"),
gettext("\t-e: Print lgroup load average\n"),
gettext("\t-h: Print this message and exit\n"),
gettext("\t-I: Print lgroup or CPU IDs only\n"),
gettext("\t-l: Print information about lgroup latencies\n"),
gettext("\t-G: Print OS view of lgroup hierarchy\n"),
gettext("\t-L: Print lgroup latency table\n"),
gettext("\t-m: Print memory information\n"),
gettext("\t-P: Parent(s) of the specified lgroups\n"),
gettext("\t-r: Print lgroup resources\n"),
gettext("\t-t: Print information about lgroup topology\n"),
gettext("\t-T: Print the hierarchy tree\n"),
gettext("\t-u unit: Specify memory unit (b,k,m,g,t,p,e)\n\n\n");
print STDERR
gettext(" The lgrp may be specified as an lgroup ID,"),
gettext(" \"root\", \"all\",\n"),
gettext(" \"intermediate\" or \"leaves\".\n\n");
printf STDERR
gettext(" The default set of options is \"%s\"\n\n"),
"-celmrt all";
print STDERR
gettext(" Without any options print topology, CPU and memory " .
"information about each\n" .
" lgroup. If any lgroup IDs are specified on the " .
"command line only print\n" .
" information about the specified lgroup.\n\n");
exit(shift);
}
# Return the input list with duplicates removed.
sub uniq
{
my %seen;
return (grep { ++$seen{$_} == 1 } @_);
}
#
# 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(@_));
}
# Round values
sub round
{
my $val = shift;
return (int($val + 0.5));
}
#
# Expand list of lgrps.
# Translate 'root' to the root lgrp id
# Translate 'all' to the list of all lgrps
# Translate 'leaves' to the list of all lgrps'
# Translate 'intermediate' to the list of intermediates.
#
sub lgrp_expand
{
my $lobj = shift;
my %seen;
my @result;
# create a hash element for every element in @lgrps
foreach my $lgrp (@_) {
push(@result, @intermediates),
next if $lgrp =~ m/^intermediate$/i;
$cmdname;
}
return @result;
}
#
# lgrp_tree(class, node)
#
# Build the tree of the lgroup hierarchy starting with the specified node or
# root if no initial node is specified. Calls itself recursively specifying each
# of the children as a starting node. Builds a reference to the list with the
# node in the end and each element being a subtree.
#
sub lgrp_tree
{
my $c = shift;
# Call itself for each of the children and combine results in a list.
}
#
# lgrp_pp(tree, prefix, childprefix, npeers)
#
# pretty-print the hierarchy tree.
# Input Arguments:
# Reference to the tree
# Prefix for me to use
# Prefix for my children to use
# Number of peers left
#
sub lgrp_pp
{
my $tree = shift;
my $myprefix = shift;
my $childprefix = shift;
my $npeers = shift;
my $printprefix = "$childprefix";
return unless defined ($el);
# Pretty-print the subtree with a new offset.
map {
} @$tree;
}
# Pretty print the whole tree
sub lgrp_prettyprint
{
my $c = shift;
}
sub lgrp_print
{
my $lgrp = shift;
my $prefix = shift;
print "$lgrp";
}
}
}
# Print all the information about lgrp.
print "\n$prefix$cpus" if $cpus;
print "\n$prefix$memstr" if $memstr;
print "\n$prefix$rsrc" if $rsrc;
# Print latency information if requested.
print "\n${prefix}";
}
print "\n";
}
# What CPUs are in this lgrp?
sub lgrp_showcpus
{
my $lgrp = shift;
my $hier = shift;
return 0 unless $ncpus;
# Sort CPU list if there is something to sort.
return (($ncpus == 1) ?
}
# How much memory does this lgrp contain?
sub lgrp_showmemory
{
my $lgrp = shift;
my $hier = shift;
return (0) unless $memory;
$memory_r);
$usedmem);
$freemem_r);
return ($memstr);
}
# Get string containing lgroup resources
{
my $lgrp = shift;
# What resources does this lgroup contain?
return ($rsrc);
}
#
# Consolidate consequtive ids as start-end
# Input: list of ids
# Output: string with space-sepated cpu values with ranges
# collapsed as x-y
#
sub lgrp_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);
}
# Print latency information if requested and the system has several lgroups.
{
return unless scalar @lgrps;
# Find maximum lgroup
# Field width for lgroup - the width of the largest lgroup and 1 space
# Field width for latency. Get the maximum latency and add 1 space.
# Make sure that width is enough to print lgroup itself.
# Print table header
print gettext("\nLgroup latencies:\n");
# Print horizontal line
map { printf("%${width}d", $_) } @$lgrps1;
print "\n";
# Print the latency table
printf "%-${lgwidth}d|", $l1;
if (!defined ($latency)) {
printf "%${width}s", "-";
} else {
printf "%${width}d", $latency;
}
}
print "\n";
}
# Print table footer
print "\n";
}
#
# Convert a number to a string representation
# The number is scaled down until it is small enough to be in a good
# human readable format i.e. in the range 0 thru 1023.
# If it's smaller than 10 there's room enough to provide one decimal place.
#
{
my $number = shift;
my $uom = shift(@measurement);
my $result;
# Get size in K.
$uom = shift(@measurement);
}
# check if we should output a decimal place after the point
} else {
}
return ("$result$uom");
}
#
# Convert memory size to the string representation
#
sub memory_to_string
{
my $number = shift;
# Zero memory - just print 0
return ("0$unit_str") unless $number;
#
# Return memory size scaled to human-readable form unless -u is
# specified.
#
my $result;
if ($scaled < 0.1) {
} elsif ($scaled < 10) {
} else {
}
return ("$result$unit_str");
}
#
# Read load averages from lgrp kstats Return hash reference indexed by lgroup ID
# for each lgroup which has load information.
#
sub get_lav
{
my $load = {};
warn(gettext("$cmdname: kstat_open() failed: %!\n")),
return $load;
warn(gettext("$cmdname: can not read lgrp kstat\n)")),
return $load;
# Collect load for each lgroup
foreach my $i (keys %$lgrp_kstats) {
next unless $lgrp_kstats->{$i}->{"lgrp$i"};
# Skip this lgroup if can't find its load average
next unless defined $lav;
}
return $load;
}