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# pgstat - tool for displaying Processor Group statistics 29*d3c97224SAlexander Kolbasov# 30*d3c97224SAlexander Kolbasov 31*d3c97224SAlexander Kolbasovuse warnings; 32*d3c97224SAlexander Kolbasovuse strict; 33*d3c97224SAlexander Kolbasovuse File::Basename; 34*d3c97224SAlexander Kolbasovuse List::Util qw(first max min); 35*d3c97224SAlexander Kolbasovuse Errno; 36*d3c97224SAlexander Kolbasovuse POSIX qw(locale_h strftime); 37*d3c97224SAlexander Kolbasovuse Getopt::Long qw(:config no_ignore_case bundling auto_version); 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 section 43*d3c97224SAlexander Kolbasov# 44*d3c97224SAlexander Kolbasov# It is possible that wnen trying to parse PG kstats, 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 Kolbasov# DEFAULT_INTERVAL - interval in seconds between snapshot if none is specified 59*d3c97224SAlexander Kolbasov# DEFAULT_COUNT - Number of iterations if none is specified 60*d3c97224SAlexander Kolbasov# HWLOAD_UNKNOWN - Value that we use to represent unknown hardware load 61*d3c97224SAlexander Kolbasov# HWLOAD_UNDEF - Value that we use to represent undefined hardware load 62*d3c97224SAlexander Kolbasov# 63*d3c97224SAlexander Kolbasovuse constant { 64*d3c97224SAlexander Kolbasov VERSION => 1.1, 65*d3c97224SAlexander Kolbasov DEFAULT_INTERVAL => 1, 66*d3c97224SAlexander Kolbasov DEFAULT_COUNT => 1, 67*d3c97224SAlexander Kolbasov RETRY_COUNT => 4, 68*d3c97224SAlexander Kolbasov RETRY_DELAY => 0.25, 69*d3c97224SAlexander Kolbasov HWLOAD_UNKNOWN => -1, 70*d3c97224SAlexander Kolbasov HWLOAD_UNDEF => -2, 71*d3c97224SAlexander Kolbasov LEVEL_OFFSET => 1, 72*d3c97224SAlexander Kolbasov}; 73*d3c97224SAlexander Kolbasov 74*d3c97224SAlexander Kolbasov# 75*d3c97224SAlexander Kolbasov# Format for fields, showing percentage headers 76*d3c97224SAlexander Kolbasov# 77*d3c97224SAlexander Kolbasovmy $pcnt_fmt = "%6s"; 78*d3c97224SAlexander Kolbasov# 79*d3c97224SAlexander Kolbasov# Format for percentages field 80*d3c97224SAlexander Kolbasov# 81*d3c97224SAlexander Kolbasovmy $pcnt = "%5.1f"; 82*d3c97224SAlexander Kolbasov 83*d3c97224SAlexander Kolbasov# 84*d3c97224SAlexander Kolbasov# Return codes 85*d3c97224SAlexander Kolbasov# 86*d3c97224SAlexander Kolbasov# 0 Successful completion. 87*d3c97224SAlexander Kolbasov# 88*d3c97224SAlexander Kolbasov# 1 An error occurred. 89*d3c97224SAlexander Kolbasov# 90*d3c97224SAlexander Kolbasov# 2 Invalid command-line options were specified. 91*d3c97224SAlexander Kolbasov# 92*d3c97224SAlexander Kolbasovuse constant { 93*d3c97224SAlexander Kolbasov E_SUCCESS => 0, 94*d3c97224SAlexander Kolbasov E_ERROR => 1, 95*d3c97224SAlexander Kolbasov E_USAGE => 2, 96*d3c97224SAlexander Kolbasov}; 97*d3c97224SAlexander Kolbasov 98*d3c97224SAlexander Kolbasov# 99*d3c97224SAlexander Kolbasov# Valid sort keys for -s and -S options 100*d3c97224SAlexander Kolbasov# 101*d3c97224SAlexander Kolbasovmy @sort_keys = qw(pg hwload swload user sys idle depth breadth); 102*d3c97224SAlexander Kolbasov 103*d3c97224SAlexander Kolbasov# Set message locale 104*d3c97224SAlexander Kolbasovsetlocale(LC_ALL, ""); 105*d3c97224SAlexander Kolbasovtextdomain(TEXT_DOMAIN); 106*d3c97224SAlexander Kolbasov 107*d3c97224SAlexander Kolbasov# Get script name for error messages 108*d3c97224SAlexander Kolbasovour $cmdname = basename($0, ".pl"); 109*d3c97224SAlexander Kolbasov 110*d3c97224SAlexander Kolbasovmy @pg_list; # -P pg,... - PG arguments 111*d3c97224SAlexander Kolbasovmy @cpu_list; # -c cpu,... - CPU arguments 112*d3c97224SAlexander Kolbasovmy @sharing_filter_neg; # -R string,... - Prune PGs 113*d3c97224SAlexander Kolbasovmy @sharing_filter; # -r string,... - Matching sharing names 114*d3c97224SAlexander Kolbasovmy $do_aggregate; # -A - Show summary in the end 115*d3c97224SAlexander Kolbasovmy $do_cpu_utilization; # -C - Show per-CPU utilization 116*d3c97224SAlexander Kolbasovmy $do_physical; # -p - Show physical relationships 117*d3c97224SAlexander Kolbasovmy $do_timestamp; # -T - Print timestamp 118*d3c97224SAlexander Kolbasovmy $do_usage; # -h - Show usage 119*d3c97224SAlexander Kolbasovmy $do_version; # -V - Verbose output 120*d3c97224SAlexander Kolbasovmy $show_top; # -t - show top N 121*d3c97224SAlexander Kolbasovmy $sort_order_a; # -S key - Ascending sort order 122*d3c97224SAlexander Kolbasovmy $sort_order_d; # -s key - Descending sort order 123*d3c97224SAlexander Kolbasovmy $verbose; # -v - Verbose output; 124*d3c97224SAlexander Kolbasov 125*d3c97224SAlexander Kolbasov$verbose = 0; 126*d3c97224SAlexander Kolbasov 127*d3c97224SAlexander Kolbasov# Parse options from the command line 128*d3c97224SAlexander KolbasovGetOptions("aggregate|A" => \$do_aggregate, 129*d3c97224SAlexander Kolbasov "cpus|c=s" => \@cpu_list, 130*d3c97224SAlexander Kolbasov "showcpu|C" => \$do_cpu_utilization, 131*d3c97224SAlexander Kolbasov "help|h|?" => \$do_usage, 132*d3c97224SAlexander Kolbasov "pgs|P=s" => \@pg_list, 133*d3c97224SAlexander Kolbasov "physical|p" => \$do_physical, 134*d3c97224SAlexander Kolbasov "relationship|r=s" => \@sharing_filter, 135*d3c97224SAlexander Kolbasov "norelationship|R=s" => \@sharing_filter_neg, 136*d3c97224SAlexander Kolbasov "sort|s=s" => \$sort_order_d, 137*d3c97224SAlexander Kolbasov "Sort|S=s" => \$sort_order_a, 138*d3c97224SAlexander Kolbasov "top|t=i" => \$show_top, 139*d3c97224SAlexander Kolbasov "timestamp|T=s" => \$do_timestamp, 140*d3c97224SAlexander Kolbasov "version|V" => \$do_version, 141*d3c97224SAlexander Kolbasov "verbose+" => \$verbose, 142*d3c97224SAlexander Kolbasov "v+" => \$verbose, 143*d3c97224SAlexander Kolbasov) || usage(E_USAGE); 144*d3c97224SAlexander Kolbasov 145*d3c97224SAlexander Kolbasov# Print usage message when -h is given 146*d3c97224SAlexander Kolbasovusage(E_SUCCESS) if $do_usage; 147*d3c97224SAlexander Kolbasov 148*d3c97224SAlexander Kolbasovif ($do_version) { 149*d3c97224SAlexander Kolbasov printf gettext("%s version %s\n"), $cmdname, VERSION; 150*d3c97224SAlexander Kolbasov exit(E_SUCCESS); 151*d3c97224SAlexander Kolbasov} 152*d3c97224SAlexander Kolbasov 153*d3c97224SAlexander Kolbasov# 154*d3c97224SAlexander Kolbasov# Verify options 155*d3c97224SAlexander Kolbasov# 156*d3c97224SAlexander Kolbasov# -T should have either u or d argument 157*d3c97224SAlexander Kolbasovif (defined($do_timestamp) && !($do_timestamp eq 'u' || $do_timestamp eq 'd')) { 158*d3c97224SAlexander Kolbasov printf STDERR gettext("%s: Invalid -T %s argument\n"), 159*d3c97224SAlexander Kolbasov $cmdname, $do_timestamp; 160*d3c97224SAlexander Kolbasov usage(E_USAGE); 161*d3c97224SAlexander Kolbasov} 162*d3c97224SAlexander Kolbasov 163*d3c97224SAlexander Kolbasovif ($sort_order_a && $sort_order_d) { 164*d3c97224SAlexander Kolbasov printf STDERR gettext("%s: -S and -s flags can not be used together\n"), 165*d3c97224SAlexander Kolbasov $cmdname; 166*d3c97224SAlexander Kolbasov usage(E_USAGE); 167*d3c97224SAlexander Kolbasov} 168*d3c97224SAlexander Kolbasov 169*d3c97224SAlexander Kolbasovif (defined ($show_top) && $show_top <= 0) { 170*d3c97224SAlexander Kolbasov printf STDERR gettext("%s: -t should specify positive integer\n"), 171*d3c97224SAlexander Kolbasov $cmdname; 172*d3c97224SAlexander Kolbasov usage(E_USAGE); 173*d3c97224SAlexander Kolbasov} 174*d3c97224SAlexander Kolbasov 175*d3c97224SAlexander Kolbasov# 176*d3c97224SAlexander Kolbasov# Figure out requested sorting of the output 177*d3c97224SAlexander Kolbasov# By default 'depth-first' is used 178*d3c97224SAlexander Kolbasov# 179*d3c97224SAlexander Kolbasovmy $sort_key; 180*d3c97224SAlexander Kolbasovmy $sort_reverse; 181*d3c97224SAlexander Kolbasov 182*d3c97224SAlexander Kolbasovif (!($sort_order_a || $sort_order_d)) { 183*d3c97224SAlexander Kolbasov $sort_key = 'depth'; 184*d3c97224SAlexander Kolbasov $sort_reverse = 1; 185*d3c97224SAlexander Kolbasov} else { 186*d3c97224SAlexander Kolbasov $sort_key = $sort_order_d || $sort_order_a; 187*d3c97224SAlexander Kolbasov $sort_reverse = defined($sort_order_d); 188*d3c97224SAlexander Kolbasov} 189*d3c97224SAlexander Kolbasov 190*d3c97224SAlexander Kolbasov# 191*d3c97224SAlexander Kolbasov# Make sure sort key is valid 192*d3c97224SAlexander Kolbasov# 193*d3c97224SAlexander Kolbasovif (!list_match($sort_key, \@sort_keys, 1)) { 194*d3c97224SAlexander Kolbasov printf STDERR gettext("%s: invalid sort key %s\n"), 195*d3c97224SAlexander Kolbasov $cmdname, $sort_key; 196*d3c97224SAlexander Kolbasov usage(E_USAGE); 197*d3c97224SAlexander Kolbasov} 198*d3c97224SAlexander Kolbasov 199*d3c97224SAlexander Kolbasov# 200*d3c97224SAlexander Kolbasov# Convert -[Rr] string1,string2,... into list (string1, string2, ...) 201*d3c97224SAlexander Kolbasov# 202*d3c97224SAlexander Kolbasov@sharing_filter = map { split /,/ } @sharing_filter; 203*d3c97224SAlexander Kolbasov@sharing_filter_neg = map { split /,/ } @sharing_filter_neg; 204*d3c97224SAlexander Kolbasov 205*d3c97224SAlexander Kolbasov# 206*d3c97224SAlexander Kolbasov# We use two PG snapshot to compare utilization between them. One snapshot is 207*d3c97224SAlexander Kolbasov# kept behind another in time. 208*d3c97224SAlexander Kolbasov# 209*d3c97224SAlexander Kolbasovmy $p = Sun::Solaris::Pg->new(-cpudata => $do_cpu_utilization, 210*d3c97224SAlexander Kolbasov -swload => 1, 211*d3c97224SAlexander Kolbasov -tags => $do_physical, 212*d3c97224SAlexander Kolbasov -retry => RETRY_COUNT, 213*d3c97224SAlexander Kolbasov -delay => RETRY_DELAY); 214*d3c97224SAlexander Kolbasov 215*d3c97224SAlexander Kolbasovif (!$p) { 216*d3c97224SAlexander Kolbasov printf STDERR 217*d3c97224SAlexander Kolbasov gettext("%s: can not obtain Processor Group information: $!\n"), 218*d3c97224SAlexander Kolbasov $cmdname; 219*d3c97224SAlexander Kolbasov exit(E_ERROR); 220*d3c97224SAlexander Kolbasov} 221*d3c97224SAlexander Kolbasov 222*d3c97224SAlexander Kolbasovmy $p_initial = $p; 223*d3c97224SAlexander Kolbasovmy $p_dup = Sun::Solaris::Pg->new(-cpudata => $do_cpu_utilization, 224*d3c97224SAlexander Kolbasov -swload => 1, 225*d3c97224SAlexander Kolbasov -tags => $do_physical, 226*d3c97224SAlexander Kolbasov -retry => RETRY_COUNT, 227*d3c97224SAlexander Kolbasov -delay => RETRY_DELAY); 228*d3c97224SAlexander Kolbasov 229*d3c97224SAlexander Kolbasovif (!$p_dup) { 230*d3c97224SAlexander Kolbasov printf STDERR 231*d3c97224SAlexander Kolbasov gettext("%s: can not obtain Processor Group information: $!\n"), 232*d3c97224SAlexander Kolbasov $cmdname; 233*d3c97224SAlexander Kolbasov exit(E_ERROR); 234*d3c97224SAlexander Kolbasov} 235*d3c97224SAlexander Kolbasov 236*d3c97224SAlexander Kolbasov# 237*d3c97224SAlexander Kolbasov# Get interval and count 238*d3c97224SAlexander Kolbasov# 239*d3c97224SAlexander Kolbasovmy $count = DEFAULT_COUNT; 240*d3c97224SAlexander Kolbasovmy $interval = DEFAULT_INTERVAL; 241*d3c97224SAlexander Kolbasov 242*d3c97224SAlexander Kolbasovif (scalar @ARGV > 0) { 243*d3c97224SAlexander Kolbasov $interval = shift @ARGV; 244*d3c97224SAlexander Kolbasov if (scalar @ARGV > 0) { 245*d3c97224SAlexander Kolbasov $count = $ARGV[0]; 246*d3c97224SAlexander Kolbasov } else { 247*d3c97224SAlexander Kolbasov $count = 0; 248*d3c97224SAlexander Kolbasov } 249*d3c97224SAlexander Kolbasov} 250*d3c97224SAlexander Kolbasov 251*d3c97224SAlexander Kolbasovif (! ($interval=~ m/^\d+\.?\d*$/)) { 252*d3c97224SAlexander Kolbasov printf STDERR 253*d3c97224SAlexander Kolbasov gettext("%s: Invalid interval %s - should be numeric\n"), 254*d3c97224SAlexander Kolbasov $cmdname, $interval; 255*d3c97224SAlexander Kolbasov usage(E_USAGE); 256*d3c97224SAlexander Kolbasov} 257*d3c97224SAlexander Kolbasov 258*d3c97224SAlexander Kolbasovif ($count && ! ($count=~ m/^\d+$/)) { 259*d3c97224SAlexander Kolbasov printf STDERR 260*d3c97224SAlexander Kolbasov gettext("%s: Invalid count %s - should be numeric\n"), 261*d3c97224SAlexander Kolbasov $cmdname, $count; 262*d3c97224SAlexander Kolbasov usage(E_USAGE); 263*d3c97224SAlexander Kolbasov} 264*d3c97224SAlexander Kolbasov 265*d3c97224SAlexander Kolbasovmy $infinite = 1 unless $count; 266*d3c97224SAlexander Kolbasov 267*d3c97224SAlexander Kolbasov# 268*d3c97224SAlexander Kolbasov# Get list of all PGs 269*d3c97224SAlexander Kolbasov# 270*d3c97224SAlexander Kolbasovmy @all_pgs = $p->all_depth_first(); 271*d3c97224SAlexander Kolbasov 272*d3c97224SAlexander Kolbasov# 273*d3c97224SAlexander Kolbasov# get list of all CPUs in the system by looking at the root PG cpus 274*d3c97224SAlexander Kolbasov# 275*d3c97224SAlexander Kolbasovmy @all_cpus = $p->cpus($p->root()); 276*d3c97224SAlexander Kolbasov 277*d3c97224SAlexander Kolbasov# PGs to work with 278*d3c97224SAlexander Kolbasovmy @pgs = @all_pgs; 279*d3c97224SAlexander Kolbasov 280*d3c97224SAlexander Kolbasovmy $rc = E_SUCCESS; 281*d3c97224SAlexander Kolbasov 282*d3c97224SAlexander Kolbasov# 283*d3c97224SAlexander Kolbasov# Convert CPU and PG lists into proper Perl lists, converting things like 284*d3c97224SAlexander Kolbasov# 1-3,5 into (1, 2, 3, 5). Also convert 'all' into the list of all CPUs or PGs 285*d3c97224SAlexander Kolbasov# 286*d3c97224SAlexander Kolbasov@cpu_list = 287*d3c97224SAlexander Kolbasov map { $_ eq 'all' ? @all_cpus : $_ } # all -> (cpu1, cpu2, ...) 288*d3c97224SAlexander Kolbasov map { split /,/ } @cpu_list; # x,y -> (x, y) 289*d3c97224SAlexander Kolbasov 290*d3c97224SAlexander Kolbasov@cpu_list = $p->expand(@cpu_list); # 1-3 -> 1 2 3 291*d3c97224SAlexander Kolbasov 292*d3c97224SAlexander Kolbasov# Same drill for PGs 293*d3c97224SAlexander Kolbasov@pg_list = 294*d3c97224SAlexander Kolbasov map { $_ eq 'all' ? @all_pgs : $_ } 295*d3c97224SAlexander Kolbasov map { split /,/ } @pg_list; 296*d3c97224SAlexander Kolbasov 297*d3c97224SAlexander Kolbasov@pg_list = $p->expand(@pg_list); 298*d3c97224SAlexander Kolbasov 299*d3c97224SAlexander Kolbasov# 300*d3c97224SAlexander Kolbasov# Convert CPU list to list of PGs 301*d3c97224SAlexander Kolbasov# 302*d3c97224SAlexander Kolbasovif (scalar @cpu_list) { 303*d3c97224SAlexander Kolbasov 304*d3c97224SAlexander Kolbasov # 305*d3c97224SAlexander Kolbasov # Warn about any invalid CPU IDs in the arguments 306*d3c97224SAlexander Kolbasov # @bad_cpus is a list of invalid CPU IDs 307*d3c97224SAlexander Kolbasov # 308*d3c97224SAlexander Kolbasov my @bad_cpus = $p->set_subtract(\@all_cpus, \@cpu_list); 309*d3c97224SAlexander Kolbasov if (scalar @bad_cpus) { 310*d3c97224SAlexander Kolbasov printf STDERR 311*d3c97224SAlexander Kolbasov gettext("%s: Invalid processor IDs %s\n"), 312*d3c97224SAlexander Kolbasov $cmdname, $p->id_collapse(@bad_cpus); 313*d3c97224SAlexander Kolbasov $rc = E_ERROR; 314*d3c97224SAlexander Kolbasov } 315*d3c97224SAlexander Kolbasov 316*d3c97224SAlexander Kolbasov # 317*d3c97224SAlexander Kolbasov # Find all PGs which have at least some CPUs from @cpu_list 318*d3c97224SAlexander Kolbasov # 319*d3c97224SAlexander Kolbasov my @pgs_from_cpus = grep { 320*d3c97224SAlexander Kolbasov my @cpus = $p->cpus($_); 321*d3c97224SAlexander Kolbasov scalar($p->intersect(\@cpus, \@cpu_list)); 322*d3c97224SAlexander Kolbasov } @all_pgs; 323*d3c97224SAlexander Kolbasov 324*d3c97224SAlexander Kolbasov # Combine PGs from @pg_list (if any) with PGs we found 325*d3c97224SAlexander Kolbasov @pg_list = (@pg_list, @pgs_from_cpus); 326*d3c97224SAlexander Kolbasov} 327*d3c97224SAlexander Kolbasov 328*d3c97224SAlexander Kolbasov# 329*d3c97224SAlexander Kolbasov# If there are any PGs specified by the user, complain about invalid ones 330*d3c97224SAlexander Kolbasov# 331*d3c97224SAlexander Kolbasov@pgs = get_pg_list($p, \@pg_list, \@sharing_filter, \@sharing_filter_neg); 332*d3c97224SAlexander Kolbasov 333*d3c97224SAlexander Kolbasovif (scalar @pg_list > 0) { 334*d3c97224SAlexander Kolbasov # 335*d3c97224SAlexander Kolbasov # Warn about any invalid PG 336*d3c97224SAlexander Kolbasov # @bad_pgs is a list of invalid CPUs in the arguments 337*d3c97224SAlexander Kolbasov # 338*d3c97224SAlexander Kolbasov my @bad_pgs = $p->set_subtract(\@all_pgs, \@pg_list); 339*d3c97224SAlexander Kolbasov if (scalar @bad_pgs) { 340*d3c97224SAlexander Kolbasov printf STDERR 341*d3c97224SAlexander Kolbasov gettext("%s: warning: invalid PG IDs %s\n"), 342*d3c97224SAlexander Kolbasov $cmdname, $p->id_collapse(@bad_pgs); 343*d3c97224SAlexander Kolbasov } 344*d3c97224SAlexander Kolbasov} 345*d3c97224SAlexander Kolbasov 346*d3c97224SAlexander Kolbasov# Do we have any PGs left? 347*d3c97224SAlexander Kolbasovif (scalar(@pgs) == 0) { 348*d3c97224SAlexander Kolbasov printf STDERR 349*d3c97224SAlexander Kolbasov gettext("%s: No processor groups matching command line arguments\n"), 350*d3c97224SAlexander Kolbasov $cmdname; 351*d3c97224SAlexander Kolbasov exit(E_USAGE); 352*d3c97224SAlexander Kolbasov} 353*d3c97224SAlexander Kolbasov 354*d3c97224SAlexander Kolbasov# 355*d3c97224SAlexander Kolbasov# Set $do_levels if we should provide output identation by level It doesn't make 356*d3c97224SAlexander Kolbasov# sense to provide identation if PGs are sorted not in topology order. 357*d3c97224SAlexander Kolbasov# 358*d3c97224SAlexander Kolbasovmy $do_levels = ($sort_key eq 'breadth' || $sort_key eq 'depth'); 359*d3c97224SAlexander Kolbasov 360*d3c97224SAlexander Kolbasov# 361*d3c97224SAlexander Kolbasov# %name_of_pg hash keeps sharing name, possibly with physical tags appended to 362*d3c97224SAlexander Kolbasov# it for each PG. 363*d3c97224SAlexander Kolbasov# 364*d3c97224SAlexander Kolbasovmy %name_of_pg; 365*d3c97224SAlexander Kolbasov 366*d3c97224SAlexander Kolbasov# 367*d3c97224SAlexander Kolbasov# For calculating proper offsets we need to know minimum and maximum level for 368*d3c97224SAlexander Kolbasov# all PGs 369*d3c97224SAlexander Kolbasov# 370*d3c97224SAlexander Kolbasovmy $max_sharename_len = length('RELATIONSHIP'); 371*d3c97224SAlexander Kolbasov 372*d3c97224SAlexander Kolbasovmy $maxlevel; 373*d3c97224SAlexander Kolbasovmy $minlevel; 374*d3c97224SAlexander Kolbasov 375*d3c97224SAlexander Kolbasovif ($do_levels) { 376*d3c97224SAlexander Kolbasov my @levels = map { $p->level($_) } @pgs; # Levels for each PG 377*d3c97224SAlexander Kolbasov $maxlevel = max(@levels); 378*d3c97224SAlexander Kolbasov $minlevel = min(@levels); 379*d3c97224SAlexander Kolbasov} 380*d3c97224SAlexander Kolbasov 381*d3c97224SAlexander Kolbasov# 382*d3c97224SAlexander Kolbasov# Walk over all PGs and find out the string length that we need to represent 383*d3c97224SAlexander Kolbasov# sharing name + physical tags + indentation level. 384*d3c97224SAlexander Kolbasov# 385*d3c97224SAlexander Kolbasovforeach my $pg (@pgs) { 386*d3c97224SAlexander Kolbasov my $name = $p->sh_name ($pg) || "unknown"; 387*d3c97224SAlexander Kolbasov my $level = $p->level($pg) || 0 if $do_levels; 388*d3c97224SAlexander Kolbasov 389*d3c97224SAlexander Kolbasov if ($do_physical) { 390*d3c97224SAlexander Kolbasov my $tags = $p->tags($pg); 391*d3c97224SAlexander Kolbasov $name = "$name [$tags]" if $tags; 392*d3c97224SAlexander Kolbasov $name_of_pg{$pg} = $name; 393*d3c97224SAlexander Kolbasov } 394*d3c97224SAlexander Kolbasov 395*d3c97224SAlexander Kolbasov $name_of_pg{$pg} = $name; 396*d3c97224SAlexander Kolbasov my $length = length($name); 397*d3c97224SAlexander Kolbasov $length += $level - $minlevel if $do_levels; 398*d3c97224SAlexander Kolbasov $max_sharename_len = $length if $length > $max_sharename_len; 399*d3c97224SAlexander Kolbasov} 400*d3c97224SAlexander Kolbasov 401*d3c97224SAlexander Kolbasov# Maximum length of PG ID field 402*d3c97224SAlexander Kolbasovmy $max_pg_len = length(max(@pgs)) + 1; 403*d3c97224SAlexander Kolbasov$max_pg_len = length('PG') if ($max_pg_len) < length('PG'); 404*d3c97224SAlexander Kolbasov 405*d3c97224SAlexander Kolbasov# 406*d3c97224SAlexander Kolbasov# 407*d3c97224SAlexander Kolbasov# %pgs hash contains various statistics per PG that is used for sorting. 408*d3c97224SAlexander Kolbasovmy %pgs; 409*d3c97224SAlexander Kolbasov 410*d3c97224SAlexander Kolbasov# Total number of main loop iterations we actually do 411*d3c97224SAlexander Kolbasovmy $total_iterations = 0; 412*d3c97224SAlexander Kolbasov 413*d3c97224SAlexander Kolbasov# 414*d3c97224SAlexander Kolbasov# For summary, keep track of minimum and maximum data per PG 415*d3c97224SAlexander Kolbasov# 416*d3c97224SAlexander Kolbasovmy $history; 417*d3c97224SAlexander Kolbasov 418*d3c97224SAlexander Kolbasov# 419*d3c97224SAlexander Kolbasov# Provide summary output when aggregation is requested and user hits ^C 420*d3c97224SAlexander Kolbasov# 421*d3c97224SAlexander Kolbasov$SIG{'INT'} = \&print_totals if $do_aggregate; 422*d3c97224SAlexander Kolbasov 423*d3c97224SAlexander Kolbasov###################################################################### 424*d3c97224SAlexander Kolbasov# Main loop 425*d3c97224SAlexander Kolbasov########### 426*d3c97224SAlexander Kolbasov 427*d3c97224SAlexander Kolbasovwhile ($infinite || $count--) { 428*d3c97224SAlexander Kolbasov # 429*d3c97224SAlexander Kolbasov # Print timestamp if -T is specified 430*d3c97224SAlexander Kolbasov # 431*d3c97224SAlexander Kolbasov if ($do_timestamp) { 432*d3c97224SAlexander Kolbasov if ($do_timestamp eq 'u') { 433*d3c97224SAlexander Kolbasov print time(), "\n"; 434*d3c97224SAlexander Kolbasov } else { 435*d3c97224SAlexander Kolbasov my $date_str = strftime "%A, %B %e, %Y %r %Z", 436*d3c97224SAlexander Kolbasov localtime; 437*d3c97224SAlexander Kolbasov print "$date_str\n"; 438*d3c97224SAlexander Kolbasov } 439*d3c97224SAlexander Kolbasov } 440*d3c97224SAlexander Kolbasov 441*d3c97224SAlexander Kolbasov # 442*d3c97224SAlexander Kolbasov # Wait for the requested interval 443*d3c97224SAlexander Kolbasov # 444*d3c97224SAlexander Kolbasov select(undef, undef, undef, $interval); 445*d3c97224SAlexander Kolbasov 446*d3c97224SAlexander Kolbasov # 447*d3c97224SAlexander Kolbasov # Print headers 448*d3c97224SAlexander Kolbasov # There are two different output formats - one regular and one verbose 449*d3c97224SAlexander Kolbasov # 450*d3c97224SAlexander Kolbasov if (!$verbose) { 451*d3c97224SAlexander Kolbasov printf "%-${max_pg_len}s %-${max_sharename_len}s ". 452*d3c97224SAlexander Kolbasov "$pcnt_fmt $pcnt_fmt %-s\n", 453*d3c97224SAlexander Kolbasov 'PG', 'RELATIONSHIP', 'HW', 'SW', 'CPUS'; 454*d3c97224SAlexander Kolbasov } else { 455*d3c97224SAlexander Kolbasov printf "%-${max_pg_len}s %-${max_sharename_len}s" . 456*d3c97224SAlexander Kolbasov " $pcnt_fmt %4s %4s $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt %s\n", 457*d3c97224SAlexander Kolbasov 'PG','RELATIONSHIP', 458*d3c97224SAlexander Kolbasov 'HW', 'UTIL', 'CAP', 459*d3c97224SAlexander Kolbasov 'SW', 'USR', 'SYS', 'IDLE', 'CPUS'; 460*d3c97224SAlexander Kolbasov } 461*d3c97224SAlexander Kolbasov 462*d3c97224SAlexander Kolbasov # 463*d3c97224SAlexander Kolbasov # Update the data in one of the snapshots 464*d3c97224SAlexander Kolbasov # 465*d3c97224SAlexander Kolbasov $p_dup->update(); 466*d3c97224SAlexander Kolbasov 467*d3c97224SAlexander Kolbasov # 468*d3c97224SAlexander Kolbasov # Do not show offlined CPUs 469*d3c97224SAlexander Kolbasov # 470*d3c97224SAlexander Kolbasov my @online_cpus = $p->online_cpus(); 471*d3c97224SAlexander Kolbasov 472*d3c97224SAlexander Kolbasov # 473*d3c97224SAlexander Kolbasov # Check whether both snapshots belong to the same generation 474*d3c97224SAlexander Kolbasov # 475*d3c97224SAlexander Kolbasov if ($p->generation() != $p_dup->generation()) { 476*d3c97224SAlexander Kolbasov printf gettext("Configuration changed!\n"); 477*d3c97224SAlexander Kolbasov # Swap $p and $p_dup; 478*d3c97224SAlexander Kolbasov $p = $p_dup; 479*d3c97224SAlexander Kolbasov $p_dup = Sun::Solaris::Pg->new( 480*d3c97224SAlexander Kolbasov -cpudata => $do_cpu_utilization, 481*d3c97224SAlexander Kolbasov -swload => 1, 482*d3c97224SAlexander Kolbasov -tags => $do_physical, 483*d3c97224SAlexander Kolbasov -retry => RETRY_COUNT, 484*d3c97224SAlexander Kolbasov -delay => RETRY_DELAY); 485*d3c97224SAlexander Kolbasov if (!$p_dup) { 486*d3c97224SAlexander Kolbasov printf STDERR gettext( 487*d3c97224SAlexander Kolbasov "%s: can not obtain Processor Group information: $!\n"), 488*d3c97224SAlexander Kolbasov $cmdname; 489*d3c97224SAlexander Kolbasov exit(E_ERROR); 490*d3c97224SAlexander Kolbasov } 491*d3c97224SAlexander Kolbasov # 492*d3c97224SAlexander Kolbasov # Recreate @pg_list since it may have changed 493*d3c97224SAlexander Kolbasov # 494*d3c97224SAlexander Kolbasov @pgs = get_pg_list($p, \@pg_list, 495*d3c97224SAlexander Kolbasov \@sharing_filter, \@sharing_filter_neg); 496*d3c97224SAlexander Kolbasov 497*d3c97224SAlexander Kolbasov next; 498*d3c97224SAlexander Kolbasov } 499*d3c97224SAlexander Kolbasov 500*d3c97224SAlexander Kolbasov %pgs = (); 501*d3c97224SAlexander Kolbasov 502*d3c97224SAlexander Kolbasov # 503*d3c97224SAlexander Kolbasov # Go over each PG and gets its utilization data 504*d3c97224SAlexander Kolbasov # 505*d3c97224SAlexander Kolbasov foreach my $pg (@pgs) { 506*d3c97224SAlexander Kolbasov my ($hwload, $utilization, $capacity, $accuracy) = 507*d3c97224SAlexander Kolbasov get_load($p, $p_dup, $pg); 508*d3c97224SAlexander Kolbasov my @cpus = $p->cpus ($pg); 509*d3c97224SAlexander Kolbasov my ($user, $sys, $idle, $swload) = 510*d3c97224SAlexander Kolbasov $p->sw_utilization($p_dup, $pg); 511*d3c97224SAlexander Kolbasov 512*d3c97224SAlexander Kolbasov # Adjust idle and swload based on rounding 513*d3c97224SAlexander Kolbasov ($swload, $idle) = get_swload($user, $sys); 514*d3c97224SAlexander Kolbasov 515*d3c97224SAlexander Kolbasov $pgs{$pg}->{pg} = $pg; 516*d3c97224SAlexander Kolbasov $pgs{$pg}->{hwload} = $hwload; 517*d3c97224SAlexander Kolbasov $pgs{$pg}->{swload} = $swload; 518*d3c97224SAlexander Kolbasov $pgs{$pg}->{user} = $user; 519*d3c97224SAlexander Kolbasov $pgs{$pg}->{sys} = $sys; 520*d3c97224SAlexander Kolbasov $pgs{$pg}->{idle} = $idle; 521*d3c97224SAlexander Kolbasov $pgs{$pg}->{utilization} = $utilization; 522*d3c97224SAlexander Kolbasov $pgs{$pg}->{capacity} = $capacity; 523*d3c97224SAlexander Kolbasov 524*d3c97224SAlexander Kolbasov # 525*d3c97224SAlexander Kolbasov # Record history 526*d3c97224SAlexander Kolbasov # 527*d3c97224SAlexander Kolbasov $history->{$pg}->{hwload} += $hwload if $hwload && $hwload >= 0; 528*d3c97224SAlexander Kolbasov $history->{$pg}->{swload} += $swload if $swload; 529*d3c97224SAlexander Kolbasov $history->{$pg}->{user} += $user if $user; 530*d3c97224SAlexander Kolbasov $history->{$pg}->{sys} += $sys if $sys; 531*d3c97224SAlexander Kolbasov $history->{$pg}->{idle} += $idle if $idle; 532*d3c97224SAlexander Kolbasov $history->{$pg}->{maxhwload} = $hwload if 533*d3c97224SAlexander Kolbasov !defined($history->{$pg}->{maxhwload}) || 534*d3c97224SAlexander Kolbasov $hwload > $history->{$pg}->{maxhwload}; 535*d3c97224SAlexander Kolbasov $history->{$pg}->{minhwload} = $hwload if 536*d3c97224SAlexander Kolbasov !defined($history->{$pg}->{minhwload}) || 537*d3c97224SAlexander Kolbasov $hwload < $history->{$pg}->{minhwload}; 538*d3c97224SAlexander Kolbasov $history->{$pg}->{maxswload} = $swload if 539*d3c97224SAlexander Kolbasov !defined($history->{$pg}->{maxswload}) || 540*d3c97224SAlexander Kolbasov $swload > $history->{$pg}->{maxswload}; 541*d3c97224SAlexander Kolbasov $history->{$pg}->{minswload} = $swload if 542*d3c97224SAlexander Kolbasov !defined($history->{$pg}->{minswload}) || 543*d3c97224SAlexander Kolbasov $swload < $history->{$pg}->{minswload}; 544*d3c97224SAlexander Kolbasov } 545*d3c97224SAlexander Kolbasov 546*d3c97224SAlexander Kolbasov # 547*d3c97224SAlexander Kolbasov # Sort the output 548*d3c97224SAlexander Kolbasov # 549*d3c97224SAlexander Kolbasov my @sorted_pgs; 550*d3c97224SAlexander Kolbasov my $npgs = scalar @pgs; 551*d3c97224SAlexander Kolbasov @sorted_pgs = pg_sort_by_key(\%pgs, $sort_key, $sort_reverse, @pgs); 552*d3c97224SAlexander Kolbasov 553*d3c97224SAlexander Kolbasov # 554*d3c97224SAlexander Kolbasov # Should only top N be displayed? 555*d3c97224SAlexander Kolbasov # 556*d3c97224SAlexander Kolbasov if ($show_top) { 557*d3c97224SAlexander Kolbasov $npgs = $show_top if $show_top < $npgs; 558*d3c97224SAlexander Kolbasov @sorted_pgs = @sorted_pgs[0..$npgs - 1]; 559*d3c97224SAlexander Kolbasov } 560*d3c97224SAlexander Kolbasov 561*d3c97224SAlexander Kolbasov # 562*d3c97224SAlexander Kolbasov # Now print everything 563*d3c97224SAlexander Kolbasov # 564*d3c97224SAlexander Kolbasov foreach my $pg (@sorted_pgs) { 565*d3c97224SAlexander Kolbasov my $shname = $name_of_pg{$pg}; 566*d3c97224SAlexander Kolbasov my $level; 567*d3c97224SAlexander Kolbasov 568*d3c97224SAlexander Kolbasov if ($do_levels) { 569*d3c97224SAlexander Kolbasov $level = $p->level($pg) - $minlevel; 570*d3c97224SAlexander Kolbasov $shname = (' ' x (LEVEL_OFFSET * $level)) . $shname; 571*d3c97224SAlexander Kolbasov } 572*d3c97224SAlexander Kolbasov 573*d3c97224SAlexander Kolbasov my $hwload = $pgs{$pg}->{hwload} || 0; 574*d3c97224SAlexander Kolbasov my $swload = $pgs{$pg}->{swload}; 575*d3c97224SAlexander Kolbasov 576*d3c97224SAlexander Kolbasov my @cpus = $p->cpus($pg); 577*d3c97224SAlexander Kolbasov @cpus = $p->intersect(\@cpus, \@online_cpus); 578*d3c97224SAlexander Kolbasov 579*d3c97224SAlexander Kolbasov my $cpus = $p->id_collapse(@cpus); 580*d3c97224SAlexander Kolbasov my $user = $pgs{$pg}->{user}; 581*d3c97224SAlexander Kolbasov my $sys = $pgs{$pg}->{sys}; 582*d3c97224SAlexander Kolbasov my $idle = $pgs{$pg}->{idle}; 583*d3c97224SAlexander Kolbasov my $utilization = $pgs{$pg}->{utilization}; 584*d3c97224SAlexander Kolbasov my $capacity = $pgs{$pg}->{capacity}; 585*d3c97224SAlexander Kolbasov 586*d3c97224SAlexander Kolbasov if (!$verbose) { 587*d3c97224SAlexander Kolbasov printf "%${max_pg_len}d %-${max_sharename_len}s " . 588*d3c97224SAlexander Kolbasov "%s %s %s\n", 589*d3c97224SAlexander Kolbasov $pg, $shname, 590*d3c97224SAlexander Kolbasov load2str($hwload), 591*d3c97224SAlexander Kolbasov load2str($swload), 592*d3c97224SAlexander Kolbasov $cpus; 593*d3c97224SAlexander Kolbasov } else { 594*d3c97224SAlexander Kolbasov printf 595*d3c97224SAlexander Kolbasov "%${max_pg_len}d %-${max_sharename_len}s " . 596*d3c97224SAlexander Kolbasov "%4s %4s %4s %4s %4s %4s %4s %s\n", 597*d3c97224SAlexander Kolbasov $pg, $shname, 598*d3c97224SAlexander Kolbasov load2str($hwload), 599*d3c97224SAlexander Kolbasov number_to_scaled_string($utilization), 600*d3c97224SAlexander Kolbasov number_to_scaled_string($capacity), 601*d3c97224SAlexander Kolbasov load2str($swload), 602*d3c97224SAlexander Kolbasov load2str($user), 603*d3c97224SAlexander Kolbasov load2str($sys), 604*d3c97224SAlexander Kolbasov load2str($idle), 605*d3c97224SAlexander Kolbasov $cpus; 606*d3c97224SAlexander Kolbasov } 607*d3c97224SAlexander Kolbasov 608*d3c97224SAlexander Kolbasov # 609*d3c97224SAlexander Kolbasov # If per-CPU utilization is requested, print it after each 610*d3c97224SAlexander Kolbasov # corresponding PG 611*d3c97224SAlexander Kolbasov # 612*d3c97224SAlexander Kolbasov if ($do_cpu_utilization) { 613*d3c97224SAlexander Kolbasov my $w = ${max_sharename_len} - length ('CPU'); 614*d3c97224SAlexander Kolbasov foreach my $cpu (sort {$a <=> $b } @cpus) { 615*d3c97224SAlexander Kolbasov my ($cpu_utilization, 616*d3c97224SAlexander Kolbasov $accuracy, $hw_utilization, 617*d3c97224SAlexander Kolbasov $swload) = 618*d3c97224SAlexander Kolbasov $p->cpu_utilization($p_dup, $pg, $cpu); 619*d3c97224SAlexander Kolbasov next unless defined $cpu_utilization; 620*d3c97224SAlexander Kolbasov my $cpuname = "CPU$cpu"; 621*d3c97224SAlexander Kolbasov if ($do_levels) { 622*d3c97224SAlexander Kolbasov $cpuname = 623*d3c97224SAlexander Kolbasov (' ' x (LEVEL_OFFSET * $level)) . 624*d3c97224SAlexander Kolbasov $cpuname; 625*d3c97224SAlexander Kolbasov 626*d3c97224SAlexander Kolbasov } 627*d3c97224SAlexander Kolbasov 628*d3c97224SAlexander Kolbasov printf "%-${max_pg_len}s " . 629*d3c97224SAlexander Kolbasov "%-${max_sharename_len}s ", 630*d3c97224SAlexander Kolbasov ' ', $cpuname; 631*d3c97224SAlexander Kolbasov if ($verbose) { 632*d3c97224SAlexander Kolbasov printf "%s %4s %4s\n", 633*d3c97224SAlexander Kolbasov load2str($cpu_utilization), 634*d3c97224SAlexander Kolbasov number_to_scaled_string($hw_utilization), 635*d3c97224SAlexander Kolbasov number_to_scaled_string($capacity); 636*d3c97224SAlexander Kolbasov } else { 637*d3c97224SAlexander Kolbasov printf "%s %s\n", 638*d3c97224SAlexander Kolbasov load2str($cpu_utilization), 639*d3c97224SAlexander Kolbasov load2str($swload); 640*d3c97224SAlexander Kolbasov } 641*d3c97224SAlexander Kolbasov } 642*d3c97224SAlexander Kolbasov } 643*d3c97224SAlexander Kolbasov } 644*d3c97224SAlexander Kolbasov 645*d3c97224SAlexander Kolbasov # 646*d3c97224SAlexander Kolbasov # Swap $p and $p_dup 647*d3c97224SAlexander Kolbasov # 648*d3c97224SAlexander Kolbasov ($p, $p_dup) = ($p_dup, $p); 649*d3c97224SAlexander Kolbasov 650*d3c97224SAlexander Kolbasov $total_iterations++; 651*d3c97224SAlexander Kolbasov} 652*d3c97224SAlexander Kolbasov 653*d3c97224SAlexander Kolbasovprint_totals() if $do_aggregate; 654*d3c97224SAlexander Kolbasov 655*d3c97224SAlexander Kolbasov 656*d3c97224SAlexander Kolbasov#################################### 657*d3c97224SAlexander Kolbasov# End of main loop 658*d3c97224SAlexander Kolbasov#################################### 659*d3c97224SAlexander Kolbasov 660*d3c97224SAlexander Kolbasov 661*d3c97224SAlexander Kolbasov# 662*d3c97224SAlexander Kolbasov# Support Subroutines 663*d3c97224SAlexander Kolbasov# 664*d3c97224SAlexander Kolbasov 665*d3c97224SAlexander Kolbasov# 666*d3c97224SAlexander Kolbasov# Print aggregated information in the end 667*d3c97224SAlexander Kolbasov# 668*d3c97224SAlexander Kolbasovsub print_totals 669*d3c97224SAlexander Kolbasov{ 670*d3c97224SAlexander Kolbasov exit ($rc) unless $total_iterations > 1; 671*d3c97224SAlexander Kolbasov 672*d3c97224SAlexander Kolbasov printf gettext("\n%s SUMMARY: UTILIZATION OVER %d SECONDS\n\n"), 673*d3c97224SAlexander Kolbasov ' ' x 10, 674*d3c97224SAlexander Kolbasov $total_iterations * $interval; 675*d3c97224SAlexander Kolbasov 676*d3c97224SAlexander Kolbasov my @sorted_pgs; 677*d3c97224SAlexander Kolbasov my $npgs = scalar @pgs; 678*d3c97224SAlexander Kolbasov 679*d3c97224SAlexander Kolbasov %pgs = (); 680*d3c97224SAlexander Kolbasov 681*d3c97224SAlexander Kolbasov # 682*d3c97224SAlexander Kolbasov # Collect data per PG 683*d3c97224SAlexander Kolbasov # 684*d3c97224SAlexander Kolbasov foreach my $pg (@pgs) { 685*d3c97224SAlexander Kolbasov $pgs{$pg}->{pg} = $pg; 686*d3c97224SAlexander Kolbasov 687*d3c97224SAlexander Kolbasov my ($hwload, $utilization, $capacity, $accuracy) = 688*d3c97224SAlexander Kolbasov get_load($p_initial, $p_dup, $pg); 689*d3c97224SAlexander Kolbasov 690*d3c97224SAlexander Kolbasov my @cpus = $p->cpus ($pg); 691*d3c97224SAlexander Kolbasov my ($user, $sys, $idle, $swload) = 692*d3c97224SAlexander Kolbasov $p_dup->sw_utilization($p_initial, $pg); 693*d3c97224SAlexander Kolbasov 694*d3c97224SAlexander Kolbasov # Adjust idle and swload based on rounding 695*d3c97224SAlexander Kolbasov ($swload, $idle) = get_swload($user, $sys); 696*d3c97224SAlexander Kolbasov 697*d3c97224SAlexander Kolbasov $pgs{$pg}->{pg} = $pg; 698*d3c97224SAlexander Kolbasov $pgs{$pg}->{swload} = $swload; 699*d3c97224SAlexander Kolbasov $pgs{$pg}->{user} = $user; 700*d3c97224SAlexander Kolbasov $pgs{$pg}->{sys} = $sys; 701*d3c97224SAlexander Kolbasov $pgs{$pg}->{idle} = $idle; 702*d3c97224SAlexander Kolbasov $pgs{$pg}->{hwload} = $hwload; 703*d3c97224SAlexander Kolbasov $pgs{$pg}->{utilization} = number_to_scaled_string($utilization); 704*d3c97224SAlexander Kolbasov $pgs{$pg}->{capacity} = number_to_scaled_string($capacity); 705*d3c97224SAlexander Kolbasov $pgs{$pg}->{minhwload} = $history->{$pg}->{minhwload}; 706*d3c97224SAlexander Kolbasov $pgs{$pg}->{maxhwload} = $history->{$pg}->{maxhwload}; 707*d3c97224SAlexander Kolbasov $pgs{$pg}->{minswload} = $history->{$pg}->{minswload} || 0; 708*d3c97224SAlexander Kolbasov $pgs{$pg}->{maxswload} = $history->{$pg}->{maxswload} || 0; 709*d3c97224SAlexander Kolbasov } 710*d3c97224SAlexander Kolbasov 711*d3c97224SAlexander Kolbasov # 712*d3c97224SAlexander Kolbasov # Sort PGs according to the sorting options 713*d3c97224SAlexander Kolbasov # 714*d3c97224SAlexander Kolbasov @sorted_pgs = pg_sort_by_key(\%pgs, $sort_key, $sort_reverse, @pgs); 715*d3c97224SAlexander Kolbasov 716*d3c97224SAlexander Kolbasov # 717*d3c97224SAlexander Kolbasov # Trim to top N if needed 718*d3c97224SAlexander Kolbasov # 719*d3c97224SAlexander Kolbasov if ($show_top) { 720*d3c97224SAlexander Kolbasov $npgs = $show_top if $show_top < $npgs; 721*d3c97224SAlexander Kolbasov @sorted_pgs = @sorted_pgs[0..$npgs - 1]; 722*d3c97224SAlexander Kolbasov } 723*d3c97224SAlexander Kolbasov 724*d3c97224SAlexander Kolbasov # 725*d3c97224SAlexander Kolbasov # Print headers 726*d3c97224SAlexander Kolbasov # 727*d3c97224SAlexander Kolbasov my $d = ' ' . '-' x 4; 728*d3c97224SAlexander Kolbasov if ($verbose) { 729*d3c97224SAlexander Kolbasov printf "%${max_pg_len}s %-${max_sharename_len}s %s " . 730*d3c97224SAlexander Kolbasov " ------HARDWARE------ ------SOFTWARE------\n", 731*d3c97224SAlexander Kolbasov ' ', ' ', ' ' x 8; 732*d3c97224SAlexander Kolbasov 733*d3c97224SAlexander Kolbasov printf "%-${max_pg_len}s %-${max_sharename_len}s", 734*d3c97224SAlexander Kolbasov 'PG', 'RELATIONSHIP'; 735*d3c97224SAlexander Kolbasov 736*d3c97224SAlexander Kolbasov printf " %4s %4s", 'UTIL', ' CAP'; 737*d3c97224SAlexander Kolbasov printf " $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt %s\n", 738*d3c97224SAlexander Kolbasov 'MIN', 'AVG', 'MAX', 'MIN', 'AVG', 'MAX', 'CPUS'; 739*d3c97224SAlexander Kolbasov } else { 740*d3c97224SAlexander Kolbasov printf "%${max_pg_len}s %-${max_sharename_len}s " . 741*d3c97224SAlexander Kolbasov "------HARDWARE------" . 742*d3c97224SAlexander Kolbasov " ------SOFTWARE------\n", ' ', ' '; 743*d3c97224SAlexander Kolbasov 744*d3c97224SAlexander Kolbasov printf "%-${max_pg_len}s %-${max_sharename_len}s", 745*d3c97224SAlexander Kolbasov 'PG', 'RELATIONSHIP'; 746*d3c97224SAlexander Kolbasov 747*d3c97224SAlexander Kolbasov printf " $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt $pcnt_fmt %s\n", 748*d3c97224SAlexander Kolbasov 'MIN', 'AVG', 'MAX', 'MIN', 'AVG', 'MAX', 'CPUS'; 749*d3c97224SAlexander Kolbasov } 750*d3c97224SAlexander Kolbasov 751*d3c97224SAlexander Kolbasov # 752*d3c97224SAlexander Kolbasov # Print information per PG 753*d3c97224SAlexander Kolbasov # 754*d3c97224SAlexander Kolbasov foreach my $pg (@sorted_pgs) { 755*d3c97224SAlexander Kolbasov my $cpus = $p->cpus($pg); 756*d3c97224SAlexander Kolbasov 757*d3c97224SAlexander Kolbasov my $shname = $name_of_pg{$pg}; 758*d3c97224SAlexander Kolbasov if ($sort_key eq 'breadth' || $sort_key eq 'depth') { 759*d3c97224SAlexander Kolbasov my $level = $p->level($pg) - $minlevel; 760*d3c97224SAlexander Kolbasov $shname = (' ' x (LEVEL_OFFSET * $level)) . $shname; 761*d3c97224SAlexander Kolbasov } 762*d3c97224SAlexander Kolbasov 763*d3c97224SAlexander Kolbasov printf "%${max_pg_len}d %-${max_sharename_len}s ", 764*d3c97224SAlexander Kolbasov $pg, $shname; 765*d3c97224SAlexander Kolbasov 766*d3c97224SAlexander Kolbasov if ($verbose) { 767*d3c97224SAlexander Kolbasov printf "%4s %4s ", 768*d3c97224SAlexander Kolbasov number_to_scaled_string($pgs{$pg}->{utilization}), 769*d3c97224SAlexander Kolbasov number_to_scaled_string($pgs{$pg}->{capacity}); 770*d3c97224SAlexander Kolbasov } 771*d3c97224SAlexander Kolbasov 772*d3c97224SAlexander Kolbasov if (!defined($pgs{$pg}->{hwload}) || 773*d3c97224SAlexander Kolbasov $pgs{$pg}->{hwload} == HWLOAD_UNDEF) { 774*d3c97224SAlexander Kolbasov printf "$pcnt_fmt $pcnt_fmt $pcnt_fmt ", 775*d3c97224SAlexander Kolbasov '-', '-', '-'; 776*d3c97224SAlexander Kolbasov } else { 777*d3c97224SAlexander Kolbasov printf "%s %s %s ", 778*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{minhwload}), 779*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{hwload}), 780*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{maxhwload}); 781*d3c97224SAlexander Kolbasov } 782*d3c97224SAlexander Kolbasov printf "%s %s %s", 783*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{minswload}), 784*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{swload}), 785*d3c97224SAlexander Kolbasov load2str($pgs{$pg}->{maxswload}); 786*d3c97224SAlexander Kolbasov 787*d3c97224SAlexander Kolbasov printf " %s\n", $cpus; 788*d3c97224SAlexander Kolbasov } 789*d3c97224SAlexander Kolbasov 790*d3c97224SAlexander Kolbasov exit ($rc); 791*d3c97224SAlexander Kolbasov} 792*d3c97224SAlexander Kolbasov 793*d3c97224SAlexander Kolbasov# 794*d3c97224SAlexander Kolbasov# pg_sort_by_key(pgs, key, inverse) 795*d3c97224SAlexander Kolbasov# Sort pgs according to the key specified 796*d3c97224SAlexander Kolbasov# 797*d3c97224SAlexander Kolbasov# Arguments: 798*d3c97224SAlexander Kolbasov# pgs hash indexed by PG ID 799*d3c97224SAlexander Kolbasov# sort keyword 800*d3c97224SAlexander Kolbasov# inverse - inverse sort result if this is T 801*d3c97224SAlexander Kolbasov# 802*d3c97224SAlexander Kolbasovsub pg_sort_by_key 803*d3c97224SAlexander Kolbasov{ 804*d3c97224SAlexander Kolbasov my $pgs = shift; 805*d3c97224SAlexander Kolbasov my $key = shift; 806*d3c97224SAlexander Kolbasov my $inverse = shift; 807*d3c97224SAlexander Kolbasov my @sorted; 808*d3c97224SAlexander Kolbasov 809*d3c97224SAlexander Kolbasov if ($key eq 'depth' || $key eq 'breadth') { 810*d3c97224SAlexander Kolbasov my $root = $p->root; 811*d3c97224SAlexander Kolbasov my @pgs = $key eq 'depth' ? 812*d3c97224SAlexander Kolbasov $p->all_depth_first() : 813*d3c97224SAlexander Kolbasov $p->all_breadth_first(); 814*d3c97224SAlexander Kolbasov @sorted = reverse(grep { exists($pgs{$_}) } @pgs); 815*d3c97224SAlexander Kolbasov } else { 816*d3c97224SAlexander Kolbasov @sorted = sort { $pgs{$a}->{$key} <=> $pgs{$b}->{$key} } @_; 817*d3c97224SAlexander Kolbasov } 818*d3c97224SAlexander Kolbasov 819*d3c97224SAlexander Kolbasov return ($inverse ? reverse(@sorted) : @sorted); 820*d3c97224SAlexander Kolbasov} 821*d3c97224SAlexander Kolbasov 822*d3c97224SAlexander Kolbasov# 823*d3c97224SAlexander Kolbasov# Convert numeric load to formatted string 824*d3c97224SAlexander Kolbasov# 825*d3c97224SAlexander Kolbasovsub load2str 826*d3c97224SAlexander Kolbasov{ 827*d3c97224SAlexander Kolbasov my $load = shift; 828*d3c97224SAlexander Kolbasov 829*d3c97224SAlexander Kolbasov return (sprintf "$pcnt_fmt", '-') if 830*d3c97224SAlexander Kolbasov !defined($load) || $load == HWLOAD_UNDEF; 831*d3c97224SAlexander Kolbasov return (sprintf "$pcnt_fmt", '?') if $load == HWLOAD_UNKNOWN; 832*d3c97224SAlexander Kolbasov return (sprintf "$pcnt%%", $load); 833*d3c97224SAlexander Kolbasov} 834*d3c97224SAlexander Kolbasov 835*d3c97224SAlexander Kolbasov# 836*d3c97224SAlexander Kolbasov# get_load(snapshot1, snapshot2, pg) 837*d3c97224SAlexander Kolbasov# 838*d3c97224SAlexander Kolbasov# Get various hardware load data for the given PG using two snapshots. 839*d3c97224SAlexander Kolbasov# Arguments: two PG snapshots and PG ID 840*d3c97224SAlexander Kolbasov# 841*d3c97224SAlexander Kolbasov# In scalar context returns the hardware load 842*d3c97224SAlexander Kolbasov# In list context returns a list 843*d3c97224SAlexander Kolbasov# (load, utilization, capacity, accuracy) 844*d3c97224SAlexander Kolbasov# 845*d3c97224SAlexander Kolbasovsub get_load 846*d3c97224SAlexander Kolbasov{ 847*d3c97224SAlexander Kolbasov my $p = shift; 848*d3c97224SAlexander Kolbasov my $p_dup = shift; 849*d3c97224SAlexander Kolbasov my $pg = shift; 850*d3c97224SAlexander Kolbasov 851*d3c97224SAlexander Kolbasov return HWLOAD_UNDEF if !$p->has_utilization($pg); 852*d3c97224SAlexander Kolbasov 853*d3c97224SAlexander Kolbasov my ($capacity, $utilization, $accuracy, $tdelta); 854*d3c97224SAlexander Kolbasov 855*d3c97224SAlexander Kolbasov 856*d3c97224SAlexander Kolbasov $accuracy = 100; 857*d3c97224SAlexander Kolbasov $utilization = 0; 858*d3c97224SAlexander Kolbasov 859*d3c97224SAlexander Kolbasov $utilization = $p->utilization($p_dup, $pg) || 0; 860*d3c97224SAlexander Kolbasov $capacity = $p_dup->capacity($pg); 861*d3c97224SAlexander Kolbasov $accuracy = $p->accuracy($p_dup, $pg) || 0; 862*d3c97224SAlexander Kolbasov $tdelta = $p->tdelta($p_dup, $pg); 863*d3c97224SAlexander Kolbasov my $utilization_per_second = $utilization; 864*d3c97224SAlexander Kolbasov $utilization_per_second /= $tdelta if $tdelta; 865*d3c97224SAlexander Kolbasov 866*d3c97224SAlexander Kolbasov my $load; 867*d3c97224SAlexander Kolbasov 868*d3c97224SAlexander Kolbasov if ($accuracy != 100) { 869*d3c97224SAlexander Kolbasov $load = HWLOAD_UNKNOWN; 870*d3c97224SAlexander Kolbasov } else { 871*d3c97224SAlexander Kolbasov $load = $capacity ? 872*d3c97224SAlexander Kolbasov $utilization_per_second * 100 / $capacity : 873*d3c97224SAlexander Kolbasov HWLOAD_UNKNOWN; 874*d3c97224SAlexander Kolbasov $capacity *= $tdelta if $tdelta; 875*d3c97224SAlexander Kolbasov } 876*d3c97224SAlexander Kolbasov 877*d3c97224SAlexander Kolbasov return (wantarray() ? 878*d3c97224SAlexander Kolbasov ($load, $utilization, $capacity, $accuracy) : 879*d3c97224SAlexander Kolbasov $load); 880*d3c97224SAlexander Kolbasov} 881*d3c97224SAlexander Kolbasov 882*d3c97224SAlexander Kolbasov# 883*d3c97224SAlexander Kolbasov# Make sure that with the rounding used, user + system + swload add up to 100%. 884*d3c97224SAlexander Kolbasov# 885*d3c97224SAlexander Kolbasov# 886*d3c97224SAlexander Kolbasovsub get_swload 887*d3c97224SAlexander Kolbasov{ 888*d3c97224SAlexander Kolbasov my $user = shift; 889*d3c97224SAlexander Kolbasov my $sys = shift; 890*d3c97224SAlexander Kolbasov my $swload; 891*d3c97224SAlexander Kolbasov my $idle; 892*d3c97224SAlexander Kolbasov 893*d3c97224SAlexander Kolbasov $user = sprintf "$pcnt", $user; 894*d3c97224SAlexander Kolbasov $sys = sprintf "$pcnt", $sys; 895*d3c97224SAlexander Kolbasov 896*d3c97224SAlexander Kolbasov $swload = $user + $sys; 897*d3c97224SAlexander Kolbasov $idle = 100 - $swload; 898*d3c97224SAlexander Kolbasov 899*d3c97224SAlexander Kolbasov return ($swload, $idle); 900*d3c97224SAlexander Kolbasov} 901*d3c97224SAlexander Kolbasov 902*d3c97224SAlexander Kolbasov# 903*d3c97224SAlexander Kolbasov# get_pg_list(cookie, pg_list, sharing_filter, sharing_filter_neg) Get list OF 904*d3c97224SAlexander Kolbasov# PGs to look at based on all PGs available, user-specified PGs and 905*d3c97224SAlexander Kolbasov# user-specified filters. 906*d3c97224SAlexander Kolbasov# 907*d3c97224SAlexander Kolbasovsub get_pg_list 908*d3c97224SAlexander Kolbasov{ 909*d3c97224SAlexander Kolbasov my $p = shift; 910*d3c97224SAlexander Kolbasov my $pg_list = shift; 911*d3c97224SAlexander Kolbasov my $sharing_filter = shift; 912*d3c97224SAlexander Kolbasov my $sharing_filter_neg = shift; 913*d3c97224SAlexander Kolbasov 914*d3c97224SAlexander Kolbasov my @all = $p->all(); 915*d3c97224SAlexander Kolbasov my @pg_list = scalar @$pg_list ? @$pg_list : @all; 916*d3c97224SAlexander Kolbasov my @pgs = $p->intersect(\@all_pgs, \@pg_list); 917*d3c97224SAlexander Kolbasov 918*d3c97224SAlexander Kolbasov # 919*d3c97224SAlexander Kolbasov # Now we have list of PGs to work with. Now apply filtering. First list 920*d3c97224SAlexander Kolbasov # only those matching -R 921*d3c97224SAlexander Kolbasov # 922*d3c97224SAlexander Kolbasov @pgs = grep { list_match($p->sh_name($_), \@sharing_filter, 0) } @pgs if 923*d3c97224SAlexander Kolbasov @sharing_filter; 924*d3c97224SAlexander Kolbasov 925*d3c97224SAlexander Kolbasov my @sharing_filter = @$sharing_filter; 926*d3c97224SAlexander Kolbasov my @sharing_filter_neg = @$sharing_filter_neg; 927*d3c97224SAlexander Kolbasov # Remove any that doesn't match -r 928*d3c97224SAlexander Kolbasov @pgs = grep { 929*d3c97224SAlexander Kolbasov !list_match($p->sh_name($_), \@sharing_filter_neg, 0) 930*d3c97224SAlexander Kolbasov } @pgs if 931*d3c97224SAlexander Kolbasov scalar @sharing_filter_neg; 932*d3c97224SAlexander Kolbasov 933*d3c97224SAlexander Kolbasov return (@pgs); 934*d3c97224SAlexander Kolbasov} 935*d3c97224SAlexander Kolbasov 936*d3c97224SAlexander Kolbasov# 937*d3c97224SAlexander Kolbasov# usage(rc) 938*d3c97224SAlexander Kolbasov# 939*d3c97224SAlexander Kolbasov# Print short usage message and exit with the given return code. 940*d3c97224SAlexander Kolbasov# If verbose is T, print a bit more information 941*d3c97224SAlexander Kolbasov# 942*d3c97224SAlexander Kolbasovsub usage 943*d3c97224SAlexander Kolbasov{ 944*d3c97224SAlexander Kolbasov my $rc = shift || E_SUCCESS; 945*d3c97224SAlexander Kolbasov 946*d3c97224SAlexander Kolbasov printf STDERR 947*d3c97224SAlexander Kolbasov gettext("Usage:\t%s [-A] [-C] [-p] [-s key | -S key] " . 948*d3c97224SAlexander Kolbasov "[-t number] [-T u | d]\n"), $cmdname; 949*d3c97224SAlexander Kolbasov print STDERR 950*d3c97224SAlexander Kolbasov gettext("\t\t[-r string] [-R string] [-P pg ...] [-c processor_id... ]\n"); 951*d3c97224SAlexander Kolbasov print STDERR 952*d3c97224SAlexander Kolbasov gettext("\t\t[interval [count]]\n\n"); 953*d3c97224SAlexander Kolbasov 954*d3c97224SAlexander Kolbasov exit ($rc); 955*d3c97224SAlexander Kolbasov} 956*d3c97224SAlexander Kolbasov 957*d3c97224SAlexander Kolbasov# 958*d3c97224SAlexander Kolbasov# list_match(val, list_ref, strict) 959*d3c97224SAlexander Kolbasov# Return T if argument matches any of the elements on the list, undef otherwise. 960*d3c97224SAlexander Kolbasov# 961*d3c97224SAlexander Kolbasovsub list_match 962*d3c97224SAlexander Kolbasov{ 963*d3c97224SAlexander Kolbasov my $arg = shift; 964*d3c97224SAlexander Kolbasov my $list = shift; 965*d3c97224SAlexander Kolbasov my $strict = shift; 966*d3c97224SAlexander Kolbasov 967*d3c97224SAlexander Kolbasov return first { $arg eq $_ } @$list if $strict; 968*d3c97224SAlexander Kolbasov return first { $arg =~ m/$_/i } @$list; 969*d3c97224SAlexander Kolbasov} 970*d3c97224SAlexander Kolbasov 971*d3c97224SAlexander Kolbasov# 972*d3c97224SAlexander Kolbasov# Convert a number to a string representation 973*d3c97224SAlexander Kolbasov# The number is scaled down until it is small enough to be in a good 974*d3c97224SAlexander Kolbasov# human readable format i.e. in the range 0 thru 1000. 975*d3c97224SAlexander Kolbasov# If it's smaller than 10 there's room enough to provide one decimal place. 976*d3c97224SAlexander Kolbasov# 977*d3c97224SAlexander Kolbasovsub number_to_scaled_string 978*d3c97224SAlexander Kolbasov{ 979*d3c97224SAlexander Kolbasov my $number = shift; 980*d3c97224SAlexander Kolbasov 981*d3c97224SAlexander Kolbasov return '-' unless defined ($number); 982*d3c97224SAlexander Kolbasov 983*d3c97224SAlexander Kolbasov # Remove any trailing spaces 984*d3c97224SAlexander Kolbasov $number =~ s/ //g; 985*d3c97224SAlexander Kolbasov 986*d3c97224SAlexander Kolbasov return $number unless $number =~ /^[.\d]+$/; 987*d3c97224SAlexander Kolbasov 988*d3c97224SAlexander Kolbasov my $scale = 1000; 989*d3c97224SAlexander Kolbasov 990*d3c97224SAlexander Kolbasov return sprintf("%4d", $number) if $number < $scale; 991*d3c97224SAlexander Kolbasov 992*d3c97224SAlexander Kolbasov my @measurement = ('K', 'M', 'B', 'T'); 993*d3c97224SAlexander Kolbasov my $uom = shift(@measurement); 994*d3c97224SAlexander Kolbasov my $result; 995*d3c97224SAlexander Kolbasov 996*d3c97224SAlexander Kolbasov my $save = $number; 997*d3c97224SAlexander Kolbasov 998*d3c97224SAlexander Kolbasov # Get size in K. 999*d3c97224SAlexander Kolbasov $number /= $scale; 1000*d3c97224SAlexander Kolbasov 1001*d3c97224SAlexander Kolbasov while (($number >= $scale) && $uom ne 'B') { 1002*d3c97224SAlexander Kolbasov $uom = shift(@measurement); 1003*d3c97224SAlexander Kolbasov $save = $number; 1004*d3c97224SAlexander Kolbasov $number /= $scale; 1005*d3c97224SAlexander Kolbasov } 1006*d3c97224SAlexander Kolbasov 1007*d3c97224SAlexander Kolbasov # check if we should output a decimal place after the point 1008*d3c97224SAlexander Kolbasov if ($save && (($save / $scale) < 10)) { 1009*d3c97224SAlexander Kolbasov $result = sprintf("%3.1f$uom", $save / $scale); 1010*d3c97224SAlexander Kolbasov } else { 1011*d3c97224SAlexander Kolbasov $result = sprintf("%3d$uom", $number); 1012*d3c97224SAlexander Kolbasov } 1013*d3c97224SAlexander Kolbasov 1014*d3c97224SAlexander Kolbasov return ("$result"); 1015*d3c97224SAlexander Kolbasov} 1016*d3c97224SAlexander Kolbasov 1017*d3c97224SAlexander Kolbasov 1018*d3c97224SAlexander Kolbasov__END__ 1019