1 1843 akolb #!/usr/perl5/bin/perl 2 0 stevel 3 1843 akolb # 4 1843 akolb # CDDL HEADER START 5 1843 akolb # 6 1843 akolb # The contents of this file are subject to the terms of the 7 1843 akolb # Common Development and Distribution License (the "License"). 8 1843 akolb # You may not use this file except in compliance with the License. 9 1843 akolb # 10 1843 akolb # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11 1843 akolb # or http://www.opensolaris.org/os/licensing. 12 1843 akolb # See the License for the specific language governing permissions 13 1843 akolb # and limitations under the License. 14 1843 akolb # 15 1843 akolb # When distributing Covered Code, include this CDDL HEADER in each 16 1843 akolb # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17 1843 akolb # If applicable, add the following below this CDDL HEADER, with the 18 1843 akolb # fields enclosed by brackets "[]" replaced with your own identifying 19 1843 akolb # information: Portions Copyright [yyyy] [name of copyright owner] 20 1843 akolb # 21 1843 akolb # CDDL HEADER END 22 1843 akolb # 23 9482 Kuriakose # Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 1843 akolb # Use is subject to license terms. 25 1843 akolb # 26 1843 akolb # psrinfo: displays information about processors 27 1843 akolb # 28 1843 akolb # See detailed comment in the end of this file. 29 1843 akolb # 30 0 stevel 31 1843 akolb use strict; 32 1843 akolb use warnings; 33 1843 akolb use locale; 34 1843 akolb use POSIX qw(locale_h strftime); 35 1843 akolb use File::Basename; 36 1843 akolb use Getopt::Long qw(:config no_ignore_case bundling auto_version); 37 1843 akolb use Sun::Solaris::Utils qw(textdomain gettext); 38 1843 akolb use Sun::Solaris::Kstat; 39 2520 akolb 40 2520 akolb # Set message locale 41 2520 akolb setlocale(LC_ALL, ""); 42 2520 akolb textdomain(TEXT_DOMAIN); 43 0 stevel 44 1843 akolb ###################################################################### 45 1843 akolb # Configuration variables 46 1843 akolb ###################################################################### 47 0 stevel 48 1843 akolb # Regexp describing cpu_info kstat fields describing CPU hierarchy. 49 1843 akolb my $valid_id_exp = qr{^(?:chip|core)_id$}; 50 0 stevel 51 1843 akolb # Translation of kstat name to human-readable form 52 1843 akolb my %translations = ('chip_id' => gettext("The physical processor"), 53 1843 akolb 'core_id' => gettext("The core")); 54 0 stevel 55 1843 akolb # Localized version of plural forms 56 1843 akolb my %pluralized_names = ('processor' => gettext("processor"), 57 1843 akolb 'processors' => gettext("processors"), 58 1843 akolb 'chip' => gettext("chip"), 59 1843 akolb 'chips' => gettext("chips"), 60 1843 akolb 'core' => gettext("core"), 61 1843 akolb 'cores' => gettext("cores")); 62 0 stevel 63 1843 akolb # Localized CPU states 64 1843 akolb my %cpu_states = ('on-line' => gettext("on-line"), 65 1843 akolb 'off-line' => gettext("off-line"), 66 1843 akolb 'faulted' => gettext("faulted"), 67 1843 akolb 'powered-off' => gettext("powered-off"), 68 1843 akolb 'no-intr' => gettext("no-intr"), 69 1843 akolb 'spare' => gettext("spare"), 70 1843 akolb 'unknown' => gettext("unknown")); 71 0 stevel 72 1843 akolb ###################################################################### 73 1843 akolb # Global variables 74 1843 akolb ###################################################################### 75 1469 hyw 76 1843 akolb # Hash with CPU ID as a key and specific per-cpu kstat hash as a value 77 1843 akolb our %cpu_list; 78 0 stevel 79 1843 akolb # Command name without path and trailing .pl - used for error messages. 80 1843 akolb our $cmdname = basename($0, ".pl"); 81 1469 hyw 82 1843 akolb # Return value 83 1843 akolb our $errors = 0; 84 0 stevel 85 1843 akolb ###################################################################### 86 1843 akolb # Helper subroutines 87 1843 akolb ###################################################################### 88 1843 akolb 89 1843 akolb # 90 1843 akolb # Print help string if specified or the standard help message and exit setting 91 1843 akolb # errno. 92 1843 akolb # 93 1843 akolb sub usage 94 0 stevel { 95 1843 akolb my (@msg) = @_; 96 1843 akolb print STDERR $cmdname, ": @msg\n" if (@msg); 97 1843 akolb print STDERR gettext("usage: \n" . 98 1843 akolb "\tpsrinfo [-v] [-p] [processor_id ...]\n" . 99 1843 akolb "\tpsrinfo -s [-p] processor_id\n"); 100 0 stevel exit(2); 101 0 stevel } 102 0 stevel 103 1843 akolb # 104 1843 akolb # Return the input list with duplicates removed. 105 1843 akolb # Count how many times we've seen each element and remove elements seen more 106 1843 akolb # than once. 107 1843 akolb # 108 1843 akolb sub uniq 109 0 stevel { 110 1843 akolb my %seen; # Have we seen this element already? 111 1843 akolb return (grep { ++$seen{$_} == 1 } @_); 112 1843 akolb } 113 0 stevel 114 1843 akolb # 115 1843 akolb # Return the intersection of two lists passed by reference 116 1843 akolb # Convert the first list to a hash with seen entries marked as 1-values 117 1843 akolb # Then grep only elements present in the first list from the second list. 118 1843 akolb # As a little optimization, use the shorter list to build a hash. 119 1843 akolb # 120 1843 akolb sub intersect 121 1843 akolb { 122 1843 akolb my ($left, $right) = @_; 123 1843 akolb my %seen; # Set to 1 for everything in the first list 124 1843 akolb # Put the shortest list in $left 125 1843 akolb scalar @$left <= scalar @$right or ($right, $left) = ($left, $right); 126 0 stevel 127 1843 akolb # Create a hash indexed by elements in @left with ones as a value. 128 1843 akolb map { $seen{$_} = 1 } @$left; 129 1843 akolb # Find members of @right present in @left 130 1843 akolb return (grep { $seen{$_} } @$right); 131 1843 akolb } 132 1843 akolb 133 1843 akolb # 134 1843 akolb # Return elements of the second list not present in the first list. Both lists 135 1843 akolb # are passed by reference. 136 1843 akolb # 137 1843 akolb sub set_subtract 138 1843 akolb { 139 1843 akolb my ($left, $right) = @_; 140 1843 akolb my %seen; # Set to 1 for everything in the first list 141 1843 akolb # Create a hash indexed by elements in @left with ones as a value. 142 1843 akolb map { $seen{$_} = 1 } @$left; 143 1843 akolb # Find members of @right present in @left 144 1843 akolb return (grep { ! $seen{$_} } @$right); 145 1843 akolb } 146 1843 akolb 147 1843 akolb # 148 1843 akolb # Sort the list numerically 149 1843 akolb # Should be called in list context 150 1843 akolb # 151 1843 akolb sub nsort 152 1843 akolb { 153 1843 akolb return (sort { $a <=> $b } @_); 154 1843 akolb } 155 1843 akolb 156 1843 akolb # 157 1843 akolb # Sort list numerically and remove duplicates 158 1843 akolb # Should be called in list context 159 1843 akolb # 160 1843 akolb sub uniqsort 161 1843 akolb { 162 1843 akolb return (sort { $a <=> $b } uniq(@_)); 163 1843 akolb } 164 1843 akolb 165 1843 akolb # 166 1843 akolb # Return the maximum value of its arguments 167 1843 akolb # 168 1843 akolb sub max 169 1843 akolb { 170 1843 akolb my $m = shift; 171 1843 akolb 172 1843 akolb foreach my $el (@_) { 173 1843 akolb $m = $el if $m < $el; 174 1843 akolb } 175 1843 akolb return ($m); 176 1843 akolb } 177 1843 akolb 178 1843 akolb # 179 1843 akolb # Pluralize name if there is more than one instance 180 1843 akolb # Arguments: name, ninstances 181 1843 akolb # 182 1843 akolb sub pluralize 183 1843 akolb { 184 1843 akolb my ($name, $count) = @_; 185 1843 akolb # Remove trailing '_id' from the name. 186 1843 akolb $name =~ s/_id$//; 187 1843 akolb my $plural_name = $count > 1 ? "${name}s" : $name; 188 1843 akolb return ($pluralized_names{$plural_name} || $plural_name) 189 1843 akolb } 190 1843 akolb 191 1843 akolb # 192 1843 akolb # Translate id name into printable form 193 1843 akolb # Look at the %translations table and replace everything found there 194 1843 akolb # Remove trailing _id from the name if there is no translation 195 1843 akolb # 196 1843 akolb sub id_translate 197 1843 akolb { 198 1843 akolb my $name = shift or return; 199 1843 akolb my $translated_name = $translations{$name}; 200 1843 akolb $name =~ s/_id$// unless $translated_name; 201 1843 akolb return ($translated_name || $name); 202 1843 akolb } 203 1843 akolb 204 1843 akolb # 205 1843 akolb # Consolidate consequtive CPU ids as start-end 206 1843 akolb # Input: list of CPUs 207 2520 akolb # Output: string with space-sepated cpu values with CPU ranges 208 2520 akolb # collapsed as x-y 209 1843 akolb # 210 1843 akolb sub collapse 211 1843 akolb { 212 1843 akolb return ('') unless @_; 213 1843 akolb my @args = uniqsort(@_); 214 1843 akolb my $start = shift(@args); 215 1843 akolb my $result = ''; 216 2520 akolb my $end = $start; # Initial range consists of the first element 217 1843 akolb foreach my $el (@args) { 218 1843 akolb if ($el == ($end + 1)) { 219 2520 akolb # 220 2520 akolb # Got consecutive ID, so extend end of range without 221 2520 akolb # printing anything since the range may extend further 222 2520 akolb # 223 1843 akolb $end = $el; 224 1843 akolb } else { 225 2520 akolb # 226 2520 akolb # Next ID is not consecutive, so print IDs gotten so 227 2520 akolb # far. 228 2520 akolb # 229 2520 akolb if ($end > $start + 1) { # range 230 1843 akolb $result = "$result $start-$end"; 231 2520 akolb } elsif ($end > $start) { # different values 232 2520 akolb $result = "$result $start $end"; 233 2520 akolb } else { # same value 234 1843 akolb $result = "$result $start"; 235 1843 akolb } 236 2520 akolb 237 2520 akolb # Try finding consecutive range starting from this ID 238 1843 akolb $start = $end = $el; 239 0 stevel } 240 0 stevel } 241 2520 akolb 242 2520 akolb # Print last ID(s) 243 1843 akolb if ($end > $start + 1) { 244 1843 akolb $result = "$result $start-$end"; 245 1843 akolb } elsif ($end > $start) { 246 1843 akolb $result = "$result $start $end"; 247 1843 akolb } else { 248 1843 akolb $result = "$result $start"; 249 1843 akolb } 250 1843 akolb # Remove any spaces in the beginning 251 1843 akolb $result =~ s/^\s+//; 252 1843 akolb return ($result); 253 1843 akolb } 254 0 stevel 255 1843 akolb # 256 1843 akolb # Expand start-end into the list of values 257 1843 akolb # Input: string containing a single numeric ID or x-y range 258 1843 akolb # Output: single value or a list of values 259 1843 akolb # Ranges with start being more than end are inverted 260 1843 akolb # 261 1843 akolb sub expand 262 1843 akolb { 263 1843 akolb my $arg = shift; 264 0 stevel 265 1843 akolb if ($arg =~ m/^\d+$/) { 266 1843 akolb # single number 267 1843 akolb return ($_); 268 1843 akolb } elsif ($arg =~ m/^(\d+)\-(\d+)$/) { 269 1843 akolb my ($start, $end) = ($1, $2); # $start-$end 270 1843 akolb # Reverse the interval if start > end 271 1843 akolb ($start, $end) = ($end, $start) if $start > $end; 272 1843 akolb return ($start .. $end); 273 1843 akolb } elsif ($arg =~ m/-/) { 274 1843 akolb printf STDERR 275 1843 akolb gettext("%s: invalid processor range %s\n"), 276 1843 akolb $cmdname, $_; 277 1843 akolb } else { 278 1843 akolb printf STDERR 279 1843 akolb gettext("%s: processor %s: Invalid argument\n"), 280 1843 akolb $cmdname, $_; 281 1843 akolb } 282 1843 akolb $errors = 2; 283 1843 akolb return (); 284 1843 akolb } 285 0 stevel 286 1843 akolb # 287 1843 akolb # Functions for constructing CPU hierarchy. Only used with -vp option. 288 1843 akolb # 289 0 stevel 290 1843 akolb # 291 1843 akolb # Return numerically sorted list of distinct values of a given cpu_info kstat 292 1843 akolb # field, spanning given CPU set. 293 1843 akolb # 294 1843 akolb # Arguments: 295 1843 akolb # Property name 296 1843 akolb # list of CPUs 297 1843 akolb # 298 1843 akolb # Treat undefined values as zeroes. 299 1843 akolb sub property_list 300 1843 akolb { 301 1843 akolb my $prop_name = shift; 302 4732 davemq return (grep {$_ >= 0} uniqsort(map { $cpu_list{$_}->{$prop_name} || 0 } @_)); 303 1843 akolb } 304 1843 akolb 305 1843 akolb # 306 1843 akolb # Return subset of CPUs sharing specified value of a given cpu_info kstat field. 307 1843 akolb # Arguments: 308 1843 akolb # Property name 309 1843 akolb # Property value 310 1843 akolb # List of CPUs to select from 311 1843 akolb # 312 1843 akolb # Treat undefined values as zeroes. 313 1843 akolb sub cpus_by_prop 314 1843 akolb { 315 1843 akolb my $prop_name = shift; 316 1843 akolb my $prop_val = shift; 317 1843 akolb 318 1843 akolb return (grep { ($cpu_list{$_}->{$prop_name} || 0) == $prop_val } @_); 319 1843 akolb } 320 1843 akolb 321 1843 akolb # 322 1843 akolb # Build component tree 323 1843 akolb # 324 1843 akolb # Arguments: 325 1843 akolb # Reference to the list of CPUs sharing the component 326 1843 akolb # Reference to the list of sub-components 327 1843 akolb # 328 1843 akolb sub build_component_tree 329 1843 akolb { 330 1843 akolb my ($cpus, $comp_list) = @_; 331 1843 akolb # Get the first component and the rest 332 1843 akolb my ($comp_name, @comps) = @$comp_list; 333 1843 akolb my $tree = {}; 334 1843 akolb if (!$comp_name) { 335 1843 akolb $tree->{cpus} = $cpus; 336 1843 akolb return ($tree); 337 1843 akolb } 338 1843 akolb 339 1843 akolb # Get all possible component values 340 1843 akolb foreach my $v (property_list($comp_name, @$cpus)) { 341 1843 akolb my @comp_cpus = cpus_by_prop ($comp_name, $v, @$cpus); 342 1843 akolb $tree->{name} = $comp_name; 343 1843 akolb $tree->{cpus} = $cpus; 344 1843 akolb $tree->{values}->{$v} = build_component_tree(\@comp_cpus, 345 1843 akolb \@comps); 346 1843 akolb } 347 1843 akolb return ($tree); 348 1843 akolb } 349 1843 akolb 350 1843 akolb # 351 1843 akolb # Print the component tree 352 1843 akolb # Arguments: 353 1843 akolb # Reference to a tree 354 1843 akolb # indentation 355 1843 akolb # Output: maximum indentation 356 1843 akolb # 357 1843 akolb sub print_component_tree 358 1843 akolb { 359 1843 akolb my ($tree, $ind) = @_; 360 1843 akolb my $spaces = ' ' x $ind; # indentation string 361 1843 akolb my $vals = $tree->{values}; 362 1843 akolb my $retval = $ind; 363 1843 akolb if ($vals) { 364 1843 akolb # This is not a leaf node 365 1843 akolb # Get node name and translate it to printable format 366 1843 akolb my $id_name = id_translate($tree->{name}); 367 1843 akolb # Examine each sub-node 368 1843 akolb foreach my $comp_val (nsort(keys %$vals)) { 369 1843 akolb my $child_tree = $vals->{$comp_val}; # Sub-tree 370 1843 akolb my $child_id = $child_tree->{name}; # Name of child node 371 1843 akolb my @cpus = @{$child_tree->{cpus}}; # CPUs for the child 372 1843 akolb my $ncpus = scalar @cpus; # Number of CPUs 373 1843 akolb my $cpuname = pluralize('processor', $ncpus); 374 1843 akolb my $cl = collapse(@cpus); # Printable CPU list 375 1843 akolb if (!$child_id) { 376 1843 akolb # Child is a leaf node 377 1843 akolb print $spaces; 378 1843 akolb printf gettext("%s has %d virtual %s"), 379 1843 akolb $id_name, $ncpus, $cpuname; 380 1843 akolb print " ($cl)\n"; 381 1843 akolb $retval = max($retval, $ind + 2); 382 1843 akolb } else { 383 1843 akolb # Child has several values. Let's see how many 384 1843 akolb my $grandchild_tree = $child_tree->{values}; 385 1843 akolb my $nvals = scalar(keys %$grandchild_tree); 386 1843 akolb my $child_id_name = pluralize($child_id, 387 1843 akolb $nvals); 388 1843 akolb print $spaces; 389 1843 akolb printf 390 1843 akolb gettext("%s has %d %s and %d virtual %s"), 391 1843 akolb $id_name, $nvals, $child_id_name, $ncpus, 392 1843 akolb $cpuname; 393 1843 akolb print " ($cl)\n"; 394 1843 akolb # Print the tree for the child 395 1843 akolb $retval = max($retval, 396 1843 akolb print_component_tree($child_tree, 397 1843 akolb $ind + 2)); 398 1843 akolb } 399 1843 akolb } 400 1843 akolb } 401 1843 akolb return ($retval); 402 1843 akolb } 403 1843 akolb 404 1843 akolb 405 1843 akolb ############################ 406 1843 akolb # Main part of the program 407 1843 akolb ############################ 408 1843 akolb 409 1843 akolb # 410 1843 akolb # Option processing 411 1843 akolb # 412 1843 akolb my ($opt_v, $opt_p, $opt_silent); 413 1843 akolb 414 1843 akolb GetOptions("p" => \$opt_p, 415 1843 akolb "v" => \$opt_v, 416 1843 akolb "s" => \$opt_silent) || usage(); 417 1843 akolb 418 1843 akolb 419 1843 akolb my $verbosity = 1; 420 1843 akolb my $phys_view; 421 1843 akolb 422 1843 akolb $verbosity |= 2 if $opt_v; 423 1843 akolb $verbosity &= ~1 if $opt_silent; 424 1843 akolb $phys_view = 1 if $opt_p; 425 1843 akolb 426 1843 akolb # Set $phys_verbose if -vp is specified 427 1843 akolb my $phys_verbose = $phys_view && ($verbosity > 1); 428 1843 akolb 429 1843 akolb # Verify options 430 1843 akolb usage(gettext("options -s and -v are mutually exclusive")) if $verbosity == 2; 431 1843 akolb 432 1843 akolb usage(gettext("must specify exactly one processor if -s used")) if 433 1843 akolb (($verbosity == 0) && scalar @ARGV != 1); 434 1843 akolb 435 1843 akolb # 436 1843 akolb # Read cpu_info kstats 437 1843 akolb # 438 1843 akolb my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or 439 1843 akolb (printf STDERR gettext("%s: kstat_open() failed: %s\n"), 440 1843 akolb $cmdname, $!), 441 1843 akolb exit(2); 442 1843 akolb my $cpu_info = $ks->{cpu_info} or 443 1843 akolb (printf STDERR gettext("%s: can not read cpu_info kstats\n"), 444 1843 akolb $cmdname), 445 1843 akolb exit(2); 446 1843 akolb 447 1843 akolb my ( 448 1843 akolb @all_cpus, # List of all CPUs in the system 449 1843 akolb @cpu_args, # CPUs to look at 450 1843 akolb @cpus, # List of CPUs to process 451 1843 akolb @id_list, # list of various xxx_id kstats representing CPU topology 452 1843 akolb %chips, # Hash with chip ID as a key and reference to the list of 453 1843 akolb # virtual CPU IDs, belonging to the chip as a value 454 1843 akolb @chip_list, # List of all chip_id values 455 1843 akolb $ctree, # The component tree 456 1843 akolb ); 457 1843 akolb 458 1843 akolb # 459 1843 akolb # Get information about each CPU. 460 1843 akolb # 461 1843 akolb # Collect list of all CPUs in @cpu_list array 462 1843 akolb # 463 1843 akolb # Construct %cpu_list hash keyed by CPU ID with cpu_info kstat hash as its 464 1843 akolb # value. 465 1843 akolb # 466 1843 akolb # Construct %chips hash keyed by chip ID. It has a 'cpus' entry, which is 467 1843 akolb # a reference to a list of CPU IDs within a chip. 468 1843 akolb # 469 1843 akolb foreach my $id (nsort(keys %$cpu_info)) { 470 1843 akolb # $id is CPU id 471 1843 akolb my $info = $cpu_info->{$id}; 472 1843 akolb 473 1843 akolb # 474 1843 akolb # The name part of the cpu_info kstat should always be a string 475 1843 akolb # cpu_info$id. 476 1843 akolb # 477 1843 akolb # The $ci hash reference holds all data for a specific CPU id. 478 1843 akolb # 479 1843 akolb my $ci = $info->{"cpu_info$id"} or next; 480 1843 akolb # Save CPU-specific information in cpu_list hash, indexed by CPU ID. 481 1843 akolb $cpu_list{$id} = $ci; 482 1843 akolb my $chip_id = $ci->{'chip_id'}; 483 1843 akolb # Collect CPUs within the chip. 484 1843 akolb # $chips{$chip_id} is a reference to a list of CPU IDs belonging to thie 485 1843 akolb # chip. It is automatically created when first referenced. 486 1843 akolb push (@{$chips{$chip_id}}, $id) if (defined($chip_id)); 487 1843 akolb # Collect list of CPU IDs in @cpus 488 1843 akolb push (@all_cpus, $id); 489 1843 akolb } 490 1843 akolb 491 1843 akolb # 492 1843 akolb # Figure out what CPUs to examine. 493 1843 akolb # Look at specific CPUs if any are specified on the command line or at all CPUs 494 1843 akolb # CPU ranges specified in the command line are expanded into lists of CPUs 495 1843 akolb # 496 1843 akolb if (scalar(@ARGV) == 0) { 497 1843 akolb @cpu_args = @all_cpus; 498 1843 akolb } else { 499 1843 akolb # Expand all x-y intervals in the argument list 500 1843 akolb @cpu_args = map { expand($_) } @ARGV; 501 1843 akolb 502 1843 akolb usage(gettext("must specify exactly one processor if -s used")) if 503 1843 akolb (($verbosity == 0) && scalar @cpu_args != 1); 504 1843 akolb 505 1843 akolb # Detect invalid CPUs in the arguments 506 1843 akolb my @bad_args = set_subtract(\@all_cpus, \@cpu_args); 507 1843 akolb my $nbadargs = scalar @bad_args; 508 1843 akolb 509 1843 akolb if ($nbadargs != 0) { 510 1843 akolb # Warn user about bad CPUs in the command line 511 1843 akolb my $argstr = collapse(@bad_args); 512 1843 akolb 513 1843 akolb if ($nbadargs > 1) { 514 1843 akolb printf STDERR gettext("%s: Invalid processors %s\n"), 515 1843 akolb $cmdname, $argstr; 516 1843 akolb } else { 517 1843 akolb printf STDERR 518 1843 akolb gettext("%s: processor %s: Invalid argument\n"), 519 1843 akolb $cmdname, $argstr; 520 1843 akolb } 521 1843 akolb $errors = 2; 522 1843 akolb } 523 1843 akolb 524 1843 akolb @cpu_args = uniqsort(intersect(\@all_cpus, \@cpu_args)); 525 1843 akolb } 526 1843 akolb 527 1843 akolb # 528 1843 akolb # In physical view, CPUs specified in the command line are only used to identify 529 1843 akolb # chips. The actual CPUs are all CPUs belonging to these chips. 530 1843 akolb # 531 1843 akolb if (! $phys_view) { 532 1843 akolb @cpus = @cpu_args; 533 1843 akolb } else { 534 1843 akolb # Get list of chips spanning all CPUs specified 535 1843 akolb @chip_list = property_list('chip_id', @cpu_args); 536 1843 akolb if (!scalar @chip_list && $errors == 0) { 537 1843 akolb printf STDERR 538 1843 akolb gettext("%s: Physical processor view not supported\n"), 539 1843 akolb $cmdname; 540 0 stevel exit(1); 541 0 stevel } 542 0 stevel 543 1843 akolb # Get list of all CPUs within these chips 544 1843 akolb @cpus = uniqsort(map { @{$chips{$_}} } @chip_list); 545 1843 akolb } 546 0 stevel 547 0 stevel 548 1843 akolb if ($phys_verbose) { 549 1843 akolb # 550 1843 akolb # 1) Look at all possible xxx_id properties and remove those that have 551 1843 akolb # NCPU values or one value. Sort the rest. 552 1843 akolb # 553 1843 akolb # 2) Drop ids which have the same number of entries as number of CPUs or 554 1843 akolb # number of chips. 555 1843 akolb # 556 1843 akolb # 3) Build the component tree for the system 557 1843 akolb # 558 1843 akolb foreach my $id (keys %$cpu_info) { 559 1843 akolb my $info = $cpu_info->{$id}; 560 1843 akolb my $name = "cpu_info$id"; 561 1843 akolb my $ci = $info->{$name}; # cpu_info kstat for this CPU 562 0 stevel 563 1843 akolb # Collect all statistic names matching $valid_id_exp 564 1843 akolb push @id_list, grep(/$valid_id_exp/, keys(%$ci)); 565 0 stevel } 566 0 stevel 567 1843 akolb # Remove duplicates 568 1843 akolb @id_list = uniq(@id_list); 569 0 stevel 570 1843 akolb my $ncpus = scalar @cpus; 571 1843 akolb my %prop_nvals; # Number of instances of each property 572 1843 akolb my $nchips = scalar @chip_list; 573 1843 akolb 574 1843 akolb # 575 1843 akolb # Get list of properties which have more than ncpus and less than nchips 576 1843 akolb # instances. 577 1843 akolb # Also collect number of instances for each property. 578 1843 akolb # 579 1843 akolb @id_list = grep { 580 1843 akolb my @ids = property_list($_, @cpus); 581 1843 akolb my $nids = scalar @ids; 582 1843 akolb $prop_nvals{$_} = $nids; 583 1843 akolb ($_ eq "chip_id") || 584 1843 akolb (($nids > $nchips) && ($nids > 1) && ($nids < $ncpus)); 585 1843 akolb } @id_list; 586 1843 akolb 587 1843 akolb # Sort @id_list by number of instances for each property 588 1843 akolb @id_list = sort { $prop_nvals{$a} <=> $prop_nvals{$b} } @id_list; 589 1843 akolb 590 1843 akolb $ctree = build_component_tree(\@cpus, \@id_list); 591 1843 akolb } 592 1843 akolb 593 1843 akolb 594 1843 akolb # 595 1843 akolb # Walk all CPUs specified and print information about them. 596 1843 akolb # Do nothing for physical view - will do everything later. 597 1843 akolb # 598 1843 akolb foreach my $id (@cpus) { 599 1843 akolb last if $phys_view; # physical view is handled later 600 1843 akolb my $cpu = $cpu_list{$id} or next; 601 1843 akolb 602 1843 akolb # Get CPU state and its modification time 603 1843 akolb my $mtime = $cpu->{'state_begin'}; 604 1843 akolb my $mstring = strftime(gettext("%m/%d/%Y %T"), localtime($mtime)); 605 1843 akolb my $status = $cpu->{'state'} || gettext("unknown"); 606 1843 akolb # Get localized version of CPU status 607 1843 akolb $status = $cpu_states{$status} || $status; 608 1843 akolb 609 1843 akolb if ($verbosity == 0) { 610 1843 akolb # Print 1 if CPU is online, 0 if offline. 611 1843 akolb printf "%d\n", $status eq 'on-line'; 612 1843 akolb } elsif (! ($verbosity & 2)) { 613 1843 akolb printf gettext("%d\t%-8s since %s\n"), 614 1843 akolb $id, $status, $mstring; 615 1843 akolb } else { 616 1843 akolb printf gettext("Status of virtual processor %d as of: "), $id; 617 1843 akolb print strftime(gettext("%m/%d/%Y %T"), localtime()); 618 1843 akolb print "\n"; 619 1843 akolb printf gettext(" %s since %s.\n"), $status, $mstring; 620 1843 akolb my $clock_speed = $cpu->{'clock_MHz'}; 621 1843 akolb my $cpu_type = $cpu->{'cpu_type'}; 622 1843 akolb 623 1843 akolb # Display clock speed 624 1843 akolb if ($clock_speed ) { 625 1843 akolb printf 626 1843 akolb gettext(" The %s processor operates at %s MHz,\n"), 627 1843 akolb $cpu_type, $clock_speed; 628 1843 akolb } else { 629 1843 akolb printf 630 1843 akolb gettext(" the %s processor operates at an unknown frequency,\n"), 631 1843 akolb $cpu_type; 632 1843 akolb } 633 1843 akolb 634 1843 akolb # Display FPU type 635 1843 akolb my $fpu = $cpu->{'fpu_type'}; 636 1843 akolb if (! $fpu) { 637 1843 akolb print 638 1843 akolb gettext("\tand has no floating point processor.\n"); 639 1843 akolb } elsif ($fpu =~ m/^[aeiouy]/) { 640 1843 akolb printf 641 1843 akolb gettext("\tand has an %s floating point processor.\n"), 642 1843 akolb $fpu; 643 1843 akolb } else { 644 1843 akolb printf 645 1843 akolb gettext("\tand has a %s floating point processor.\n"), 646 1843 akolb $fpu; 647 1843 akolb } 648 1843 akolb } 649 1843 akolb } 650 1843 akolb 651 1843 akolb # 652 1843 akolb # Physical view print 653 1843 akolb # 654 1843 akolb if ($phys_view) { 655 1843 akolb if ($verbosity == 1) { 656 1843 akolb print scalar @chip_list, "\n"; 657 1843 akolb } elsif ($verbosity == 0) { 658 1843 akolb # Print 1 if all CPUs are online, 0 otherwise. 659 1843 akolb foreach my $chip_id (@chip_list) { 660 1843 akolb # Get CPUs on a chip 661 1843 akolb my @chip_cpus = uniqsort(@{$chips{$chip_id}}); 662 1843 akolb # List of all on-line CPUs on a chip 663 1843 akolb my @online_cpus = grep { 664 1843 akolb ($cpu_list{$_}->{state}) eq 'on-line' 665 1843 akolb } @chip_cpus; 666 1843 akolb 667 1843 akolb # 668 1843 akolb # Print 1 if number of online CPUs equals number of all 669 1843 akolb # CPUs 670 1843 akolb # 671 1843 akolb printf 672 1843 akolb "%d\n", scalar @online_cpus == scalar @chip_cpus; 673 1843 akolb } 674 1843 akolb } else { 675 1843 akolb # Walk the property tree and print everything in it. 676 1843 akolb my $tcores = $ctree->{values}; 677 1843 akolb my $cname = id_translate($ctree->{name}); 678 1843 akolb foreach my $chip (nsort(keys %$tcores)) { 679 1843 akolb my $chipref = $tcores->{$chip}; 680 1843 akolb my @chip_cpus = @{$chipref->{cpus}}; 681 1843 akolb my $ncpus = scalar @chip_cpus; 682 1843 akolb my $cpu_id = $chip_cpus[0]; 683 1843 akolb my $cpu = $cpu_list{$cpu_id}; 684 1843 akolb my $brand = $cpu->{brand} || gettext("(unknown)"); 685 1843 akolb my $impl = $cpu->{implementation} || 686 1843 akolb gettext("(unknown)"); 687 9482 Kuriakose my $socket = $cpu->{socket_type}; 688 1843 akolb # 689 1843 akolb # Remove cpuid and chipid information from 690 1843 akolb # implementation string and print it. 691 1843 akolb # 692 1843 akolb $impl =~ s/(cpuid|chipid)\s*\w+\s+//; 693 1843 akolb $brand = '' if $impl && $impl =~ /^$brand/; 694 1843 akolb # List of CPUs on a chip 695 1843 akolb my $cpu_name = pluralize('processor', $ncpus); 696 1843 akolb # Collapse range of CPUs into a-b string 697 1843 akolb my $cl = collapse(@chip_cpus); 698 1843 akolb my $childname = $chipref->{name}; 699 1843 akolb if (! $childname) { 700 1843 akolb printf gettext("%s has %d virtual %s "), 701 1843 akolb $cname, $ncpus, $cpu_name; 702 1843 akolb print "($cl)\n"; 703 1843 akolb print " $impl\n" if $impl; 704 9482 Kuriakose print "\t$brand" if $brand; 705 9482 Kuriakose print "\t[ Socket: $socket ]" if $socket && 706 9482 Kuriakose $socket ne "Unknown"; 707 9482 Kuriakose print "\n"; 708 0 stevel } else { 709 1843 akolb # Get child count 710 1843 akolb my $nchildren = 711 1843 akolb scalar(keys(%{$chipref->{values}})); 712 1843 akolb $childname = pluralize($childname, $nchildren); 713 1843 akolb printf 714 1843 akolb gettext("%s has %d %s and %d virtual %s "), 715 1843 akolb $cname, $nchildren, $childname, $ncpus, 716 1843 akolb $cpu_name; 717 1843 akolb print "($cl)\n"; 718 1843 akolb my $ident = print_component_tree ($chipref, 2); 719 1843 akolb my $spaces = ' ' x $ident; 720 1843 akolb print "$spaces$impl\n" if $impl; 721 1843 akolb print "$spaces $brand\n" if $brand; 722 0 stevel } 723 0 stevel } 724 0 stevel } 725 0 stevel } 726 0 stevel 727 1843 akolb exit($errors); 728 0 stevel 729 1843 akolb __END__ 730 0 stevel 731 1843 akolb # The psrinfo command displays information about virtual and physical processors 732 1843 akolb # in a system. It gets all the information from the 'cpu_info' kstat. 733 1843 akolb # 734 1843 akolb # See detailed comment in the end of this file. 735 1843 akolb # 736 1843 akolb # 737 1843 akolb # 738 1843 akolb # This kstat 739 1843 akolb # has the following components: 740 1843 akolb # 741 1843 akolb # module: cpu_info 742 1843 akolb # instance: CPU ID 743 1843 akolb # name: cpu_infoID where ID is CPU ID 744 1843 akolb # class: misc 745 1843 akolb # 746 1843 akolb # The psrinfo command translates this information from kstat-specific 747 1843 akolb # representation to user-friendly format. 748 1843 akolb # 749 1843 akolb # The psrinfo command has several basic modes of operations: 750 1843 akolb # 751 1843 akolb # 1) Without options, it displays a line per CPU with CPU ID and its status and 752 1843 akolb # the time the status was last set in the following format: 753 1843 akolb # 754 1843 akolb # 0 on-line since MM/DD/YYYY HH:MM:SS 755 1843 akolb # 1 on-line since MM/DD/YYYY HH:MM:SS 756 1843 akolb # ... 757 1843 akolb # 758 1843 akolb # In this mode, the psrinfo command walks the list of CPUs (either from a 759 1843 akolb # command line or all CPUs) and prints the 'state' and 'state_begin' fields 760 1843 akolb # of cpu_info kstat structure for each CPU. The 'state_begin' is converted to 761 1843 akolb # local time. 762 1843 akolb # 763 1843 akolb # 2) With -s option and a single CPU ID as an argument, it displays 1 if the CPU 764 1843 akolb # is online and 0 otherwise. 765 1843 akolb # 766 1843 akolb # 3) With -p option, it displays the number of physical processors in a system. 767 1843 akolb # If any CPUs are specified in the command line, it displays the number of 768 1843 akolb # physical processors containing all virtual CPUs specified. The physical 769 1843 akolb # processor is identified by the 'chip_id' field of the cpu_info kstat. 770 1843 akolb # 771 1843 akolb # The code just walks over all CPUs specified and checks how many different 772 1843 akolb # core_id values they span. 773 1843 akolb # 774 1843 akolb # 4) With -v option, it displays several lines of information per virtual CPU, 775 1843 akolb # including its status, type, operating speed and FPU type. For example: 776 1843 akolb # 777 1843 akolb # Status of virtual processor 0 as of: MM/DD/YYYY HH:MM:SS 778 1843 akolb # on-line since MM/DD/YYYY HH:MM:SS. 779 1843 akolb # The i386 processor operates at XXXX MHz, 780 1843 akolb # and has an i387 compatible floating point processor. 781 1843 akolb # Status of virtual processor 1 as of: MM/DD/YYYY HH:MM:SS 782 1843 akolb # on-line since MM/DD/YYYY HH:MM:SS. 783 1843 akolb # The i386 processor operates at XXXX MHz, 784 1843 akolb # and has an i387 compatible floating point processor. 785 1843 akolb # 786 1843 akolb # This works in the same way as 1), just more kstat fields are massaged in the 787 1843 akolb # output. 788 1843 akolb # 789 1843 akolb # 5) With -vp option, it reports additional information about each physical 790 1843 akolb # processor. This information includes information about sub-components of 791 1843 akolb # each physical processor and virtual CPUs in each sub-component. For 792 1843 akolb # example: 793 1843 akolb # 794 1843 akolb # The physical processor has 2 cores and 4 virtual processors (0-3) 795 1843 akolb # The core has 2 virtual processors (0 1) 796 1843 akolb # The core has 2 virtual processors (2 3) 797 1843 akolb # x86 (GenuineIntel family 15 model 4 step 4 clock 3211 MHz) 798 1843 akolb # Intel(r) Pentium(r) D CPU 3.20GHz 799 1843 akolb # 800 1843 akolb # The implementation does not know anything about physical CPU components 801 1843 akolb # such as cores. Instead it looks at various cpu_info kstat statistics that 802 1843 akolb # look like xxx_id and tries to reconstruct the CPU hierarchy based on these 803 1843 akolb # fields. This works as follows: 804 1843 akolb # 805 1843 akolb # a) All kstats statistic names matching the $valid_id_exp regular expression 806 1843 akolb # are examined and each kstat statistic name is associated with the number 807 1843 akolb # of distinct entries in it. 808 1843 akolb # 809 1843 akolb # b) The resulting list of kstat statistic names is sorted according to the 810 1843 akolb # number of distinct entries, matching each name. For example, there are 811 1843 akolb # fewer chip_id values than core_id values. This implies that the core is 812 1843 akolb # a sub-component of a chip. 813 1843 akolb # 814 1843 akolb # c) All kstat names that have the same number of values as the number of 815 1843 akolb # physical processors ('chip_id' values) or the number of virtual 816 1843 akolb # processors are removed from the list. 817 1843 akolb # 818 1843 akolb # d) The resulting list represents the CPU hierarchy of the machine. It is 819 1843 akolb # translated into a tree showing the hardware hierarchy. Each level of the 820 1843 akolb # hierarchy contains the name, reference to a list of CPUs at this level 821 1843 akolb # and subcomponents, indexed by the value of each component. 822 1843 akolb # The example system above is represented by the following tree: 823 1843 akolb # 824 1843 akolb # $tree = 825 1843 akolb # { 826 1843 akolb # 'name' => 'chip_id', 827 1843 akolb # 'cpus' => [ '0', '1', '2', '3' ] 828 1843 akolb # 'values' => 829 1843 akolb # { 830 1843 akolb # '0' => 831 1843 akolb # { 832 1843 akolb # 'name' => 'core_id', 833 1843 akolb # 'cpus' => [ '0', '1', '2', '3' ] 834 1843 akolb # 'values' => 835 1843 akolb # { 836 1843 akolb # '0' => { 'cpus' => [ '0', '1' ] } 837 1843 akolb # '1' => { 'cpus' => [ '2', '3' ] }, 838 1843 akolb # }, 839 1843 akolb # } 840 1843 akolb # }, 841 1843 akolb # }; 842 1843 akolb # 843 1843 akolb # Each node contains reference to a list of virtual CPUs at this level of 844 1843 akolb # hierarchy - one list for a system as a whole, one for chip 0 and one two 845 1843 akolb # for each cores. node. Non-leaf nodes also contain the symbolic name of 846 1843 akolb # the component as represented in the cpu_info kstat and a hash of 847 1843 akolb # subnodes, indexed by the value of the component. The tree is built by 848 1843 akolb # the build_component_tree() function. 849 1843 akolb # 850 1843 akolb # e) The resulting tree is pretty-printed showing the number of 851 1843 akolb # sub-components and virtual CPUs in each sub-component. The tree is 852 1843 akolb # printed by the print_component_tree() function. 853 1843 akolb # 854