5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
24 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
28 # pginfo - tool for displaying Processor Group information
35 use POSIX
qw(locale_h);
36 use Getopt
::Long
qw(:config no_ignore_case bundling auto_version);
37 use List
::Util
qw(first max min);
38 use Sun
::Solaris
::Utils
qw(textdomain gettext);
44 # It is possible that wnen trying to parse PG information, PG generation changes
45 # which will cause PG new method to fail with errno set to EAGAIN In this case
46 # we retry open up to RETRY_COUNT times pausing RETRY_DELAY seconds between each
49 # When printing PGs we print them as a little tree with each PG shifted by
50 # LEVEL_OFFSET from each parent. For example:
52 # PG RELATIONSHIP CPUs
68 # 0 Successful completion.
70 # 1 An error occurred.
72 # 2 Invalid command-line options were specified.
82 setlocale
(LC_ALL
, "");
83 textdomain
(TEXT_DOMAIN
);
85 # Get script name for error messages
86 our $cmdname = basename
($0, ".pl");
91 my $do_cpulist; # -C - Show CPU IDs
92 my $do_cpus; # -c - Treat args as CPU IDs
93 my $do_physical; # -p - Show physical relationships
94 my $do_sharing_only; # -S - Only show sharing relationships
95 my $do_tree; # -T - Show ASCII tree
96 my $do_usage; # -h - Show usage
97 my $do_version; # -V - Show version
98 my $script_mode; # -I - Only show IDs
99 my $verbose = 0; # -v - Verbose output
100 my @sharing_filter; # -r string,...
101 my @sharing_filter_neg; # -R string,...
106 # Parse options from the command line
107 GetOptions
("cpus|c" => \
$do_cpus,
108 "idlist|I" => \
$script_mode,
109 "cpulist|C" => \
$do_cpulist,
110 "physical|p" => \
$do_physical,
111 "help|h|?" => \
$do_usage,
112 "sharing|s" => \
$do_sharing_only,
113 "relationship|r=s" => \
@sharing_filter,
114 "norelationship|R=s" => \
@sharing_filter_neg,
115 "tree|topology|T" => \
$do_tree,
116 "version|V" => \
$do_version,
117 "verbose+" => \
$verbose,
121 # Print usage message when -h is given
122 usage
(E_SUCCESS
) if $do_usage;
126 # Print version information and exit
128 printf gettext
("%s version %s\n"), $cmdname, VERSION
;
133 # Verify options compatibility
135 if ($script_mode && $do_cpulist) {
137 gettext
("%s: options -I and -C can not be used at the same time\n"),
142 if (($script_mode || $do_cpulist) &&
143 ($do_physical || $do_sharing_only ||
146 gettext
("%s: options -C and -I can not be used with -p -s or -T\n"),
151 if ($do_physical && $do_sharing_only) {
153 gettext
("%s: option -p can not be used with -s\n"), $cmdname;
157 if ($do_tree && $do_sharing_only) {
159 gettext
("%s: option -T can not be used with -s\n"),
164 if ($verbose && !($script_mode || $do_cpulist || $do_sharing_only)) {
172 my $p = Sun
::Solaris
::Pg
->new(-tags
=> $do_physical,
173 -retry
=> RETRY_COUNT
,
174 '-delay' => RETRY_DELAY
);
178 gettext
("%s: can not obtain Processor Group information: $!\n"),
184 # Convert -[Rr] string1,string2,... into list (string1, string2, ...)
186 @sharing_filter = map { split /,/ } @sharing_filter;
187 @sharing_filter_neg = map { split /,/ } @sharing_filter_neg;
190 # Get list of all PGs in the system
192 my @all_pgs = $p->all_depth_first();
194 if (scalar(@all_pgs) == 0) {
196 gettext
("%s: this system does not have any Processor groups\n"),
202 # @pgs is the list of PGs we are going to work with after all the option
208 # get list of all CPUs in the system by looking at the root PG cpus
210 my @all_cpus = $p->cpus($p->root());
213 # If there are arguments in the command line, treat them as either PG IDs or as
214 # CPUs that should be converted to PG IDs.
215 # Arguments can be specified as x-y x,y,z and use special keyword 'all'
219 # Convert 'all' in arguments to all CPUs or all PGs
222 my @all = $do_cpus ?
@all_cpus : @all_pgs;
223 @args = map { $_ eq 'all' ?
@all : $_ } @ARGV;
225 # Expand any x-y,z ranges
226 @args = $p->expand(@args);
229 # @bad_cpus is a list of invalid CPU IDs
230 my @bad_cpus = $p->set_subtract(\
@all_cpus, \
@args);
231 if (scalar @bad_cpus) {
233 gettext
("%s: Invalid processor IDs %s\n"),
234 $cmdname, $p->id_collapse(@bad_cpus);
238 # List of PGs is the list of any PGs that contain specified CPUs
241 my @cpus = $p->cpus($_);
242 scalar($p->intersect(\
@cpus, \
@args));
245 # @pgs is a list of valid CPUs in the arguments
246 @pgs = $p->intersect(\
@all_pgs, \
@args);
247 # @bad_pgs is a list of invalid PG IDs
248 my @bad_pgs = $p->set_subtract(\
@all_pgs, \
@args);
249 if (scalar @bad_pgs) {
251 gettext
("%s: Invalid PG IDs %s\n"),
252 $cmdname, $p->id_collapse(@bad_pgs);
259 # Now we have list of PGs to work with. Now apply filtering. First list only
262 @pgs = grep { list_match
($p->sh_name($_), @sharing_filter) } @pgs if
263 scalar @sharing_filter;
265 # Remove any that doesn't match -r
266 @pgs = grep { !list_match
($p->sh_name($_), @sharing_filter_neg) } @pgs if
267 scalar @sharing_filter_neg;
269 # Do we have any PGs left?
270 if (scalar(@pgs) == 0) {
272 gettext
("%s: no processor groups matching command line arguments %s\n"),
278 # Global list of PGs that should be excluded from the output - it is only used
279 # when tree mode is specified.
283 @exclude_pgs = grep {
284 list_match
($p->sh_name($_), @sharing_filter_neg)
288 # In tree mode add PGs that are in the lineage of given PGs
290 @pgs = pg_lineage
($p, @pgs)
294 # -I is specified, print list of all PGs
298 @pgs = sort { $a <=> $b } @pgs;
307 # -C is specified, print list of all CPUs belonging to PGs
310 my @cpu_list = $p->uniqsort(map { $p->cpus($_) } @pgs);
315 # Mapping of relationships to list of PGs
316 my %pgs_by_relationship;
318 # Maximum length of all sharing names
319 my $max_sharename_len = length('RELATIONSHIP');
321 # Maximum length of PG ID
322 my $max_pg_len = length(max
(@pgs)) + 1;
325 # For calculating proper offsets we need to know minimum and maximum level for
328 my @levels = map { $p->level($_) } @pgs;
329 my $maxlevel = max
(@levels);
330 my $minlevel = min
(@levels);
332 # Calculate maximum string length that should be used to represent PGs
333 foreach my $pg (@pgs) {
334 my $name = $p->sh_name ($pg) || "unknown";
335 my $level = $p->level($pg) || 0;
338 my $tags = $p->tags($pg);
339 $name = "$name [$tags]" if $tags;
342 my $length = length($name) + $level - $minlevel;
343 $max_sharename_len = $length if $length > $max_sharename_len;
346 if ($do_sharing_only) {
348 # -s - only print sharing relationships
350 # Get list of sharing relationships
351 my @relationships = $p->sharing_relationships(@pgs);
354 printf "%-${max_sharename_len}s %s\n",
355 'RELATIONSHIP', 'PGs';
356 foreach my $rel (@relationships) {
357 my @pg_rel = grep { $p->sh_name($_) eq $rel }
359 my $pg_rel = $p->id_collapse (@pg_rel);
360 $pgs_by_relationship{$rel} = \
@pg_rel;
364 foreach my $rel (@relationships) {
365 printf "%-${max_sharename_len}s", $rel;
367 my @pgs = @
{$pgs_by_relationship{$rel}};
368 my $pgs = $p->id_collapse (@pgs);
379 # Print PGs either in list form or tree form
384 $header = sprintf "%-${max_pg_len}s %-${max_sharename_len}s" .
386 'PG', 'RELATIONSHIP', 'CPUs';
389 map { pg_print
($p, $_) } @pgs;
392 # Construct a tree from PG hierarchy and prune any PGs that are
393 # specified with -R option
395 my $pg_tree = pg_make_tree
($p);
396 map { pg_remove_from_tree
($pg_tree, $_) } @exclude_pgs;
399 my @top_level = grep {
400 $pg_tree->{$_} && !defined($pg_tree->{$_}->{parent
})
403 # Print each top-level node as ASCII tree
404 foreach my $pg (@top_level) {
405 my $children = $pg_tree->{$pg}->{children
};
406 my @children = $children ? @
{$children} : ();
407 @children = $p->intersect(\
@children, \
@pgs);
408 pg_print_tree
($p, $pg_tree, $pg, '', '', scalar @children);
415 ######################################################################
420 # pg_print(cookie, pg)
421 # print PG information in list mode
427 my $sharing = $p->sh_name($pg);
429 my $tags = $p->tags($pg);
430 $sharing = "$sharing [$tags]" if $tags;
432 my $level = $p->level($pg) - $minlevel;
433 $sharing = (' ' x
(LEVEL_OFFSET
* $level)) . $sharing;
434 my $cpus = $p->cpus($pg);
435 printf "%-${max_pg_len}d %-${max_sharename_len}s", $pg, $sharing;
441 # pg_showcpus(cookie, pg)
442 # Print CPUs in the current PG
449 my @cpus = $p->cpus($pg);
450 my $ncpus = scalar @cpus;
451 return 0 unless $ncpus;
452 my $cpu_string = $p->cpus($pg);
453 return (($ncpus == 1) ?
455 "CPUs: $cpu_string");
459 # pg_print_node(cookie, pg)
460 # print PG as ASCII tree node
467 my $sharing = $p->sh_name($pg);
469 my $tags = $p->tags($pg);
470 $sharing = "$sharing [$tags]" if $tags;
473 print "$pg ($sharing)";
474 my $cpus = pg_showcpus
($p, $pg);
480 # pg_print_tree(cookie, tree, pg, prefix, childprefix, npeers)
481 # print ASCII tree of PGs in the tree
482 # prefix should be used for the current node, childprefix for children nodes
483 # npeers is the number of peers of the current node
490 return unless defined ($pg); # done!
492 my $childprefix = shift;
495 # Get list of my children
496 my $children = $pg_tree->{$pg}->{children
};
497 my @children = $children ? @
{$children} : ();
498 @children = $p->intersect(\
@children, \
@pgs);
499 my $nchildren = scalar @children;
501 my $printprefix = "$childprefix";
502 my $printpostfix = $npeers ?
"| " : " ";
504 my $bar = $npeers ?
"|" : "`";
506 print $childprefix ?
$childprefix : "";
507 print $prefix ?
"$bar" . "-- " : "";
508 pg_print_node
($p, $pg);
510 my $new_prefix = $npeers ?
$prefix : " ";
512 # Print the subtree with a new offset, starting from each child
514 pg_print_tree
($p, $pg_tree, $_, "| ",
515 "$childprefix$new_prefix", --$nchildren)
520 # list_match(arg, list)
521 # Return arg if argument matches any of the elements on the list
527 return first
{ $arg =~ m/$_/i } @_;
531 # Make a version of PG parent-children relationships from cookie
538 foreach my $pg ($p->all()) {
539 my @children = $p->children($pg);
540 $pg_tree->{$pg}->{parent
} = $p->parent($pg);
541 $pg_tree->{$pg}->{children
} = \
@children;
548 # pg_remove_from_tree(tree, pg)
549 # Prune PG from the tree
551 sub pg_remove_from_tree
555 my $node = $pg_tree->{$pg};
558 my @children = @
{$node->{children
}};
559 my $parent = $node->{parent
};
563 # Children have a new parent
565 map { $pg_tree->{$_}->{parent
} = $parent } @children;
568 # All children move to the parent (if there is one)
570 if (defined($parent) && ($parent_node = $pg_tree->{$parent})) {
572 # Merge children from parent and @children list
574 my @parent_children = @
{$parent_node->{children
}};
576 # Remove myself from parent children
578 @parent_children = grep { $_ != $pg } @parent_children;
579 @parent_children = $p->nsort(@parent_children, @children);
580 $parent_node->{children
} = \
@parent_children;
583 # Remove current node
584 delete $pg_tree->{$pg};
588 # For a given list of PGs return the full lineage
593 return unless scalar @_;
595 my @parents = grep { defined($_) } map { $p->parent ($_) } @_;
597 return ($p->uniq(@_, @parents, pg_lineage
($p, @parents)));
601 # Print usage information and exit with the return code specified
607 gettext
("Usage:\t%s [-T] [-p] [-v] [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"),
610 gettext
("\t%s -s [-v] [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"), $cmdname;
611 printf STDERR gettext
("\t%s -C | -I [-r string] [-R string] [pg ... | -c processor_id ...]\n\n"),
613 printf STDERR gettext
("\t%s -h\n\n"), $cmdname;