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.
416