1c6402783Sakolb#! /usr/perl5/bin/perl 2c6402783Sakolb# 3c6402783Sakolb# CDDL HEADER START 4c6402783Sakolb# 5c6402783Sakolb# The contents of this file are subject to the terms of the 6c6402783Sakolb# Common Development and Distribution License (the "License"). 7c6402783Sakolb# You may not use this file except in compliance with the License. 8c6402783Sakolb# 9c6402783Sakolb# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10c6402783Sakolb# or http://www.opensolaris.org/os/licensing. 11c6402783Sakolb# See the License for the specific language governing permissions 12c6402783Sakolb# and limitations under the License. 13c6402783Sakolb# 14c6402783Sakolb# When distributing Covered Code, include this CDDL HEADER in each 15c6402783Sakolb# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16c6402783Sakolb# If applicable, add the following below this CDDL HEADER, with the 17c6402783Sakolb# fields enclosed by brackets "[]" replaced with your own identifying 18c6402783Sakolb# information: Portions Copyright [yyyy] [name of copyright owner] 19c6402783Sakolb# 20c6402783Sakolb# CDDL HEADER END 21c6402783Sakolb# 22c6402783Sakolb 23c6402783Sakolb# 247595fad9Sakolb# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25c6402783Sakolb# Use is subject to license terms. 26c6402783Sakolb# 27c6402783Sakolb 28c6402783Sakolb# 29c6402783Sakolb# lgrpinfo: display information about locality groups. 30c6402783Sakolb# 31c6402783Sakolb 32*df0345f7SJohn Sonnenscheinrequire 5.8.4; 33c6402783Sakolbuse warnings; 34c6402783Sakolbuse strict; 35c6402783Sakolbuse Getopt::Long qw(:config no_ignore_case bundling auto_version); 36c6402783Sakolbuse File::Basename; 37c6402783Sakolb# Sun::Solaris::Kstat is used to extract per-lgroup load average. 38c6402783Sakolbuse Sun::Solaris::Kstat; 39c6402783Sakolbuse POSIX qw(locale_h); 40c6402783Sakolbuse Sun::Solaris::Utils qw(textdomain gettext); 41c6402783Sakolbuse Sun::Solaris::Lgrp ':CONSTANTS'; 42c6402783Sakolb 43c6402783Sakolbuse constant KB => 1024; 44c6402783Sakolb 45c6402783Sakolb# 46c6402783Sakolb# Amount of load contributed by a single thread. The value is exported by the 47c6402783Sakolb# kernel in the 'loadscale' variable of lgroup kstat, but in case it is missing 48c6402783Sakolb# we use the current default value as the best guess. 49c6402783Sakolb# 50c6402783Sakolbuse constant LGRP_LOADAVG_THREAD_MAX => 65516; 51c6402783Sakolb 52c6402783Sakolb# Get script name 53c6402783Sakolbour $cmdname = basename($0, ".pl"); 54c6402783Sakolb 55c6402783Sakolb# Get liblgrp version 56c6402783Sakolbmy $version = Sun::Solaris::Lgrp::lgrp_version(); 57c6402783Sakolb 58c6402783Sakolbour $VERSION = "%I% (liblgrp version $version)"; 59c6402783Sakolb 60c6402783Sakolb# The $loads hash keeps per-lgroup load average. 61c6402783Sakolbour $loads = {}; 62c6402783Sakolb 63c6402783Sakolb######################################## 64c6402783Sakolb# Main body 65c6402783Sakolb## 66c6402783Sakolb 67c6402783Sakolb# Set message locale 68c6402783Sakolbsetlocale(LC_ALL, ""); 69c6402783Sakolbtextdomain(TEXT_DOMAIN); 70c6402783Sakolb 71c6402783Sakolb# Parse command-line options 72c6402783Sakolbour($opt_a, $opt_l, $opt_m, $opt_c, $opt_C, $opt_e, $opt_t, $opt_h, $opt_u, 73c6402783Sakolb $opt_r, $opt_L, $opt_P, $opt_I, $opt_T, $opt_G); 74c6402783Sakolb 75c6402783SakolbGetOptions("a" => \$opt_a, 76c6402783Sakolb "c" => \$opt_c, 77c6402783Sakolb "C" => \$opt_C, 78c6402783Sakolb "e" => \$opt_e, 79c6402783Sakolb "G" => \$opt_G, 80c6402783Sakolb "h|?" => \$opt_h, 81c6402783Sakolb "l" => \$opt_l, 82c6402783Sakolb "L" => \$opt_L, 83c6402783Sakolb "I" => \$opt_I, 84c6402783Sakolb "m" => \$opt_m, 85c6402783Sakolb "r" => \$opt_r, 86c6402783Sakolb "t" => \$opt_t, 87c6402783Sakolb "T" => \$opt_T, 88c6402783Sakolb "u=s" => \$opt_u, 89c6402783Sakolb "P" => \$opt_P) || usage(3); 90c6402783Sakolb 91c6402783Sakolbusage(0) if $opt_h; 92c6402783Sakolb 93c6402783Sakolb# Check for conflicting options 94c6402783Sakolbmy $nfilters = 0; 95c6402783Sakolb$nfilters++ if $opt_C; 96c6402783Sakolb$nfilters++ if $opt_P; 97c6402783Sakolb$nfilters++ if $opt_T; 98c6402783Sakolb 99c6402783Sakolbif ($nfilters > 1) { 100c6402783Sakolb printf STDERR 101c6402783Sakolb gettext("%s: Options -C, -T and -P can not be used together\n"), 102c6402783Sakolb $cmdname; 103c6402783Sakolb usage(3); 104c6402783Sakolb} 105c6402783Sakolb 106c6402783Sakolbif ($opt_T && ($opt_I || $opt_t)) { 107c6402783Sakolb printf STDERR 108c6402783Sakolb gettext("%s: Option -T can not be used with -I, -t\n"), 109c6402783Sakolb $cmdname; 110c6402783Sakolb usage(3); 111c6402783Sakolb} 112c6402783Sakolb 113c6402783Sakolbif ($opt_T && scalar @ARGV) { 114c6402783Sakolb printf STDERR 115c6402783Sakolb gettext("%s: Warning: with '-T' all lgroups on the command line "), 116c6402783Sakolb $cmdname; 117c6402783Sakolb printf STDERR gettext("are ignored\n\n"); 118c6402783Sakolb} 119c6402783Sakolb 120c6402783Sakolbif ($opt_L && $opt_I) { 121c6402783Sakolb printf STDERR gettext("%s: Option -I can not be used with -L\n"), 122c6402783Sakolb $cmdname; 123c6402783Sakolb usage(3); 124c6402783Sakolb} 125c6402783Sakolb 126c6402783Sakolb# Figure out what to do based on options 127c6402783Sakolbmy $do_default = 1 unless 128c6402783Sakolb $opt_a || $opt_l || $opt_m || $opt_c || $opt_e || $opt_t || $opt_r; 129c6402783Sakolb 130c6402783Sakolb 131c6402783Sakolbmy $l = Sun::Solaris::Lgrp->new($opt_G ? LGRP_VIEW_OS : LGRP_VIEW_CALLER) or 132c6402783Sakolb die(gettext("$cmdname: can not get lgroup information from the system\n")); 133c6402783Sakolb 134c6402783Sakolb 135c6402783Sakolb# Get list of all lgroups, the root and the list of intermediates 136c6402783Sakolbmy @lgrps = nsort($l->lgrps); 137c6402783Sakolbmy $root = $l->root; 138c6402783Sakolbmy @intermediates = grep { $_ != $root && !$l->isleaf($_) } @lgrps; 139c6402783Sakolbmy $is_uma = (scalar @lgrps == 1); 140c6402783Sakolb 141c6402783Sakolb# Print everything if -a is specified or it is default without -T 142c6402783Sakolbmy $do_all = 1 if $opt_a || ($do_default && !($opt_T || $opt_L)); 143c6402783Sakolb 144c6402783Sakolb# Print individual information if do_all or requested specific print 145c6402783Sakolbmy $do_lat = 1 if $do_all || $opt_l; 146c6402783Sakolbmy $do_memory = 1 if $do_all || $opt_m; 147c6402783Sakolbmy $do_cpu = 1 if $do_all || $opt_c; 148c6402783Sakolbmy $do_topo = 1 if $do_all || $opt_t; 149c6402783Sakolbmy $do_rsrc = 1 if $do_all || $opt_r; 150c6402783Sakolbmy $do_load = 1 if $do_all || $opt_e; 151c6402783Sakolbmy $do_table = 1 if $opt_a || $opt_L; 152c6402783Sakolbmy $do_something = ($do_lat || $do_memory || $do_cpu || $do_topo || 153c6402783Sakolb $do_rsrc || $do_load); 154c6402783Sakolb 155c6402783Sakolb# Does the liblgrp(3LIB) has enough capabilities to support resource view? 156c6402783Sakolbif ($do_rsrc && LGRP_VER_CURRENT == 1) { 157c6402783Sakolb if ($opt_r) { 158c6402783Sakolb printf STDERR 159c6402783Sakolb gettext("%s: sorry, your system does not support"), 160c6402783Sakolb $cmdname; 161c6402783Sakolb printf STDERR " lgrp_resources(3LGRP)\n"; 162c6402783Sakolb } 163c6402783Sakolb $do_rsrc = 0; 164c6402783Sakolb} 165c6402783Sakolb 166c6402783Sakolb# Get list of lgrps from arguments, expanding symbolic names like 167c6402783Sakolb# "root" and "leaves" 168c6402783Sakolb# Use all lgroups if none are specified on the command line 169c6402783Sakolbmy @lgrp_list = (scalar (@ARGV) && !$opt_T) ? lgrp_expand($l, @ARGV) : @lgrps; 170c6402783Sakolb 171c6402783Sakolb# Apply 'Parent' or 'Children' operations if requested 172c6402783Sakolb@lgrp_list = map { $l->parents($_) } @lgrp_list if $opt_P; 173c6402783Sakolb@lgrp_list = map { $l->children($_) } @lgrp_list if $opt_C; 174c6402783Sakolb 175c6402783Sakolb# Drop repeating elements and sort lgroups numerically. 176c6402783Sakolb@lgrp_list = uniqsort(@lgrp_list); 177c6402783Sakolb 178c6402783Sakolb# If both -L and -c are specified, just print list of CPUs. 179c6402783Sakolbif ($opt_c && $opt_I) { 180c6402783Sakolb my @cpus = uniqsort(map { $l->cpus($_, LGRP_CONTENT_HIERARCHY) } 181c6402783Sakolb @lgrp_list); 182c6402783Sakolb print "@cpus\n"; 183c6402783Sakolb exit(0); 184c6402783Sakolb} 185c6402783Sakolb 186c6402783Sakolbmy $unit_str = "K"; 187c6402783Sakolbmy $units = KB; 188c6402783Sakolb 189c6402783Sakolb# Convert units to canonical numeric and string formats. 190c6402783Sakolbif ($opt_u) { 191c6402783Sakolb if ($opt_u =~ /^b$/i) { 192c6402783Sakolb $units = 1; 193c6402783Sakolb $unit_str = "B"; 194c6402783Sakolb } elsif ($opt_u =~ /^k$/i) { 195c6402783Sakolb $units = KB; 196c6402783Sakolb $unit_str = "K"; 197c6402783Sakolb } elsif ($opt_u =~ /^m$/i) { 198c6402783Sakolb $units = KB * KB; 199c6402783Sakolb $unit_str = "M"; 200c6402783Sakolb } elsif ($opt_u =~ /^g$/i) { 201c6402783Sakolb $units = KB * KB * KB; 202c6402783Sakolb $unit_str = "G"; 203c6402783Sakolb } elsif ($opt_u =~ /^t$/i) { 204c6402783Sakolb $units = KB * KB * KB * KB; 205c6402783Sakolb $unit_str = "T"; 206c6402783Sakolb } elsif ($opt_u =~ /^p$/i) { 207c6402783Sakolb $units = KB * KB * KB * KB * KB; 208c6402783Sakolb $unit_str = "P"; 209c6402783Sakolb } elsif ($opt_u =~ /^e$/i) { 210c6402783Sakolb $units = KB * KB * KB * KB * KB * KB; 211c6402783Sakolb $unit_str = "E"; 212c6402783Sakolb } elsif (! ($opt_u =~ /^m$/i)) { 213c6402783Sakolb printf STDERR 214c6402783Sakolb gettext("%s: invalid unit '$opt_u', should be [b|k|m|g|t|p|e]"), 215c6402783Sakolb $cmdname; 216c6402783Sakolb printf STDERR gettext(", using the default.\n\n"); 217c6402783Sakolb $opt_u = 0; 218c6402783Sakolb } 219c6402783Sakolb} 220c6402783Sakolb 221c6402783Sakolb# Collect load average data if requested. 222c6402783Sakolb$loads = get_lav() if $do_load; 223c6402783Sakolb 224c6402783Sakolb# Get latency values for each lgroup. 225c6402783Sakolbmy %self_latencies; 226c6402783Sakolbmap { $self_latencies{$_} = $l->latency($_, $_) } @lgrps; 227c6402783Sakolb 228c6402783Sakolb# If -T is specified, just print topology and return. 229c6402783Sakolbif ($opt_T) { 230c6402783Sakolb lgrp_prettyprint($l); 231c6402783Sakolb print_latency_table(\@lgrps, \@lgrps) if $do_table; 232c6402783Sakolb exit(0); 233c6402783Sakolb} 234c6402783Sakolb 235c6402783Sakolbif (!scalar @lgrp_list) { 236c6402783Sakolb printf STDERR gettext("%s: No matching lgroups found!\n"), $cmdname; 237c6402783Sakolb exit(2); 238c6402783Sakolb} 239c6402783Sakolb 240c6402783Sakolb# Just print list of lgrps if doing just filtering 241c6402783Sakolb(print "@lgrp_list\n"), exit 0 if $opt_I; 242c6402783Sakolb 243c6402783Sakolbif ($do_something) { 244c6402783Sakolb # Walk through each requested lgrp and print whatever is requested. 245c6402783Sakolb foreach my $lgrp (@lgrp_list) { 246c6402783Sakolb my $is_leaf = $l->isleaf($lgrp); 247c6402783Sakolb my ($children, $parents, $cpus, $memstr, $rsrc); 248c6402783Sakolb 249c6402783Sakolb my $prefix = ($lgrp == $root) ? 250c6402783Sakolb "root": $is_leaf ? gettext("leaf") : gettext("intermediate"); 251c6402783Sakolb printf gettext("lgroup %d (%s):"), $lgrp, $prefix; 252c6402783Sakolb 253c6402783Sakolb if ($do_topo) { 254c6402783Sakolb # Get children of this lgrp. 255c6402783Sakolb my @children = $l->children($lgrp); 256c6402783Sakolb $children = $is_leaf ? 257c6402783Sakolb gettext("Children: none") : 258c6402783Sakolb gettext("Children: ") . lgrp_collapse(@children); 259c6402783Sakolb # Are there any parents for this lgrp? 260c6402783Sakolb my @parents = $l->parents($lgrp); 261c6402783Sakolb $parents = @parents ? 262c6402783Sakolb gettext(", Parent: ") . "@parents" : 263c6402783Sakolb ""; 264c6402783Sakolb } 265c6402783Sakolb 266c6402783Sakolb if ($do_cpu) { 267c6402783Sakolb $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY); 268c6402783Sakolb } 269c6402783Sakolb if ($do_memory) { 270c6402783Sakolb $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY); 271c6402783Sakolb } 272c6402783Sakolb if ($do_rsrc) { 273c6402783Sakolb $rsrc = lgrp_showresources($lgrp); 274c6402783Sakolb } 275c6402783Sakolb 276c6402783Sakolb # Print all the information about lgrp. 277c6402783Sakolb print "\n\t$children$parents" if $do_topo; 278c6402783Sakolb print "\n\t$cpus" if $do_cpu && $cpus; 279c6402783Sakolb print "\n\t$memstr" if $do_memory && $memstr; 280c6402783Sakolb print "\n\t$rsrc" if $do_rsrc; 281c6402783Sakolb print "\n\t$loads->{$lgrp}" if defined ($loads->{$lgrp}); 282c6402783Sakolb if ($do_lat && defined($self_latencies{$lgrp})) { 283c6402783Sakolb printf gettext("\n\tLatency: %d"), $self_latencies{$lgrp}; 284c6402783Sakolb } 285c6402783Sakolb print "\n"; 286c6402783Sakolb } 287c6402783Sakolb} 288c6402783Sakolb 289c6402783Sakolbprint_latency_table(\@lgrps, \@lgrp_list) if $do_table; 290c6402783Sakolb 291c6402783Sakolbexit 0; 292c6402783Sakolb 293c6402783Sakolb# 294c6402783Sakolb# usage(exit_status) 295c6402783Sakolb# print usage message and exit with the specified exit status. 296c6402783Sakolb# 297c6402783Sakolbsub usage 298c6402783Sakolb{ 299c6402783Sakolb printf STDERR gettext("Usage:\t%s"), $cmdname; 300c6402783Sakolb print STDERR " [-aceGlLmrt] [-u unit] [-C|-P] [lgrp] ...\n"; 301c6402783Sakolb print STDERR " \t$cmdname -I [-c] [-G] [-C|-P] [lgrp] ...\n"; 302c6402783Sakolb print STDERR " \t$cmdname -T [-aceGlLmr] [-u unit]\n"; 303c6402783Sakolb print STDERR " \t$cmdname -h\n\n"; 304c6402783Sakolb 305c6402783Sakolb printf STDERR 306c6402783Sakolb gettext(" Display information about locality groups\n\n" . 307c6402783Sakolb "\t-a: Equivalent to \"%s\" without -T and to \"%s\" with -T\n"), 308c6402783Sakolb "-celLmrt", "-celLmr"; 309c6402783Sakolb 310c6402783Sakolb print STDERR 311c6402783Sakolb gettext("\t-c: Print CPU information\n"), 312c6402783Sakolb gettext("\t-C: Children of the specified lgroups\n"), 313c6402783Sakolb gettext("\t-e: Print lgroup load average\n"), 314c6402783Sakolb gettext("\t-h: Print this message and exit\n"), 315c6402783Sakolb gettext("\t-I: Print lgroup or CPU IDs only\n"), 316c6402783Sakolb gettext("\t-l: Print information about lgroup latencies\n"), 317c6402783Sakolb gettext("\t-G: Print OS view of lgroup hierarchy\n"), 318c6402783Sakolb gettext("\t-L: Print lgroup latency table\n"), 319c6402783Sakolb gettext("\t-m: Print memory information\n"), 320c6402783Sakolb gettext("\t-P: Parent(s) of the specified lgroups\n"), 321c6402783Sakolb gettext("\t-r: Print lgroup resources\n"), 322c6402783Sakolb gettext("\t-t: Print information about lgroup topology\n"), 323c6402783Sakolb gettext("\t-T: Print the hierarchy tree\n"), 324c6402783Sakolb gettext("\t-u unit: Specify memory unit (b,k,m,g,t,p,e)\n\n\n"); 325c6402783Sakolb 326c6402783Sakolb print STDERR 327c6402783Sakolb gettext(" The lgrp may be specified as an lgroup ID,"), 328c6402783Sakolb gettext(" \"root\", \"all\",\n"), 329c6402783Sakolb gettext(" \"intermediate\" or \"leaves\".\n\n"); 330c6402783Sakolb 331c6402783Sakolb printf STDERR 332c6402783Sakolb gettext(" The default set of options is \"%s\"\n\n"), 333c6402783Sakolb "-celmrt all"; 334c6402783Sakolb 335c6402783Sakolb print STDERR 336c6402783Sakolb gettext(" Without any options print topology, CPU and memory " . 337c6402783Sakolb "information about each\n" . 338c6402783Sakolb " lgroup. If any lgroup IDs are specified on the " . 339c6402783Sakolb "command line only print\n" . 340c6402783Sakolb " information about the specified lgroup.\n\n"); 341c6402783Sakolb 342c6402783Sakolb exit(shift); 343c6402783Sakolb} 344c6402783Sakolb 345c6402783Sakolb# Return the input list with duplicates removed. 346c6402783Sakolbsub uniq 347c6402783Sakolb{ 348c6402783Sakolb my %seen; 349c6402783Sakolb return (grep { ++$seen{$_} == 1 } @_); 350c6402783Sakolb} 351c6402783Sakolb 352c6402783Sakolb# 353c6402783Sakolb# Sort the list numerically 354c6402783Sakolb# Should be called in list context 355c6402783Sakolb# 356c6402783Sakolbsub nsort 357c6402783Sakolb{ 358c6402783Sakolb return (sort { $a <=> $b } @_); 359c6402783Sakolb} 360c6402783Sakolb 361c6402783Sakolb# 362c6402783Sakolb# Sort list numerically and remove duplicates 363c6402783Sakolb# Should be called in list context 364c6402783Sakolb# 365c6402783Sakolbsub uniqsort 366c6402783Sakolb{ 367c6402783Sakolb return (sort { $a <=> $b } uniq(@_)); 368c6402783Sakolb} 369c6402783Sakolb 370c6402783Sakolb# Round values 371c6402783Sakolbsub round 372c6402783Sakolb{ 373c6402783Sakolb my $val = shift; 374c6402783Sakolb 375c6402783Sakolb return (int($val + 0.5)); 376c6402783Sakolb} 377c6402783Sakolb 378c6402783Sakolb# 379c6402783Sakolb# Expand list of lgrps. 380c6402783Sakolb# Translate 'root' to the root lgrp id 381c6402783Sakolb# Translate 'all' to the list of all lgrps 382c6402783Sakolb# Translate 'leaves' to the list of all lgrps' 383c6402783Sakolb# Translate 'intermediate' to the list of intermediates. 384c6402783Sakolb# 385c6402783Sakolbsub lgrp_expand 386c6402783Sakolb{ 387c6402783Sakolb my $lobj = shift; 388c6402783Sakolb my %seen; 389c6402783Sakolb my @result; 390c6402783Sakolb 391c6402783Sakolb # create a hash element for every element in @lgrps 392c6402783Sakolb map { $seen{$_}++ } @lgrps; 393c6402783Sakolb 394c6402783Sakolb foreach my $lgrp (@_) { 395c6402783Sakolb push(@result, $lobj->root), next if $lgrp =~ m/^root$/i; 396c6402783Sakolb push(@result, @lgrps), next if $lgrp =~ m/^all$/i; 397c6402783Sakolb push(@result, $lobj->leaves), next if $lgrp =~ m/^leaves$/i; 398c6402783Sakolb push(@result, @intermediates), 399c6402783Sakolb next if $lgrp =~ m/^intermediate$/i; 400c6402783Sakolb push(@result, $lgrp), 401c6402783Sakolb next if $lgrp =~ m/^\d+$/ && $seen{$lgrp}; 402c6402783Sakolb printf STDERR gettext("%s: skipping invalid lgrp $lgrp\n"), 403c6402783Sakolb $cmdname; 404c6402783Sakolb } 405c6402783Sakolb 406c6402783Sakolb return @result; 407c6402783Sakolb} 408c6402783Sakolb 409c6402783Sakolb# 410c6402783Sakolb# lgrp_tree(class, node) 411c6402783Sakolb# 412c6402783Sakolb# Build the tree of the lgroup hierarchy starting with the specified node or 413c6402783Sakolb# root if no initial node is specified. Calls itself recursively specifying each 414c6402783Sakolb# of the children as a starting node. Builds a reference to the list with the 415c6402783Sakolb# node in the end and each element being a subtree. 416c6402783Sakolb# 417c6402783Sakolbsub lgrp_tree 418c6402783Sakolb{ 419c6402783Sakolb my $c = shift; 420c6402783Sakolb my $lgrp = shift || $c->root; 421c6402783Sakolb 422c6402783Sakolb # Call itself for each of the children and combine results in a list. 423c6402783Sakolb [ (map { lgrp_tree($c, $_) } $c->children($lgrp)), $lgrp ]; 424c6402783Sakolb} 425c6402783Sakolb 426c6402783Sakolb# 427c6402783Sakolb# lgrp_pp(tree, prefix, childprefix, npeers) 428c6402783Sakolb# 429c6402783Sakolb# pretty-print the hierarchy tree. 430c6402783Sakolb# Input Arguments: 431c6402783Sakolb# Reference to the tree 432c6402783Sakolb# Prefix for me to use 433c6402783Sakolb# Prefix for my children to use 434c6402783Sakolb# Number of peers left 435c6402783Sakolb# 436c6402783Sakolbsub lgrp_pp 437c6402783Sakolb{ 438c6402783Sakolb my $tree = shift; 439c6402783Sakolb my $myprefix = shift; 440c6402783Sakolb my $childprefix = shift; 441c6402783Sakolb my $npeers = shift; 442c6402783Sakolb my $el = pop @$tree; 443c6402783Sakolb my $nchildren = scalar @$tree; 444c6402783Sakolb my $printprefix = "$childprefix"; 445c6402783Sakolb my $printpostfix = $npeers ? "| " : " "; 446c6402783Sakolb 447c6402783Sakolb return unless defined ($el); 448c6402783Sakolb 449c6402783Sakolb my $bar = $npeers ? "|" : "`"; 450c6402783Sakolb print $childprefix ? $childprefix : ""; 451c6402783Sakolb print $myprefix ? "$bar" . "-- " : ""; 452c6402783Sakolb lgrp_print($el, "$printprefix$printpostfix"); 453c6402783Sakolb 454c6402783Sakolb my $new_prefix = $npeers ? $myprefix : " "; 455c6402783Sakolb 456c6402783Sakolb # Pretty-print the subtree with a new offset. 457c6402783Sakolb map { 458c6402783Sakolb lgrp_pp($_, "| ", "$childprefix$new_prefix", --$nchildren) 459c6402783Sakolb } @$tree; 460c6402783Sakolb} 461c6402783Sakolb 462c6402783Sakolb# Pretty print the whole tree 463c6402783Sakolbsub lgrp_prettyprint 464c6402783Sakolb{ 465c6402783Sakolb my $c = shift; 466c6402783Sakolb my $tree = lgrp_tree $c; 467c6402783Sakolb lgrp_pp($tree, '', '', scalar $tree - 1); 468c6402783Sakolb} 469c6402783Sakolb 470c6402783Sakolbsub lgrp_print 471c6402783Sakolb{ 472c6402783Sakolb my $lgrp = shift; 473c6402783Sakolb my $prefix = shift; 474c6402783Sakolb my ($cpus, $memstr, $rsrc); 475c6402783Sakolb my $is_interm = ($lgrp != $root && !$l->isleaf($lgrp)); 476c6402783Sakolb my $not_root = $is_uma || $lgrp != $root; 477c6402783Sakolb 478c6402783Sakolb print "$lgrp"; 479c6402783Sakolb 480c6402783Sakolb if ($do_cpu && $not_root) { 481c6402783Sakolb $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY); 482c6402783Sakolb } 483c6402783Sakolb if ($do_memory && $not_root) { 484c6402783Sakolb $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY); 485c6402783Sakolb } 486c6402783Sakolb if ($do_rsrc && ($is_uma || $is_interm)) { 487c6402783Sakolb $rsrc = lgrp_showresources($lgrp) if $do_rsrc; 488c6402783Sakolb } 489c6402783Sakolb 490c6402783Sakolb # Print all the information about lgrp. 491c6402783Sakolb 492c6402783Sakolb print "\n$prefix$cpus" if $cpus; 493c6402783Sakolb print "\n$prefix$memstr" if $memstr; 494c6402783Sakolb print "\n$prefix$rsrc" if $rsrc; 495c6402783Sakolb print "\n$prefix$loads->{$lgrp}" if defined ($loads->{$lgrp}); 496c6402783Sakolb 497c6402783Sakolb # Print latency information if requested. 498c6402783Sakolb if ($do_lat && $lgrp != $root && defined($self_latencies{$lgrp})) { 499c6402783Sakolb print "\n${prefix}"; 500c6402783Sakolb printf gettext("Latency: %d"), $self_latencies{$lgrp}; 501c6402783Sakolb } 502c6402783Sakolb print "\n"; 503c6402783Sakolb} 504c6402783Sakolb 505c6402783Sakolb# What CPUs are in this lgrp? 506c6402783Sakolbsub lgrp_showcpus 507c6402783Sakolb{ 508c6402783Sakolb my $lgrp = shift; 509c6402783Sakolb my $hier = shift; 510c6402783Sakolb 511c6402783Sakolb my @cpus = $l->cpus($lgrp, $hier); 512c6402783Sakolb my $ncpus = @cpus; 513c6402783Sakolb return 0 unless $ncpus; 514c6402783Sakolb # Sort CPU list if there is something to sort. 515c6402783Sakolb @cpus = nsort(@cpus) if ($ncpus > 1); 516c6402783Sakolb my $cpu_string = lgrp_collapse(@cpus); 517c6402783Sakolb return (($ncpus == 1) ? 518c6402783Sakolb gettext("CPU: ") . $cpu_string: 519c6402783Sakolb gettext("CPUs: ") . $cpu_string); 520c6402783Sakolb} 521c6402783Sakolb 522c6402783Sakolb# How much memory does this lgrp contain? 523c6402783Sakolbsub lgrp_showmemory 524c6402783Sakolb{ 525c6402783Sakolb my $lgrp = shift; 526c6402783Sakolb my $hier = shift; 527c6402783Sakolb 528c6402783Sakolb my $memory = $l->mem_size($lgrp, LGRP_MEM_SZ_INSTALLED, $hier); 529c6402783Sakolb return (0) unless $memory; 530c6402783Sakolb my $freemem = $l->mem_size($lgrp, LGRP_MEM_SZ_FREE, $hier) || 0; 531c6402783Sakolb 532c6402783Sakolb my $memory_r = memory_to_string($memory); 533c6402783Sakolb my $freemem_r = memory_to_string($freemem); 534c6402783Sakolb my $usedmem = memory_to_string($memory - $freemem); 535c6402783Sakolb 536c6402783Sakolb my $memstr = sprintf(gettext("Memory: installed %s"), 537c6402783Sakolb $memory_r); 538c6402783Sakolb $memstr = $memstr . sprintf(gettext(", allocated %s"), 539c6402783Sakolb $usedmem); 540c6402783Sakolb $memstr = $memstr . sprintf(gettext(", free %s"), 541c6402783Sakolb $freemem_r); 542c6402783Sakolb return ($memstr); 543c6402783Sakolb} 544c6402783Sakolb 545c6402783Sakolb# Get string containing lgroup resources 546c6402783Sakolbsub lgrp_showresources 547c6402783Sakolb{ 548c6402783Sakolb my $lgrp = shift; 549c6402783Sakolb my $rsrc_prefix = gettext("Lgroup resources:"); 550c6402783Sakolb # What resources does this lgroup contain? 551c6402783Sakolb my @resources_cpu = nsort($l->resources($lgrp, LGRP_RSRC_CPU)); 552c6402783Sakolb my @resources_mem = nsort($l->resources($lgrp, LGRP_RSRC_MEM)); 553c6402783Sakolb my $rsrc = @resources_cpu || @resources_mem ? "" : gettext("none"); 554c6402783Sakolb $rsrc = $rsrc_prefix . $rsrc; 555c6402783Sakolb my $rsrc_cpu = lgrp_collapse(@resources_cpu); 556c6402783Sakolb my $rsrc_mem = lgrp_collapse(@resources_mem); 557c6402783Sakolb my $lcpu = gettext("CPU"); 558c6402783Sakolb my $lmemory = gettext("memory"); 559c6402783Sakolb $rsrc = "$rsrc $rsrc_cpu ($lcpu);" if scalar @resources_cpu; 560c6402783Sakolb $rsrc = "$rsrc $rsrc_mem ($lmemory)" if scalar @resources_mem; 561c6402783Sakolb return ($rsrc); 562c6402783Sakolb} 563c6402783Sakolb 564c6402783Sakolb# 565c6402783Sakolb# Consolidate consequtive ids as start-end 566c6402783Sakolb# Input: list of ids 567c6402783Sakolb# Output: string with space-sepated cpu values with ranges 568c6402783Sakolb# collapsed as x-y 569c6402783Sakolb# 570c6402783Sakolbsub lgrp_collapse 571c6402783Sakolb{ 572c6402783Sakolb return ('') unless @_; 573c6402783Sakolb my @args = uniqsort(@_); 574c6402783Sakolb my $start = shift(@args); 575c6402783Sakolb my $result = ''; 576c6402783Sakolb my $end = $start; # Initial range consists of the first element 577c6402783Sakolb foreach my $el (@args) { 578c6402783Sakolb if ($el == ($end + 1)) { 579c6402783Sakolb # 580c6402783Sakolb # Got consecutive ID, so extend end of range without 581c6402783Sakolb # printing anything since the range may extend further 582c6402783Sakolb # 583c6402783Sakolb $end = $el; 584c6402783Sakolb } else { 585c6402783Sakolb # 586c6402783Sakolb # Next ID is not consecutive, so print IDs gotten so 587c6402783Sakolb # far. 588c6402783Sakolb # 589c6402783Sakolb if ($end > $start + 1) { # range 590c6402783Sakolb $result = "$result $start-$end"; 591c6402783Sakolb } elsif ($end > $start) { # different values 592c6402783Sakolb $result = "$result $start $end"; 593c6402783Sakolb } else { # same value 594c6402783Sakolb $result = "$result $start"; 595c6402783Sakolb } 596c6402783Sakolb 597c6402783Sakolb # Try finding consecutive range starting from this ID 598c6402783Sakolb $start = $end = $el; 599c6402783Sakolb } 600c6402783Sakolb } 601c6402783Sakolb 602c6402783Sakolb # Print last ID(s) 603c6402783Sakolb if ($end > $start + 1) { 604c6402783Sakolb $result = "$result $start-$end"; 605c6402783Sakolb } elsif ($end > $start) { 606c6402783Sakolb $result = "$result $start $end"; 607c6402783Sakolb } else { 608c6402783Sakolb $result = "$result $start"; 609c6402783Sakolb } 610c6402783Sakolb # Remove any spaces in the beginning 611c6402783Sakolb $result =~ s/^\s+//; 612c6402783Sakolb return ($result); 613c6402783Sakolb} 614c6402783Sakolb 615c6402783Sakolb# Print latency information if requested and the system has several lgroups. 616c6402783Sakolbsub print_latency_table 617c6402783Sakolb{ 618c6402783Sakolb my ($lgrps1, $lgrps2) = @_; 619c6402783Sakolb 620c6402783Sakolb return unless scalar @lgrps; 621c6402783Sakolb 622c6402783Sakolb # Find maximum lgroup 623c6402783Sakolb my $max = $root; 624c6402783Sakolb map { $max = $_ if $max < $_ } @$lgrps1; 625c6402783Sakolb 626c6402783Sakolb # Field width for lgroup - the width of the largest lgroup and 1 space 627c6402783Sakolb my $lgwidth = length($max) + 1; 628c6402783Sakolb # Field width for latency. Get the maximum latency and add 1 space. 629c6402783Sakolb my $width = length($l->latency($root, $root)) + 1; 630c6402783Sakolb # Make sure that width is enough to print lgroup itself. 631c6402783Sakolb $width = $lgwidth if $width < $lgwidth; 632c6402783Sakolb 633c6402783Sakolb # Print table header 634c6402783Sakolb print gettext("\nLgroup latencies:\n"); 635c6402783Sakolb # Print horizontal line 636c6402783Sakolb print "\n", "-" x ($lgwidth + 1); 637c6402783Sakolb map { print '-' x $width } @$lgrps1; 638c6402783Sakolb print "\n", " " x $lgwidth, "|"; 639c6402783Sakolb map { printf("%${width}d", $_) } @$lgrps1; 640c6402783Sakolb print "\n", "-" x ($lgwidth + 1); 641c6402783Sakolb map { print '-' x $width } @$lgrps1; 642c6402783Sakolb print "\n"; 643c6402783Sakolb 644c6402783Sakolb # Print the latency table 645c6402783Sakolb foreach my $l1 (@$lgrps2) { 646c6402783Sakolb printf "%-${lgwidth}d|", $l1; 647c6402783Sakolb foreach my $l2 (@lgrps) { 648c6402783Sakolb my $latency = $l->latency($l1, $l2); 649c6402783Sakolb if (!defined ($latency)) { 650c6402783Sakolb printf "%${width}s", "-"; 651c6402783Sakolb } else { 652c6402783Sakolb printf "%${width}d", $latency; 653c6402783Sakolb } 654c6402783Sakolb } 655c6402783Sakolb print "\n"; 656c6402783Sakolb } 657c6402783Sakolb 658c6402783Sakolb # Print table footer 659c6402783Sakolb print "-" x ($lgwidth + 1); 660c6402783Sakolb map { print '-' x $width } @lgrps; 661c6402783Sakolb print "\n"; 662c6402783Sakolb} 663c6402783Sakolb 664c6402783Sakolb# 665c6402783Sakolb# Convert a number to a string representation 666c6402783Sakolb# The number is scaled down until it is small enough to be in a good 667c6402783Sakolb# human readable format i.e. in the range 0 thru 1023. 668c6402783Sakolb# If it's smaller than 10 there's room enough to provide one decimal place. 669c6402783Sakolb# 670c6402783Sakolbsub number_to_scaled_string 671c6402783Sakolb{ 672c6402783Sakolb my $number = shift; 673c6402783Sakolb 674c6402783Sakolb my $scale = KB; 675c6402783Sakolb my @measurement = ('K', 'M', 'G', 'T', 'P', 'E'); # Measurement 676c6402783Sakolb my $uom = shift(@measurement); 677c6402783Sakolb my $result; 678c6402783Sakolb 6797595fad9Sakolb my $save = $number; 6807595fad9Sakolb 681c6402783Sakolb # Get size in K. 682c6402783Sakolb $number /= KB; 683c6402783Sakolb 684c6402783Sakolb while (($number >= $scale) && $uom ne 'E') { 685c6402783Sakolb $uom = shift(@measurement); 686c6402783Sakolb $save = $number; 6877595fad9Sakolb $number /= $scale; 688c6402783Sakolb } 689c6402783Sakolb 690c6402783Sakolb # check if we should output a decimal place after the point 691c6402783Sakolb if ($save && (($save / $scale) < 10)) { 692c6402783Sakolb $result = sprintf("%2.1f", $save / $scale); 693c6402783Sakolb } else { 694c6402783Sakolb $result = round($number); 695c6402783Sakolb } 696c6402783Sakolb return ("$result$uom"); 697c6402783Sakolb} 698c6402783Sakolb 699c6402783Sakolb# 700c6402783Sakolb# Convert memory size to the string representation 701c6402783Sakolb# 702c6402783Sakolbsub memory_to_string 703c6402783Sakolb{ 704c6402783Sakolb my $number = shift; 705c6402783Sakolb 706c6402783Sakolb # Zero memory - just print 0 707c6402783Sakolb return ("0$unit_str") unless $number; 708c6402783Sakolb 709c6402783Sakolb # 710c6402783Sakolb # Return memory size scaled to human-readable form unless -u is 711c6402783Sakolb # specified. 712c6402783Sakolb # 713c6402783Sakolb return (number_to_scaled_string($number)) unless $opt_u; 714c6402783Sakolb 715c6402783Sakolb my $scaled = $number / $units; 716c6402783Sakolb my $result; 717c6402783Sakolb 718c6402783Sakolb if ($scaled < 0.1) { 719c6402783Sakolb $result = sprintf("%2.1g", $scaled); 720c6402783Sakolb } elsif ($scaled < 10) { 721c6402783Sakolb $result = sprintf("%2.1f", $scaled); 722c6402783Sakolb } else { 723c6402783Sakolb $result = int($scaled + 0.5); 724c6402783Sakolb } 725c6402783Sakolb return ("$result$unit_str"); 726c6402783Sakolb} 727c6402783Sakolb 728c6402783Sakolb# 729c6402783Sakolb# Read load averages from lgrp kstats Return hash reference indexed by lgroup ID 730c6402783Sakolb# for each lgroup which has load information. 731c6402783Sakolb# 732c6402783Sakolbsub get_lav 733c6402783Sakolb{ 734c6402783Sakolb my $load = {}; 735c6402783Sakolb 736c6402783Sakolb my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or 737c6402783Sakolb warn(gettext("$cmdname: kstat_open() failed: %!\n")), 738c6402783Sakolb return $load; 739c6402783Sakolb 740c6402783Sakolb my $lgrp_kstats = $ks->{lgrp} or 741c6402783Sakolb warn(gettext("$cmdname: can not read lgrp kstat\n)")), 742c6402783Sakolb return $load; 743c6402783Sakolb 744c6402783Sakolb # Collect load for each lgroup 745c6402783Sakolb foreach my $i (keys %$lgrp_kstats) { 746c6402783Sakolb next unless $lgrp_kstats->{$i}->{"lgrp$i"}; 747c6402783Sakolb my $lav = $lgrp_kstats->{$i}->{"lgrp$i"}->{"load average"}; 748c6402783Sakolb # Skip this lgroup if can't find its load average 749c6402783Sakolb next unless defined $lav; 750c6402783Sakolb my $scale = $lgrp_kstats->{$i}->{"lgrp$i"}->{"loadscale"} || 751c6402783Sakolb LGRP_LOADAVG_THREAD_MAX; 752c6402783Sakolb $load->{$i} = sprintf (gettext("Load: %4.3g"), $lav / $scale); 753c6402783Sakolb } 754c6402783Sakolb return $load; 755c6402783Sakolb} 756