xref: /titanic_52/usr/src/cmd/pginfo/pginfo.pl (revision d3c9722485327eb5b96de2f2108e9a84bd46096d)
1*d3c97224SAlexander Kolbasov#! /usr/perl5/bin/perl
2*d3c97224SAlexander Kolbasov#
3*d3c97224SAlexander Kolbasov# CDDL HEADER START
4*d3c97224SAlexander Kolbasov#
5*d3c97224SAlexander Kolbasov# The contents of this file are subject to the terms of the
6*d3c97224SAlexander Kolbasov# Common Development and Distribution License (the "License").
7*d3c97224SAlexander Kolbasov# You may not use this file except in compliance with the License.
8*d3c97224SAlexander Kolbasov#
9*d3c97224SAlexander Kolbasov# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*d3c97224SAlexander Kolbasov# or http://www.opensolaris.org/os/licensing.
11*d3c97224SAlexander Kolbasov# See the License for the specific language governing permissions
12*d3c97224SAlexander Kolbasov# and limitations under the License.
13*d3c97224SAlexander Kolbasov#
14*d3c97224SAlexander Kolbasov# When distributing Covered Code, include this CDDL HEADER in each
15*d3c97224SAlexander Kolbasov# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*d3c97224SAlexander Kolbasov# If applicable, add the following below this CDDL HEADER, with the
17*d3c97224SAlexander Kolbasov# fields enclosed by brackets "[]" replaced with your own identifying
18*d3c97224SAlexander Kolbasov# information: Portions Copyright [yyyy] [name of copyright owner]
19*d3c97224SAlexander Kolbasov#
20*d3c97224SAlexander Kolbasov# CDDL HEADER END
21*d3c97224SAlexander Kolbasov#
22*d3c97224SAlexander Kolbasov
23*d3c97224SAlexander Kolbasov#
24*d3c97224SAlexander Kolbasov# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25*d3c97224SAlexander Kolbasov#
26*d3c97224SAlexander Kolbasov
27*d3c97224SAlexander Kolbasov#
28*d3c97224SAlexander Kolbasov# pginfo - tool for displaying Processor Group information
29*d3c97224SAlexander Kolbasov#
30*d3c97224SAlexander Kolbasov
31*d3c97224SAlexander Kolbasovuse warnings;
32*d3c97224SAlexander Kolbasovuse strict;
33*d3c97224SAlexander Kolbasovuse File::Basename;
34*d3c97224SAlexander Kolbasovuse Errno;
35*d3c97224SAlexander Kolbasovuse POSIX qw(locale_h);
36*d3c97224SAlexander Kolbasovuse Getopt::Long qw(:config no_ignore_case bundling auto_version);
37*d3c97224SAlexander Kolbasovuse List::Util qw(first max min);
38*d3c97224SAlexander Kolbasovuse Sun::Solaris::Utils qw(textdomain gettext);
39*d3c97224SAlexander Kolbasovuse Sun::Solaris::Pg;
40*d3c97224SAlexander Kolbasov
41*d3c97224SAlexander Kolbasov#
42*d3c97224SAlexander Kolbasov# Constants
43*d3c97224SAlexander Kolbasov#
44*d3c97224SAlexander Kolbasov# It is possible that wnen trying to parse PG information, PG generation changes
45*d3c97224SAlexander Kolbasov# which will cause PG new method to fail with errno set to EAGAIN In this case
46*d3c97224SAlexander Kolbasov# we retry open up to RETRY_COUNT times pausing RETRY_DELAY seconds between each
47*d3c97224SAlexander Kolbasov# retry.
48*d3c97224SAlexander Kolbasov#
49*d3c97224SAlexander Kolbasov# When printing PGs we print them as a little tree with each PG shifted by
50*d3c97224SAlexander Kolbasov# LEVEL_OFFSET from each parent. For example:
51*d3c97224SAlexander Kolbasov#
52*d3c97224SAlexander Kolbasov# PG  RELATIONSHIP                    CPUs
53*d3c97224SAlexander Kolbasov# 0   System                          0-7
54*d3c97224SAlexander Kolbasov# 3    Socket                         0 2 4 6
55*d3c97224SAlexander Kolbasov# 2     Cache                         0 2 4 6
56*d3c97224SAlexander Kolbasov#
57*d3c97224SAlexander Kolbasov
58*d3c97224SAlexander Kolbasovuse constant {
59*d3c97224SAlexander Kolbasov	VERSION		=> 1.1,
60*d3c97224SAlexander Kolbasov	LEVEL_OFFSET	=> 1,
61*d3c97224SAlexander Kolbasov	RETRY_COUNT	=> 4,
62*d3c97224SAlexander Kolbasov        RETRY_DELAY	=> 0.25,
63*d3c97224SAlexander Kolbasov};
64*d3c97224SAlexander Kolbasov
65*d3c97224SAlexander Kolbasov#
66*d3c97224SAlexander Kolbasov# Return codes
67*d3c97224SAlexander Kolbasov#
68*d3c97224SAlexander Kolbasov#     0    Successful completion.
69*d3c97224SAlexander Kolbasov#
70*d3c97224SAlexander Kolbasov#     1    An error occurred.
71*d3c97224SAlexander Kolbasov#
72*d3c97224SAlexander Kolbasov#     2    Invalid command-line options were specified.
73*d3c97224SAlexander Kolbasov#
74*d3c97224SAlexander Kolbasovuse constant {
75*d3c97224SAlexander Kolbasov	E_SUCCESS => 0,
76*d3c97224SAlexander Kolbasov	E_ERROR => 1,
77*d3c97224SAlexander Kolbasov	E_USAGE => 2,
78*d3c97224SAlexander Kolbasov};
79*d3c97224SAlexander Kolbasov
80*d3c97224SAlexander Kolbasov
81*d3c97224SAlexander Kolbasov# Set message locale
82*d3c97224SAlexander Kolbasovsetlocale(LC_ALL, "");
83*d3c97224SAlexander Kolbasovtextdomain(TEXT_DOMAIN);
84*d3c97224SAlexander Kolbasov
85*d3c97224SAlexander Kolbasov# Get script name for error messages
86*d3c97224SAlexander Kolbasovour $cmdname = basename($0, ".pl");
87*d3c97224SAlexander Kolbasov
88*d3c97224SAlexander Kolbasov#
89*d3c97224SAlexander Kolbasov# Process options
90*d3c97224SAlexander Kolbasov#
91*d3c97224SAlexander Kolbasovmy $do_cpulist;			# -C - Show CPU IDs
92*d3c97224SAlexander Kolbasovmy $do_cpus;			# -c - Treat args as CPU IDs
93*d3c97224SAlexander Kolbasovmy $do_physical;		# -p - Show physical relationships
94*d3c97224SAlexander Kolbasovmy $do_sharing_only;		# -S - Only show sharing relationships
95*d3c97224SAlexander Kolbasovmy $do_tree;			# -T - Show ASCII tree
96*d3c97224SAlexander Kolbasovmy $do_usage;			# -h - Show usage
97*d3c97224SAlexander Kolbasovmy $do_version;			# -V - Show version
98*d3c97224SAlexander Kolbasovmy $script_mode;		# -I - Only show IDs
99*d3c97224SAlexander Kolbasovmy $verbose = 0;		# -v - Verbose output
100*d3c97224SAlexander Kolbasovmy @sharing_filter;		# -r string,...
101*d3c97224SAlexander Kolbasovmy @sharing_filter_neg;		# -R string,...
102*d3c97224SAlexander Kolbasov
103*d3c97224SAlexander Kolbasov# Exit code
104*d3c97224SAlexander Kolbasovmy $rc = E_SUCCESS;
105*d3c97224SAlexander Kolbasov
106*d3c97224SAlexander Kolbasov# Parse options from the command line
107*d3c97224SAlexander KolbasovGetOptions("cpus|c"		=> \$do_cpus,
108*d3c97224SAlexander Kolbasov	   "idlist|I"		=> \$script_mode,
109*d3c97224SAlexander Kolbasov	   "cpulist|C"		=> \$do_cpulist,
110*d3c97224SAlexander Kolbasov	   "physical|p"		=> \$do_physical,
111*d3c97224SAlexander Kolbasov	   "help|h|?"		=> \$do_usage,
112*d3c97224SAlexander Kolbasov	   "sharing|s"		=> \$do_sharing_only,
113*d3c97224SAlexander Kolbasov	   "relationship|r=s"	=> \@sharing_filter,
114*d3c97224SAlexander Kolbasov	   "norelationship|R=s" => \@sharing_filter_neg,
115*d3c97224SAlexander Kolbasov	   "tree|topology|T"	=> \$do_tree,
116*d3c97224SAlexander Kolbasov	   "version|V"		=> \$do_version,
117*d3c97224SAlexander Kolbasov	   "verbose+"		=> \$verbose,
118*d3c97224SAlexander Kolbasov	   "v+"			=> \$verbose,
119*d3c97224SAlexander Kolbasov) || usage(E_USAGE);
120*d3c97224SAlexander Kolbasov
121*d3c97224SAlexander Kolbasov# Print usage message when -h is given
122*d3c97224SAlexander Kolbasovusage(E_SUCCESS) if $do_usage;
123*d3c97224SAlexander Kolbasov
124*d3c97224SAlexander Kolbasovif ($do_version) {
125*d3c97224SAlexander Kolbasov	#
126*d3c97224SAlexander Kolbasov	# Print version information and exit
127*d3c97224SAlexander Kolbasov	#
128*d3c97224SAlexander Kolbasov	printf gettext("%s version %s\n"), $cmdname, VERSION;
129*d3c97224SAlexander Kolbasov	exit(E_SUCCESS);
130*d3c97224SAlexander Kolbasov}
131*d3c97224SAlexander Kolbasov
132*d3c97224SAlexander Kolbasov#
133*d3c97224SAlexander Kolbasov# Verify options compatibility
134*d3c97224SAlexander Kolbasov#
135*d3c97224SAlexander Kolbasovif ($script_mode && $do_cpulist) {
136*d3c97224SAlexander Kolbasov	printf STDERR
137*d3c97224SAlexander Kolbasov	  gettext("%s: options -I and -C can not be used at the same time\n"),
138*d3c97224SAlexander Kolbasov	    $cmdname;
139*d3c97224SAlexander Kolbasov	usage(E_USAGE);
140*d3c97224SAlexander Kolbasov}
141*d3c97224SAlexander Kolbasov
142*d3c97224SAlexander Kolbasovif (($script_mode || $do_cpulist) &&
143*d3c97224SAlexander Kolbasov    ($do_physical || $do_sharing_only ||
144*d3c97224SAlexander Kolbasov    $do_tree)) {
145*d3c97224SAlexander Kolbasov	printf STDERR
146*d3c97224SAlexander Kolbasov	  gettext("%s: options -C and -I can not be used with -p -s or -T\n"),
147*d3c97224SAlexander Kolbasov	    $cmdname;
148*d3c97224SAlexander Kolbasov	usage(E_USAGE);
149*d3c97224SAlexander Kolbasov}
150*d3c97224SAlexander Kolbasov
151*d3c97224SAlexander Kolbasovif ($do_physical && $do_sharing_only) {
152*d3c97224SAlexander Kolbasov	printf STDERR
153*d3c97224SAlexander Kolbasov	  gettext("%s: option -p can not be used with -s\n"), $cmdname;
154*d3c97224SAlexander Kolbasov	usage(E_USAGE);
155*d3c97224SAlexander Kolbasov}
156*d3c97224SAlexander Kolbasov
157*d3c97224SAlexander Kolbasovif ($do_tree && $do_sharing_only) {
158*d3c97224SAlexander Kolbasov	printf STDERR
159*d3c97224SAlexander Kolbasov	  gettext("%s: option -T can not be used with -s\n"),
160*d3c97224SAlexander Kolbasov	    $cmdname;
161*d3c97224SAlexander Kolbasov	usage(E_USAGE);
162*d3c97224SAlexander Kolbasov}
163*d3c97224SAlexander Kolbasov
164*d3c97224SAlexander Kolbasovif ($verbose && !($script_mode || $do_cpulist || $do_sharing_only)) {
165*d3c97224SAlexander Kolbasov	$do_tree = 1;
166*d3c97224SAlexander Kolbasov	$do_physical = 1;
167*d3c97224SAlexander Kolbasov}
168*d3c97224SAlexander Kolbasov
169*d3c97224SAlexander Kolbasov#
170*d3c97224SAlexander Kolbasov# Get PG information
171*d3c97224SAlexander Kolbasov#
172*d3c97224SAlexander Kolbasovmy $p = Sun::Solaris::Pg->new(-tags => $do_physical,
173*d3c97224SAlexander Kolbasov			      -retry => RETRY_COUNT,
174*d3c97224SAlexander Kolbasov			      '-delay' => RETRY_DELAY);
175*d3c97224SAlexander Kolbasov
176*d3c97224SAlexander Kolbasovif (!$p) {
177*d3c97224SAlexander Kolbasov	printf STDERR
178*d3c97224SAlexander Kolbasov	  gettext("%s: can not obtain Processor Group information: $!\n"),
179*d3c97224SAlexander Kolbasov	    $cmdname;
180*d3c97224SAlexander Kolbasov	exit(E_ERROR);
181*d3c97224SAlexander Kolbasov}
182*d3c97224SAlexander Kolbasov
183*d3c97224SAlexander Kolbasov#
184*d3c97224SAlexander Kolbasov# Convert -[Rr] string1,string2,... into list (string1, string2, ...)
185*d3c97224SAlexander Kolbasov#
186*d3c97224SAlexander Kolbasov@sharing_filter = map { split /,/ } @sharing_filter;
187*d3c97224SAlexander Kolbasov@sharing_filter_neg = map { split /,/ } @sharing_filter_neg;
188*d3c97224SAlexander Kolbasov
189*d3c97224SAlexander Kolbasov#
190*d3c97224SAlexander Kolbasov# Get list of all PGs in the system
191*d3c97224SAlexander Kolbasov#
192*d3c97224SAlexander Kolbasovmy @all_pgs = $p->all_depth_first();
193*d3c97224SAlexander Kolbasov
194*d3c97224SAlexander Kolbasovif (scalar(@all_pgs) == 0) {
195*d3c97224SAlexander Kolbasov	printf STDERR
196*d3c97224SAlexander Kolbasov	  gettext("%s: this system does not have any Processor groups\n"),
197*d3c97224SAlexander Kolbasov	    $cmdname;
198*d3c97224SAlexander Kolbasov	exit(E_ERROR);
199*d3c97224SAlexander Kolbasov}
200*d3c97224SAlexander Kolbasov
201*d3c97224SAlexander Kolbasov#
202*d3c97224SAlexander Kolbasov# @pgs is the list of PGs we are going to work with after all the option
203*d3c97224SAlexander Kolbasov# processing
204*d3c97224SAlexander Kolbasov#
205*d3c97224SAlexander Kolbasovmy @pgs = @all_pgs;
206*d3c97224SAlexander Kolbasov
207*d3c97224SAlexander Kolbasov#
208*d3c97224SAlexander Kolbasov# get list of all CPUs in the system by looking at the root PG cpus
209*d3c97224SAlexander Kolbasov#
210*d3c97224SAlexander Kolbasovmy @all_cpus = $p->cpus($p->root());
211*d3c97224SAlexander Kolbasov
212*d3c97224SAlexander Kolbasov#
213*d3c97224SAlexander Kolbasov# If there are arguments in the command line, treat them as either PG IDs or as
214*d3c97224SAlexander Kolbasov# CPUs that should be converted to PG IDs.
215*d3c97224SAlexander Kolbasov# Arguments can be specified as x-y x,y,z and use special keyword 'all'
216*d3c97224SAlexander Kolbasov#
217*d3c97224SAlexander Kolbasovif (scalar @ARGV) {
218*d3c97224SAlexander Kolbasov	#
219*d3c97224SAlexander Kolbasov	# Convert 'all' in arguments to all CPUs or all PGs
220*d3c97224SAlexander Kolbasov	#
221*d3c97224SAlexander Kolbasov	my @args;
222*d3c97224SAlexander Kolbasov	my @all = $do_cpus ? @all_cpus : @all_pgs;
223*d3c97224SAlexander Kolbasov	@args = map { $_ eq 'all' ? @all : $_ } @ARGV;
224*d3c97224SAlexander Kolbasov
225*d3c97224SAlexander Kolbasov	# Expand any x-y,z ranges
226*d3c97224SAlexander Kolbasov	@args =  $p->expand(@args);
227*d3c97224SAlexander Kolbasov
228*d3c97224SAlexander Kolbasov	if ($do_cpus) {
229*d3c97224SAlexander Kolbasov		# @bad_cpus is a list of invalid CPU IDs
230*d3c97224SAlexander Kolbasov		my @bad_cpus =  $p->set_subtract(\@all_cpus, \@args);
231*d3c97224SAlexander Kolbasov		if (scalar @bad_cpus) {
232*d3c97224SAlexander Kolbasov			printf STDERR
233*d3c97224SAlexander Kolbasov			  gettext("%s: Invalid processor IDs %s\n"),
234*d3c97224SAlexander Kolbasov			    $cmdname, $p->id_collapse(@bad_cpus);
235*d3c97224SAlexander Kolbasov			$rc = E_ERROR;
236*d3c97224SAlexander Kolbasov		}
237*d3c97224SAlexander Kolbasov		#
238*d3c97224SAlexander Kolbasov		# List of PGs is the list of any PGs that contain specified CPUs
239*d3c97224SAlexander Kolbasov		#
240*d3c97224SAlexander Kolbasov		@pgs = grep {
241*d3c97224SAlexander Kolbasov			my @cpus = $p->cpus($_);
242*d3c97224SAlexander Kolbasov			scalar($p->intersect(\@cpus, \@args));
243*d3c97224SAlexander Kolbasov		} @all_pgs;
244*d3c97224SAlexander Kolbasov	} else {
245*d3c97224SAlexander Kolbasov		# @pgs is a list of valid CPUs in the arguments
246*d3c97224SAlexander Kolbasov		@pgs = $p->intersect(\@all_pgs, \@args);
247*d3c97224SAlexander Kolbasov		# @bad_pgs is a list of invalid PG IDs
248*d3c97224SAlexander Kolbasov		my @bad_pgs = $p->set_subtract(\@all_pgs, \@args);
249*d3c97224SAlexander Kolbasov		if (scalar @bad_pgs) {
250*d3c97224SAlexander Kolbasov			printf STDERR
251*d3c97224SAlexander Kolbasov			  gettext("%s: Invalid PG IDs %s\n"),
252*d3c97224SAlexander Kolbasov			    $cmdname, $p->id_collapse(@bad_pgs);
253*d3c97224SAlexander Kolbasov			$rc = E_ERROR;
254*d3c97224SAlexander Kolbasov		}
255*d3c97224SAlexander Kolbasov	}
256*d3c97224SAlexander Kolbasov}
257*d3c97224SAlexander Kolbasov
258*d3c97224SAlexander Kolbasov#
259*d3c97224SAlexander Kolbasov# Now we have list of PGs to work with. Now apply filtering. First list only
260*d3c97224SAlexander Kolbasov# those matching -R
261*d3c97224SAlexander Kolbasov#
262*d3c97224SAlexander Kolbasov@pgs = grep { list_match($p->sh_name($_), @sharing_filter) } @pgs if
263*d3c97224SAlexander Kolbasov  scalar @sharing_filter;
264*d3c97224SAlexander Kolbasov
265*d3c97224SAlexander Kolbasov# Remove any that doesn't match -r
266*d3c97224SAlexander Kolbasov@pgs = grep { !list_match($p->sh_name($_), @sharing_filter_neg) } @pgs if
267*d3c97224SAlexander Kolbasov  scalar @sharing_filter_neg;
268*d3c97224SAlexander Kolbasov
269*d3c97224SAlexander Kolbasov# Do we have any PGs left?
270*d3c97224SAlexander Kolbasovif (scalar(@pgs) == 0) {
271*d3c97224SAlexander Kolbasov	printf STDERR
272*d3c97224SAlexander Kolbasov	gettext("%s: no processor groups matching command line arguments %s\n"),
273*d3c97224SAlexander Kolbasov	    $cmdname, "@ARGV";
274*d3c97224SAlexander Kolbasov	exit(E_ERROR);
275*d3c97224SAlexander Kolbasov}
276*d3c97224SAlexander Kolbasov
277*d3c97224SAlexander Kolbasov#
278*d3c97224SAlexander Kolbasov# Global list of PGs that should be excluded from the output - it is only used
279*d3c97224SAlexander Kolbasov# when tree mode is specified.
280*d3c97224SAlexander Kolbasov#
281*d3c97224SAlexander Kolbasovmy @exclude_pgs;
282*d3c97224SAlexander Kolbasovif ($do_tree) {
283*d3c97224SAlexander Kolbasov	@exclude_pgs = grep {
284*d3c97224SAlexander Kolbasov		list_match($p->sh_name($_), @sharing_filter_neg)
285*d3c97224SAlexander Kolbasov	} @all_pgs;
286*d3c97224SAlexander Kolbasov
287*d3c97224SAlexander Kolbasov	#
288*d3c97224SAlexander Kolbasov	# In tree mode add PGs that are in the lineage of given PGs
289*d3c97224SAlexander Kolbasov	#
290*d3c97224SAlexander Kolbasov	@pgs = pg_lineage($p, @pgs)
291*d3c97224SAlexander Kolbasov}
292*d3c97224SAlexander Kolbasov
293*d3c97224SAlexander Kolbasov#
294*d3c97224SAlexander Kolbasov# -I is specified, print list of all PGs
295*d3c97224SAlexander Kolbasov#
296*d3c97224SAlexander Kolbasovif ($script_mode) {
297*d3c97224SAlexander Kolbasov	if (scalar(@pgs)) {
298*d3c97224SAlexander Kolbasov		@pgs = sort { $a <=> $b } @pgs;
299*d3c97224SAlexander Kolbasov		print "@pgs\n";
300*d3c97224SAlexander Kolbasov	} else {
301*d3c97224SAlexander Kolbasov		print "none\n";
302*d3c97224SAlexander Kolbasov	}
303*d3c97224SAlexander Kolbasov	exit($rc);
304*d3c97224SAlexander Kolbasov}
305*d3c97224SAlexander Kolbasov
306*d3c97224SAlexander Kolbasov#
307*d3c97224SAlexander Kolbasov# -C is specified, print list of all CPUs belonging to PGs
308*d3c97224SAlexander Kolbasov#
309*d3c97224SAlexander Kolbasovif ($do_cpulist) {
310*d3c97224SAlexander Kolbasov	my @cpu_list = $p->uniqsort(map { $p->cpus($_) } @pgs);
311*d3c97224SAlexander Kolbasov	print "@cpu_list\n";
312*d3c97224SAlexander Kolbasov	exit($rc);
313*d3c97224SAlexander Kolbasov}
314*d3c97224SAlexander Kolbasov
315*d3c97224SAlexander Kolbasov# Mapping of relationships to list of PGs
316*d3c97224SAlexander Kolbasovmy %pgs_by_relationship;
317*d3c97224SAlexander Kolbasov
318*d3c97224SAlexander Kolbasov# Maximum length of all sharing names
319*d3c97224SAlexander Kolbasovmy $max_sharename_len = length('RELATIONSHIP');
320*d3c97224SAlexander Kolbasov
321*d3c97224SAlexander Kolbasov# Maximum length of PG ID
322*d3c97224SAlexander Kolbasovmy $max_pg_len = length(max(@pgs)) + 1;
323*d3c97224SAlexander Kolbasov
324*d3c97224SAlexander Kolbasov#
325*d3c97224SAlexander Kolbasov# For calculating proper offsets we need to know minimum and maximum level for
326*d3c97224SAlexander Kolbasov# all PGs
327*d3c97224SAlexander Kolbasov#
328*d3c97224SAlexander Kolbasovmy @levels = map { $p->level($_) } @pgs;
329*d3c97224SAlexander Kolbasovmy $maxlevel = max(@levels);
330*d3c97224SAlexander Kolbasovmy $minlevel = min(@levels);
331*d3c97224SAlexander Kolbasov
332*d3c97224SAlexander Kolbasov# Calculate maximum string length that should be used to represent PGs
333*d3c97224SAlexander Kolbasovforeach my $pg (@pgs) {
334*d3c97224SAlexander Kolbasov	my $name =  $p->sh_name ($pg) || "unknown";
335*d3c97224SAlexander Kolbasov	my $level = $p->level($pg) || 0;
336*d3c97224SAlexander Kolbasov
337*d3c97224SAlexander Kolbasov	if ($do_physical) {
338*d3c97224SAlexander Kolbasov		my $tags = $p->tags($pg);
339*d3c97224SAlexander Kolbasov		$name = "$name [$tags]" if $tags;
340*d3c97224SAlexander Kolbasov	}
341*d3c97224SAlexander Kolbasov
342*d3c97224SAlexander Kolbasov	my $length = length($name) + $level - $minlevel;
343*d3c97224SAlexander Kolbasov	$max_sharename_len = $length if $length > $max_sharename_len;
344*d3c97224SAlexander Kolbasov}
345*d3c97224SAlexander Kolbasov
346*d3c97224SAlexander Kolbasovif ($do_sharing_only) {
347*d3c97224SAlexander Kolbasov	#
348*d3c97224SAlexander Kolbasov	# -s - only print sharing relationships
349*d3c97224SAlexander Kolbasov
350*d3c97224SAlexander Kolbasov	# Get list of sharing relationships
351*d3c97224SAlexander Kolbasov	my @relationships = $p->sharing_relationships(@pgs);
352*d3c97224SAlexander Kolbasov
353*d3c97224SAlexander Kolbasov	if ($verbose) {
354*d3c97224SAlexander Kolbasov		printf "%-${max_sharename_len}s %s\n",
355*d3c97224SAlexander Kolbasov		  'RELATIONSHIP', 'PGs';
356*d3c97224SAlexander Kolbasov		foreach my $rel (@relationships) {
357*d3c97224SAlexander Kolbasov			my @pg_rel = grep { $p->sh_name($_) eq $rel }
358*d3c97224SAlexander Kolbasov			  @pgs;
359*d3c97224SAlexander Kolbasov			my $pg_rel = $p->id_collapse (@pg_rel);
360*d3c97224SAlexander Kolbasov			$pgs_by_relationship{$rel} = \@pg_rel;
361*d3c97224SAlexander Kolbasov		}
362*d3c97224SAlexander Kolbasov	}
363*d3c97224SAlexander Kolbasov
364*d3c97224SAlexander Kolbasov	foreach my $rel (@relationships) {
365*d3c97224SAlexander Kolbasov		printf "%-${max_sharename_len}s", $rel;
366*d3c97224SAlexander Kolbasov		if ($verbose) {
367*d3c97224SAlexander Kolbasov			my @pgs = @{$pgs_by_relationship{$rel}};
368*d3c97224SAlexander Kolbasov			my $pgs = $p->id_collapse (@pgs);
369*d3c97224SAlexander Kolbasov			print ' ', $pgs;
370*d3c97224SAlexander Kolbasov		}
371*d3c97224SAlexander Kolbasov		print "\n";
372*d3c97224SAlexander Kolbasov	}
373*d3c97224SAlexander Kolbasov
374*d3c97224SAlexander Kolbasov	# we are done
375*d3c97224SAlexander Kolbasov	exit($rc);
376*d3c97224SAlexander Kolbasov}
377*d3c97224SAlexander Kolbasov
378*d3c97224SAlexander Kolbasov#
379*d3c97224SAlexander Kolbasov# Print PGs either in list form or tree form
380*d3c97224SAlexander Kolbasov#
381*d3c97224SAlexander Kolbasovif (!$do_tree) {
382*d3c97224SAlexander Kolbasov	my $header;
383*d3c97224SAlexander Kolbasov
384*d3c97224SAlexander Kolbasov	$header = sprintf "%-${max_pg_len}s %-${max_sharename_len}s" .
385*d3c97224SAlexander Kolbasov	  "   %s\n",
386*d3c97224SAlexander Kolbasov	    'PG', 'RELATIONSHIP', 'CPUs';
387*d3c97224SAlexander Kolbasov
388*d3c97224SAlexander Kolbasov	print $header;
389*d3c97224SAlexander Kolbasov	map { pg_print ($p, $_) } @pgs;
390*d3c97224SAlexander Kolbasov} else {
391*d3c97224SAlexander Kolbasov	#
392*d3c97224SAlexander Kolbasov	# Construct a tree from PG hierarchy and prune any PGs that are
393*d3c97224SAlexander Kolbasov	# specified with -R option
394*d3c97224SAlexander Kolbasov	#
395*d3c97224SAlexander Kolbasov	my $pg_tree = pg_make_tree($p);
396*d3c97224SAlexander Kolbasov	map { pg_remove_from_tree($pg_tree, $_) } @exclude_pgs;
397*d3c97224SAlexander Kolbasov
398*d3c97224SAlexander Kolbasov	# Find top-level PGs
399*d3c97224SAlexander Kolbasov	my @top_level = grep {
400*d3c97224SAlexander Kolbasov		$pg_tree->{$_} && !defined($pg_tree->{$_}->{parent})
401*d3c97224SAlexander Kolbasov	} @pgs;
402*d3c97224SAlexander Kolbasov
403*d3c97224SAlexander Kolbasov	# Print each top-level node as ASCII tree
404*d3c97224SAlexander Kolbasov	foreach my $pg (@top_level) {
405*d3c97224SAlexander Kolbasov		my $children = $pg_tree->{$pg}->{children};
406*d3c97224SAlexander Kolbasov		my @children = $children ? @{$children} : ();
407*d3c97224SAlexander Kolbasov		@children = $p->intersect(\@children, \@pgs);
408*d3c97224SAlexander Kolbasov		pg_print_tree($p, $pg_tree, $pg, '', '', scalar @children);
409*d3c97224SAlexander Kolbasov	}
410*d3c97224SAlexander Kolbasov}
411*d3c97224SAlexander Kolbasov
412*d3c97224SAlexander Kolbasov# We are done!
413*d3c97224SAlexander Kolbasovexit($rc);
414*d3c97224SAlexander Kolbasov
415*d3c97224SAlexander Kolbasov######################################################################
416*d3c97224SAlexander Kolbasov# Internal functions
417*d3c97224SAlexander Kolbasov#
418*d3c97224SAlexander Kolbasov
419*d3c97224SAlexander Kolbasov#
420*d3c97224SAlexander Kolbasov# pg_print(cookie, pg)
421*d3c97224SAlexander Kolbasov# print PG information in list mode
422*d3c97224SAlexander Kolbasov#
423*d3c97224SAlexander Kolbasovsub pg_print
424*d3c97224SAlexander Kolbasov{
425*d3c97224SAlexander Kolbasov	my $p = shift;
426*d3c97224SAlexander Kolbasov	my $pg = shift;
427*d3c97224SAlexander Kolbasov	my $sharing = $p->sh_name($pg);
428*d3c97224SAlexander Kolbasov	if ($do_physical) {
429*d3c97224SAlexander Kolbasov		my $tags = $p->tags($pg);
430*d3c97224SAlexander Kolbasov		$sharing = "$sharing [$tags]" if $tags;
431*d3c97224SAlexander Kolbasov	}
432*d3c97224SAlexander Kolbasov	my $level = $p->level($pg) - $minlevel;
433*d3c97224SAlexander Kolbasov	$sharing = (' ' x (LEVEL_OFFSET * $level)) . $sharing;
434*d3c97224SAlexander Kolbasov	my $cpus = $p->cpus($pg);
435*d3c97224SAlexander Kolbasov	printf "%-${max_pg_len}d %-${max_sharename_len}s", $pg, $sharing;
436*d3c97224SAlexander Kolbasov	print "   $cpus";
437*d3c97224SAlexander Kolbasov	print "\n";
438*d3c97224SAlexander Kolbasov}
439*d3c97224SAlexander Kolbasov
440*d3c97224SAlexander Kolbasov#
441*d3c97224SAlexander Kolbasov# pg_showcpus(cookie, pg)
442*d3c97224SAlexander Kolbasov# Print CPUs in the current PG
443*d3c97224SAlexander Kolbasov#
444*d3c97224SAlexander Kolbasovsub pg_showcpus
445*d3c97224SAlexander Kolbasov{
446*d3c97224SAlexander Kolbasov	my $p = shift;
447*d3c97224SAlexander Kolbasov	my $pg = shift;
448*d3c97224SAlexander Kolbasov
449*d3c97224SAlexander Kolbasov	my @cpus = $p->cpus($pg);
450*d3c97224SAlexander Kolbasov	my $ncpus = scalar @cpus;
451*d3c97224SAlexander Kolbasov	return 0 unless $ncpus;
452*d3c97224SAlexander Kolbasov	my $cpu_string = $p->cpus($pg);
453*d3c97224SAlexander Kolbasov	return (($ncpus == 1) ?
454*d3c97224SAlexander Kolbasov		"CPU: $cpu_string":
455*d3c97224SAlexander Kolbasov		"CPUs: $cpu_string");
456*d3c97224SAlexander Kolbasov}
457*d3c97224SAlexander Kolbasov
458*d3c97224SAlexander Kolbasov#
459*d3c97224SAlexander Kolbasov# pg_print_node(cookie, pg)
460*d3c97224SAlexander Kolbasov# print PG as ASCII tree node
461*d3c97224SAlexander Kolbasov#
462*d3c97224SAlexander Kolbasovsub pg_print_node
463*d3c97224SAlexander Kolbasov{
464*d3c97224SAlexander Kolbasov	my $p = shift;
465*d3c97224SAlexander Kolbasov	my $pg = shift;
466*d3c97224SAlexander Kolbasov
467*d3c97224SAlexander Kolbasov	my $sharing = $p->sh_name($pg);
468*d3c97224SAlexander Kolbasov	if ($do_physical) {
469*d3c97224SAlexander Kolbasov		my $tags = $p->tags($pg);
470*d3c97224SAlexander Kolbasov		$sharing = "$sharing [$tags]" if $tags;
471*d3c97224SAlexander Kolbasov	}
472*d3c97224SAlexander Kolbasov
473*d3c97224SAlexander Kolbasov	print "$pg ($sharing)";
474*d3c97224SAlexander Kolbasov	my $cpus = pg_showcpus($p, $pg);
475*d3c97224SAlexander Kolbasov	print " $cpus";
476*d3c97224SAlexander Kolbasov	print "\n";
477*d3c97224SAlexander Kolbasov}
478*d3c97224SAlexander Kolbasov
479*d3c97224SAlexander Kolbasov#
480*d3c97224SAlexander Kolbasov# pg_print_tree(cookie, tree, pg, prefix, childprefix, npeers)
481*d3c97224SAlexander Kolbasov# print ASCII tree of PGs in the tree
482*d3c97224SAlexander Kolbasov# prefix should be used for the current node, childprefix for children nodes
483*d3c97224SAlexander Kolbasov# npeers is the number of peers of the current node
484*d3c97224SAlexander Kolbasov#
485*d3c97224SAlexander Kolbasovsub pg_print_tree
486*d3c97224SAlexander Kolbasov{
487*d3c97224SAlexander Kolbasov	my $p = shift;
488*d3c97224SAlexander Kolbasov	my $pg_tree = shift;
489*d3c97224SAlexander Kolbasov	my $pg = shift;
490*d3c97224SAlexander Kolbasov	return unless defined ($pg);	# done!
491*d3c97224SAlexander Kolbasov	my $prefix = shift;
492*d3c97224SAlexander Kolbasov	my $childprefix = shift;
493*d3c97224SAlexander Kolbasov	my $npeers = shift;
494*d3c97224SAlexander Kolbasov
495*d3c97224SAlexander Kolbasov	# Get list of my children
496*d3c97224SAlexander Kolbasov	my $children = $pg_tree->{$pg}->{children};
497*d3c97224SAlexander Kolbasov	my @children = $children ? @{$children} : ();
498*d3c97224SAlexander Kolbasov	@children = $p->intersect(\@children, \@pgs);
499*d3c97224SAlexander Kolbasov	my $nchildren = scalar @children;
500*d3c97224SAlexander Kolbasov
501*d3c97224SAlexander Kolbasov	my $printprefix = "$childprefix";
502*d3c97224SAlexander Kolbasov	my $printpostfix = $npeers ? "|   " : "    ";
503*d3c97224SAlexander Kolbasov
504*d3c97224SAlexander Kolbasov	my $bar = $npeers ? "|" : "`";
505*d3c97224SAlexander Kolbasov
506*d3c97224SAlexander Kolbasov	print $childprefix ? $childprefix : "";
507*d3c97224SAlexander Kolbasov	print $prefix ? "$bar" . "-- " : "";
508*d3c97224SAlexander Kolbasov	pg_print_node ($p, $pg);
509*d3c97224SAlexander Kolbasov
510*d3c97224SAlexander Kolbasov	my $new_prefix = $npeers ? $prefix : "    ";
511*d3c97224SAlexander Kolbasov
512*d3c97224SAlexander Kolbasov	# Print the subtree with a new offset, starting from each child
513*d3c97224SAlexander Kolbasov	map {
514*d3c97224SAlexander Kolbasov		pg_print_tree($p, $pg_tree, $_, "|   ",
515*d3c97224SAlexander Kolbasov		      "$childprefix$new_prefix", --$nchildren)
516*d3c97224SAlexander Kolbasov	} @children;
517*d3c97224SAlexander Kolbasov}
518*d3c97224SAlexander Kolbasov
519*d3c97224SAlexander Kolbasov#
520*d3c97224SAlexander Kolbasov# list_match(arg, list)
521*d3c97224SAlexander Kolbasov# Return arg if argument matches any of the elements on the list
522*d3c97224SAlexander Kolbasov#
523*d3c97224SAlexander Kolbasovsub list_match
524*d3c97224SAlexander Kolbasov{
525*d3c97224SAlexander Kolbasov	my $arg = shift;
526*d3c97224SAlexander Kolbasov
527*d3c97224SAlexander Kolbasov	return first { $arg =~ m/$_/i } @_;
528*d3c97224SAlexander Kolbasov}
529*d3c97224SAlexander Kolbasov
530*d3c97224SAlexander Kolbasov#
531*d3c97224SAlexander Kolbasov# Make a version of PG parent-children relationships from cookie
532*d3c97224SAlexander Kolbasov#
533*d3c97224SAlexander Kolbasovsub pg_make_tree
534*d3c97224SAlexander Kolbasov{
535*d3c97224SAlexander Kolbasov	my $p = shift;
536*d3c97224SAlexander Kolbasov	my $pg_tree = ();
537*d3c97224SAlexander Kolbasov
538*d3c97224SAlexander Kolbasov	foreach my $pg ($p->all()) {
539*d3c97224SAlexander Kolbasov		my @children = $p->children($pg);
540*d3c97224SAlexander Kolbasov		$pg_tree->{$pg}->{parent} = $p->parent($pg);
541*d3c97224SAlexander Kolbasov		$pg_tree->{$pg}->{children} = \@children;
542*d3c97224SAlexander Kolbasov	}
543*d3c97224SAlexander Kolbasov
544*d3c97224SAlexander Kolbasov	return ($pg_tree);
545*d3c97224SAlexander Kolbasov}
546*d3c97224SAlexander Kolbasov
547*d3c97224SAlexander Kolbasov#
548*d3c97224SAlexander Kolbasov# pg_remove_from_tree(tree, pg)
549*d3c97224SAlexander Kolbasov# Prune PG from the tree
550*d3c97224SAlexander Kolbasov#
551*d3c97224SAlexander Kolbasovsub pg_remove_from_tree
552*d3c97224SAlexander Kolbasov{
553*d3c97224SAlexander Kolbasov	my $pg_tree = shift;
554*d3c97224SAlexander Kolbasov	my $pg = shift;
555*d3c97224SAlexander Kolbasov	my $node = $pg_tree->{$pg};
556*d3c97224SAlexander Kolbasov	return unless $node;
557*d3c97224SAlexander Kolbasov
558*d3c97224SAlexander Kolbasov	my @children = @{$node->{children}};
559*d3c97224SAlexander Kolbasov	my $parent = $node->{parent};
560*d3c97224SAlexander Kolbasov	my $parent_node;
561*d3c97224SAlexander Kolbasov
562*d3c97224SAlexander Kolbasov	#
563*d3c97224SAlexander Kolbasov	# Children have a new parent
564*d3c97224SAlexander Kolbasov	#
565*d3c97224SAlexander Kolbasov	map { $pg_tree->{$_}->{parent} = $parent } @children;
566*d3c97224SAlexander Kolbasov
567*d3c97224SAlexander Kolbasov	#
568*d3c97224SAlexander Kolbasov	# All children move to the parent (if there is one)
569*d3c97224SAlexander Kolbasov	#
570*d3c97224SAlexander Kolbasov	if (defined($parent) && ($parent_node = $pg_tree->{$parent})) {
571*d3c97224SAlexander Kolbasov		#
572*d3c97224SAlexander Kolbasov		# Merge children from parent and @children list
573*d3c97224SAlexander Kolbasov		#
574*d3c97224SAlexander Kolbasov		my @parent_children = @{$parent_node->{children}};
575*d3c97224SAlexander Kolbasov		#
576*d3c97224SAlexander Kolbasov		# Remove myself from parent children
577*d3c97224SAlexander Kolbasov		#
578*d3c97224SAlexander Kolbasov		@parent_children = grep { $_ != $pg } @parent_children;
579*d3c97224SAlexander Kolbasov		@parent_children = $p->nsort(@parent_children, @children);
580*d3c97224SAlexander Kolbasov		$parent_node->{children} = \@parent_children;
581*d3c97224SAlexander Kolbasov	}
582*d3c97224SAlexander Kolbasov
583*d3c97224SAlexander Kolbasov	# Remove current node
584*d3c97224SAlexander Kolbasov	delete $pg_tree->{$pg};
585*d3c97224SAlexander Kolbasov}
586*d3c97224SAlexander Kolbasov
587*d3c97224SAlexander Kolbasov#
588*d3c97224SAlexander Kolbasov# For a given list of PGs return the full lineage
589*d3c97224SAlexander Kolbasov#
590*d3c97224SAlexander Kolbasovsub pg_lineage
591*d3c97224SAlexander Kolbasov{
592*d3c97224SAlexander Kolbasov	my $p = shift;
593*d3c97224SAlexander Kolbasov	return unless scalar @_;
594*d3c97224SAlexander Kolbasov
595*d3c97224SAlexander Kolbasov	my @parents = grep { defined($_) } map { $p->parent ($_) } @_;
596*d3c97224SAlexander Kolbasov
597*d3c97224SAlexander Kolbasov	return ($p->uniq(@_, @parents, pg_lineage ($p, @parents)));
598*d3c97224SAlexander Kolbasov}
599*d3c97224SAlexander Kolbasov
600*d3c97224SAlexander Kolbasov#
601*d3c97224SAlexander Kolbasov# Print usage information and exit with the return code specified
602*d3c97224SAlexander Kolbasov#
603*d3c97224SAlexander Kolbasovsub usage
604*d3c97224SAlexander Kolbasov{
605*d3c97224SAlexander Kolbasov	my $rc = shift;
606*d3c97224SAlexander Kolbasov	printf STDERR
607*d3c97224SAlexander Kolbasov	  gettext("Usage:\t%s [-T] [-p] [-v] [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"),
608*d3c97224SAlexander Kolbasov	    $cmdname;
609*d3c97224SAlexander Kolbasov	printf STDERR
610*d3c97224SAlexander Kolbasov	  gettext("\t%s -s [-v] [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"), $cmdname;
611*d3c97224SAlexander Kolbasov	printf STDERR gettext("\t%s -C | -I [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"),
612*d3c97224SAlexander Kolbasov	  $cmdname;
613*d3c97224SAlexander Kolbasov	printf STDERR gettext("\t%s -h\n\n"), $cmdname;
614*d3c97224SAlexander Kolbasov
615*d3c97224SAlexander Kolbasov	exit($rc);
616*d3c97224SAlexander Kolbasov}
617*d3c97224SAlexander Kolbasov
618*d3c97224SAlexander Kolbasov__END__
619