xref: /illumos-gate/usr/src/cmd/lgrpinfo/lgrpinfo.pl (revision df0345f7)
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