diff options
Diffstat (limited to 'usr/src')
44 files changed, 7364 insertions, 226 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 6868fa894a..fd41167eda 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -211,6 +211,7 @@ COMMON_SUBDIRS= \ lastcomm \ ldap \ ldapcachemgr \ + lgrpinfo \ line \ link \ listen \ @@ -562,6 +563,7 @@ MSGSUBDIRS= \ last \ ldap \ ldapcachemgr \ + lgrpinfo \ locale \ lofiadm \ logadm \ @@ -605,6 +607,7 @@ MSGSUBDIRS= \ prtdiag \ ps \ psrinfo \ + ptools \ pwconv \ pwd \ raidctl \ diff --git a/usr/src/cmd/lgrpinfo/Makefile b/usr/src/cmd/lgrpinfo/Makefile new file mode 100644 index 0000000000..9178046633 --- /dev/null +++ b/usr/src/cmd/lgrpinfo/Makefile @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= lgrpinfo + +include ../Makefile.cmd + +OWNER = root +GROUP = sys + +TARGET= all + +.KEEP_STATE: + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +%: %.pl + $(SED) -e "s@TEXT_DOMAIN@\"$(TEXT_DOMAIN)\"@" $< > $@ + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +$(PROG).po: $(PROG).pl + $(XGETTEXT) -d $(PROG) $(PROG).pl + +lint: + +include ../Makefile.targ diff --git a/usr/src/cmd/lgrpinfo/lgrpinfo.pl b/usr/src/cmd/lgrpinfo/lgrpinfo.pl new file mode 100755 index 0000000000..5e641ef189 --- /dev/null +++ b/usr/src/cmd/lgrpinfo/lgrpinfo.pl @@ -0,0 +1,756 @@ +#! /usr/perl5/bin/perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# lgrpinfo: display information about locality groups. +# + +require 5.6.1; +use warnings; +use strict; +use Getopt::Long qw(:config no_ignore_case bundling auto_version); +use File::Basename; +# Sun::Solaris::Kstat is used to extract per-lgroup load average. +use Sun::Solaris::Kstat; +use POSIX qw(locale_h); +use Sun::Solaris::Utils qw(textdomain gettext); +use Sun::Solaris::Lgrp ':CONSTANTS'; + +use constant KB => 1024; + +# +# Amount of load contributed by a single thread. The value is exported by the +# kernel in the 'loadscale' variable of lgroup kstat, but in case it is missing +# we use the current default value as the best guess. +# +use constant LGRP_LOADAVG_THREAD_MAX => 65516; + +# Get script name +our $cmdname = basename($0, ".pl"); + +# Get liblgrp version +my $version = Sun::Solaris::Lgrp::lgrp_version(); + +our $VERSION = "%I% (liblgrp version $version)"; + +# The $loads hash keeps per-lgroup load average. +our $loads = {}; + +######################################## +# Main body +## + +# Set message locale +setlocale(LC_ALL, ""); +textdomain(TEXT_DOMAIN); + +# Parse command-line options +our($opt_a, $opt_l, $opt_m, $opt_c, $opt_C, $opt_e, $opt_t, $opt_h, $opt_u, + $opt_r, $opt_L, $opt_P, $opt_I, $opt_T, $opt_G); + +GetOptions("a" => \$opt_a, + "c" => \$opt_c, + "C" => \$opt_C, + "e" => \$opt_e, + "G" => \$opt_G, + "h|?" => \$opt_h, + "l" => \$opt_l, + "L" => \$opt_L, + "I" => \$opt_I, + "m" => \$opt_m, + "r" => \$opt_r, + "t" => \$opt_t, + "T" => \$opt_T, + "u=s" => \$opt_u, + "P" => \$opt_P) || usage(3); + +usage(0) if $opt_h; + +# Check for conflicting options +my $nfilters = 0; +$nfilters++ if $opt_C; +$nfilters++ if $opt_P; +$nfilters++ if $opt_T; + +if ($nfilters > 1) { + printf STDERR + gettext("%s: Options -C, -T and -P can not be used together\n"), + $cmdname; + usage(3); +} + +if ($opt_T && ($opt_I || $opt_t)) { + printf STDERR + gettext("%s: Option -T can not be used with -I, -t\n"), + $cmdname; + usage(3); +} + +if ($opt_T && scalar @ARGV) { + printf STDERR + gettext("%s: Warning: with '-T' all lgroups on the command line "), + $cmdname; + printf STDERR gettext("are ignored\n\n"); +} + +if ($opt_L && $opt_I) { + printf STDERR gettext("%s: Option -I can not be used with -L\n"), + $cmdname; + usage(3); +} + +# Figure out what to do based on options +my $do_default = 1 unless + $opt_a || $opt_l || $opt_m || $opt_c || $opt_e || $opt_t || $opt_r; + + +my $l = Sun::Solaris::Lgrp->new($opt_G ? LGRP_VIEW_OS : LGRP_VIEW_CALLER) or + die(gettext("$cmdname: can not get lgroup information from the system\n")); + + +# Get list of all lgroups, the root and the list of intermediates +my @lgrps = nsort($l->lgrps); +my $root = $l->root; +my @intermediates = grep { $_ != $root && !$l->isleaf($_) } @lgrps; +my $is_uma = (scalar @lgrps == 1); + +# Print everything if -a is specified or it is default without -T +my $do_all = 1 if $opt_a || ($do_default && !($opt_T || $opt_L)); + +# Print individual information if do_all or requested specific print +my $do_lat = 1 if $do_all || $opt_l; +my $do_memory = 1 if $do_all || $opt_m; +my $do_cpu = 1 if $do_all || $opt_c; +my $do_topo = 1 if $do_all || $opt_t; +my $do_rsrc = 1 if $do_all || $opt_r; +my $do_load = 1 if $do_all || $opt_e; +my $do_table = 1 if $opt_a || $opt_L; +my $do_something = ($do_lat || $do_memory || $do_cpu || $do_topo || + $do_rsrc || $do_load); + +# Does the liblgrp(3LIB) has enough capabilities to support resource view? +if ($do_rsrc && LGRP_VER_CURRENT == 1) { + if ($opt_r) { + printf STDERR + gettext("%s: sorry, your system does not support"), + $cmdname; + printf STDERR " lgrp_resources(3LGRP)\n"; + } + $do_rsrc = 0; +} + +# Get list of lgrps from arguments, expanding symbolic names like +# "root" and "leaves" +# Use all lgroups if none are specified on the command line +my @lgrp_list = (scalar (@ARGV) && !$opt_T) ? lgrp_expand($l, @ARGV) : @lgrps; + +# Apply 'Parent' or 'Children' operations if requested +@lgrp_list = map { $l->parents($_) } @lgrp_list if $opt_P; +@lgrp_list = map { $l->children($_) } @lgrp_list if $opt_C; + +# Drop repeating elements and sort lgroups numerically. +@lgrp_list = uniqsort(@lgrp_list); + +# If both -L and -c are specified, just print list of CPUs. +if ($opt_c && $opt_I) { + my @cpus = uniqsort(map { $l->cpus($_, LGRP_CONTENT_HIERARCHY) } + @lgrp_list); + print "@cpus\n"; + exit(0); +} + +my $unit_str = "K"; +my $units = KB; + +# Convert units to canonical numeric and string formats. +if ($opt_u) { + if ($opt_u =~ /^b$/i) { + $units = 1; + $unit_str = "B"; + } elsif ($opt_u =~ /^k$/i) { + $units = KB; + $unit_str = "K"; + } elsif ($opt_u =~ /^m$/i) { + $units = KB * KB; + $unit_str = "M"; + } elsif ($opt_u =~ /^g$/i) { + $units = KB * KB * KB; + $unit_str = "G"; + } elsif ($opt_u =~ /^t$/i) { + $units = KB * KB * KB * KB; + $unit_str = "T"; + } elsif ($opt_u =~ /^p$/i) { + $units = KB * KB * KB * KB * KB; + $unit_str = "P"; + } elsif ($opt_u =~ /^e$/i) { + $units = KB * KB * KB * KB * KB * KB; + $unit_str = "E"; + } elsif (! ($opt_u =~ /^m$/i)) { + printf STDERR + gettext("%s: invalid unit '$opt_u', should be [b|k|m|g|t|p|e]"), + $cmdname; + printf STDERR gettext(", using the default.\n\n"); + $opt_u = 0; + } +} + +# Collect load average data if requested. +$loads = get_lav() if $do_load; + +# Get latency values for each lgroup. +my %self_latencies; +map { $self_latencies{$_} = $l->latency($_, $_) } @lgrps; + +# If -T is specified, just print topology and return. +if ($opt_T) { + lgrp_prettyprint($l); + print_latency_table(\@lgrps, \@lgrps) if $do_table; + exit(0); +} + +if (!scalar @lgrp_list) { + printf STDERR gettext("%s: No matching lgroups found!\n"), $cmdname; + exit(2); +} + +# Just print list of lgrps if doing just filtering +(print "@lgrp_list\n"), exit 0 if $opt_I; + +if ($do_something) { + # Walk through each requested lgrp and print whatever is requested. + foreach my $lgrp (@lgrp_list) { + my $is_leaf = $l->isleaf($lgrp); + my ($children, $parents, $cpus, $memstr, $rsrc); + + my $prefix = ($lgrp == $root) ? + "root": $is_leaf ? gettext("leaf") : gettext("intermediate"); + printf gettext("lgroup %d (%s):"), $lgrp, $prefix; + + if ($do_topo) { + # Get children of this lgrp. + my @children = $l->children($lgrp); + $children = $is_leaf ? + gettext("Children: none") : + gettext("Children: ") . lgrp_collapse(@children); + # Are there any parents for this lgrp? + my @parents = $l->parents($lgrp); + $parents = @parents ? + gettext(", Parent: ") . "@parents" : + ""; + } + + if ($do_cpu) { + $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY); + } + if ($do_memory) { + $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY); + } + if ($do_rsrc) { + $rsrc = lgrp_showresources($lgrp); + } + + # Print all the information about lgrp. + print "\n\t$children$parents" if $do_topo; + print "\n\t$cpus" if $do_cpu && $cpus; + print "\n\t$memstr" if $do_memory && $memstr; + print "\n\t$rsrc" if $do_rsrc; + print "\n\t$loads->{$lgrp}" if defined ($loads->{$lgrp}); + if ($do_lat && defined($self_latencies{$lgrp})) { + printf gettext("\n\tLatency: %d"), $self_latencies{$lgrp}; + } + print "\n"; + } +} + +print_latency_table(\@lgrps, \@lgrp_list) if $do_table; + +exit 0; + +# +# usage(exit_status) +# print usage message and exit with the specified exit status. +# +sub usage +{ + printf STDERR gettext("Usage:\t%s"), $cmdname; + print STDERR " [-aceGlLmrt] [-u unit] [-C|-P] [lgrp] ...\n"; + print STDERR " \t$cmdname -I [-c] [-G] [-C|-P] [lgrp] ...\n"; + print STDERR " \t$cmdname -T [-aceGlLmr] [-u unit]\n"; + print STDERR " \t$cmdname -h\n\n"; + + printf STDERR + gettext(" Display information about locality groups\n\n" . + "\t-a: Equivalent to \"%s\" without -T and to \"%s\" with -T\n"), + "-celLmrt", "-celLmr"; + + print STDERR + gettext("\t-c: Print CPU information\n"), + gettext("\t-C: Children of the specified lgroups\n"), + gettext("\t-e: Print lgroup load average\n"), + gettext("\t-h: Print this message and exit\n"), + gettext("\t-I: Print lgroup or CPU IDs only\n"), + gettext("\t-l: Print information about lgroup latencies\n"), + gettext("\t-G: Print OS view of lgroup hierarchy\n"), + gettext("\t-L: Print lgroup latency table\n"), + gettext("\t-m: Print memory information\n"), + gettext("\t-P: Parent(s) of the specified lgroups\n"), + gettext("\t-r: Print lgroup resources\n"), + gettext("\t-t: Print information about lgroup topology\n"), + gettext("\t-T: Print the hierarchy tree\n"), + gettext("\t-u unit: Specify memory unit (b,k,m,g,t,p,e)\n\n\n"); + + print STDERR + gettext(" The lgrp may be specified as an lgroup ID,"), + gettext(" \"root\", \"all\",\n"), + gettext(" \"intermediate\" or \"leaves\".\n\n"); + + printf STDERR + gettext(" The default set of options is \"%s\"\n\n"), + "-celmrt all"; + + print STDERR + gettext(" Without any options print topology, CPU and memory " . + "information about each\n" . + " lgroup. If any lgroup IDs are specified on the " . + "command line only print\n" . + " information about the specified lgroup.\n\n"); + + exit(shift); +} + +# Return the input list with duplicates removed. +sub uniq +{ + my %seen; + return (grep { ++$seen{$_} == 1 } @_); +} + +# +# Sort the list numerically +# Should be called in list context +# +sub nsort +{ + return (sort { $a <=> $b } @_); +} + +# +# Sort list numerically and remove duplicates +# Should be called in list context +# +sub uniqsort +{ + return (sort { $a <=> $b } uniq(@_)); +} + +# Round values +sub round +{ + my $val = shift; + + return (int($val + 0.5)); +} + +# +# Expand list of lgrps. +# Translate 'root' to the root lgrp id +# Translate 'all' to the list of all lgrps +# Translate 'leaves' to the list of all lgrps' +# Translate 'intermediate' to the list of intermediates. +# +sub lgrp_expand +{ + my $lobj = shift; + my %seen; + my @result; + + # create a hash element for every element in @lgrps + map { $seen{$_}++ } @lgrps; + + foreach my $lgrp (@_) { + push(@result, $lobj->root), next if $lgrp =~ m/^root$/i; + push(@result, @lgrps), next if $lgrp =~ m/^all$/i; + push(@result, $lobj->leaves), next if $lgrp =~ m/^leaves$/i; + push(@result, @intermediates), + next if $lgrp =~ m/^intermediate$/i; + push(@result, $lgrp), + next if $lgrp =~ m/^\d+$/ && $seen{$lgrp}; + printf STDERR gettext("%s: skipping invalid lgrp $lgrp\n"), + $cmdname; + } + + return @result; +} + +# +# lgrp_tree(class, node) +# +# Build the tree of the lgroup hierarchy starting with the specified node or +# root if no initial node is specified. Calls itself recursively specifying each +# of the children as a starting node. Builds a reference to the list with the +# node in the end and each element being a subtree. +# +sub lgrp_tree +{ + my $c = shift; + my $lgrp = shift || $c->root; + + # Call itself for each of the children and combine results in a list. + [ (map { lgrp_tree($c, $_) } $c->children($lgrp)), $lgrp ]; +} + +# +# lgrp_pp(tree, prefix, childprefix, npeers) +# +# pretty-print the hierarchy tree. +# Input Arguments: +# Reference to the tree +# Prefix for me to use +# Prefix for my children to use +# Number of peers left +# +sub lgrp_pp +{ + my $tree = shift; + my $myprefix = shift; + my $childprefix = shift; + my $npeers = shift; + my $el = pop @$tree; + my $nchildren = scalar @$tree; + my $printprefix = "$childprefix"; + my $printpostfix = $npeers ? "| " : " "; + + return unless defined ($el); + + my $bar = $npeers ? "|" : "`"; + print $childprefix ? $childprefix : ""; + print $myprefix ? "$bar" . "-- " : ""; + lgrp_print($el, "$printprefix$printpostfix"); + + my $new_prefix = $npeers ? $myprefix : " "; + + # Pretty-print the subtree with a new offset. + map { + lgrp_pp($_, "| ", "$childprefix$new_prefix", --$nchildren) + } @$tree; +} + +# Pretty print the whole tree +sub lgrp_prettyprint +{ + my $c = shift; + my $tree = lgrp_tree $c; + lgrp_pp($tree, '', '', scalar $tree - 1); +} + +sub lgrp_print +{ + my $lgrp = shift; + my $prefix = shift; + my ($cpus, $memstr, $rsrc); + my $is_interm = ($lgrp != $root && !$l->isleaf($lgrp)); + my $not_root = $is_uma || $lgrp != $root; + + print "$lgrp"; + + if ($do_cpu && $not_root) { + $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY); + } + if ($do_memory && $not_root) { + $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY); + } + if ($do_rsrc && ($is_uma || $is_interm)) { + $rsrc = lgrp_showresources($lgrp) if $do_rsrc; + } + + # Print all the information about lgrp. + + print "\n$prefix$cpus" if $cpus; + print "\n$prefix$memstr" if $memstr; + print "\n$prefix$rsrc" if $rsrc; + print "\n$prefix$loads->{$lgrp}" if defined ($loads->{$lgrp}); + + # Print latency information if requested. + if ($do_lat && $lgrp != $root && defined($self_latencies{$lgrp})) { + print "\n${prefix}"; + printf gettext("Latency: %d"), $self_latencies{$lgrp}; + } + print "\n"; +} + +# What CPUs are in this lgrp? +sub lgrp_showcpus +{ + my $lgrp = shift; + my $hier = shift; + + my @cpus = $l->cpus($lgrp, $hier); + my $ncpus = @cpus; + return 0 unless $ncpus; + # Sort CPU list if there is something to sort. + @cpus = nsort(@cpus) if ($ncpus > 1); + my $cpu_string = lgrp_collapse(@cpus); + return (($ncpus == 1) ? + gettext("CPU: ") . $cpu_string: + gettext("CPUs: ") . $cpu_string); +} + +# How much memory does this lgrp contain? +sub lgrp_showmemory +{ + my $lgrp = shift; + my $hier = shift; + + my $memory = $l->mem_size($lgrp, LGRP_MEM_SZ_INSTALLED, $hier); + return (0) unless $memory; + my $freemem = $l->mem_size($lgrp, LGRP_MEM_SZ_FREE, $hier) || 0; + + my $memory_r = memory_to_string($memory); + my $freemem_r = memory_to_string($freemem); + my $usedmem = memory_to_string($memory - $freemem); + + my $memstr = sprintf(gettext("Memory: installed %s"), + $memory_r); + $memstr = $memstr . sprintf(gettext(", allocated %s"), + $usedmem); + $memstr = $memstr . sprintf(gettext(", free %s"), + $freemem_r); + return ($memstr); +} + +# Get string containing lgroup resources +sub lgrp_showresources +{ + my $lgrp = shift; + my $rsrc_prefix = gettext("Lgroup resources:"); + # What resources does this lgroup contain? + my @resources_cpu = nsort($l->resources($lgrp, LGRP_RSRC_CPU)); + my @resources_mem = nsort($l->resources($lgrp, LGRP_RSRC_MEM)); + my $rsrc = @resources_cpu || @resources_mem ? "" : gettext("none"); + $rsrc = $rsrc_prefix . $rsrc; + my $rsrc_cpu = lgrp_collapse(@resources_cpu); + my $rsrc_mem = lgrp_collapse(@resources_mem); + my $lcpu = gettext("CPU"); + my $lmemory = gettext("memory"); + $rsrc = "$rsrc $rsrc_cpu ($lcpu);" if scalar @resources_cpu; + $rsrc = "$rsrc $rsrc_mem ($lmemory)" if scalar @resources_mem; + return ($rsrc); +} + +# +# Consolidate consequtive ids as start-end +# Input: list of ids +# Output: string with space-sepated cpu values with ranges +# collapsed as x-y +# +sub lgrp_collapse +{ + return ('') unless @_; + my @args = uniqsort(@_); + my $start = shift(@args); + my $result = ''; + my $end = $start; # Initial range consists of the first element + foreach my $el (@args) { + if ($el == ($end + 1)) { + # + # Got consecutive ID, so extend end of range without + # printing anything since the range may extend further + # + $end = $el; + } else { + # + # Next ID is not consecutive, so print IDs gotten so + # far. + # + if ($end > $start + 1) { # range + $result = "$result $start-$end"; + } elsif ($end > $start) { # different values + $result = "$result $start $end"; + } else { # same value + $result = "$result $start"; + } + + # Try finding consecutive range starting from this ID + $start = $end = $el; + } + } + + # Print last ID(s) + if ($end > $start + 1) { + $result = "$result $start-$end"; + } elsif ($end > $start) { + $result = "$result $start $end"; + } else { + $result = "$result $start"; + } + # Remove any spaces in the beginning + $result =~ s/^\s+//; + return ($result); +} + +# Print latency information if requested and the system has several lgroups. +sub print_latency_table +{ + my ($lgrps1, $lgrps2) = @_; + + return unless scalar @lgrps; + + # Find maximum lgroup + my $max = $root; + map { $max = $_ if $max < $_ } @$lgrps1; + + # Field width for lgroup - the width of the largest lgroup and 1 space + my $lgwidth = length($max) + 1; + # Field width for latency. Get the maximum latency and add 1 space. + my $width = length($l->latency($root, $root)) + 1; + # Make sure that width is enough to print lgroup itself. + $width = $lgwidth if $width < $lgwidth; + + # Print table header + print gettext("\nLgroup latencies:\n"); + # Print horizontal line + print "\n", "-" x ($lgwidth + 1); + map { print '-' x $width } @$lgrps1; + print "\n", " " x $lgwidth, "|"; + map { printf("%${width}d", $_) } @$lgrps1; + print "\n", "-" x ($lgwidth + 1); + map { print '-' x $width } @$lgrps1; + print "\n"; + + # Print the latency table + foreach my $l1 (@$lgrps2) { + printf "%-${lgwidth}d|", $l1; + foreach my $l2 (@lgrps) { + my $latency = $l->latency($l1, $l2); + if (!defined ($latency)) { + printf "%${width}s", "-"; + } else { + printf "%${width}d", $latency; + } + } + print "\n"; + } + + # Print table footer + print "-" x ($lgwidth + 1); + map { print '-' x $width } @lgrps; + print "\n"; +} + +# +# Convert a number to a string representation +# The number is scaled down until it is small enough to be in a good +# human readable format i.e. in the range 0 thru 1023. +# If it's smaller than 10 there's room enough to provide one decimal place. +# +sub number_to_scaled_string +{ + my $number = shift; + + my $scale = KB; + my @measurement = ('K', 'M', 'G', 'T', 'P', 'E'); # Measurement + my $uom = shift(@measurement); + my $result; + + # Get size in K. + $number /= KB; + + my $save = $number; + while (($number >= $scale) && $uom ne 'E') { + $uom = shift(@measurement); + $save = $number; + $number = ($number + ($scale / 2)) / $scale; + } + + # check if we should output a decimal place after the point + if ($save && (($save / $scale) < 10)) { + $result = sprintf("%2.1f", $save / $scale); + } else { + $result = round($number); + } + return ("$result$uom"); +} + +# +# Convert memory size to the string representation +# +sub memory_to_string +{ + my $number = shift; + + # Zero memory - just print 0 + return ("0$unit_str") unless $number; + + # + # Return memory size scaled to human-readable form unless -u is + # specified. + # + return (number_to_scaled_string($number)) unless $opt_u; + + my $scaled = $number / $units; + my $result; + + if ($scaled < 0.1) { + $result = sprintf("%2.1g", $scaled); + } elsif ($scaled < 10) { + $result = sprintf("%2.1f", $scaled); + } else { + $result = int($scaled + 0.5); + } + return ("$result$unit_str"); +} + +# +# Read load averages from lgrp kstats Return hash reference indexed by lgroup ID +# for each lgroup which has load information. +# +sub get_lav +{ + my $load = {}; + + my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or + warn(gettext("$cmdname: kstat_open() failed: %!\n")), + return $load; + + my $lgrp_kstats = $ks->{lgrp} or + warn(gettext("$cmdname: can not read lgrp kstat\n)")), + return $load; + + # Collect load for each lgroup + foreach my $i (keys %$lgrp_kstats) { + next unless $lgrp_kstats->{$i}->{"lgrp$i"}; + my $lav = $lgrp_kstats->{$i}->{"lgrp$i"}->{"load average"}; + # Skip this lgroup if can't find its load average + next unless defined $lav; + my $scale = $lgrp_kstats->{$i}->{"lgrp$i"}->{"loadscale"} || + LGRP_LOADAVG_THREAD_MAX; + $load->{$i} = sprintf (gettext("Load: %4.3g"), $lav / $scale); + } + return $load; +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index 8dcfb87192..c291bd84b3 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -3356,6 +3356,7 @@ static const mdb_dcmd_t dcmds[] = { /* from lgrp.c */ { "lgrp", "?[-q] [-p | -Pih]", "display an lgrp", lgrp}, + { "lgrp_set", "", "display bitmask of lgroups as a list", lgrp_set}, /* from log.c */ { "msgbuf", "?[-v]", "print most recent console messages", msgbuf }, @@ -3679,11 +3680,16 @@ static const mdb_walker_t walkers[] = { leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini }, /* from lgrp.c */ - { "lgrp_cpulist", "given an lgrp, walk cpus", - lgrp_cpulist_walk_init, lgrp_cpulist_walk_step, - NULL }, - { "lgrptbl", "walk the lgrp table", + { "lgrp_cpulist", "walk CPUs in a given lgroup", + lgrp_cpulist_walk_init, lgrp_cpulist_walk_step, NULL }, + { "lgrptbl", "walk lgroup table", lgrp_walk_init, lgrp_walk_step, NULL }, + { "lgrp_parents", "walk up lgroup lineage from given lgroup", + lgrp_parents_walk_init, lgrp_parents_walk_step, NULL }, + { "lgrp_rsrc_mem", "walk lgroup memory resources of given lgroup", + lgrp_rsrc_mem_walk_init, lgrp_set_walk_step, NULL }, + { "lgrp_rsrc_cpu", "walk lgroup CPU resources of given lgroup", + lgrp_rsrc_cpu_walk_init, lgrp_set_walk_step, NULL }, /* from list.c */ { LIST_WALK_NAME, LIST_WALK_DESC, diff --git a/usr/src/cmd/mdb/common/modules/genunix/lgrp.c b/usr/src/cmd/mdb/common/modules/genunix/lgrp.c index 95d9a98326..b79a4ef755 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/lgrp.c +++ b/usr/src/cmd/mdb/common/modules/genunix/lgrp.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -467,39 +467,278 @@ lgrp_walk_init(mdb_walk_state_t *wsp) return (WALK_NEXT); } + +/* + * Common routine for several walkers. + * Read lgroup from wsp->walk_addr and call wsp->walk_callback for it. + * Normally returns the result of the callback. + * Returns WALK_DONE if walk_addr is NULL and WALK_ERR if cannot read the + * lgroup. + */ +static int +lgrp_walk_step_common(mdb_walk_state_t *wsp) +{ + lgrp_t lgrp; + + if (wsp->walk_addr == NULL) + return (WALK_DONE); + + if (mdb_vread(&lgrp, sizeof (lgrp_t), wsp->walk_addr) == -1) { + mdb_warn("unable to read lgrp at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(wsp->walk_addr, &lgrp, wsp->walk_cbdata)); +} + +/* + * Get one lgroup from the lgroup table and adjust lwd_iter to point to the next + * one. + */ int lgrp_walk_step(mdb_walk_state_t *wsp) { lgrp_walk_data_t *lwd = wsp->walk_data; + int status = lgrp_walk_step_common(wsp); + + if (status == WALK_NEXT) { + lwd->lwd_iter++; + + if (lwd->lwd_iter >= lwd->lwd_nlgrps) { + status = WALK_DONE; + } else { + wsp->walk_addr = lwd->lwd_lgrp_tbl[lwd->lwd_iter]; + + if (wsp->walk_addr == NULL) { + mdb_warn("NULL lgrp pointer in lgrp_table[%d]", + lwd->lwd_iter); + return (WALK_ERR); + } + } + } + + return (status); +} + +/* + * Initialize walker to traverse parents of lgroups. Nothing to do here. + */ +/* ARGSUSED */ +int +lgrp_parents_walk_init(mdb_walk_state_t *wsp) +{ + return (WALK_NEXT); +} + +/* + * Call wsp callback on current lgroup in wsp and replace the lgroup with its + * parent. + */ +int +lgrp_parents_walk_step(mdb_walk_state_t *wsp) +{ lgrp_t lgrp; int status; + if (wsp->walk_addr == NULL) + return (WALK_DONE); - if (mdb_vread(&lgrp, sizeof (struct lgrp), - wsp->walk_addr) == -1) { - mdb_warn("unable to read lgrp at %p", - wsp->walk_addr); + if (mdb_vread(&lgrp, sizeof (struct lgrp), wsp->walk_addr) == -1) { + mdb_warn("couldn't read 'lgrp' at %p", wsp->walk_addr); return (WALK_ERR); } - status = wsp->walk_callback(wsp->walk_addr, &lgrp, - wsp->walk_cbdata); + status = wsp->walk_callback(wsp->walk_addr, &lgrp, wsp->walk_cbdata); - if (status != WALK_NEXT) - return (status); + if (status == WALK_NEXT) + wsp->walk_addr = (uintptr_t)lgrp.lgrp_parent; + + return (status); +} + +/* + * Given the set return the ID of the first member of the set. + * Returns LGRP_NONE if the set has no elements smaller than max_lgrp. + */ +static lgrp_id_t +lgrp_set_get_first(klgrpset_t set, int max_lgrp) +{ + lgrp_id_t id; + klgrpset_t bit = 1; - lwd->lwd_iter++; + if (set == (klgrpset_t)0) + return (LGRP_NONE); + + for (id = 0; (id < max_lgrp) && !(set & bit); id++, bit <<= 1) + ; + + if (id >= max_lgrp) + id = LGRP_NONE; + + return (id); +} - if (lwd->lwd_iter >= lwd->lwd_nlgrps) +/* + * lgrp_set_walk_data is used to walk lgroups specified by a set. + * On every iteration one element is removed from the set. + */ +typedef struct lgrp_set_walk_data { + int lswd_nlgrps; /* Number of lgroups */ + uintptr_t *lwsd_lgrp_tbl; /* Full lgroup table */ + klgrpset_t lwsd_set; /* Set of lgroups to walk */ +} lgrp_set_walk_data_t; + +/* + * Initialize iterator for walkers over a set of lgroups + */ +static int +lgrp_set_walk_init(mdb_walk_state_t *wsp, klgrpset_t set) +{ + lgrp_set_walk_data_t *lwsd; + int nlgrps; + lgrp_id_t id; + GElf_Sym sym; + + /* Nothing to do if the set is empty */ + if (set == (klgrpset_t)0) return (WALK_DONE); - wsp->walk_addr = lwd->lwd_lgrp_tbl[lwd->lwd_iter]; + lwsd = mdb_zalloc(sizeof (lgrp_set_walk_data_t), UM_SLEEP | UM_GC); + + /* Get the total number of lgroups */ + if (mdb_readsym(&nlgrps, sizeof (int), "lgrp_alloc_max") == -1) { + mdb_warn("symbol 'lgrp_alloc_max' not found"); + return (WALK_ERR); + } + + if (nlgrps < 0) { + mdb_warn("lgrp_alloc_max of bounds (%d)\n", nlgrps); + return (WALK_ERR); + } + + nlgrps++; - if (wsp->walk_addr == NULL) { - mdb_warn("NULL lgrp pointer in lgrp_table[%d]", - lwd->lwd_iter); + /* Find ID of the first lgroup in the set */ + if ((id = lgrp_set_get_first(set, nlgrps)) == LGRP_NONE) { + mdb_warn("No set elements within %d lgroups\n", nlgrps); return (WALK_ERR); } + /* Read lgroup_table and copy it to lwsd_lgrp_tbl */ + if (mdb_lookup_by_name("lgrp_table", &sym) == -1) { + mdb_warn("failed to find 'lgrp_table'"); + return (WALK_ERR); + } + + /* Get number of valid entries in lgrp_table */ + if (sym.st_size < nlgrps * sizeof (lgrp_t *)) { + mdb_warn("lgrp_table size inconsistent with lgrp_alloc_max"); + return (WALK_ERR); + } + + lwsd->lwsd_lgrp_tbl = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC); + lwsd->lswd_nlgrps = nlgrps; + + if (mdb_readsym(lwsd->lwsd_lgrp_tbl, nlgrps * sizeof (lgrp_t *), + "lgrp_table") == -1) { + mdb_warn("unable to read lgrp_table"); + return (WALK_ERR); + } + + wsp->walk_data = lwsd; + + /* Save the first lgroup from the set and remove it from the set */ + wsp->walk_addr = lwsd->lwsd_lgrp_tbl[id]; + lwsd->lwsd_set = set & ~(1 << id); + return (WALK_NEXT); } + +/* + * Get current lgroup and advance the lgroup to the next one in the lwsd_set. + */ +int +lgrp_set_walk_step(mdb_walk_state_t *wsp) +{ + lgrp_id_t id = 0; + lgrp_set_walk_data_t *lwsd = wsp->walk_data; + int status = lgrp_walk_step_common(wsp); + + if (status == WALK_NEXT) { + id = lgrp_set_get_first(lwsd->lwsd_set, lwsd->lswd_nlgrps); + if (id == LGRP_NONE) { + status = WALK_DONE; + } else { + /* Move to the next lgroup in the set */ + wsp->walk_addr = lwsd->lwsd_lgrp_tbl[id]; + + /* Remove id from the set */ + lwsd->lwsd_set = lwsd->lwsd_set & ~(1 << id); + } + } + + return (status); +} + +/* + * Initialize resource walker for a given lgroup and resource. The lgroup + * address is specified in walk_addr. + */ +static int +lgrp_rsrc_walk_init(mdb_walk_state_t *wsp, int resource) +{ + lgrp_t lgrp; + + if (mdb_vread(&lgrp, sizeof (struct lgrp), wsp->walk_addr) == -1) { + mdb_warn("couldn't read 'lgrp' at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (lgrp_set_walk_init(wsp, lgrp.lgrp_set[resource])); +} + +/* + * Initialize CPU resource walker + */ +int +lgrp_rsrc_cpu_walk_init(mdb_walk_state_t *wsp) +{ + return (lgrp_rsrc_walk_init(wsp, LGRP_RSRC_CPU)); +} + +/* + * Initialize memory resource walker + */ +int +lgrp_rsrc_mem_walk_init(mdb_walk_state_t *wsp) +{ + return (lgrp_rsrc_walk_init(wsp, LGRP_RSRC_MEM)); +} + +/* + * Display bitmap as a list of integers + */ +/* ARGSUSED */ +int +lgrp_set(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uint64_t set = (uint64_t)addr; + uint64_t mask = 1; + int i = 0; + + if (!(flags & DCMD_ADDRSPEC)) { + return (DCMD_USAGE); + } + + if (set == 0) + return (DCMD_OK); + + for (; set != (uint64_t)0; i++, mask <<= 1) { + if (set & mask) { + mdb_printf("%d ", i); + set &= ~mask; + } + } + mdb_printf("\n"); + return (DCMD_OK); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/lgrp.h b/usr/src/cmd/mdb/common/modules/genunix/lgrp.h index d6b3af1286..ed6d9f4aa9 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/lgrp.h +++ b/usr/src/cmd/mdb/common/modules/genunix/lgrp.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,7 +41,15 @@ extern int lgrp_cpulist_walk_step(mdb_walk_state_t *); extern int lgrp_walk_init(mdb_walk_state_t *); extern int lgrp_walk_step(mdb_walk_state_t *); +extern int lgrp_parents_walk_init(mdb_walk_state_t *); +extern int lgrp_parents_walk_step(mdb_walk_state_t *); + +extern int lgrp_rsrc_cpu_walk_init(mdb_walk_state_t *); +extern int lgrp_rsrc_mem_walk_init(mdb_walk_state_t *); +extern int lgrp_set_walk_step(mdb_walk_state_t *); + extern int lgrp(uintptr_t, uint_t, int, const mdb_arg_t *); +extern int lgrp_set(uintptr_t, uint_t, int, const mdb_arg_t *); extern int print_range(int start, int end, int separator); extern void print_cpuset_range(ulong_t *cs, int words, int width); diff --git a/usr/src/cmd/perl/5.6.1/contrib/Makefile b/usr/src/cmd/perl/5.6.1/contrib/Makefile index 983abcacad..498ed01b0b 100644 --- a/usr/src/cmd/perl/5.6.1/contrib/Makefile +++ b/usr/src/cmd/perl/5.6.1/contrib/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -45,6 +45,7 @@ PERL_DYNAMIC_EXT = \ $(SUN_SOLARIS)/Task \ $(SUN_SOLARIS)/Exacct \ $(SUN_SOLARIS)/Privilege \ + $(SUN_SOLARIS)/Lgrp \ $(SUN_SOLARIS)/Ucred # Add any pure-perl extensions here. diff --git a/usr/src/cmd/perl/5.8.4/contrib/Makefile b/usr/src/cmd/perl/5.8.4/contrib/Makefile index 618d139432..32aa28c230 100644 --- a/usr/src/cmd/perl/5.8.4/contrib/Makefile +++ b/usr/src/cmd/perl/5.8.4/contrib/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -49,6 +49,7 @@ PERL_DYNAMIC_EXT = \ $(SUN_SOLARIS)/Task \ $(SUN_SOLARIS)/Exacct \ $(SUN_SOLARIS)/Privilege \ + $(SUN_SOLARIS)/Lgrp \ $(SUN_SOLARIS)/Ucred # Add any pure-perl extensions here. diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes new file mode 100644 index 0000000000..6f50cb517b --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +Version %I% +Initial Public Source release. diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm new file mode 100644 index 0000000000..c2ae106a96 --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm @@ -0,0 +1,302 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Lgrp.pm provides procedural and object-oriented interface to the Solaris +# liblgrp(3LIB) library. +# + +#ident "%Z%%M% %I% %E% SMI" + +require 5.6.1; +use strict; +use warnings; +use Carp; + +package Sun::Solaris::Lgrp; + +our $VERSION = '%I%'; +use XSLoader; +XSLoader::load(__PACKAGE__, $VERSION); + +require Exporter; + +our @ISA = qw(Exporter); + +our (@EXPORT_OK, %EXPORT_TAGS); + +# Things to export +my @lgrp_constants = qw(LGRP_AFF_NONE LGRP_AFF_STRONG LGRP_AFF_WEAK + LGRP_CONTENT_DIRECT LGRP_CONTENT_HIERARCHY + LGRP_MEM_SZ_FREE LGRP_MEM_SZ_INSTALLED LGRP_VER_CURRENT + LGRP_VER_NONE LGRP_VIEW_CALLER + LGRP_VIEW_OS LGRP_NONE + LGRP_RSRC_CPU LGRP_RSRC_MEM + LGRP_CONTENT_ALL LGRP_LAT_CPU_TO_MEM +); + +my @proc_constants = qw(P_PID P_LWPID P_MYID); + +my @constants = (@lgrp_constants, @proc_constants); + +my @functions = qw(lgrp_affinity_get lgrp_affinity_set + lgrp_children lgrp_cookie_stale lgrp_cpus lgrp_fini + lgrp_home lgrp_init lgrp_latency lgrp_latency_cookie + lgrp_mem_size lgrp_nlgrps lgrp_parents + lgrp_root lgrp_version lgrp_view lgrp_resources + lgrp_isleaf lgrp_lgrps lgrp_leaves); + +my @all = (@constants, @functions); + +# Define symbolic names for various subsets of export lists +%EXPORT_TAGS = ('CONSTANTS' => \@constants, + 'LGRP_CONSTANTS' => \@lgrp_constants, + 'PROC_CONSTANTS' => \@proc_constants, + 'FUNCTIONS' => \@functions, + 'ALL' => \@all); + +# Define things that are ok ot export. +@EXPORT_OK = ( @{ $EXPORT_TAGS{'ALL'} } ); + +# +# _usage(): print error message and terminate the program. +# +sub _usage +{ + my $msg = shift; + Carp::croak "Usage: Sun::Solaris::Lgrp::$msg"; +} + +# +# lgrp_isleaf($cookie, $lgrp) +# Returns T if lgrp is leaf, F otherwise. +# +sub lgrp_isleaf +{ + scalar @_ == 2 or _usage "lgrp_isleaf(cookie, lgrp)"; + return (!lgrp_children(shift, shift)); +} + +# +# lgrp_lgrps($cookie, [$lgrp]) +# Returns: list of lgrps in a subtree starting from $lgrp. +# If $root is not specified, use lgrp_root. +# undef on failure. +sub lgrp_lgrps +{ + scalar @_ > 0 or _usage("lgrp_lgrps(cookie, [lgrp])"); + my $cookie = shift; + my $root = shift; + $root = lgrp_root($cookie) unless defined $root; + return unless defined $root; + my @children = lgrp_children($cookie, $root); + my @result; + + # + # Concatenate root with subtrees for every children. Every subtree is + # obtained by calling lgrp_lgrps recursively with each of the children + # as the argument. + # + @result = @children ? + ($root, map {lgrp_lgrps($cookie, $_)} @children) : + ($root); + return (wantarray ? @result : scalar @result); +} + +# +# lgrp_leaves($cookie, [$lgrp]) +# Returns: list of leaves in the hierarchy starting from $lgrp. +# If $lgrp is not specified, use lgrp_root. +# undef on failure. +# +sub lgrp_leaves +{ + scalar @_ > 0 or _usage("lgrp_leaves(cookie, [lgrp])"); + my $cookie = shift; + my $root = shift; + $root = lgrp_root($cookie) unless defined $root; + return unless defined $root; + my @result = grep { + lgrp_isleaf($cookie, $_) + } lgrp_lgrps($cookie, $root); + return (wantarray ? @result : scalar @result); +} + +###################################################################### +# Object-Oriented interface. +###################################################################### + +# +# cookie: extract cookie from the argument. +# If the argument is scalar, it is the cookie itself, otherwise it is the +# reference to the object and the cookie value is in $self->{COOKIE}. +# +sub cookie +{ + my $self = shift; + return ((ref $self) ? $self->{COOKIE} : $self); +} + +# +# new: The object constructor +# +sub new +{ + my $class = shift; + my ($self, $view); + $view = shift; + $self->{COOKIE} = ($view ? lgrp_init($view) : lgrp_init()) or + croak("lgrp_init: $!\n"), return; + bless($self, $class) if defined($class); + bless($self) unless defined($class); + return ($self); +} + +# +# DESTROY: the object destructor. +# +sub DESTROY +{ + lgrp_fini(cookie(shift)); +} + +############################################################ +# Wrapper methods. +# +sub stale +{ + scalar @_ == 1 or _usage("stale(class)"); + return (lgrp_cookie_stale(cookie(shift))); +} + +sub view +{ + scalar @_ == 1 or _usage("view(class)"); + return (lgrp_view(cookie(shift))); +} + +sub root +{ + scalar @_ == 1 or _usage("root(class)"); + return (lgrp_root(cookie(shift))); +} + +sub nlgrps +{ + scalar @_ == 1 or _usage("nlgrps(class)"); + return (lgrp_nlgrps(cookie(shift))); +} + +sub lgrps +{ + scalar @_ > 0 or _usage("lgrps(class, [lgrp])"); + return (lgrp_lgrps(cookie(shift), shift)); +} + +sub leaves +{ + scalar @_ > 0 or _usage("leaves(class, [lgrp])"); + return (lgrp_leaves(cookie(shift), shift)); +} + +sub version +{ + scalar @_ > 0 or _usage("leaves(class, [version])"); + shift; + return (lgrp_version(shift || 0)); +} + +sub children +{ + scalar @_ == 2 or _usage("children(class, lgrp)"); + return (lgrp_children(cookie(shift), shift)); +} + +sub parents +{ + scalar @_ == 2 or _usage("parents(class, lgrp)"); + return (lgrp_parents(cookie(shift), shift)); +} + +sub mem_size +{ + scalar @_ == 4 or _usage("mem_size(class, lgrp, type, content)"); + return (lgrp_mem_size(cookie(shift), shift, shift, shift)); +} + +sub cpus +{ + scalar @_ == 3 or _usage("cpus(class, lgrp, content)"); + return (lgrp_cpus(cookie(shift), shift, shift)); +} + +sub isleaf +{ + scalar @_ == 2 or _usage("isleaf(class, lgrp)"); + lgrp_isleaf(cookie(shift), shift); +} + +sub resources +{ + scalar @_ == 3 or _usage("resources(class, lgrp, resource)"); + return (lgrp_resources(cookie(shift), shift, shift)); +} + +sub latency +{ + scalar @_ == 3 or _usage("latency(class, from, to)"); + return (lgrp_latency_cookie(cookie(shift), shift, shift)); +} + +# Methods that do not require cookie +sub home +{ + scalar @_ == 3 or _usage("home(class, idtype, id)"); + shift; + return (lgrp_home(shift, shift)); +} + +sub affinity_get +{ + scalar @_ == 4 or _usage("affinity_get(class, idtype, id, lgrp)"); + shift; + return (lgrp_affinity_get(shift, shift, shift)); +} + +sub affinity_set +{ + scalar @_ == 5 or + _usage("affinity_set(class, idtype, id, lgrp, affinity)"); + shift; + return (lgrp_affinity_set(shift, shift, shift, shift)); +} + +1; + +__END__ diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs new file mode 100644 index 0000000000..a2df0fd073 --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs @@ -0,0 +1,397 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Lgrp.xs contains XS wrappers for the system locality group library + * liblgrp(3LIB). + */ + +#include <sys/errno.h> +#include <sys/lgrp_user.h> + +/* + * On i386 Solaris defines SP, which conflicts with the perl definition of SP + * We don't need the Solaris one, so get rid of it to avoid warnings. + */ +#undef SP + +/* Perl XS includes. */ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +/* Return undef in scalar context and empty list in list context */ +#define LGRP_BADVAL() { \ + if (GIMME_V == G_ARRAY) \ + XSRETURN_EMPTY; \ + else \ + XSRETURN_UNDEF; \ +} + +/* + * Push all values from input array onto the perl return stack. + */ +#define PUSHARRAY(array, nitems) \ +{ \ + int x; \ + \ + if (nitems < 0) { \ + LGRP_BADVAL() \ + } else if (nitems > 0) { \ + EXTEND(SP, nitems); \ + for (x = 0; x < nitems; x++) { \ + PUSHs(sv_2mortal(newSVnv(array[x]))); \ + } \ + } \ +} + +/* + * Several constants are not present in the first version of the Lgrp API, + * we define them here. + * + * lgrp_resources() and lgrp_latency_cookie() only appear in API v2. If the + * module is linked with old version of liblgrp(3LIB) there is no lgrp_resources + * symbol in the library and perl wrapper returns empty list and sets errno to + * EINVAL. + * + * The lgrp_latency_cookie() is emulated using lgrp_latency(). + */ +#if LGRP_VER_CURRENT == 1 +#define LGRP_CONTENT_ALL LGRP_CONTENT_HIERARCHY +#define LGRP_LAT_CPU_TO_MEM 0 +#define LGRP_RSRC_CPU 0 /* CPU resources */ +#define LGRP_RSRC_MEM 1 /* memory resources */ + +#define LGRP_RESOURCES(c, lgrp, type) \ + { errno = EINVAL; LGRP_BADVAL(); } + +/* + * Simulate lgrp_latency_cookie() which just fails. This macro is never called + * and we just define it so that the C compiler will not complain about the + * missing symbol. + */ +#define lgrp_latency_cookie(c, f, t, b) (errno = EINVAL, -1) + +#else +#define LGRP_RESOURCES(c, lgrp, type) { \ + int nr; \ + lgrp_id_t *lgrps; \ + \ + errno = 0; \ + nr = lgrp_resources(c, lgrp, NULL, 0, type); \ + if (nr < 0) \ + LGRP_BADVAL(); \ + if (GIMME_V == G_SCALAR) \ + XSRETURN_IV(nr); \ + if (nr == 0) { \ + XSRETURN_EMPTY; \ + } else if (New(0, lgrps, nr, lgrp_id_t) == NULL) { \ + errno = ENOMEM; \ + LGRP_BADVAL(); \ + } else { \ + nr = lgrp_resources(c, lgrp, lgrps, nr, type); \ + PUSHARRAY(lgrps, nr); \ + Safefree(lgrps); \ + } \ +} +#endif + +/* + * Special version of lgrp_latency_cookie(). Use lgrp_latency() for liblgrp V1 + * and lgrp_latency_cookie for V2. + */ +static int +_lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to, + int between) +{ + return (LGRP_VER_CURRENT < 2 ? + lgrp_latency(from, to) : + lgrp_latency_cookie(cookie, from, to, between)); +} + +/* + * Most functions in liblgrp return -1 on failure. The perl equivalent returns + * 'undef' instead. The macro should be call after the RETVAL is set to the + * return value of the function. + */ +#define RETURN_UNDEF_IF_FAIL { if (RETVAL < 0) XSRETURN_UNDEF; } + +/* + * End of C part, start of XS part. + * + * The XS code exported to perl is below here. Note that the XS preprocessor + * has its own commenting syntax, so all comments from this point on are in + * that form. + */ + +MODULE = Sun::Solaris::Lgrp PACKAGE = Sun::Solaris::Lgrp +PROTOTYPES: ENABLE + + # + # Define any constants that need to be exported. By doing it this way we can + # avoid the overhead of using the DynaLoader package, and in addition constants + # defined using this mechanism are eligible for inlining by the perl + # interpreter at compile time. + # +BOOT: + { + HV *stash; + + stash = gv_stashpv("Sun::Solaris::Lgrp", TRUE); + newCONSTSUB(stash, "LGRP_AFF_NONE", newSViv(LGRP_AFF_NONE)); + newCONSTSUB(stash, "LGRP_AFF_STRONG", newSViv(LGRP_AFF_STRONG)); + newCONSTSUB(stash, "LGRP_AFF_WEAK", newSViv(LGRP_AFF_WEAK)); + newCONSTSUB(stash, "LGRP_VER_CURRENT", newSViv(LGRP_VER_CURRENT)); + newCONSTSUB(stash, "LGRP_VER_NONE", newSViv(LGRP_VER_NONE)); + newCONSTSUB(stash, "LGRP_NONE", newSViv(LGRP_NONE)); + newCONSTSUB(stash, "LGRP_RSRC_CPU", newSViv(LGRP_RSRC_CPU)); + newCONSTSUB(stash, "LGRP_RSRC_MEM", newSViv(LGRP_RSRC_MEM)); + newCONSTSUB(stash, "LGRP_CONTENT_HIERARCHY", + newSViv(LGRP_CONTENT_HIERARCHY)); + newCONSTSUB(stash, "LGRP_CONTENT_DIRECT", newSViv(LGRP_CONTENT_DIRECT)); + newCONSTSUB(stash, "LGRP_VIEW_CALLER", newSViv(LGRP_VIEW_CALLER)); + newCONSTSUB(stash, "LGRP_VIEW_OS", newSViv(LGRP_VIEW_OS)); + newCONSTSUB(stash, "LGRP_MEM_SZ_FREE", newSViv(LGRP_MEM_SZ_FREE)); + newCONSTSUB(stash, "LGRP_MEM_SZ_INSTALLED", + newSViv(LGRP_MEM_SZ_INSTALLED)); + newCONSTSUB(stash, "LGRP_CONTENT_ALL", newSViv(LGRP_CONTENT_ALL)); + newCONSTSUB(stash, "LGRP_LAT_CPU_TO_MEM", newSViv(LGRP_LAT_CPU_TO_MEM)); + newCONSTSUB(stash, "P_PID", newSViv(P_PID)); + newCONSTSUB(stash, "P_LWPID", newSViv(P_LWPID)); + newCONSTSUB(stash, "P_MYID", newSViv(P_MYID)); + } + + # + # The code below uses POSTCALL directive which allows to return 'undef' + # whenever a C function returns a negative value. + # + + + # + # lgrp_init([view]) + # Use LGRP_VIEW_OS as the default view. + # +lgrp_cookie_t +lgrp_init(lgrp_view_t view = LGRP_VIEW_OS) + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +lgrp_view_t +lgrp_view(cookie) + lgrp_cookie_t cookie + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +lgrp_affinity_t +lgrp_affinity_get(idtype, id, lgrp) + idtype_t idtype; + id_t id; + lgrp_id_t lgrp; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +int +lgrp_affinity_set(idtype, id, lgrp, affinity) + idtype_t idtype; + id_t id; + lgrp_id_t lgrp; + lgrp_affinity_t affinity; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + XSRETURN_YES; + +int +lgrp_cookie_stale(cookie) + lgrp_cookie_t cookie; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +int +lgrp_fini(cookie) + lgrp_cookie_t cookie; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + XSRETURN_YES; + +lgrp_id_t +lgrp_home(idtype, id) + idtype_t idtype; + id_t id; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +int +lgrp_latency(lgrp_id_t from,lgrp_id_t to) + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +lgrp_mem_size_t +lgrp_mem_size(cookie, lgrp, type, content) + lgrp_cookie_t cookie + lgrp_id_t lgrp + int type + lgrp_content_t content + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +int +lgrp_nlgrps(cookie) + lgrp_cookie_t cookie; + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +lgrp_id_t +lgrp_root(cookie) + lgrp_cookie_t cookie + POSTCALL: + RETURN_UNDEF_IF_FAIL; + +int +lgrp_version(int version = LGRP_VER_NONE) + + # + # lgrp_latency_cookie calls our internal wrapper _lgrp_latency_cookie() which + # works for both old and new versions of liblgrp. + # +int +lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to, int between = 0) + CODE: + RETVAL = _lgrp_latency_cookie(cookie, from, to, between); + POSTCALL: + RETURN_UNDEF_IF_FAIL; + OUTPUT: + RETVAL + + # + # Functions below convert C arrays into Perl lists. They use XS PPCODE + # directive to avoid implicit RETVAL assignments and manipulate perl + # stack directly. + # + # When called in scalar context functions return the number of elements + # in the list or undef on failure. + # + # The PUSHARRAY() macro defined above pushes all values from the C array to + # the perl stack. + # + + # + # @children = lgrp_children($cookie, $parent). + # +void +lgrp_children(cookie, lgrp) + lgrp_cookie_t cookie; + lgrp_id_t lgrp; + PREINIT: + lgrp_id_t *lgrps; + int count; + PPCODE: + errno = 0; + if ((count = lgrp_children(cookie, lgrp, NULL, 0)) < 0) + LGRP_BADVAL(); + + if (GIMME_V == G_SCALAR) + XSRETURN_IV(count); + + if (count > 0) { + if (New(0, lgrps, count, lgrp_id_t) == NULL) { + errno = ENOMEM; + LGRP_BADVAL(); + } else { + count = lgrp_children(cookie, lgrp, lgrps, count); + PUSHARRAY(lgrps, count); + Safefree(lgrps); + } + } + + # + # @parents = lgrp_parents($cookie, $lgrp). + # +void +lgrp_parents(cookie, lgrp) + lgrp_cookie_t cookie; + lgrp_id_t lgrp; + PREINIT: + lgrp_id_t *lgrps; + int count; + PPCODE: + errno = 0; + if ((count = lgrp_parents(cookie, lgrp, NULL, 0)) < 0) + LGRP_BADVAL(); + + if (GIMME_V == G_SCALAR) + XSRETURN_IV(count); + + if (count > 0) { + if (New(0, lgrps, count, lgrp_id_t) == NULL) { + errno = ENOMEM; + LGRP_BADVAL(); + } else { + count = lgrp_parents(cookie, lgrp, lgrps, count); + PUSHARRAY(lgrps, count); + Safefree(lgrps); + } + } + + # + # @parents = lgrp_cpus($cookie, $lgrp, $content). + # Content should be LGRP_CONTENT_HIERARCHY or LGRP_CONTENT_ALL or + # LGRP_CONTENT_DIRECT +void +lgrp_cpus(cookie, lgrp, content) + lgrp_cookie_t cookie; + lgrp_id_t lgrp; + lgrp_content_t content; + PREINIT: + int ncpus; + processorid_t *cpus; + PPCODE: + errno = 0; + if ((ncpus = lgrp_cpus(cookie, lgrp, NULL, 0, content)) < 0) + LGRP_BADVAL(); + + if (GIMME_V == G_SCALAR) + XSRETURN_IV(ncpus); + + if (ncpus > 0) { + if (New(0, cpus, ncpus, processorid_t) == NULL) { + errno = ENOMEM; + LGRP_BADVAL(); + } else { + ncpus = lgrp_cpus(cookie, lgrp, cpus, ncpus, content); + PUSHARRAY(cpus, ncpus); + Safefree(cpus); + } + } + +void +lgrp_resources(cookie, lgrp, type) + lgrp_cookie_t cookie; + lgrp_id_t lgrp; + int type; + PPCODE: + LGRP_RESOURCES(cookie, lgrp, type); diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST new file mode 100644 index 0000000000..ea60af447b --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST @@ -0,0 +1,10 @@ +Changes +Makefile.PL +MANIFEST +Lgrp.pm +Lgrp.xs +README +typemap +pod/Lgrp.pod +t/Lgrp.t +t/Lgrp_api.t diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL new file mode 100644 index 0000000000..821f43fb29 --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL @@ -0,0 +1,89 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +require 5.6.1; +use strict; +use warnings; +use ExtUtils::MakeMaker; + +# #defines. +my @defines = ( DEFINE => exists($ENV{RELEASE_BUILD}) ? '-DNDEBUG' : '' ); + +# List of POD pages to install. +my @man3pods = ( MAN3PODS => {} ); + + +# +# If not building as part of ON. +# +if (! exists($ENV{CODEMGR_WS})) { + + # + # Suppress the setting of LD_RUN_PATH. The ON build environment + # contains a modified MakeMaker that does this automatically, so we + # only need to do this if we are building outside of ON. + # + package MY; + no warnings qw(once); + + # Override const_loadlibs to remove LD_RUN_PATH cruft. + *const_loadlibs = sub + { + my $self = shift(@_); + delete($self->{LD_RUN_PATH}); + return($self->SUPER::const_loadlibs(@_)); + }; + + # Override dynamic_lib to get rid of LD_RUN_PATH cruft. + *dynamic_lib = sub + { + my $self = shift(@_); + my $txt = $self->SUPER::dynamic_lib(@_); + $txt =~ s/LD_RUN_PATH=\S*\s*//; + return($txt); + }; + + # Turn off debugging. + @defines = (); + + # + # Install the POD documentation for non-ON builds. + # + my $man3pfx = '$(INST_MAN3DIR)/Sun::Solaris::Lgrp'; + @man3pods = ( + MAN3PODS => { 'pod/Lgrp.pod' => $man3pfx . '.$(MAN3EXT)' } + ); +} + +WriteMakefile( + NAME => 'Sun::Solaris::Lgrp', + VERSION_FROM => 'Lgrp.pm', + LIBS => ['-llgrp '], + @defines, + @man3pods, +); diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README new file mode 100644 index 0000000000..c5c7d9ddfd --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +Licensing +--------- +You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +or http://www.opensolaris.org/os/licensing. +See the License for the specific language governing permissions +and limitations under the License. + +Availability +------------ +This module is only available for Solaris 9u6 onwards. + +Description +----------- +This module provided access to the Solaris locality group library +liblgrp(3LIB). It provides both functional and object-oriented interface to all +functions defined in liblgrp(3LIB). + +The t directory contains two files: Lgrp.t and Lgrp_api.t which contain lots +of examples (and serve as tests). + +Installation +------------ + +1. Uncompress and untar the archive +2. cd to the module directory +3. perl Makefile.PL; make test; make install + +If you are using gcc and wish to build this module against the perl shipped as +part of Solaris, see the Solaris-PerlGcc module, also available from CPAN. diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON new file mode 100644 index 0000000000..4e195006ca --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +This module is released as Public Source via the Comprehensive Perl Archive +Network (http://www.cpan.org). If it is updated for any reason, a new +release should be pushed to CPAN. + +The version number of this module is derived from the $VERSION variable in +Lgrp.pm. This in turn contains the SID of the Lgrp.pm file, so if +anything under this directory is modified, a delta should be made to Lgrp.pm +to update the module version number correctly. If this is not done, it will +not be possible to upload the generated archive to CPAN, as CPAN requires that +uploaded archives have unique names. + +To prepare the contents of the directory for release, they should be packaged +up as a .tar.gz archive. The procedure for this is: + +$ /usr/perl5/bin/perl Makefile.PL +$ make tardist + +This will produce the file for upload to CPAN. Contact cpan-support@sun.com +for details of how to do this. diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod new file mode 100644 index 0000000000..00d07c0a9c --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod @@ -0,0 +1,738 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Sun::Solaris::Lgrp documentation. +# + +=head1 NAME + +Lgrp - Perl interface to Solaris liblgrp library. + +=head1 SYNOPSIS + + use Sun::Solaris::Lgrp qw(:ALL); + + # initialize lgroup interface + my $cookie = lgrp_init(LGRP_VIEW_OS | LGRP_VIEW_CALLER); + my $l = Sun::Solaris::Lgrp->new(LGRP_VIEW_OS | LGRP_VIEW_CALLER); + + my $version = lgrp_version(LGRP_VER_CURRENT | LGRP_VER_NONE); + $version = $l->version(LGRP_VER_CURRENT | LGRP_VER_NONE); + + $home = lgrp_home(P_PID, P_MYID); + $home = l->home(P_PID, P_MYID); + + lgrp_affinity_set(P_PID, $pid, $lgrp, + LGRP_AFF_STRONG | LGRP_AFF_WEAK | LGRP_AFF_NONE); + $l->affinity_set(P_PID, $pid, $lgrp, + LGRP_AFF_STRONG | LGRP_AFF_WEAK | LGRP_AFF_NONE); + + my $affinity = lgrp_affinity_get(P_PID, $pid, $lgrp); + $affinity = $l->affinity_get(P_PID, $pid, $lgrp); + + my $nlgrps = lgrp_nlgrps($cookie); + $nlgrps = $l->nlgrps(); + + my $root = lgrp_root($cookie); + $root = l->root(); + + $latency = lgrp_latency($lgrp1, $lgrp2); + $latency = $l->latency($lgrp1, $lgrp2); + + my @children = lgrp_children($cookie, $lgrp); + @children = l->children($lgrp); + + my @parents = lgrp_parents($cookie, $lgrp); + @parents = l->parents($lgrp); + + my @lgrps = lgrp_lgrps($cookie); + @lgrps = l->lgrps(); + + @lgrps = lgrp_lgrps($cookie, $lgrp); + @lgrps = l->lgrps($lgrp); + + my @leaves = lgrp_leaves($cookie); + @leaves = l->leaves(); + + my $is_leaf = lgrp_isleaf($cookie, $lgrp); + $is_leaf = $l->is_leaf($lgrp); + + my @cpus = lgrp_cpus($cookie, $lgrp, + LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT); + @cpus = l->cpus($lgrp, LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT); + + my $memsize = lgrp_mem_size($cookie, $lgrp, + LGRP_MEM_SZ_INSTALLED | LGRP_MEM_SZ_FREE, + LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT); + $memsize = l->mem_size($lgrp, + LGRP_MEM_SZ_INSTALLED | LGRP_MEM_SZ_FREE, + LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT); + + my $is_stale = lgrp_cookie_stale($cookie); + $stale = l->stale(); + + lgrp_fini($cookie); + + # The following is available for API version greater than 1: + + my @lgrps = lgrp_resources($cookie, $lgrp, LGRP_RSRC_CPU); + + # Get latencies from cookie + $latency = lgrp_latency_cookie($cookie, $from, $to); + +=head1 DESCRIPTION + +This module provides access to the C<liblgrp(3LIB)> library and to various +constants and functions defined in C<sys/lgrp_sys.h> header file. It provides +both the procedural and object interface to the library. The procedural +interface requires (in most cases) passing a transparent cookie around. The +object interface hides all the cookie manipulations from the user. + +Functions returning scalar value indicate error by returning B<undef>. The +caller may examine the B<$!> variable to get the C<errno> value. + +Functions returning list value return the number of elements in the list when +called in scalar context. In case of error the empty list is return in the array +context and B<undef> is returned in the scalar context. + +=head2 CONSTANTS + +The constants are exported with B<:CONSTANTS> or B<:ALL> tags: + + use Sun::Solaris::Lgrp ':ALL'; + +or + + use Sun::Solaris::Lgrp ':CONSTANTS'; + +The following constants are available for use in Perl programs: + + + LGRP_NONE + + LGRP_VER_CURRENT + LGRP_VER_NONE + + LGRP_VIEW_CALLER + LGRP_VIEW_OS + + LGRP_AFF_NONE + LGRP_AFF_STRONG + LGRP_AFF_WEAK + + LGRP_CONTENT_DIRECT + LGRP_CONTENT_HIERARCHY + + LGRP_MEM_SZ_FREE + LGRP_MEM_SZ_INSTALLED + + LGRP_RSRC_CPU (1) + LGRP_RSRC_MEM (1) + LGRP_CONTENT_ALL (1) + LGRP_LAT_CPU_TO_MEM(1) + + P_PID + P_LWPID + P_MYID + +(1) Available for versions of the liblgrp(3LIB) API greater than 1. + +=head2 functions + +A detailed description of each function follows. Since this module is intended +to provide a Perl interface to the routines in L<liblgrp(3LIB)>, a very short +description is given for the corresponding functions in this module and a +reference is given to the complete description in the L<liblgrp(3LIB)> man +pages. Any differences or additional functionality in the Perl module are +highlighted and fully documented here. + +=over + +=item lgrp_init([LGRP_VIEW_CALLER | LGRP_VIEW_OS]) + +The function initializes the lgroup interface and takes a snapshot of the lgroup +hierarchy with the given view. Given the view, L<lgrp_init()> returns a cookie +representing this snapshot of the lgroup hierarchy. This cookie should be used +with other routines in the lgroup interface needing the lgroup hierarchy. The +L<lgrp_fini()> function should be called with the cookie when it is no longer +needed. Unlike L<lgrp_init (3LGRP)>, C<LGRP_VIEW_OS> is assumed as the default if +no view is provided. + +Upon successful completion, L<lgrp_init()> returns a cookie. Otherwise it returns +B<undef> and sets B<$!> to indicate the error. + +See L<lgrp_init(3LGRP)> for more information. + +=item lgrp_fini($cookie) + +The function takes a cookie, frees the snapshot of the lgroup hierarchy created +by L<lgrp_init()>, and cleans up anything else set up by L<lgrp_init()>. After +this function is called, the cookie returned by the lgroup interface might no +longer be valid and should not be used. + +Upon successful completion, 1 is returned. Otherwise, B<undef> is returned and +B<$!> is set to indicate the error. + +See L<lgrp_fini(3LGRP)> for more information. + +=item lgrp_view($cookie) + +The function takes a cookie representing the snapshot of the lgroup hierarchy +and returns the snapshot's view of the lgroup hierarchy. + +If the given view is C<LGRP_VIEW_CALLER>, the snapshot contains only the +resources that are available to the caller (such as those with respect to +processor sets). When the view is C<LGRP_VIEW_OS>, the snapshot contains what +is available to the operating system. + +Upon succesful completion, the function returns the view for the snapshot of the +lgroup hierarchy represented by the given cookie. Otherwise, B<undef> is +returned and C<$!> is set. + +See L<lgrp_view(3LGRP)> for more information. + +=item lgrp_home($idtype, $id) + +Returns the home lgroup for the given process or thread. The B<$idtype> argument +should be C<P_PID> to specify a process and the B<$id> argument should be its +process id. Otherwise, the B<$idtype> argument should be C<P_LWPID> to specify a +thread and the B<$id> argument should be its LWP id. The value C<P_MYID> can be +used for the id argument to specify the current process or thread. + +Upon successful completion, C<lgrp_home()> returns the id of the home lgroup of +the specified process or thread. Otherwise, B<undef> is returned and B<$!> is +set to indicate the error. + +See L<lgrp_home(3LGRP)> for more information. + +=item lgrp_cookie_stale($cookie) + +Upon successful completion, the function returns whether the cookie is +stale. Otherwise, it returns B<undef> and sets B<$!> to indicate the error. + +The L<lgrp_cookie_stale()> function will fail with C<EINVAL> if the cookie is +not valid. + +See L<lgrp_cookie_stale(3LGRP)> for more information. + +=item lgrp_cpus($cookie, $lgrp, $context) + +The function takes a cookie representing a snapshot of the lgroup hierarchy and +returns the list of CPUs in the lgroup specified by B<$lgrp>. The B<$context> +argument should be set to one of the following values to specify whether the +direct contents or everything in this lgroup including its children should be +returned: + +=over + +=item LGRP_CONTENT_HIERARCHY + +Everything within this hierarchy. + +=item LGRP_CONTENT_DIRECT + +Directly contained in lgroup. + +=back + +When called in scalar context, L<lgrp_cpus()> function returns the number of +CPUs, contained in the specified lgroup. + +In case of error B<undef> is returned in scalar context and B<$!> is set to +indicate the error. In list context the empty list is returned and B<$!> is set. + +See L<lgrp_cpus(3LGRP)> for more information. + +=item lgrp_children($cookie, $lgrp) + +The function takes a cookie representing a snapshot of the lgroup hierarchy and +returns the list of lgroups that are children of the specified lgroup. + +When called in scalar context, L<lgrp_children()> function returns the number of +children lgroups for the specified lgroup. + +In case of error B<undef> or empty list is returned and B<$!> is set to indicate +the error. + +See L<lgrp_children(3LGRP)> for more information. + +=item lgrp_parents($cookie, $lgrp) + +The function takes a cookie representing a snapshot of the lgroup hierarchy and +returns the list of parent of the specified lgroup. + +When called in scalar context, L<lgrp_parents()> function returns the number of +parent lgroups for the specified lgroup. + +In case of error B<undef> or empty list is returned and B<$!> is set to indicate +the error. + +See L<lgrp_parents(3LGRP)> for more information. + +=item lgrp_nlgrps($cookie) + +The function takes a cookie representing a snapshot of the lgroup hierarchy. It +returns the number of lgroups in the hierarchy where the number is always at +least one. + +In case of error B<undef> is returned and B<$!> is set to EINVAL indicatng that +the cookie is not valid. + +See L<lgrp_nlgrps(3LGRP)> for more information. + +=item lgrp_root($cookie) + +The function returns the root lgroup ID. In case of error B<undef> is returned +and B<$!> is set to EINVAL indicatng that the cookie is not valid. + +See L<lgrp_root(3LGRP)> for more information. + +=item lgrp_mem_size($cookie, $lgrp, $type, $content) + +The function takes a cookie representing a snapshot of the lgroup hierarchy. The +function returns the memory size of the given lgroup in bytes. The B<$type> +argument should be set to one of the following values: + +=over + +=item LGRP_MEM_SZ_FREE + +Free memory. + +=item LGRP_MEM_SZ_INSTALLED + +Installed memory. + +=back + +The B<$content> argument should be set to one of the following values to specify +whether the direct contents or everything in this lgroup including its children +should be returned: + +=over + +=item LGRP_CONTENT_HIERARCHY + +Everything within this hierarchy. + +=item LGRP_CONTENT_DIRECT + +Directly contained in lgroup. + +=back + +The total sizes include all the memory in the lgroup including its children, +while the others reflect only the memory contained directly in the given lgroup. + +Upon successful completion, the size in bytes is returned. Otherwise, B<undef> +is returned and B<$!> is set to indicate the error. + +See L<lgrp_mem_size(3LGRP)> for more information. + +=item lgrp_version([$version]) + +The function takes an interface version number, B$version>, as an argument and +returns an lgroup interface version. The B<$version> argument should be the +value of C<LGRP_VER_CURRENT> or C<LGRP_VER_NONE> to find out the current lgroup +interface version on the running system. + +If B<$version> is still supported by the implementation, then L<lgrp_version()> +returns the requested version. If C<LGRP_VER_NONE> is returned, the +implementation cannot support the requested version. + +If B<$version> is C<LGRP_VER_NONE>, L<lgrp_version()> returns the current version of +the library. + +The following example tests whether the version of the +interface used by the caller is supported: + + lgrp_version(LGRP_VER_CURRENT) == LGRP_VER_CURRENT or + die("Built with unsupported lgroup interface"); + +See L<lgrp_version(3LGRP)> for more information. + +=item lgrp_affinity_set($idtype, $id, $lgrp, $affinity) + +The function sets of LWPs specified by the B<$idtype> and B<$id> arguments have +for the given lgroup. + +The function sets the affinity that the LWP or set of LWPs specified by $idtype +and $id have for the given lgroup. The lgroup affinity can be set to +C<LGRP_AFF_STRONG>, C<LGRP_AFF_WEAK>, or C<LGRP_AFF_NONE>. + +If the B<$idtype> is C<P_PID>, the affinity is retrieved for one of the LWPs in +the process or set for all the LWPs of the process with process id (PID) B<$id>. +The affinity is retrieved or set for the LWP of the current process with LWP id +$id if idtype is C<P_LWPID>. If $id is C<P_MYID>, then the current LWP or +process is specified. + +There are different levels of affinity that can be specified by a thread for a +particuliar lgroup. The levels of affinity are the following from strongest to +weakest: + +=over + +=item LGRP_AFF_STRONG + +Strong affinity. + +=item LGRP_AFF_WEAK + +Weak affinity. + +=item LGRP_AFF_NONE + +No affinity. + +=back + +Upon successful completion, L<lgrp_affinity_set()> return 1. Otherwise, it +returns B<undef> and set B<$!> to indicate the error. + +See L<lgrp_affinity_set(3LGRP)> for more information. + +=item lgrp_affinity_get($idtype, $id, $lgrp) + +The function returns the affinity that the LWP has to a given lgrp. See +L<lgrp_affinity_get()> for detailed description. + +See L<lgrp_affinity_get(3LGRP)> for more information. + +=item lgrp_latency_cookie($cookie, $from, $to, [$between=LGRP_LAT_CPU_TO_MEM]) + +The function takes a cookie representing a snapshot of the lgroup hierarchy and +returns the latency value between a hardware resource in the $from lgroup to a +hardware resource in the B<$to> lgroup. If B<$from> is the same lgroup as $to, the +latency value within that lgroup is returned. + +The optional B<between> argument should be set to C<LGRP_LAT_CPU_TO_MEM> to specify +between which hardware resources the latency should be measured. Currently the +only valid value is C<LGRP_LAT_CPU_TO_MEM> which represents latency from CPU to +memory. + +Upon successful completion, lgrp_latency_cookie() return 1. Otherwise, it +returns B<undef> and set B<$!> to indicate the error. For LGRP API version 1 the +L<lgrp_latency_cookie()> is an alias for L<lgrp_latency()>. + +See L<lgrp_latency_cookie(3LGRP)> for more information. + +=item lgrp_latency($from, $to) + +The function is similiar to the L<lgrp_latency_cookie()> function, but returns the +latency between the given lgroups at the given instant in time. Since lgroups +may be freed and reallocated, this function may not be able to provide a +consistent answer across calls. For that reason, it is recommended that +L<lgrp_latency_cookie()> function be used in its place. + +See L<lgrp_latency(3LGRP)> for more information. + +=item lgrp_resources($cookie, $lgrp, $type) + +Return the list of lgroups directly containing resources of the specified type. +The resources are represented by a set of lgroups in which each lgroup directly +contains CPU and/or memory resources. + +The type can be specified as + +=over + +=item C<LGRP_RSRC_CPU> + +CPU resources + +=item C<LGRP_RSRC_MEM> + +Memory resources + +=back + +In case of error B<undef> or empty list is returned and B<$!> is set to indicate +the error. + +This function is only available for API version 2 and will return B<undef> or +empty list for API version 1 and set $! to C<EINVAL>. + +See C<lgrp_resources(3LGRP)> for more information. + +=item lgrp_lgrps($cookie, [$lgrp]) + +Returns list of all lgroups in a hierarchy starting from $lgrp. If B<$lgrp> is +not specified, uses the value of lgrp_root($cookie). Returns the empty list on +failure. + +When called in scalar context, returns the total number of lgroups in the +system. + +=item lgrp_leaves($cookie, [$lgrp]) + +Returns list of all leaf lgroups in a hierarchy starting from $lgrp. If $lgrp is +not specified, uses the value of lgrp_root($cookie). Returns B<undef> or empty +list on failure. + +When called in scalar context, returns the total number of leaf lgroups in the +system. + +=item lgrp_isleaf($cookie, $lgrp) + +Returns B<True> if $lgrp is leaf (has no children), B<False> otherwise. + +=back + +=head2 Object Methods + +=over + +=item new([$view]) + +Creates a new Sun::Solaris::Lgrp object. An optional argument is passed to +L<lgrp_init()> function. By default uses C<LGRP_VIEW_OS>. + +=item cookie() + +Returns a transparent cookie that may be passed to functions accepting cookie. + +=item version([$version]) + +Without the argument returns the current version of the L<liblgrp(3LIB)> +library. This is a wrapper for L<lgrp_version()> with C<LGRP_VER_NONE> as the +default version argument. + +=item stale() + +Returns B<T> if the lgroup information in the object is stale, B<F> +otherwise. It is a wrapper for L<lgrp_cookie_stale()>. + +=item view() + +Returns the snapshot's view of the lgroup hierarchy. It is a wrapper for +L<lgrp_view()>. + +=item root() + +Returns the root lgroup. It is a wrapper for L<lgrp_root()>. + +=item children($lgrp) + +Returns the list of lgroups that are children of the specified lgroup. This is a +wrapper for L<lgrp_children()>. + +=item parents($lgrp) + +Returns the list of lgroups that are parents of the specified lgroup. This is a +wrapper for L<lgrp_parents()>. + +=item nlgrps() + +Returns the number of lgroups in the hierarchy. This is a wrapper for +L<lgrp_nlgrps()>. + +=item mem_size($lgrp, $type, $content) + +Returns the memory size of the given lgroup in bytes. This is a wrapper for +L<lgrp_mem_size()>. + +=item cpus($lgrp, $context) + +Returns the list of CPUs in the lgroup specified by $lgrp. This is a wrapper for +L<lgrp_cpus()>. + +=item resources($lgrp, $type) + +Returns the list of lgroups directly containing resources of the specified +type. This is a wrapper for L<lgrp_resources()>. + +=item home($idtype, $id) + +Returns the home lgroup for the given process or thread. This is a wrapper for +L<lgrp_home()>. + +=item affinity_get($idtype, $id, $lgrp) + +Returns the affinity that the LWP has to a given lgrp. This is a wrapper for +L<lgrp_affinity_get()>. + +=item affinity_set($idtype, $id, $lgrp, $affinity) + +Sets of LWPs specified by the $idtype and $id arguments have for the given lgroup. +This is a wrapper for L<lgrp_affinity_set()>. + +=item lgrps([$lgrp]) + +Returns list of all lgroups in a hierarchy starting from $lgrp (or the +L<lgrp_root()> if $lgrp is not specified). This is a wrapper for L<lgrp_lgrps()>. + +=item leaves([$lgrp]) + +Returns list of all leaf lgroups in a hierarchy starting from B<$lgrp>. If $lgrp +is not specified, uses the value of lgrp_root(). This is a wrapper for +L<lgrp_leaves()>. + +=item isleaf($lgrp) + +Returns B<True> if B<$lgrp> is leaf (has no children), B<False> otherwise. +This is a wrapper for L<lgrp_isleaf()>. + +=item latency($from, $to) + +Returns the latency value between a hardware resource in the $from lgroup to a +hardware resource in the B<$to> lgroup. It will use L<lgrp_latency()> for +version 1 of liblgrp(3LIB) and L<lgrp_latency_cookie()> for newer versions. + +=back + +=head2 EXPORTS + +By default nothing is exported from this module. The following tags can be used +to selectively import constants and functions defined in this module: + +=over + +=item :LGRP_CONSTANTS + +LGRP_AFF_NONE, LGRP_AFF_STRONG, LGRP_AFF_WEAK, LGRP_CONTENT_DIRECT, +LGRP_CONTENT_HIERARCHY, LGRP_MEM_SZ_FREE, LGRP_MEM_SZ_INSTALLED, +LGRP_VER_CURRENT, LGRP_VER_NONE, LGRP_VIEW_CALLER, LGRP_VIEW_OS, +LGRP_NONE, LGRP_RSRC_CPU, LGRP_RSRC_MEM, LGRP_CONTENT_ALL, +LGRP_LAT_CPU_TO_MEM. + +=item :PROC_CONSTANTS + +P_PID, P_LWPID P_MYID + +=item :CONSTANTS + +:LGRP_CONSTANTS, :PROC_CONSTANTS + +=item :FUNCTIONS + +lgrp_affinity_get(), lgrp_affinity_set(), lgrp_children(), lgrp_cookie_stale(), +lgrp_cpus(), lgrp_fini(), lgrp_home(), lgrp_init(), lgrp_latency(), +lgrp_latency_cookie(), lgrp_mem_size(), lgrp_nlgrps(), lgrp_parents(), +lgrp_root(), lgrp_version(), lgrp_view(), lgrp_resources(), +lgrp_lgrps(), lgrp_leaves(), lgrp_isleaf(), lgrp_lgrps(), lgrp_leaves(). + +=item :ALL + +:CONSTANTS, :FUNCTIONS + +=back + +=head2 Error values + +The functions in this module return B<undef> or an empty list when an underlying +library function fails. The B<$!> is set to provide more information values for +the error. The following error codes are possible: + +=over + +=item EINVAL + +The value supplied is not valid. + +=item ENOMEM + +There was not enough system memory to complete an operation. + +=item EPERM + +The effective user of the calling process does not have appropriate privileges, +and its real or effective user ID does not match the real or effective user ID +of one of the threads. + +=item ESRCH + +The specified process or thread was not found. + +=back + +=head2 Difference in the API versions + +The C<liblgrp(3LIB)> is versioned. The exact version which was used to compile a +module is available through B<lgrp_version> function. + +Version 2 of the lgrpp_user API introduced the following constants and +functions, nbot present in version 1: + +=over + +=item C<LGRP_RSRC_CPU> constant + +=item C<LGRP_RSRC_MEM> constant + +=item C<LGRP_CONTENT_ALL> constant + +=item C<LGRP_LAT_CPU_TO_MEM> constant + +=item C<lgrp_resources()> function + +=item C<lgrp_latency_cookie()> function + +=back + +The C<LGRP_RSRC_CPU> and C<LGRP_RSRC_MEM> are not defined for version 1. The +L<lgrp_resources()> function is defined for version 1 but always returns empty +list. The L<lgrp_latency_cookie()> function is an alias for lgrp_latency for +version 1. + +=head1 ATTRIBUTES + +See L<attributes(5)> for descriptions of the following attributes: + + ___________________________________________________________ + | ATTRIBUTE TYPE | ATTRIBUTE VALUE | + |_____________________________|_____________________________| + | Availability | SUNWpl5u | + |_____________________________|_____________________________| + | Interface Stability | Unstable | + |_____________________________|_____________________________| + + +=head1 SEE ALSO + +L<liblgrp(3LIB)>, +L<lgrp_affinity_get(3LGRP)>, +L<lgrp_affinity_set(3LGRP)>, +L<lgrp_children(3LGRP)>, +L<lgrp_cookie_stale(3LGRP)>, +L<lgrp_cpus(3LGRP)>, +L<lgrp_fini(3LGRP)>, +L<lgrp_home(3LGRP)>, +L<lgrp_init(3LGRP)>, +L<lgrp_latency(3LGRP)>, +L<lgrp_mem_size(3LGRP)>, +L<lgrp_nlgrps(3LGRP)>, +L<lgrp_parents(3LGRP)>, +L<lgrp_root(3LGRP)>, +L<lgrp_version(3LGRP)>, +L<lgrp_view(3LGRP)>, +L<lgrp_resources(3LGRP)>, +L<lgrp_latency_cookie(3LGRP)>, +L<attributes(5)> + +=cut diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t new file mode 100755 index 0000000000..eb242a7499 --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t @@ -0,0 +1,308 @@ +#! /usr/perl5/bin/perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Tests for Sun::Solaris::Lgrp API. +# +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl Lgrp.t' +# +# The test uses Test module which is available on Perl 5.6 and later. +# + + +use strict; +use warnings; +use Test; + +# Tests to run +BEGIN { plan tests => 63 } + +use Sun::Solaris::Lgrp ':ALL'; + +# +###################################################################### + +my ($home, $fail); + +###################################################################### +# Check that all exported constants can be accessed. +$fail = 0; +foreach my $constname (qw( + LGRP_AFF_NONE LGRP_AFF_STRONG LGRP_AFF_WEAK LGRP_CONTENT_DIRECT + LGRP_CONTENT_HIERARCHY LGRP_MEM_SZ_FREE + LGRP_MEM_SZ_INSTALLED LGRP_VER_CURRENT LGRP_VER_NONE + LGRP_VIEW_CALLER LGRP_VIEW_OS LGRP_RSRC_CPU LGRP_RSRC_MEM + LGRP_CONTENT_ALL LGRP_LAT_CPU_TO_MEM)) { + next if (eval "my \$a = $constname; 1"); + $fail++; +} + +ok($fail, 0, 'All Constants defined' ); + +######################### + +###################################################################### +# Verify lgrp_version +## +my $version = lgrp_version(-1); +ok($version, LGRP_VER_NONE, 'incorrect lgrp version unsupported'); + +$version = lgrp_version(LGRP_VER_NONE); +ok($version, LGRP_VER_CURRENT, 'lgrp version is current'); + +$version = lgrp_version(LGRP_VER_CURRENT); +ok($version, LGRP_VER_CURRENT, 'support LGRP_VER_CURRENT version'); +# +####################################################################### + +###################################################################### +# Verify that lgrp_init()/lgrp_fini work. +## +my $c = lgrp_init(LGRP_VIEW_CALLER); +ok($c) or + die("lgrp_init: $!"); + +my $view = lgrp_view($c); + +ok($view, LGRP_VIEW_CALLER, 'View is LGRP_VIEW_CALLER'); + +my $fin = lgrp_fini($c); +ok($fin); + +# Try to free it again, it should fail. +$fin = lgrp_fini($c); +ok($fin, undef, 'lgrp_fini second time should fail'); + +$c = lgrp_init(LGRP_VIEW_OS); +ok($c) or + die("lgrp_init: $!"); + +$view = lgrp_view($c); + +ok($view, LGRP_VIEW_OS, 'View is LGRP_VIEW_OS'); +# +###################################################################### + +###################################################################### +# root should have ID 0. +## +my $root = lgrp_root($c); +ok($root, 0, 'root should have id zero'); +# +###################################################################### +# Verify lgrp_nlgrps() +## +my $nlgrps = lgrp_nlgrps($c); +ok($nlgrps); + +my @lgrps = lgrp_lgrps($c); +ok(@lgrps); +ok(scalar @lgrps, $nlgrps, 'lgrp_nlgrps() should match number of lgrps'); +ok($nlgrps, lgrp_lgrps($c), 'lgrp_lgrps() in scalar context is sane'); + +###################################################################### +# All root children should have root as their one and only one parent +## +$fail = 0; +my @children = lgrp_children($c, $root); +ok(scalar @children, lgrp_children($c, $root), 'lgrp_children as scalar'); +my @leaves = lgrp_leaves $c; +ok(scalar @leaves); +ok(scalar @leaves, lgrp_leaves $c); +ok(scalar @children <= scalar @leaves); + +my @parents; + +my $fail_lgrp_parents = 0; + +foreach my $l (@children) { + @parents = lgrp_parents($c, $l) or + (print STDERR "# lgrp_parents: $!\n"), $fail++, last; + my $nparents = @parents; + my ($parent, @rest) = @parents; + $fail++ if $parent != $root; + $fail++ unless $nparents == 1; + $fail_lgrp_parents++ if $nparents != lgrp_parents($c, $l); +} +ok($fail, 0, 'correct parents for children'); +ok($fail_lgrp_parents, 0, 'correct lgrp_parents() as scalar'); + +###################################################################### +# Illegal parents have no children +## +@children = lgrp_children($c, -1); +my $nchildren = lgrp_children($c, -1); +ok(scalar @children, 0, 'Illegal parents have no children'); +# Same in scalar context +ok($nchildren, undef, 'No children means undef as scalar'); + +###################################################################### +# root should have no parents. +## +@parents = lgrp_parents($c, $root); +ok(scalar @parents, 0, 'root should have no parents'); +# Same in scalar context +ok(lgrp_parents($c, $root), 0); +# +###################################################################### +# Illegal children have no paremts +## +@parents = lgrp_parents($c, -1); +my $nparents = lgrp_parents($c, -1); +ok(scalar @parents, 0, 'Illegal children have no paremts'); +# Same in scalar context +ok($nparents, undef, 'No parents means undef as scalar'); +# +###################################################################### +# Root should have non-zero CPUs and memory size +## +my @cpus = lgrp_cpus($c, $root, LGRP_CONTENT_HIERARCHY); +my $ncpus = lgrp_cpus($c, $root, LGRP_CONTENT_HIERARCHY); +ok(scalar @cpus, $ncpus); +ok($ncpus); +ok(lgrp_mem_size($c, $root, LGRP_MEM_SZ_INSTALLED, LGRP_CONTENT_HIERARCHY)); +my @ncpus_bad = lgrp_cpus($c, $root, -1); +ok(scalar @ncpus_bad, 0, 'Bad argument to lgrp_cpus should return empty'); +my $ncpus_bad = lgrp_cpus($c, $root, -1); +ok($ncpus_bad, undef, 'Bad argument to lgrp_cpus should return undef'); +# +###################################################################### + +###################################################################### +# The cookie should not be stale +# +ok(! lgrp_cookie_stale($c)); +# +###################################################################### + +###################################################################### +# Can we call lgrp_latency? +# The latencies from lgrp_latency and lgrp_latency_cookie should match. +## +my $latency = lgrp_latency($root, $root); +ok(defined $latency); + +my $latency1 = lgrp_latency_cookie($c, $root, $root); +ok(defined $latency1); +ok($latency, $latency1, 'Latencies should match'); +# +###################################################################### +# Can we call lgrp_resources? +## +my @lgrps_c = lgrp_resources($c, $root, LGRP_RSRC_CPU); +my $nresources = lgrp_resources($c, $root, LGRP_RSRC_CPU); +ok(!defined $nresources) if $version < 2; +ok(scalar @lgrps_c, 0) if $version < 2; +ok($nresources) if $version >= 2; +ok(@lgrps_c) if $version >= 2; + +## +# lgrp_fini should always succeed. +ok(lgrp_fini($c)); + + +###################################################################### +# Now test Object-Oriented interface. +## +$c = Sun::Solaris::Lgrp->new or + die "Lgrp->new(LGRP_VIEW_OS): $!"; + +ok($c->view, LGRP_VIEW_OS); +ok($c->stale, 0, 'cookie is not stale'); +ok($nlgrps, $c->nlgrps, 'nlgrps'); +my @lg1 = $c->lgrps; +ok(@lgrps, @lg1); +my@leaves1 = $c->leaves; +ok(@leaves, @leaves1) or + print STDERR "# \@leaves: @leaves, \@leaves1: @leaves\n"; +ok($root, $c->root); +@cpus = lgrp_cpus($c->cookie, $root, LGRP_CONTENT_HIERARCHY); +my @cpus1 = $c->cpus($root, LGRP_CONTENT_HIERARCHY); +ok(@cpus, @cpus1) or + print STDERR "# \@cpus: @cpus, \@cpus1: @cpus1\n"; +ok(lgrp_latency($root, $root), $c->latency($root, $root)); +my @lgrps_c1 = $c->resources($root, LGRP_RSRC_CPU); +ok(@lgrps_c, @lgrps_c1); +ok(lgrp_version(LGRP_VER_NONE), $c->version); + +# +###################################################################### +# Can we call lgrp_home? +## +$home = lgrp_home(P_PID, P_MYID); +ok(defined($home)); +my $home1 = $c->home(P_PID, P_MYID); +ok($home1 == $home); +$home1 = lgrp_home(P_LWPID, 1); +ok($home1 == $home); +$home1 = $c->home(P_LWPID, 1); +ok($home1 == $home); + +# +###################################################################### +# Can we call lgrp_affinity_set? +## +my $affinity; + +ok(LGRP_AFF_WEAK); +ok(P_LWPID); + +$affinity = $c->affinity_set(P_PID, P_MYID, $home, LGRP_AFF_WEAK); +ok($affinity); + +$affinity = $c->affinity_set(P_LWPID, 1, $home, LGRP_AFF_WEAK); +ok($affinity); + +$affinity = lgrp_affinity_set(P_PID, P_MYID, $home, LGRP_AFF_WEAK); +ok($affinity); + +$affinity = lgrp_affinity_set(P_LWPID, 1, $home, LGRP_AFF_WEAK); +ok($affinity); + +# +###################################################################### +# Can we call lgrp_affinity_get? +## +$affinity = lgrp_affinity_get(P_PID, P_MYID, $home); +ok($affinity = LGRP_AFF_WEAK); + +$affinity = lgrp_affinity_get(P_LWPID, 1, $home); +ok($affinity == LGRP_AFF_WEAK); + +$affinity = $c->affinity_get(P_PID, P_MYID, $home); +ok($affinity == LGRP_AFF_WEAK); + +$affinity = $c->affinity_get(P_LWPID, 1, $home); +ok($affinity == LGRP_AFF_WEAK); + +# +###################################################################### +# THE END! +######### diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t new file mode 100755 index 0000000000..db08ddd495 --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t @@ -0,0 +1,323 @@ +#! /usr/perl5/bin/perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +require 5.8.0; +use strict; +use warnings; + +###################################################################### +# Tests for Sun::Solaris::Lgrp API. +# +# This is an example script that demonstrates use of Sun::Solaris::Lgrp module. +# It can be used to test the module itself, the liblgrp library or the in-kernel +# implementation. +###################################################################### + +# Tests to run +use Test::More tests => 33; + +# Verify that we can load the module +BEGIN { use_ok('Sun::Solaris::Lgrp') }; + +use Sun::Solaris::Lgrp ':ALL'; + +my ($home, $fail); + +###################################################################### +# Verify that lgrp_init() works. +## +my $c = Sun::Solaris::Lgrp->new(LGRP_VIEW_OS); +ok($c, 'lgrp_init') or die("lgrp_init: $!"); +# +###################################################################### + +###################################################################### +# root should have ID 0. +## +my $root = $c->root; +is($root, 0, 'root should have id zero'); + +# +###################################################################### +# Verify lgrp_nlgrps() +## +my $nlgrps = $c->nlgrps; +ok($nlgrps, 'lgrp_nlgrps') or + diag("lgrp_nlgrps: $!"); + +my $is_numa = ($nlgrps > 1); + +my @lgrps = $c->lgrps; +ok(scalar @lgrps, 'Can get lgrps list') or + diag("lgrp_lgrps: $!"); + +is(scalar @lgrps, $nlgrps, 'lgrp_nlgrps() should match number of lgrps'); + +###################################################################### +# All root children should have root as their one and only one parent +## +$fail = 0; +my (@children) = $c->children($root); +my @leaves = $c->leaves; +ok(@leaves, 'There are some leaves'); + +cmp_ok(@children, '<=', @leaves, 'Root should have nchildren <= nleaves'); +my @parents; + +foreach my $l (@children) { + (@parents) = $c->parents($l) or + diag("lgrp_parents: $!"); + my $nparents = @parents; + my ($parent, @rest) = @parents; + $fail++ if $parent != $root; + $fail++ unless $nparents == 1; +} +is($fail, 0, 'correct parents for children'); + +###################################################################### +# Each lgrp other than root should have a single parent and +# root should have no parents. +## + +$fail = 0; +foreach my $l (lgrp_lgrps($c)) { + next if $l == $root; + my (@parents) = $c->parents($l) or + diag("lgrp_parents: $!"); + my $nparents = @parents; + $fail++ unless $nparents == 1; +} +is($fail, 0, 'All non-leaf lgrps should have single parent'); + +@parents = $c->parents($root); +ok(!@parents, 'root should have no parents'); +# +####################################################################### + +###################################################################### +# Lgrp affinity tests. +####################### + +###################################################################### +# lgrp_affinity-set should change home lgrp. +## +SKIP: { + skip 'Test only valid on NUMA platform', 1 unless $is_numa; + my $leaf = $leaves[0]; # Pickup any non-root lgrp. + $home = $c->home(P_PID, P_MYID); + + # Pickup any lgrp not equal to the current one. + my $lgrp = ($home == $root ? $leaf : $root); + # Set affinity to the new lgrp. + $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_STRONG) or + diag("lgrp_affinity_set(): $!"); + # Our home should change to a new lgrp. + $home = $c->home(P_PID, P_MYID); + is($home, $lgrp, 'Home lgrp should change after strong affinity is set'); + # Drop affinity to the lgrp. + $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_NONE) or + diag("lgrp_affinity_set(): $!"); +} + +###################################################################### +# Should be able to set affinity to any legal value +## + +my @affs = (LGRP_AFF_WEAK, LGRP_AFF_STRONG, LGRP_AFF_NONE); + +foreach my $aff (@affs) { + $c->affinity_set(P_PID, P_MYID, $root, $aff) or + diag("lgrp_affinity_set(): $!"); + my $affinity = $c->affinity_get(P_PID, $$, $root); + is($affinity, $aff, "affinity should be $aff"); +} + +# +###################################################################### + +###################################################################### +# Root should have non-zero CPUs and memory size +# Also, its memory size should be consistent with the one reported by +# sysconfig. +## +my @rcpus = $c->cpus($root, LGRP_CONTENT_HIERARCHY) or + die("lgrp_cpus: $!"); +my $ncpus = @rcpus; +ok($ncpus, 'there are CPUs in the system'); + +my $memsize = $c->mem_size($root, + LGRP_MEM_SZ_INSTALLED, + LGRP_CONTENT_HIERARCHY) or + diag("lgrp_mem_size(): $!"); + +ok($memsize, 'memory size is non-zero'); +# +###################################################################### + +###################################################################### +# The cookie should not be stale +is($c->stale, 0, 'Cookie should not be stale'); +# +###################################################################### + +###################################################################### +# Latency should be non-zero. +my $latency = lgrp_latency($root, $root); +ok(defined $latency, 'lgrp_latency() is working') or + diag("lgrp_latency: $!"); + +my $latency1 = $c->latency($root, $root); +ok(defined $latency1, 'lgrp_latency_cookie() is working') or + diag("lgrp_latency_cookie: $!"); + +is($latency, $latency1, 'Latencies should match'); +# +###################################################################### + +###################################################################### +# Verify latency matrix. +## +SKIP: { + skip 'Test only valid on NUMA platform', 9 unless $is_numa; + + cmp_ok($latency, '>', 0, "Latency from root to self should be positive"); + my $latencies; + my $min_latency = 10000; + my $max_latency = 0; + my $badlatency = 0; + my $assymetrical = 0; + my $diagonalmin = 0; + my $badself = 0; + my $nlatencies; + + foreach my $l1 (@lgrps) { + foreach my $l2 (@lgrps) { + $latencies->{$l1}{$l2} = $c->latency($l1, $l2); + $nlatencies++ if $latencies->{$l1}{$l2}; + } + } + + # There should be at least some lgroups which have latencies. + my @d_lgrps = grep { defined $latencies->{$_}{$_} } @leaves; + ok(@d_lgrps, 'There should be at least some lgroups which have latencies'); + + # All diagonal latencies should be the same. + my $lat_diag_lgrp = $d_lgrps[0]; + my $lat_diag = $latencies->{$lat_diag_lgrp}{$lat_diag_lgrp}; + my @badlatencies = grep { $latencies->{$_}{$_} != $lat_diag } @d_lgrps; + is(scalar @badlatencies, 0, 'All diagonal latencies should be the same') or + diag("diagonal latency: $lat_diag; bad latencies: @badlatencies"); + + my %l_cpus; + my %l_mem; + my $lgrps_nomem; + my $lgrps_nocpus; + + foreach my $l1 (@lgrps) { + $l_cpus{$l1} = scalar $c->cpus($l1, LGRP_CONTENT_HIERARCHY); + $l_mem{$l1} = $c->mem_size($l1, LGRP_MEM_SZ_INSTALLED, + LGRP_CONTENT_HIERARCHY); + $lgrps_nomem++ unless $l_mem{$l1}; + $lgrps_nocpus++ unless $c->cpus($l1, LGRP_CONTENT_HIERARCHY); + } + + # Verify latencies consistency + foreach my $l1 (@lgrps) { + # Can't get latency if source doesn't have CPUs + next unless $l_cpus{$l1}; + my $self_latency = $latencies->{$l1}{$l1}; + $lat_diag = $self_latency if $self_latency; + + foreach my $l2 (@lgrps) { + # Can't get latenciy if destination doesn't have memory + next unless $l_mem{$l2}; + + if (! $latencies->{$l1}{$l2}) { + $badlatency++; + diag("Invalid latency between $l1 and $l2"); + next; + } + + $max_latency = $latencies->{$l1}{$l2} if + $latencies->{$l1}{$l2} > $max_latency; + $min_latency = $latencies->{$l1}{$l2} if + $latencies->{$l1}{$l2} < $min_latency; + + # Latencies should be symmetrical but only if they are valid. + if ($latencies->{$l2}{$l1} && + $latencies->{$l1}{$l2} != $latencies->{$l2}{$l1}) { + $assymetrical++; + diag("latency($l1, $l2) != latency($l2, $l1)"); + } + + $diagonalmin++ if $c->isleaf($l1) && $c->isleaf($l2) && + $self_latency && $self_latency > $latencies->{$l1}{$l2}; + } + } + + SKIP: { + skip 'Symmetry test only valid if all lgroups have memory and CPUs', + 1 if $lgrps_nomem || $lgrps_nocpus; + is($assymetrical, 0, 'Latencies should be symmetrical'); + } + + is($diagonalmin, 0, 'Latency should be minimal on diagonals'); + is($badlatency, 0, 'Latency should be defined'); + is($max_latency, $latencies->{$root}{$root}, + 'Root should have maximum latencies'); + cmp_ok($min_latency, '>', 0, 'Minimum latency should be positive') if + $nlatencies; + cmp_ok($min_latency, '<=', $max_latency, + 'Minimum latency should be less then maximum') if $nlatencies; +} + +###################################################################### +# Verify lgrp_resources API +## +SKIP: { + skip 'lgrp_resources() is not supported', 3 if + ((LGRP_VER_CURRENT == 1) || !$is_numa); + + my @lgrps_c = $c->resources($root, LGRP_RSRC_CPU); + ok(scalar @lgrps_c, 'there are CPU resources in the system'); + $fail = 0; + my $nc = 0; + foreach my $l (@lgrps_c) { + $fail++ unless $c->isleaf($l); + my @cpu_l = $c->cpus($l, LGRP_CONTENT_DIRECT); + $nc += @cpu_l; + } + is($fail, 0, 'Each lgrp containing CPU resources should be leaf'); + is($nc, $ncpus, 'Number of CPUs should match'); +} + +# +###################################################################### +# THE END! +######### diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap new file mode 100644 index 0000000000..eb4995900a --- /dev/null +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +TYPEMAP + id_t T_IV + idtype_t T_IV + lgrp_affinity_t T_IV + lgrp_content_t T_IV + lgrp_cookie_t T_UV + lgrp_id_t T_IV + lgrp_view_t T_UV + lgrp_mem_size_t T_IV diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c index e87d2a9b84..743990ad2a 100644 --- a/usr/src/cmd/prstat/prstat.c +++ b/usr/src/cmd/prstat/prstat.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -75,8 +75,12 @@ #define PSINFO_HEADER_PROC \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP " +#define PSINFO_HEADER_PROC_LGRP \ +" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/NLWP " #define PSINFO_HEADER_LWP \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/LWPID " +#define PSINFO_HEADER_LWP_LGRP \ +" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/LWPID " #define USAGE_HEADER_PROC \ " PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP " #define USAGE_HEADER_LWP \ @@ -99,6 +103,8 @@ "ZONEID NLWP SIZE RSS MEMORY TIME CPU ZONE " #define PSINFO_LINE \ "%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %-.16s/%d" +#define PSINFO_LINE_LGRP \ +"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %4d %-.16s/%d" #define USAGE_LINE \ "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\ "%3.3s %-.12s/%d" @@ -133,6 +139,7 @@ static table_t cpu_tbl = {0, 0, NULL}; /* selected processors */ static table_t set_tbl = {0, 0, NULL}; /* selected processor sets */ static table_t prj_tbl = {0, 0, NULL}; /* selected projects */ static table_t tsk_tbl = {0, 0, NULL}; /* selected tasks */ +static table_t lgr_tbl = {0, 0, NULL}; /* selected lgroups */ static zonetbl_t zone_tbl = {0, 0, NULL}; /* selected zones */ static nametbl_t euid_tbl = {0, 0, NULL}; /* selected effective users */ static nametbl_t ruid_tbl = {0, 0, NULL}; /* selected real users */ @@ -147,6 +154,7 @@ static list_t users; /* list of users */ static list_t tasks; /* list of tasks */ static list_t projects; /* list of projects */ static list_t zones; /* list of zones */ +static list_t lgroups; /* list of lgroups */ static volatile uint_t sigwinch = 0; static volatile uint_t sigtstp = 0; @@ -235,13 +243,21 @@ list_print(list_t *list) break; case LT_LWPS: if (opts.o_outpmode & OPT_LWPS) { - if (opts.o_outpmode & OPT_PSINFO) - (void) printf(PSINFO_HEADER_LWP); + if (opts.o_outpmode & OPT_PSINFO) { + if (opts.o_outpmode & OPT_LGRP) + (void) printf(PSINFO_HEADER_LWP_LGRP); + else + (void) printf(PSINFO_HEADER_LWP); + } if (opts.o_outpmode & OPT_MSACCT) (void) printf(USAGE_HEADER_LWP); } else { - if (opts.o_outpmode & OPT_PSINFO) - (void) printf(PSINFO_HEADER_PROC); + if (opts.o_outpmode & OPT_PSINFO) { + if (opts.o_outpmode & OPT_LGRP) + (void) printf(PSINFO_HEADER_PROC_LGRP); + else + (void) printf(PSINFO_HEADER_PROC); + } if (opts.o_outpmode & OPT_MSACCT) (void) printf(USAGE_HEADER_PROC); } @@ -341,10 +357,20 @@ list_print(list_t *list) if (opts.o_outpmode & OPT_TTY) (void) putchar('\r'); stripfname(lwp->li_info.pr_fname); - (void) printf(PSINFO_LINE, - (int)lwp->li_info.pr_pid, pname, - psize, prssize, pstate, ppri, pnice, - ptime, pcpu, lwp->li_info.pr_fname, lwpid); + if (opts.o_outpmode & OPT_LGRP) { + (void) printf(PSINFO_LINE_LGRP, + (int)lwp->li_info.pr_pid, pname, + psize, prssize, pstate, ppri, pnice, + ptime, pcpu, + (int)lwp->li_info.pr_lwp.pr_lgrp, + lwp->li_info.pr_fname, lwpid); + } else { + (void) printf(PSINFO_LINE, + (int)lwp->li_info.pr_pid, pname, + psize, prssize, pstate, ppri, pnice, + ptime, pcpu, + lwp->li_info.pr_fname, lwpid); + } (void) putp(t_eol); (void) putchar('\n'); } @@ -502,10 +528,15 @@ list_update(list_t *list, lwp_info_t *lwp) if ((list->l_type == LT_ZONES) && (id->id_zoneid != lwp->li_info.pr_zoneid)) continue; + if ((list->l_type == LT_LGRPS) && + (id->id_lgroup != lwp->li_info.pr_lwp.pr_lgrp)) + continue; id->id_nproc++; id->id_taskid = lwp->li_info.pr_taskid; id->id_projid = lwp->li_info.pr_projid; id->id_zoneid = lwp->li_info.pr_zoneid; + id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp; + if (lwp->li_flags & LWP_REPRESENT) { id->id_size += lwp->li_info.pr_size; id->id_rssize += lwp->li_info.pr_rssize; @@ -533,6 +564,7 @@ update: id->id_projid = lwp->li_info.pr_projid; id->id_taskid = lwp->li_info.pr_taskid; id->id_zoneid = lwp->li_info.pr_zoneid; + id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp; id->id_nproc++; if (lwp->li_flags & LWP_REPRESENT) { id->id_size = lwp->li_info.pr_size; @@ -756,7 +788,9 @@ prstat_scandir(DIR *procdir) if (!has_element(&cpu_tbl, lwpsinfo->pr_onpro) || !has_element(&set_tbl, - lwpsinfo->pr_bindpset)) + lwpsinfo->pr_bindpset) || + !has_element(&lgr_tbl, + lwpsinfo->pr_lgrp)) continue; nlwps++; if ((opts.o_outpmode & (OPT_PSETS | OPT_LWPS)) @@ -787,7 +821,8 @@ prstat_scandir(DIR *procdir) } } else { if (!has_element(&cpu_tbl, psinfo.pr_lwp.pr_onpro) || - !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset)) { + !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset) || + !has_element(&lgr_tbl, psinfo.pr_lwp.pr_lgrp)) { fd_close(fds->fds_psinfo); continue; } @@ -883,6 +918,8 @@ list_refresh(list_t *list) list_update(&projects, lwp); if (opts.o_outpmode & OPT_ZONES) list_update(&zones, lwp); + if (opts.o_outpmode & OPT_LGRP) + list_update(&lgroups, lwp); lwp->li_flags &= ~LWP_ALIVE; lwp = lwp->li_next; @@ -1155,7 +1192,7 @@ main(int argc, char **argv) lwpid_init(); fd_init(Setrlimit()); - while ((opt = getopt(argc, argv, "vcmaRLtu:U:n:p:C:P:s:S:j:k:TJz:Z")) + while ((opt = getopt(argc, argv, "vcHmaRLtu:U:n:p:C:P:h:s:S:j:k:TJz:Z")) != (int)EOF) { switch (opt) { case 'R': @@ -1165,6 +1202,12 @@ main(int argc, char **argv) opts.o_outpmode &= ~OPT_TERMCAP; opts.o_outpmode &= ~OPT_FULLSCREEN; break; + case 'h': + fill_table(&lgr_tbl, optarg, 'h'); + break; + case 'H': + opts.o_outpmode |= OPT_LGRP; + break; case 'm': case 'v': opts.o_outpmode &= ~OPT_PSINFO; @@ -1263,15 +1306,25 @@ main(int argc, char **argv) "-a, -J, -T or -Z\n")); if ((opts.o_outpmode & OPT_USERS) && - (opts.o_outpmode & (OPT_TASKS | OPT_PROJECTS | OPT_ZONES))) + (opts.o_outpmode & + (OPT_TASKS | OPT_PROJECTS | OPT_ZONES))) Die(gettext("-a option cannot be used with " "-t, -J, -T or -Z\n")); if (((opts.o_outpmode & OPT_TASKS) && (opts.o_outpmode & (OPT_PROJECTS|OPT_ZONES))) || ((opts.o_outpmode & OPT_PROJECTS) && - (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) { - Die(gettext("-J, -T and -Z options are mutually exclusive\n")); + (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) { + Die(gettext( + "-J, -T and -Z options are mutually exclusive\n")); + } + + /* + * There is not enough space to combine microstate information and + * lgroup information and still fit in 80-column output. + */ + if ((opts.o_outpmode & OPT_LGRP) && (opts.o_outpmode & OPT_MSACCT)) { + Die(gettext("-H and -m options are mutually exclusive\n")); } if (argc > optind) @@ -1299,11 +1352,13 @@ main(int argc, char **argv) list_alloc(&tasks, opts.o_nbottom); list_alloc(&projects, opts.o_nbottom); list_alloc(&zones, opts.o_nbottom); + list_alloc(&lgroups, opts.o_nbottom); list_setkeyfunc(sortk, &opts, &lwps, LT_LWPS); list_setkeyfunc(NULL, &opts, &users, LT_USERS); list_setkeyfunc(NULL, &opts, &tasks, LT_TASKS); list_setkeyfunc(NULL, &opts, &projects, LT_PROJECTS); list_setkeyfunc(NULL, &opts, &zones, LT_ZONES); + list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS); if (opts.o_outpmode & OPT_TERMCAP) curses_on(); if ((procdir = opendir("/proc")) == NULL) diff --git a/usr/src/cmd/prstat/prstat.h b/usr/src/cmd/prstat/prstat.h index 7bfe4798a6..1a13329845 100644 --- a/usr/src/cmd/prstat/prstat.h +++ b/usr/src/cmd/prstat/prstat.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -69,6 +69,7 @@ extern "C" { #define OPT_PROJECTS 0x1000 /* report about system projects */ #define OPT_ZONES 0x2000 /* report about zones */ #define OPT_PSETS 0x4000 /* report for specified psets */ +#define OPT_LGRP 0x8000 /* report home lgroups */ /* * Flags to keep track of process or lwp status @@ -84,6 +85,7 @@ extern "C" { #define LT_TASKS 0x0004 #define LT_PROJECTS 0x0008 #define LT_ZONES 0x0010 +#define LT_LGRPS 0x0020 /* * Linked list of per-process or per-lwp statistics @@ -110,13 +112,15 @@ typedef struct lwp_info { } lwp_info_t; /* - * Linked list of collective per-uid, per-taskid, or per-projid statistics + * Linked list of collective per-uid, per-taskid, per-projid or per-lgroup + * statistics */ typedef struct id_info { uid_t id_uid; /* user id */ taskid_t id_taskid; /* task id */ projid_t id_projid; /* project id */ zoneid_t id_zoneid; /* zone id */ + int id_lgroup; /* lgroup id */ uint_t id_nproc; /* number of processes */ size_t id_size; /* memory usage */ size_t id_rssize; /* resident set size */ diff --git a/usr/src/cmd/prstat/prutil.c b/usr/src/cmd/prstat/prutil.c index 8785f6a107..638dc8086f 100644 --- a/usr/src/cmd/prstat/prutil.c +++ b/usr/src/cmd/prstat/prutil.c @@ -106,8 +106,8 @@ void Usage() { (void) fprintf(stderr, gettext( - "Usage:\tprstat [-acJLmRtTvZ] [-u euidlist] [-U uidlist]\n" - "\t[-p pidlist] [-P cpulist] [-C psrsetlist]\n" + "Usage:\tprstat [-acHJLmRtTvZ] [-u euidlist] [-U uidlist]\n" + "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n" "\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n" "\t[-s key | -S key] [-n nprocs[,nusers]]\n" "\t[interval [counter]]\n")); diff --git a/usr/src/cmd/ps/ps.c b/usr/src/cmd/ps/ps.c index 1f769a308d..0bbb763eef 100644 --- a/usr/src/cmd/ps/ps.c +++ b/usr/src/cmd/ps/ps.c @@ -65,7 +65,7 @@ #define max(a, b) ((a) < (b) ? (b) : (a)) #define NTTYS 20 /* initial size of table for -t option */ -#define SIZ 30 /* initial size of tables for -p, -s, -g, and -z */ +#define SIZ 30 /* initial size of tables for -p, -s, -g, -h and -z */ /* * Size of buffer holding args for t, p, s, g, u, U, G, z options. @@ -131,7 +131,8 @@ enum fname { /* enumeration of field names */ F_PSET, /* bound processor set */ F_ZONE, /* zone name */ F_ZONEID, /* zone id */ - F_CTID /* process contract id */ + F_CTID, /* process contract id */ + F_LGRP /* process home lgroup */ }; struct field { @@ -204,6 +205,7 @@ static struct def_field fname[] = { { "zone", "ZONE", 8, 8 }, { "zoneid", "ZONEID", 5, 5 }, { "ctid", "CTID", 5, 5 }, + { "lgrp", "LGRP", 4, 2 }, }; #define NFIELDS (sizeof (fname) / sizeof (fname[0])) @@ -228,6 +230,8 @@ static int sflg; static int tflg; static int zflg; static int Zflg; +static int hflg; +static int Hflg; static uid_t tuid = -1; static int errflg; @@ -252,6 +256,13 @@ static pid_t *pid = NULL; /* for p option */ static size_t pidsz = 0; static size_t npid = 0; +static int *lgrps = NULL; /* list of lgroup IDs for for h option */ +static size_t lgrps_size = 0; /* size of the lgrps list */ +static size_t nlgrps = 0; /* number elements in the list */ + +/* Maximum possible lgroup ID value */ +#define MAX_LGRP_ID 256 + static pid_t *grpid = NULL; /* for g option */ static size_t grpidsz = 0; static int ngrpid = 0; @@ -354,9 +365,60 @@ main(int argc, char **argv) fname[F_PID].width = fname[F_PPID].width = pidwidth; fname[F_PGID].width = fname[F_SID].width = pidwidth; - while ((c = getopt(argc, argv, "jlfceAadLPyZt:p:g:u:U:G:n:s:o:z:")) != - EOF) + while ((c = getopt(argc, argv, "jlfceAadLPyZHh:t:p:g:u:U:G:n:s:o:z:")) + != EOF) switch (c) { + case 'H': /* Show home lgroups */ + Hflg++; + break; + case 'h': + /* + * Show processes/threads with given home lgroups + */ + hflg++; + p1 = optarg; + do { + int id; + + /* + * Get all IDs in the list, verify for + * correctness and place in lgrps array. + */ + parg = getarg(&p1); + /* Convert string to integer */ + ret = str2id(parg, (pid_t *)&id, 0, + MAX_LGRP_ID); + /* Complain if ID didn't parse correctly */ + if (ret != 0) { + pgerrflg++; + (void) fprintf(stderr, + gettext("ps: %s "), parg); + if (ret == EINVAL) + (void) fprintf(stderr, + gettext("is an invalid " + "non-numeric argument")); + else + (void) fprintf(stderr, + gettext("exceeds valid " + "range")); + (void) fprintf(stderr, + gettext(" for -h option\n")); + continue; + } + + /* Extend lgrps array if needed */ + if (nlgrps == lgrps_size) { + /* Double the size of the lgrps array */ + if (lgrps_size == 0) + lgrps_size = SIZ; + lgrps_size *= 2; + lgrps = Realloc(lgrps, + lgrps_size * sizeof (int)); + } + /* place the id in the lgrps table */ + lgrps[nlgrps++] = id; + } while (*p1); + break; case 'l': /* long listing */ lflg++; break; @@ -379,7 +441,7 @@ main(int argc, char **argv) case 'e': /* (obsolete) list every process */ Aflg++; tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0; - zflg = 0; + zflg = hflg = 0; break; case 'a': /* @@ -604,7 +666,7 @@ main(int argc, char **argv) * If an appropriate option has not been specified, use the * current terminal and effective uid as the default. */ - if (!(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) { + if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) { psinfo_t info; int procfd; char *name; @@ -642,7 +704,7 @@ main(int argc, char **argv) } if (Aflg) { Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0; - zflg = 0; + zflg = hflg = 0; } if (Aflg | aflg | dflg) tflg = 0; @@ -741,6 +803,8 @@ main(int argc, char **argv) } if (fflg) (void) printf(" STIME"); + if (Hflg) + (void) printf(" LGRP"); if (Lflg) (void) printf(" TTY LTIME CMD\n"); else @@ -748,7 +812,7 @@ main(int argc, char **argv) } - if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|gflg|sflg|zflg) && + if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) && npid <= PTHRESHOLD) { /* * If we are looking at specific processes go straight @@ -858,6 +922,8 @@ retry: found++; /* sessid in s option arg list */ else if (zflg && search(zoneid, nzoneid, info.pr_zoneid)) found++; /* zoneid in z option arg list */ + else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp)) + found++; /* home lgroup in h option arg list */ if (!found && !tflg && !aflg) return (1); if (!prfind(found, &info, &tp)) @@ -919,11 +985,12 @@ static void usage(void) /* print usage message and quit */ { static char usage1[] = - "ps [ -aAdeflcjLPyZ ] [ -o format ] [ -t termlist ]"; + "ps [ -aAdefHlcjLPyZ ] [ -o format ] [ -t termlist ]"; static char usage2[] = "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]"; static char usage3[] = - "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ] [ -z zonelist ]"; + "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ] [ -z zonelist ] " + "[-h lgrplist]"; static char usage4[] = " 'format' is one or more of:"; static char usage5[] = @@ -934,7 +1001,7 @@ usage(void) /* print usage message and quit */ "zoneid"; static char usage7[] = "\tf s c lwp nlwp psr tty addr wchan fname comm args " - "projid project pset"; + "projid project pset lgrp"; (void) fprintf(stderr, gettext("usage: %s\n%s\n%s\n%s\n%s\n%s\n%s\n"), @@ -1339,6 +1406,12 @@ prcom(psinfo_t *psinfo, char *ttyp) else prtime(psinfo->pr_start, 9, 1); } + + if (Hflg) { + /* Display home lgroup */ + (void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp); + } + (void) printf(" %-8.14s", ttyp); /* TTY */ if (Lflg) { tm = psinfo->pr_lwp.pr_time.tv_sec; @@ -1362,6 +1435,7 @@ prcom(psinfo_t *psinfo, char *ttyp) return; } + /* * PRARGSZ == length of cmd arg string. */ @@ -1748,6 +1822,10 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) else (void) printf("%*ld", width, (long)psinfo->pr_contract); break; + case F_LGRP: + /* Display home lgroup */ + (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp); + break; } } @@ -1991,6 +2069,10 @@ przom(psinfo_t *psinfo) (void) printf("%8.8s ", zonename); } } + if (Hflg) { + /* Display home lgroup */ + (void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */ + } if (fflg) { if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) (void) printf("%8.8s ", pwd->pw_name); diff --git a/usr/src/cmd/ptools/Makefile b/usr/src/cmd/ptools/Makefile index df73467065..081145ded2 100644 --- a/usr/src/cmd/ptools/Makefile +++ b/usr/src/cmd/ptools/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,12 +18,14 @@ # # CDDL HEADER END # + # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 1994-2003 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# ident "%Z%%M% %I% %E% SMI" +# + include ../Makefile.cmd @@ -52,6 +53,8 @@ LEGACY_SUBDIRS =\ # NEW_SUBDIRS = \ pargs \ + plgrp \ + pmadvise \ ppriv \ preap @@ -62,6 +65,13 @@ install := TARGET = install clean := TARGET = clean clobber := TARGET = clobber lint := TARGET = lint +_msg := TARGET = _msg + +# +# Commands with messages support +# +POFILES = plgrp/plgrp.po pmadvise/pmadvise.po +POFILE = ptools.po .KEEP_STATE: @@ -74,4 +84,15 @@ $(NEW_SUBDIRS): FRC $(LEGACY_SUBDIRS): FRC @cd $@; pwd; $(MAKE) PTOOL_TYPE=LEGACY -f ../Makefile.ptool $(TARGET) + +# +# Combine all messages files into a single file and copy it to +# MSGDOMAIN directory +# +_msg: ${POFILES} + $(RM) $(POFILE) + $(CAT) $(POFILES) > $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + FRC: diff --git a/usr/src/cmd/ptools/Makefile.bld b/usr/src/cmd/ptools/Makefile.bld index 4ef5cb60d7..446bdac5ae 100644 --- a/usr/src/cmd/ptools/Makefile.bld +++ b/usr/src/cmd/ptools/Makefile.bld @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,12 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" -# PROG:sh = basename `cd ..; pwd` @@ -42,7 +41,9 @@ LDLIBS_pcred = -lproc LDLIBS_pfiles = -lproc -lnsl LDLIBS_pflags = -lproc LDLIBS_pldd = -lproc +LDLIBS_plgrp = -lproc -llgrp LDLIBS_pmap = -lproc +LDLIBS_pmadvise = -lproc LDLIBS_ppriv = -lproc LDLIBS_preap = -lproc LDLIBS_prun = -lproc diff --git a/usr/src/cmd/ptools/plgrp/plgrp.c b/usr/src/cmd/ptools/plgrp/plgrp.c new file mode 100644 index 0000000000..0f8bb3f2bc --- /dev/null +++ b/usr/src/cmd/ptools/plgrp/plgrp.c @@ -0,0 +1,1591 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The plgrp utility allows a user to display and modify the home lgroup and + * lgroup affinities of the specified threads + */ + +#include <ctype.h> +#include <errno.h> +#include <libintl.h> +#include <libproc.h> +#include <locale.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <libgen.h> +#include <sys/lgrp_user.h> + + +/* + * Delimiters + */ +#define DELIMIT_AFF '/' /* lgroup affinity from lgroups */ +#define DELIMIT_LGRP "," /* lgroups from each other */ +#define DELIMIT_LWP "/" /* thread/LWP IDs from process ID */ +#define DELIMIT_RANGE '-' /* range of IDs (eg. lgroup) */ +#define DELIMIT_AFF_LST ',' /* list of affinities from another list */ + +/* + * Exit values other than EXIT_{SUCCESS,FAILURE} + */ +#define EXIT_NONFATAL 2 /* non-fatal errors */ + +/* + * Header and format strings + */ +#define HDR_PLGRP_AFF_GET " PID/LWPID HOME AFFINITY\n" +#define HDR_PLGRP_AFF_SET " PID/LWPID HOME AFFINITY\n" +#define HDR_PLGRP_HOME_GET " PID/LWPID HOME\n" +#define HDR_PLGRP_HOME_SET " PID/LWPID HOME\n" + +/* + * Part of the HDR_PLGRP_AFF_SET header used to calculate space needed to + * represent changing home as old => new + */ +#define HDR_PLGRP_HOME_CHANGE "HOME " + +#define FMT_AFF "%d/%s" +#define FMT_AFF_STR "%s" +#define FMT_HOME "%-6d" +#define FMT_NEWHOME "%d => %d" +#define FMT_THREAD "%8d/%-8d" + +/* + * How much to allocate for lgroup bitmap array as it grows + */ +#define LGRP_BITMAP_CHUNK 8 + +/* + * Strings that can be given for lgroups + */ +#define LGRP_ALL_STR "all" +#define LGRP_LEAVES_STR "leaves" +#define LGRP_ROOT_STR "root" + +/* + * Strings corresponding to lgroup affinities + */ +#define LGRP_AFF_NONE_STR "none" +#define LGRP_AFF_STRONG_STR "strong" +#define LGRP_AFF_WEAK_STR "weak" + +/* + * Invalid value for lgroup affinity + */ +#define LGRP_AFF_INVALID -1 + +/* + * Number of args needed for lgroup system call + */ +#define LGRPSYS_NARGS 3 + +#ifndef TEXT_DOMAIN /* should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */ +#endif + +/* + * plgrp(1) operations + */ +typedef enum plgrp_ops { + PLGRP_AFFINITY_GET, + PLGRP_AFFINITY_SET, + PLGRP_HOME_GET, + PLGRP_HOME_SET, + PLGRP_NO_OP +} plgrp_ops_t; + +/* + * Arguments specified to plgrp(1) and any state needed to do everything + * that plgrp(1) does for one operation from inside Plwp_iter_all() + */ +typedef struct plgrp_args { + struct ps_prochandle *Ph; /* proc handle for process */ + const char *lwps; /* LWPs */ + lgrp_id_t *lgrps; /* lgroups */ + lgrp_affinity_t *affs; /* lgroup affinities */ + int nlgrps; /* number of lgroups */ + int nelements; /* number of elements */ + int index; /* index */ + int nthreads; /* threads processed */ + plgrp_ops_t op; /* operation */ +} plgrp_args_t; + +/* + * How many signals caught from terminal + * We bail out as soon as possible when interrupt is set + */ +static int interrupt = 0; + +/* + * How many non-fatal errors ocurred + */ +static int nerrors = 0; + +/* + * Name of this program + */ +static char *progname; + +/* + * Root of the lgroup hierarchy + */ +static lgrp_id_t root = LGRP_NONE; + +/* + * Bitmap of all lgroups in the system + */ +static char *lgrps_bitmap = NULL; + +/* + * Size of lgrps_bitmap array + */ +static int lgrps_bitmap_nelements = 0; + +/* + * Macro LGRP_VALID returns true when lgrp is present in the system. + */ +#define LGRP_VALID(lgrp) (lgrps_bitmap[lgrp] != 0) + + +/* + * Maximum lgroup value. + */ +static int max_lgrpid = LGRP_NONE; + +/* + * Total possible number of lgroups + */ +#define NLGRPS (max_lgrpid + 1) + + +static void +usage(int rc) +{ + (void) fprintf(stderr, + gettext("Usage:\t%s [-h] <pid> | <core> [/lwps] ...\n"), progname); + (void) fprintf(stderr, + gettext("\t%s [-F] -a <lgroup list> <pid>[/lwps] ...\n"), progname); + (void) fprintf(stderr, + gettext("\t%s [-F] -A <lgroup list>/none|weak|strong[,...] " + " <pid>[/lwps] ...\n"), progname); + (void) fprintf(stderr, + gettext("\t%s [-F] -H <lgroup list> <pid>[/lwps] ...\n"), progname); + (void) fprintf(stderr, + gettext("\n\twhere <lgroup list> is a comma separated list of\n" + "\tone or more of the following:\n\n" + "\t - lgroup ID\n" + "\t - Range of lgroup IDs specified as\n" + "\t\t<start lgroup ID>-<end lgroup ID>\n" + "\t - \"all\"\n" + "\t - \"root\"\n" + "\t - \"leaves\"\n\n")); + + exit(rc); +} + +/* + * Handler for catching signals from terminal + */ +/* ARGSUSED */ +static void +intr(int sig) +{ + interrupt++; +} + + +/* + * Return string name for given lgroup affinity + */ +static char * +lgrp_affinity_string(lgrp_affinity_t aff) +{ + char *rc = "unknown"; + + switch (aff) { + case LGRP_AFF_STRONG: + rc = "strong"; + break; + case LGRP_AFF_WEAK: + rc = "weak"; + break; + case LGRP_AFF_NONE: + rc = "none"; + break; + default: + break; + } + + return (rc); +} + + +/* + * Add a new lgroup into lgroup array in "arg", growing lgroup and affinity + * arrays if necessary + */ +static void +lgrps_add_lgrp(plgrp_args_t *arg, int id) +{ + + if (arg->nlgrps == arg->nelements) { + arg->nelements += LGRP_BITMAP_CHUNK; + + arg->lgrps = realloc(arg->lgrps, + arg->nelements * sizeof (lgrp_id_t)); + if (arg->lgrps == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + exit(EXIT_FAILURE); + } + + arg->affs = realloc(arg->affs, + arg->nelements * sizeof (lgrp_affinity_t)); + + if (arg->affs == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + exit(EXIT_FAILURE); + } + } + + arg->lgrps[arg->nlgrps] = id; + arg->affs[arg->nlgrps] = LGRP_AFF_INVALID; + arg->nlgrps++; +} + + +/* + * Return an array having '1' for each lgroup present in given subtree under + * specified lgroup in lgroup hierarchy + */ +static void +lgrps_bitmap_init(lgrp_cookie_t cookie, lgrp_id_t lgrpid, char **bitmap_array, + int *bitmap_nelements) +{ + lgrp_id_t *children; + int i; + int nchildren; + + if (lgrpid < 0) { + lgrpid = lgrp_root(cookie); + if (lgrpid < 0) + return; + } + + /* + * If new lgroup cannot fit, grow the array and fill unused portion + * with zeroes. + */ + while (lgrpid >= *bitmap_nelements) { + *bitmap_nelements += LGRP_BITMAP_CHUNK; + *bitmap_array = realloc(*bitmap_array, + *bitmap_nelements * sizeof (char)); + if (*bitmap_array == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + exit(EXIT_FAILURE); + } + bzero(*bitmap_array + NLGRPS, + (*bitmap_nelements - NLGRPS) * sizeof (char)); + } + + /* + * Insert lgroup into bitmap and update max lgroup ID seen so far + */ + (*bitmap_array)[lgrpid] = 1; + if (lgrpid > max_lgrpid) + max_lgrpid = lgrpid; + + /* + * Get children of specified lgroup and insert descendants of each + * of them + */ + nchildren = lgrp_children(cookie, lgrpid, NULL, 0); + if (nchildren > 0) { + children = malloc(nchildren * sizeof (lgrp_id_t)); + if (children == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + exit(EXIT_FAILURE); + } + if (lgrp_children(cookie, lgrpid, children, nchildren) != + nchildren) { + free(children); + return; + } + + for (i = 0; i < nchildren; i++) + lgrps_bitmap_init(cookie, children[i], bitmap_array, + bitmap_nelements); + + free(children); + } +} + + +/* + * Parse lgroup affinity from given string + * + * Return lgroup affinity or LGRP_AFF_INVALID if string doesn't match any + * existing lgroup affinity and return pointer to position just after affinity + * string. + */ +static lgrp_affinity_t +parse_lgrp_affinity(char *string, char **next) +{ + int rc = LGRP_AFF_INVALID; + + if (string == NULL) + return (LGRP_AFF_INVALID); + + /* + * Skip delimiter + */ + if (string[0] == DELIMIT_AFF) + string++; + + /* + * Return lgroup affinity matching string + */ + if (strncmp(string, LGRP_AFF_NONE_STR, strlen(LGRP_AFF_NONE_STR)) + == 0) { + rc = LGRP_AFF_NONE; + *next = string + strlen(LGRP_AFF_NONE_STR); + } else if (strncmp(string, + LGRP_AFF_WEAK_STR, strlen(LGRP_AFF_WEAK_STR)) == 0) { + rc = LGRP_AFF_WEAK; + *next = string + strlen(LGRP_AFF_WEAK_STR); + } else if (strncmp(string, LGRP_AFF_STRONG_STR, + strlen(LGRP_AFF_STRONG_STR)) == 0) { + rc = LGRP_AFF_STRONG; + *next = string + strlen(LGRP_AFF_STRONG_STR); + } + + return (rc); +} + + +/* + * Parse lgroups from given string + * Returns the set containing all lgroups parsed or NULL. + */ +static int +parse_lgrps(lgrp_cookie_t cookie, plgrp_args_t *arg, char *s) +{ + lgrp_id_t i; + char *token; + + if (cookie == LGRP_COOKIE_NONE || s == NULL || NLGRPS <= 0) + return (0); + + /* + * Parse first lgroup (if any) + */ + token = strtok(s, DELIMIT_LGRP); + if (token == NULL) + return (-1); + + do { + /* + * Parse lgroups + */ + if (isdigit(*token)) { + lgrp_id_t first; + lgrp_id_t last; + char *p; + + /* + * lgroup ID(s) + * + * Can be <lgroup ID>[-<lgroup ID>] + */ + p = strchr(token, DELIMIT_RANGE); + first = atoi(token); + if (p == NULL) + last = first; + else + last = atoi(++p); + + for (i = first; i <= last; i++) { + /* + * Add valid lgroups to lgroup array + */ + if ((i >= 0) && (i < NLGRPS) && LGRP_VALID(i)) + lgrps_add_lgrp(arg, i); + else { + (void) fprintf(stderr, + gettext("%s: bad lgroup %d\n"), + progname, i); + nerrors++; + } + } + } else if (strncmp(token, LGRP_ALL_STR, + strlen(LGRP_ALL_STR)) == 0) { + /* + * Add "all" lgroups to lgroups array + */ + for (i = 0; i < NLGRPS; i++) { + if (LGRP_VALID(i)) + lgrps_add_lgrp(arg, i); + } + } else if (strncmp(token, LGRP_ROOT_STR, + strlen(LGRP_ROOT_STR)) == 0) { + if (root < 0) + root = lgrp_root(cookie); + lgrps_add_lgrp(arg, root); + } else if (strncmp(token, LGRP_LEAVES_STR, + strlen(LGRP_LEAVES_STR)) == 0) { + /* + * Add leaf lgroups to lgroups array + */ + for (i = 0; i < NLGRPS; i++) { + if (LGRP_VALID(i) && + lgrp_children(cookie, i, NULL, 0) == 0) + lgrps_add_lgrp(arg, i); + } + } else { + return (-1); + } + } while (token = strtok(NULL, DELIMIT_LGRP)); + + return (0); +} + +/* + * Print array of lgroup IDs, collapsing any consecutive runs of IDs into a + * range (eg. 2,3,4 into 2-4) + */ +static void +print_lgrps(lgrp_id_t *lgrps, int nlgrps) +{ + lgrp_id_t start; + lgrp_id_t end; + int i; + + /* + * Initial range consists of the first element + */ + start = end = lgrps[0]; + + for (i = 1; i < nlgrps; i++) { + lgrp_id_t lgrpid; + + lgrpid = lgrps[i]; + if (lgrpid == end + 1) { + /* + * Got consecutive lgroup ID, so extend end of range + * without printing anything since the range may extend + * further + */ + end = lgrpid; + } else { + /* + * Next lgroup ID is not consecutive, so print lgroup + * IDs gotten so far. + */ + if (end == start) { /* same value */ + (void) printf("%d,", (int)start); + } else if (end > start + 1) { /* range */ + (void) printf("%d-%d,", (int)start, (int)end); + } else { /* different values */ + (void) printf("%d,%d,", (int)start, (int)end); + } + + /* + * Try finding consecutive range starting from this + * lgroup ID + */ + start = end = lgrpid; + } + } + + /* + * Print last lgroup ID(s) + */ + if (end == start) { + (void) printf("%d", (int)start); + } else if (end > start + 1) { + (void) printf("%d-%d", (int)start, (int)end); + } else { + (void) printf("%d,%d", (int)start, (int)end); + } +} + +/* + * Print lgroup affinities given array of lgroups, corresponding array of + * affinities, and number of elements. + * Skip any lgroups set to LGRP_NONE or having invalid affinity. + */ +static void +print_affinities(lgrp_id_t *lgrps, lgrp_affinity_t *affs, int nelements) +{ + int i; + lgrp_id_t *lgrps_none; + lgrp_id_t *lgrps_strong; + lgrp_id_t *lgrps_weak; + int nlgrps_none; + int nlgrps_strong; + int nlgrps_weak; + + nlgrps_strong = nlgrps_weak = nlgrps_none = 0; + + lgrps_strong = malloc(nelements * sizeof (lgrp_id_t)); + lgrps_weak = malloc(nelements * sizeof (lgrp_id_t)); + lgrps_none = malloc(nelements * sizeof (lgrp_id_t)); + + if (lgrps_strong == NULL || lgrps_weak == NULL || lgrps_none == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + interrupt = 1; + return; + } + + /* + * Group lgroups by affinity + */ + for (i = 0; i < nelements; i++) { + lgrp_id_t lgrpid = lgrps[i]; + + /* + * Skip any lgroups set to LGRP_NONE + */ + if (lgrpid == LGRP_NONE) + continue; + + switch (affs[i]) { + case LGRP_AFF_STRONG: + lgrps_strong[nlgrps_strong++] = lgrpid; + break; + case LGRP_AFF_WEAK: + lgrps_weak[nlgrps_weak++] = lgrpid; + break; + case LGRP_AFF_NONE: + lgrps_none[nlgrps_none++] = lgrpid; + break; + default: + /* + * Skip any lgroups with invalid affinity. + */ + break; + } + } + + /* + * Print all lgroups with same affinity together + */ + if (nlgrps_strong) { + print_lgrps(lgrps_strong, nlgrps_strong); + (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_STRONG)); + if (nlgrps_weak || nlgrps_none) + (void) printf("%c", DELIMIT_AFF_LST); + } + + if (nlgrps_weak) { + print_lgrps(lgrps_weak, nlgrps_weak); + (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_WEAK)); + if (nlgrps_none) + (void) printf("%c", DELIMIT_AFF_LST); + } + + if (nlgrps_none) { + print_lgrps(lgrps_none, nlgrps_none); + (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_NONE)); + } + + free(lgrps_strong); + free(lgrps_weak); + free(lgrps_none); +} + + +/* + * Print heading for specified operation + */ +static void +print_heading(plgrp_ops_t op) +{ + + switch (op) { + case PLGRP_AFFINITY_GET: + (void) printf(HDR_PLGRP_AFF_GET); + break; + + case PLGRP_AFFINITY_SET: + (void) printf(HDR_PLGRP_AFF_SET); + break; + + case PLGRP_HOME_GET: + (void) printf(HDR_PLGRP_HOME_GET); + break; + + case PLGRP_HOME_SET: + (void) printf(HDR_PLGRP_HOME_SET); + break; + + default: + break; + } +} + +/* + * Use /proc to call lgrp_affinity_get() in another process + */ +static lgrp_affinity_t +Plgrp_affinity_get(struct ps_prochandle *Ph, idtype_t idtype, id_t id, + lgrp_id_t lgrp) +{ + lgrp_affinity_args_t args; + argdes_t Pargd[3]; + argdes_t *Pargdp; + int Pnargs; + int Pretval; + sysret_t retval; + int syscall; + + /* + * Fill in arguments needed for syscall(SYS_lgrpsys, + * LGRP_SYS_AFFINITY_GET, 0, &args) + */ + syscall = SYS_lgrpsys; + + args.idtype = idtype; + args.id = id; + args.lgrp = lgrp; + args.aff = LGRP_AFF_INVALID; + + /* + * Fill out /proc argument descriptors for syscall(SYS_lgrpsys, + * LGRP_SYS_AFFINITY_GET, idtype, id) + */ + Pnargs = LGRPSYS_NARGS; + Pargdp = &Pargd[0]; + Pargdp->arg_value = LGRP_SYS_AFFINITY_GET; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = 0; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = 0; + Pargdp->arg_object = &args; + Pargdp->arg_type = AT_BYREF; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = sizeof (lgrp_affinity_args_t); + Pargdp++; + + /* + * Have agent LWP call syscall with appropriate arguments in target + * process + */ + Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]); + if (Pretval) { + errno = (Pretval < 0) ? ENOSYS : Pretval; + return (LGRP_AFF_INVALID); + } + + return (retval.sys_rval1); +} + + +/* + * Use /proc to call lgrp_affinity_set() in another process + */ +static int +Plgrp_affinity_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id, + lgrp_id_t lgrp, lgrp_affinity_t aff) +{ + lgrp_affinity_args_t args; + argdes_t Pargd[3]; + argdes_t *Pargdp; + int Pnargs; + int Pretval; + sysret_t retval; + int syscall; + + /* + * Fill in arguments needed for syscall(SYS_lgrpsys, + * LGRP_SYS_AFFINITY_SET, 0, &args) + */ + syscall = SYS_lgrpsys; + + args.idtype = idtype; + args.id = id; + args.lgrp = lgrp; + args.aff = aff; + + /* + * Fill out /proc argument descriptors for syscall(SYS_lgrpsys, + * LGRP_SYS_AFFINITY_SET, idtype, id) + */ + Pnargs = LGRPSYS_NARGS; + Pargdp = &Pargd[0]; + Pargdp->arg_value = LGRP_SYS_AFFINITY_SET; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = 0; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = 0; + Pargdp->arg_object = &args; + Pargdp->arg_type = AT_BYREF; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = sizeof (lgrp_affinity_args_t); + Pargdp++; + + /* + * Have agent LWP call syscall with appropriate arguments in + * target process + */ + Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]); + if (Pretval) { + errno = (Pretval < 0) ? ENOSYS : Pretval; + return (-1); + } + + return (retval.sys_rval1); +} + +/* + * Use /proc to call lgrp_home() in another process + */ +static lgrp_id_t +Plgrp_home(struct ps_prochandle *Ph, idtype_t idtype, id_t id) +{ + argdes_t Pargd[3]; + argdes_t *Pargdp; + int Pnargs; + int Pretval; + sysret_t retval; + int syscall; + + /* + * Fill in arguments needed for syscall(SYS_lgrpsys, + * LGRP_SYS_HOME, idtype, id) + */ + syscall = SYS_lgrpsys; + + /* + * Fill out /proc argument descriptors for syscall(SYS_lgrpsys, + * LGRP_SYS_HOME, idtype, id) + */ + Pnargs = LGRPSYS_NARGS; + Pargdp = &Pargd[0]; + Pargdp->arg_value = LGRP_SYS_HOME; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = idtype; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + Pargdp->arg_value = id; + Pargdp->arg_object = NULL; + Pargdp->arg_type = AT_BYVAL; + Pargdp->arg_inout = AI_INPUT; + Pargdp->arg_size = 0; + Pargdp++; + + /* + * Have agent LWP call syscall with appropriate arguments in + * target process + */ + Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]); + if (Pretval) { + errno = (Pretval < 0) ? ENOSYS : Pretval; + return (-1); + } + + return (retval.sys_rval1); +} + +/* + * Use /proc to call lgrp_affinity_set(3LGRP) to set home lgroup of given + * thread + */ +static int +Plgrp_home_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id, + lgrp_id_t lgrp) +{ + return (Plgrp_affinity_set(Ph, idtype, id, lgrp, + LGRP_AFF_STRONG)); +} + + +/* + * Do plgrp(1) operation on specified thread + */ +static int +do_op(plgrp_args_t *plgrp_args, id_t pid, id_t lwpid, + const lwpsinfo_t *lwpsinfo) +{ + lgrp_affinity_t *affs; + lgrp_affinity_t *cur_affs; + lgrp_id_t home; + int i; + lgrp_affinity_t *init_affs; + lgrp_id_t *lgrps; + lgrp_id_t *lgrps_changed; + int nlgrps; + lgrp_id_t old_home; + lgrp_id_t lgrpid; + struct ps_prochandle *Ph; + int nchanged; + + /* + * No args, so nothing to do. + */ + if (plgrp_args == NULL) + return (0); + + /* + * Unpack plgrp(1) arguments and state needed to process this LWP + */ + Ph = plgrp_args->Ph; + lgrps = plgrp_args->lgrps; + affs = plgrp_args->affs; + nlgrps = plgrp_args->nlgrps; + + switch (plgrp_args->op) { + + case PLGRP_HOME_GET: + /* + * Get and display home lgroup for given LWP + */ + home = lwpsinfo->pr_lgrp; + (void) printf(FMT_HOME"\n", (int)home); + break; + + case PLGRP_AFFINITY_GET: + /* + * Get and display this LWP's home lgroup and affinities + * for specified lgroups + */ + home = lwpsinfo->pr_lgrp; + (void) printf(FMT_HOME, (int)home); + + /* + * Collect affinity values + */ + for (i = 0; i < nlgrps; i++) { + affs[i] = Plgrp_affinity_get(Ph, P_LWPID, lwpid, + lgrps[i]); + + if (affs[i] == LGRP_AFF_INVALID) { + nerrors++; + (void) fprintf(stderr, + gettext("%s: cannot get affinity" + " for lgroup %d for %d/%d: %s\n"), + progname, lgrps[i], pid, lwpid, + strerror(errno)); + } + } + + /* + * Print affinities for each type. + */ + print_affinities(lgrps, affs, nlgrps); + (void) printf("\n"); + + break; + + case PLGRP_HOME_SET: + /* + * Get home lgroup before and after setting it and display + * change. If more than one lgroup and one LWP are specified, + * then home LWPs to lgroups in round robin fashion. + */ + old_home = lwpsinfo->pr_lgrp; + + i = plgrp_args->index; + if (Plgrp_home_set(Ph, P_LWPID, lwpid, lgrps[i]) != 0) { + nerrors++; + (void) fprintf(stderr, + gettext("%s: cannot set home lgroup of %d/%d" + " to lgroup %d: %s\n"), + progname, pid, lwpid, lgrps[i], + strerror(errno)); + (void) printf("\n"); + } else { + int len; + int width = strlen(HDR_PLGRP_HOME_CHANGE); + + home = Plgrp_home(Ph, P_LWPID, lwpid); + + if (home < 0) { + (void) fprintf(stderr, + gettext("%s cannot get home lgroup for" + " %d/%d: %s\n"), + progname, pid, lwpid, strerror(errno)); + nerrors++; + } + + len = printf(FMT_NEWHOME, (int)old_home, (int)home); + if (len < width) + (void) printf("%*c\n", (int)(width - len), ' '); + } + + plgrp_args->index = (i + 1) % nlgrps; + + break; + + case PLGRP_AFFINITY_SET: + /* + * Set affinities for specified lgroups and print old and new + * affinities and any resulting change in home lgroups + */ + + /* + * Get initial home lgroup as it may change. + */ + old_home = lwpsinfo->pr_lgrp; + + /* + * Need to allocate arrays indexed by lgroup (ID) for + * affinities and lgroups because user may specify affinity + * for same lgroup multiple times.... + * + * Keeping these arrays by lgroup (ID) eliminates any + * duplication and makes it easier to just print initial and + * final lgroup affinities (instead of trying to keep a list + * of lgroups specified which may include duplicates) + */ + init_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t)); + cur_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t)); + lgrps_changed = malloc(NLGRPS * sizeof (lgrp_id_t)); + + if (init_affs == NULL || cur_affs == NULL || + lgrps_changed == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + progname); + Prelease(Ph, PRELEASE_RETAIN); + exit(EXIT_NONFATAL); + } + + /* + * Initialize current and initial lgroup affinities and + * lgroups changed + */ + for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) { + + if (!LGRP_VALID(lgrpid)) { + init_affs[lgrpid] = LGRP_AFF_INVALID; + } else { + init_affs[lgrpid] = + Plgrp_affinity_get(Ph, P_LWPID, + lwpid, lgrpid); + + if (init_affs[lgrpid] == LGRP_AFF_INVALID) { + nerrors++; + (void) fprintf(stderr, + gettext("%s: cannot get" + " affinity for lgroup %d" + " for %d/%d: %s\n"), + progname, lgrpid, pid, lwpid, + strerror(errno)); + } + } + + cur_affs[lgrpid] = init_affs[lgrpid]; + lgrps_changed[lgrpid] = LGRP_NONE; + } + + /* + * Change affinities. + */ + for (i = 0; i < nlgrps; i++) { + lgrp_affinity_t aff = affs[i]; + + lgrpid = lgrps[i]; + + /* + * If the suggested affinity is the same as the current + * one, skip this lgroup. + */ + if (aff == cur_affs[lgrpid]) + continue; + + /* + * Set affinity to the new value + */ + if (Plgrp_affinity_set(Ph, P_LWPID, lwpid, lgrpid, + aff) < 0) { + nerrors++; + (void) fprintf(stderr, + gettext("%s: cannot set" + " %s affinity for lgroup %d" + " for %d/%d: %s\n"), + progname, lgrp_affinity_string(aff), + lgrpid, pid, lwpid, + strerror(errno)); + continue; + } + + /* + * Get the new value and verify that it changed as + * expected. + */ + cur_affs[lgrpid] = + Plgrp_affinity_get(Ph, P_LWPID, lwpid, lgrpid); + + if (cur_affs[lgrpid] == LGRP_AFF_INVALID) { + nerrors++; + (void) fprintf(stderr, + gettext("%s: cannot get" + " affinity for lgroup %d" + " for %d/%d: %s\n"), + progname, lgrpid, pid, lwpid, + strerror(errno)); + continue; + } + + if (aff != cur_affs[lgrpid]) { + (void) fprintf(stderr, + gettext("%s: affinity for" + " lgroup %d is set to %d instead of %d" + " for %d/%d\n"), + progname, lgrpid, cur_affs[lgrpid], aff, + pid, lwpid); + nerrors++; + } + } + + /* + * Compare current and initial affinities and mark lgroups with + * changed affinities. + */ + nchanged = 0; + for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) { + if (init_affs[lgrpid] != cur_affs[lgrpid]) { + lgrps_changed[lgrpid] = lgrpid; + nchanged++; + } + } + + if (nchanged == 0) { + /* + * Nothing changed, so just print current affinities for + * specified lgroups. + */ + for (i = 0; i < nlgrps; i++) { + lgrps_changed[lgrps[i]] = lgrps[i]; + } + + (void) printf("%-*d", + (int)strlen(HDR_PLGRP_HOME_CHANGE), + (int)old_home); + + print_affinities(lgrps_changed, cur_affs, NLGRPS); + (void) printf("\n"); + } else { + int width = strlen(HDR_PLGRP_HOME_CHANGE); + + /* + * Some lgroup affinities changed, so display old + * and new home lgroups for thread and its old and new + * affinities for affected lgroups + */ + home = Plgrp_home(Ph, P_LWPID, lwpid); + if (home < 0) { + (void) fprintf(stderr, + gettext("%s: cannot get home" + " for %d/%d: %s\n"), + progname, pid, lwpid, strerror(errno)); + nerrors++; + } + if (old_home != home) { + int len; + + /* + * Fit string into fixed width + */ + len = printf(FMT_NEWHOME, + (int)old_home, (int)home); + if (len < width) + (void) printf("%*c", width - len, ' '); + } else { + (void) printf("%-*d", width, (int)home); + } + + /* + * Print change in affinities from old to new + */ + print_affinities(lgrps_changed, init_affs, NLGRPS); + (void) printf(" => "); + print_affinities(lgrps_changed, cur_affs, NLGRPS); + (void) printf("\n"); + } + + free(lgrps_changed); + free(init_affs); + free(cur_affs); + + break; + + default: + break; + } + + return (0); +} + + +/* + * Routine called by Plwp_iter_all() as it iterates through LWPs of another + * process + */ +/* ARGSUSED */ +static int +Plwp_iter_handler(void *arg, const lwpstatus_t *lwpstatus, + const lwpsinfo_t *lwpsinfo) +{ + id_t lwpid; + struct ps_prochandle *Ph; + const pstatus_t *pstatus; + plgrp_args_t *plgrp_args; + + /* + * Nothing to do if no arguments + */ + if (arg == NULL || interrupt) + return (0); + + /* + * Unpack plgrp(1) arguments and state needed to process this LWP + */ + plgrp_args = arg; + Ph = plgrp_args->Ph; + + /* + * Just return if no /proc handle for process + */ + if (Ph == NULL) + return (0); + + pstatus = Pstatus(Ph); + + /* + * Skip agent LWP and any LWPs that weren't specified + */ + lwpid = lwpsinfo->pr_lwpid; + if (lwpid == pstatus->pr_agentid || + !proc_lwp_in_set(plgrp_args->lwps, lwpid)) + return (0); + + plgrp_args->nthreads++; + + /* + * Do all plgrp(1) operations specified on given thread + */ + (void) printf(FMT_THREAD" ", (int)pstatus->pr_pid, (int)lwpid); + return (do_op(plgrp_args, pstatus->pr_pid, lwpid, lwpsinfo)); +} + +/* + * Get target process specified in "pidstring" argument to do operation(s) + * specified in "plgrp_todo" using /proc and agent LWP + */ +static void +do_process(char *pidstring, plgrp_args_t *plgrp_todo, int force) +{ + int error; + const char *lwps; + struct ps_prochandle *Ph; + + /* + * Nothing to do, so return. + */ + if (plgrp_todo == NULL || interrupt) + return; + + /* + * Grab target process or core and return + * /proc handle for process and string of LWP + * IDs + */ + Ph = proc_arg_xgrab(pidstring, NULL, + PR_ARG_ANY, force | PGRAB_RETAIN | PGRAB_NOSTOP, &error, &lwps); + if (Ph == NULL) { + (void) fprintf(stderr, + gettext("%s: Unable to grab process %s: %s\n"), + progname, pidstring, Pgrab_error(error)); + nerrors++; + return; + } + + /* + * Fill in remaining plgrp(1) arguments and state needed to do + * plgrp(1) operation(s) on desired LWPs in our handler + * called by Plwp_iter_all() as it iterates over LWPs + * in given process + */ + plgrp_todo->Ph = Ph; + plgrp_todo->lwps = lwps; + + /* + * Iterate over LWPs in process and do specified + * operation(s) on those specified + */ + if (Plwp_iter_all(Ph, Plwp_iter_handler, plgrp_todo) != 0) { + (void) fprintf(stderr, + gettext("%s: error iterating over threads\n"), + progname); + nerrors++; + } + + Prelease(Ph, PRELEASE_RETAIN); +} + + +/* + * Parse command line and kick off any resulting actions + * + * plgrp(1) has the following command line syntax: + * + * plgrp [-h] <pid> | <core> [/lwps] ... + * plgrp [-F] -a <lgroup>,... <pid>[/lwps] ... + * plgrp [-F] -H <lgroup>,... <pid>[/lwps] ... + * plgrp [-F] -A <lgroup>,... [/none|weak|strong] ... <pid>[/lwps] ... + * + * where <lgroup> is an lgroup ID, "all", "root", "leaves". + */ +int +main(int argc, char *argv[]) +{ + lgrp_affinity_t aff; + char *affstring; + int c; + lgrp_cookie_t cookie; + int Fflag; + int i; + int opt_seen; + plgrp_args_t plgrp_todo; + char *s; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + opt_seen = 0; + + /* + * Get name of program + */ + progname = basename(argv[0]); + + /* + * Not much to do when only name of program given + */ + if (argc == 1) + usage(0); + + /* + * Catch signals from terminal, so they can be handled asynchronously + * when we're ready instead of when we're not (;-) + */ + if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) + (void) sigset(SIGHUP, intr); + if (sigset(SIGINT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGINT, intr); + if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGQUIT, intr); + (void) sigset(SIGPIPE, intr); + (void) sigset(SIGTERM, intr); + + /* + * Take snapshot of lgroup hierarchy + */ + cookie = lgrp_init(LGRP_VIEW_OS); + if (cookie == LGRP_COOKIE_NONE) { + (void) fprintf(stderr, + gettext("%s: Fatal error: cannot get lgroup" + " information from the OS: %s\n"), + progname, strerror(errno)); + return (EXIT_FAILURE); + } + + root = lgrp_root(cookie); + lgrps_bitmap_init(cookie, root, &lgrps_bitmap, &lgrps_bitmap_nelements); + + /* + * Remember arguments and state needed to do plgrp(1) operation + * on desired LWPs + */ + bzero(&plgrp_todo, sizeof (plgrp_args_t)); + plgrp_todo.op = PLGRP_HOME_GET; + + /* + * Parse options + */ + opterr = 0; + Fflag = 0; + while (!interrupt && (c = getopt(argc, argv, "a:A:FhH:")) != -1) { + /* + * Parse option and only allow one option besides -F to be + * specified + */ + switch (c) { + + case 'h': /* Get home lgroup */ + /* + * Only allow one option (besides -F) to be specified + */ + if (opt_seen) + usage(EXIT_FAILURE); + opt_seen = 1; + + plgrp_todo.op = PLGRP_HOME_GET; + break; + + case 'H': /* Set home lgroup */ + + /* + * Fail if already specified option (besides -F) + * or no more arguments + */ + if (opt_seen || optind >= argc) { + usage(EXIT_FAILURE); + } + opt_seen = 1; + + plgrp_todo.op = PLGRP_HOME_SET; + + if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0) + usage(EXIT_FAILURE); + + /* If there are no valid lgroups exit immediately */ + if (plgrp_todo.nlgrps == 0) { + (void) fprintf(stderr, + gettext("%s: no valid lgroups" + " specified for -%c\n\n"), + progname, c); + usage(EXIT_FAILURE); + } + + break; + + case 'a': /* Get lgroup affinity */ + + /* + * Fail if already specified option (besides -F) + * or no more arguments + */ + if (opt_seen || optind >= argc) { + usage(EXIT_FAILURE); + } + opt_seen = 1; + + plgrp_todo.op = PLGRP_AFFINITY_GET; + + if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0) + usage(EXIT_FAILURE); + + /* If there are no valid lgroups exit immediately */ + if (plgrp_todo.nlgrps == 0) { + (void) fprintf(stderr, + gettext("%s: no valid lgroups specified" + " for -%c\n\n"), + progname, c); + usage(EXIT_FAILURE); + } + + break; + + case 'A': /* Set lgroup affinity */ + + /* + * Fail if already specified option (besides -F) + * or no more arguments + */ + if (opt_seen || optind >= argc) { + usage(EXIT_FAILURE); + } + opt_seen = 1; + + plgrp_todo.op = PLGRP_AFFINITY_SET; + + /* + * 'affstring' is the unparsed prtion of the affinity + * specification like 1,2/none,2/weak,0/strong + * + * 'next' is the next affinity specification to parse. + */ + affstring = optarg; + while (affstring != NULL && strlen(affstring) > 0) { + char *next; + + /* + * affstring points to the first affinity + * specification. Split the string by + * DELIMIT_AFF separator and parse lgroups and + * affinity value separately. + */ + s = strchr(affstring, DELIMIT_AFF); + if (s == NULL) { + (void) fprintf(stderr, + gettext("%s: invalid " + "syntax >%s<\n"), + progname, affstring); + usage(EXIT_FAILURE); + } + + aff = parse_lgrp_affinity(s, &next); + if (aff == LGRP_AFF_INVALID) { + (void) fprintf(stderr, + gettext("%s: invalid " + "affinity >%s<\n"), + progname, affstring); + usage(EXIT_FAILURE); + } + + /* + * next should either point to the empty string + * or to the DELIMIT_AFF_LST separator. + */ + if (*next != '\0') { + if (*next != DELIMIT_AFF_LST) { + (void) fprintf(stderr, + gettext("%s: invalid " + "syntax >%s<\n"), + progname, next); + usage(EXIT_FAILURE); + } + *next = '\0'; + next++; + } + + + /* + * Now parse the list of lgroups + */ + if (parse_lgrps(cookie, &plgrp_todo, + affstring) < 0) { + usage(EXIT_FAILURE); + } + + /* + * Set desired affinity for specified lgroup to + * the specified affinity. + */ + for (i = 0; i < plgrp_todo.nlgrps; i++) { + if (plgrp_todo.affs[i] == + LGRP_AFF_INVALID) + plgrp_todo.affs[i] = aff; + } + + /* + * We processed the leftmost element of the + * list. Advance affstr to the remaining part of + * the list. and repeat. + */ + affstring = next; + } + + /* + * If there are no valid lgroups, exit immediately + */ + if (plgrp_todo.nlgrps == 0) { + (void) fprintf(stderr, + gettext("%s: no valid lgroups specified " + "for -%c\n\n"), progname, c); + usage(EXIT_FAILURE); + } + + break; + + case 'F': /* Force */ + + /* + * Only allow one occurrence + */ + if (Fflag != 0) { + usage(EXIT_FAILURE); + } + + /* + * Set flag to force /proc to grab process even though + * it's been grabbed by another process already + */ + Fflag = PGRAB_FORCE; + break; + + case '?': /* Unrecognized option */ + default: + usage(EXIT_FAILURE); + break; + + } + } + + /* + * Should have more arguments left at least for PID or core + */ + if (optind >= argc) + usage(EXIT_FAILURE); + + (void) lgrp_fini(cookie); + + /* + * Print heading and process each [pid | core]/lwps argument + */ + print_heading(plgrp_todo.op); + for (i = optind; i < argc && !interrupt; i++) { + do_process(argv[i], &plgrp_todo, Fflag); + } + + if (plgrp_todo.nthreads == 0) { + (void) fprintf(stderr, gettext("%s: no matching LWPs found\n"), + progname); + } + + return ((nerrors ||interrupt) ? EXIT_NONFATAL : EXIT_SUCCESS); +} diff --git a/usr/src/cmd/ptools/pmadvise/pmadvise.c b/usr/src/cmd/ptools/pmadvise/pmadvise.c new file mode 100644 index 0000000000..b3caca2b0d --- /dev/null +++ b/usr/src/cmd/ptools/pmadvise/pmadvise.c @@ -0,0 +1,1269 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * pmadvise + * + * ptool wrapper for madvise(3C) to apply memory advice to running processes + * + * usage: pmadvise -o option[,option] [-v] [-F] pid ... + * (Give "advice" about a process's memory) + * -o option[,option]: options are + * private=<advice> + * shared=<advice> + * heap=<advice> + * stack=<advice> + * <segaddr>[:<length>]=<advice> + * valid <advice> is one of: + * normal, random, sequential, willneed, dontneed, + * free, access_lwp, access_many, access_default + * -v: verbose output + * -F: force grabbing of the target process(es) + * pid: process id list + * + * + * Advice passed to this tool are organized into various lists described here: + * rawadv_list: includes all specific advice from command line (specific + * advice being those given to a particular address range rather + * than a type like "heap" or "stack". In contrast, these + * types are referred to as generic advice). Duplicates allowed. + * List ordered by addr, then by size (largest size first). + * Created once per run. + * merged_list: includes all specific advice from the rawadv_list as well as + * all generic advice. This must be recreated for each process + * as the generic advice will apply to different regions for + * different processes. Duplicates allowed. List ordered by addr, + * then by size (largest size first). Created once per pid. + * chopped_list: used for verbose output only. This list parses the merged + * list such that it eliminates any overlap and combines the + * advice. Easiest to think of this visually: if you take all + * the advice in the merged list and lay them down on a memory + * range of the entire process (laying on top of each other when + * necessary), then flatten them into one layer, combining advice + * in the case of overlap, you get the chopped_list of advice. + * Duplicate entries not allowed (since there is no overlap by + * definition in this list). List ordered by addr. Created once + * per pid. + * + * Example: + * merged_list: |-----adv1----|---------adv3---------| + * |--adv2--|--adv4--|-----adv5----| + * || + * \/ + * chopped_list: |adv1|-adv1,2-|-adv3,4-|----adv3,5---| + * + * maplist: list of memory mappings for a particular process. Used to create + * generic advice entries for merged_list and for pmap like verbose + * output. Created once per pid. + * + * Multiple lists are necessary because the actual advice applied given a set + * of generic and specific advice changes from process to process, so for each + * pid pmadvise is passed, it must create a new merged_list from which to apply + * advice (and a new chopped_list if verbose output is requested). + * + * Pseudo-code: + * I. Input advice from command line + * II. Create [raw advice list] of specific advice + * III. Iterate through PIDs: + * A. Create [map list] + * B. Merge generic advice and [raw advice list] into [merged list] + * C. Apply advice from [merged list]; upon error: + * i. output madvise error message + * ii. remove element from [merged list] + * D. If verbose output: + * i. Create [chopped list] from [merged list] + * ii. Iterate through [map list]: + * a. output advice as given by [merged list] + * iii. Delete [chopped list] + * E. Delete [merged list] + * F. Delete [map list] + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> +#include <link.h> +#include <libelf.h> +#include <locale.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/mkdev.h> +#include <assert.h> +#include <libproc.h> +#include <libgen.h> +#include <signal.h> + +#ifndef TEXT_DOMAIN /* should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */ +#endif + +#define KILOBYTE 1024 + +/* + * Round up the value to the nearest kilobyte + */ +#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE) + +#define NO_ADVICE 0 + +/* + * The following definitions are used as the third argument in insert_addr() + * NODUPS = no duplicates are not allowed, thus if the addr being inserted + * already exists in the list, return without inserting again. + * + * YESDUPS = yes duplicates are allowed, thus always insert the addr + * regardless of whether it already exists in the list or not. + */ +#define NODUPS 1 +#define YESDUPS 0 + +/* + * Advice that can be passed to madvise fit into three groups that each + * contain 3 mutually exclusive options. These groups are defined below: + * Group 1: normal, random, sequential + * Group 2: willneed, dontneed, free + * Group 3: default, accesslwp, accessmany + * Thus, advice that includes (at most) one from each group is valid. + * + * The following #define's are used as masks to determine which group(s) a + * particular advice fall under. + */ + +#define GRP1_ADV (1 << MADV_NORMAL | 1 << MADV_RANDOM | \ + 1 << MADV_SEQUENTIAL) +#define GRP2_ADV (1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \ + 1 << MADV_FREE) +#define GRP3_ADV (1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \ + 1 << MADV_ACCESS_MANY) + +static int create_maplist(void *, const prmap_t *, const char *); +static int pr_madvise(struct ps_prochandle *, caddr_t, size_t, int); + +static char *mflags(uint_t); +static char *advtostr(int); + +static int addr_width, size_width; +static char *progname; +static struct ps_prochandle *Pr; + +typedef struct lwpstack { + lwpid_t lwps_lwpid; + stack_t lwps_stack; +} lwpstack_t; + +static lwpstack_t *stacks; +static uint_t nstacks; + +/* + * Used to set the advice type var (at_map) when parsing the arguments to + * pmadvise. Later, when creating the map list, at_map is used as a mask + * to determine if any generic advice applies to each memory mapping. + */ +enum atype_enum { + AT_PRIVM, + AT_SHARED, + AT_HEAP, + AT_STACK, + AT_SEG, + AT_NTYPES +}; + +static char *suboptstr[] = { + "private", + "shared", + "heap", + "stack", + NULL +}; + + +int generic_adv[] = {NO_ADVICE, NO_ADVICE, NO_ADVICE, NO_ADVICE}; +int at_map = 0; + +typedef struct saddr_struct { + uintptr_t addr; + size_t length; + int adv; + struct saddr_struct *next; +} saddr_t; +static int apply_advice(saddr_t **); +static void set_advice(int *, int); +static void create_choplist(saddr_t **, saddr_t *); + +/* + * The segment address advice from the command line + */ +saddr_t *rawadv_list = NULL; +/* + * The rawadv_list + list entries for the generic advice (if any). + * This must be recreated for each PID as the memory maps might be different. + */ +saddr_t *merged_list = NULL; +/* + * The merged_list cut up so as to remove all overlap + * e.g. if merged_list contained two entries: + * + * [0x38000:0x3e000) = adv1 + * [0x3a000:0x3c000) = adv2 + * + * the chopped list will contain three entries: + * + * [0x38000:0x3a000) = adv1 + * [0x3a000:0x3c000) = adv1,adv2 + * [0x3c000:0x3e000) = adv1 + * + */ +saddr_t *chopped_list = NULL; + +typedef struct mapnode_struct { + prmap_t *pmp; + char label[PATH_MAX]; + int mtypes; + struct mapnode_struct *next; +} mapnode_t; + +mapnode_t *maplist_head = NULL; +mapnode_t *maplist_tail = NULL; +static void print_advice(saddr_t *, mapnode_t *); + +int opt_verbose; + +static char *advicestr[] = { + "normal", + "random", + "sequential", + "willneed", + "dontneed", + "free", + "access_default", + "access_lwp", + "access_many" +}; + +/* + * How many signals caught from terminal + * We bail out as soon as possible when interrupt is set + */ +static int interrupt = 0; + +/* + * Interrupt handler + */ +static void intr(int); + +/* + * Iterative function passed to Plwp_iter to + * get alt and main stacks for given lwp. + */ +static int +getstack(void *data, const lwpstatus_t *lsp) +{ + int *np = (int *)data; + + if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) { + stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK; + stacks[*np].lwps_lwpid = lsp->pr_lwpid; + (*np)++; + } + + if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) { + stacks[*np].lwps_lwpid = lsp->pr_lwpid; + (*np)++; + } + + return (0); +} + +/* + * We compare the high memory addresses since stacks are faulted in from + * high memory addresses to low memory addresses, and our prmap_t + * structures identify only the range of addresses that have been faulted + * in so far. + */ +static int +cmpstacks(const void *ap, const void *bp) +{ + const lwpstack_t *as = ap; + const lwpstack_t *bs = bp; + uintptr_t a = (uintptr_t)as->lwps_stack.ss_sp + as->lwps_stack.ss_size; + uintptr_t b = (uintptr_t)bs->lwps_stack.ss_sp + bs->lwps_stack.ss_size; + + if (a < b) + return (1); + if (a > b) + return (-1); + return (0); +} + +/* + * Prints usage and exits + */ +static void +usage() +{ + (void) fprintf(stderr, + gettext("usage:\t%s -o option[,option] [-v] [-F] pid ...\n"), + progname); + (void) fprintf(stderr, + gettext(" (Give \"advice\" about a process's memory)\n" + " -o option[,option]: options are\n" + " private=<advice>\n" + " shared=<advice>\n" + " heap=<advice>\n" + " stack=<advice>\n" + " <segaddr>[:<length>]=<advice>\n" + " valid <advice> is one of:\n" + " normal, random, sequential, willneed, dontneed,\n" + " free, access_lwp, access_many, access_default\n" + " -v: verbose output\n" + " -F: force grabbing of the target process(es)\n" + " pid: process id list\n")); + exit(2); +} + +/* + * Function to parse advice from options string + */ +static int +get_advice(char *optarg) +{ + /* + * Determine which advice is given, we use shifted values as + * multiple pieces of advice may apply for a particular region. + * (See comment above regarding GRP[1,2,3]_ADV definitions for + * breakdown of advice groups). + */ + if (strcmp(optarg, "access_default") == 0) + return (1 << MADV_ACCESS_DEFAULT); + else if (strcmp(optarg, "access_many") == 0) + return (1 << MADV_ACCESS_MANY); + else if (strcmp(optarg, "access_lwp") == 0) + return (1 << MADV_ACCESS_LWP); + else if (strcmp(optarg, "sequential") == 0) + return (1 << MADV_SEQUENTIAL); + else if (strcmp(optarg, "willneed") == 0) + return (1 << MADV_WILLNEED); + else if (strcmp(optarg, "dontneed") == 0) + return (1 << MADV_DONTNEED); + else if (strcmp(optarg, "random") == 0) + return (1 << MADV_RANDOM); + else if (strcmp(optarg, "normal") == 0) + return (1 << MADV_NORMAL); + else if (strcmp(optarg, "free") == 0) + return (1 << MADV_FREE); + else { + (void) fprintf(stderr, gettext("%s: invalid advice: %s\n"), + progname, optarg); + usage(); + return (-1); + } +} + +/* + * Function to convert character size indicators into actual size + * (i.e., 123M => sz = 123 * 1024 * 1024) + */ +static size_t +atosz(char *optarg, char **endptr) +{ + size_t sz = 0; + + if (optarg == NULL || optarg[0] == '\0') + return (0); + + sz = strtoll(optarg, endptr, 0); + + switch (**endptr) { + case 'E': + case 'e': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'P': + case 'p': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'T': + case 't': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'G': + case 'g': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'M': + case 'm': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'K': + case 'k': + sz *= KILOBYTE; + /* FALLTHRU */ + case 'B': + case 'b': + (*endptr)++; + /* FALLTHRU */ + default: + break; + } + return (sz); +} + +/* + * Inserts newaddr into list. dups indicates whether we allow duplicate + * addr entries in the list (valid values are NODUPS and YESDUPS). + */ +static void +insert_addr(saddr_t **list, saddr_t *newaddr, int dups) +{ + saddr_t *prev = *list; + saddr_t *psaddr; + + if (*list == NULL) { + newaddr->next = *list; + *list = newaddr; + return; + } + + for (psaddr = (*list)->next; psaddr != NULL; psaddr = psaddr->next) { + if ((dups == NODUPS) && (psaddr->addr == newaddr->addr)) { + free(newaddr); + return; + } + + /* + * primary level of comparison is by address; smaller addr 1st + * secondary level of comparison is by length; bigger length 1st + */ + if ((psaddr->addr > newaddr->addr) || + (psaddr->addr == newaddr->addr && + psaddr->length < newaddr->length)) + break; + + prev = psaddr; + } + + prev->next = newaddr; + newaddr->next = psaddr; +} + +/* + * Deletes given element from list + */ +static void +delete_addr(saddr_t **list, saddr_t *delme) +{ + saddr_t *prev = *list; + + if (delme == *list) { + *list = delme->next; + free(delme); + return; + } + + while (prev != NULL && prev->next != delme) { + prev = prev->next; + } + + if (prev) { + prev->next = delme->next; + free(delme); + } +} + +/* + * Delete entire list + */ +static void +delete_list(saddr_t **list) +{ + saddr_t *psaddr = *list; + + while (psaddr != NULL) { + saddr_t *temp = psaddr; + + psaddr = psaddr->next; + free(temp); + } + *list = NULL; +} + +static saddr_t * +parse_suboptions(char *value) +{ + char *endptr; + saddr_t *psaddr = malloc(sizeof (saddr_t)); + + /* + * This must (better) be a segment addr + */ + psaddr->addr = + strtoull(value, &endptr, 16); + + /* + * Check to make sure strtoul worked correctly (a properly formatted + * string will terminate in a ':' (if size is given) or an '=' (if size + * is not specified). Also check to make sure a 0 addr wasn't returned + * indicating strtoll was unable to convert). + */ + if ((psaddr->addr == 0) || (*endptr != ':' && *endptr != '=')) { + free(psaddr); + (void) fprintf(stderr, + gettext("%s: invalid option %s\n"), + progname, value); + usage(); + } else { + /* init other fields */ + psaddr->length = 0; + psaddr->adv = NO_ADVICE; + psaddr->next = NULL; + + /* skip past address */ + value = endptr; + + /* check for length */ + if (*value == ':') { + /* skip the ":" */ + value++; + psaddr->length = atosz(value, &endptr); + } + + if (*endptr != '=') { + (void) fprintf(stderr, + gettext("%s: invalid option %s\n"), + progname, value); + /* + * if improperly formatted, free mem, print usage, and + * exit Note: usage ends with a call to exit() + */ + free(psaddr); + usage(); + } + /* skip the "=" */ + value = endptr + 1; + at_map |= (1 << AT_SEG); + psaddr->adv = + get_advice(value); + } + + return (psaddr); +} + +/* + * Create labels for non-anon, non-heap mappings + */ +static char * +make_name(struct ps_prochandle *Pr, uintptr_t addr, const char *mapname, + char *buf, size_t bufsz) +{ + const pstatus_t *Psp = Pstatus(Pr); + char fname[100]; + struct stat statb; + int len; + + if (strcmp(mapname, "a.out") == 0 && + Pexecname(Pr, buf, bufsz) != NULL) + return (buf); + + if (Pobjname(Pr, addr, buf, bufsz) != NULL) { + if ((len = resolvepath(buf, buf, bufsz)) > 0) { + buf[len] = '\0'; + return (buf); + } + } + + if (*mapname != '\0') { + (void) snprintf(fname, sizeof (fname), "/proc/%d/object/%s", + (int)Psp->pr_pid, mapname); + if (stat(fname, &statb) == 0) { + dev_t dev = statb.st_dev; + ino_t ino = statb.st_ino; + (void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu", + (ulong_t)major(dev), (ulong_t)minor(dev), ino); + return (buf); + } + } + + return (NULL); +} + +/* + * Create label for anon mappings + */ +static char * +anon_name(char *name, const pstatus_t *Psp, + uintptr_t vaddr, size_t size, int mflags, int shmid, int *mtypes) +{ + if (mflags & MA_ISM) { + if (shmid == -1) + (void) snprintf(name, PATH_MAX, " [ %s shmid=null ]", + (mflags & MA_NORESERVE) ? "ism" : "dism"); + else + (void) snprintf(name, PATH_MAX, " [ %s shmid=0x%x ]", + (mflags & MA_NORESERVE) ? "ism" : "dism", shmid); + *mtypes |= (1 << AT_SHARED); + } else if (mflags & MA_SHM) { + if (shmid == -1) + (void) sprintf(name, " [ shmid=null ]"); + else + (void) sprintf(name, " [ shmid=0x%x ]", shmid); + *mtypes |= (1 << AT_SHARED); + + } else if (vaddr + size > Psp->pr_stkbase && + vaddr < Psp->pr_stkbase + Psp->pr_stksize) { + (void) strcpy(name, " [ stack ]"); + *mtypes |= (1 << AT_STACK); + + } else if ((mflags & MA_ANON) && + vaddr + size > Psp->pr_brkbase && + vaddr < Psp->pr_brkbase + Psp->pr_brksize) { + (void) strcpy(name, " [ heap ]"); + *mtypes |= (1 << AT_HEAP); + + } else { + lwpstack_t key, *stk; + + key.lwps_stack.ss_sp = (void *)vaddr; + key.lwps_stack.ss_size = size; + if (nstacks > 0 && + (stk = bsearch(&key, stacks, nstacks, sizeof (stacks[0]), + cmpstacks)) != NULL) { + (void) snprintf(name, PATH_MAX, " [ %s tid=%d ]", + (stk->lwps_stack.ss_flags & SS_ONSTACK) ? + "altstack" : "stack", + stk->lwps_lwpid); + *mtypes |= (1 << AT_STACK); + } else { + (void) strcpy(name, " [ anon ]"); + *mtypes |= (1 << AT_PRIVM); + } + } + + return (name); +} + +/* + * Create linked list of mappings for current process + * In addition, add generic advice and raw advice + * entries to merged_list. + */ +/* ARGSUSED */ +static int +create_maplist(void *arg, const prmap_t *pmp, const char *object_name) +{ + const pstatus_t *Psp = Pstatus(Pr); + mapnode_t *newmap = malloc(sizeof (mapnode_t)); + saddr_t *newaddr; + saddr_t *psaddr; + char *lname = NULL; + int i; + + if (interrupt) + return (0); + + newmap->pmp = malloc(sizeof (prmap_t)); + newmap->label[0] = '\0'; + newmap->mtypes = 0; + newmap->next = NULL; + (void) memcpy(newmap->pmp, pmp, sizeof (prmap_t)); + + /* + * If the mapping is not anon or not part of the heap, make a name + * for it. We don't want to report the heap as a.out's data. + */ + if (!(pmp->pr_mflags & MA_ANON) || + (pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase || + pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize)) { + lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname, + newmap->label, sizeof (newmap->label)); + if (pmp->pr_mflags & MA_SHARED) + newmap->mtypes |= 1 << AT_SHARED; + else + newmap->mtypes |= 1 << AT_PRIVM; + } + + if (lname == NULL && (pmp->pr_mflags & MA_ANON)) { + lname = anon_name(newmap->label, Psp, pmp->pr_vaddr, + pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, + &newmap->mtypes); + } + + /* + * Add raw advice that applies to this mapping to the merged_list + */ + psaddr = rawadv_list; + /* + * Advance to point in rawadv_list that applies to this mapping + */ + while (psaddr && psaddr->addr < pmp->pr_vaddr) + psaddr = psaddr->next; + /* + * Copy over to merged_list, check to see if size needs to be filled in + */ + while (psaddr && psaddr->addr < (pmp->pr_vaddr + pmp->pr_size)) { + newaddr = malloc(sizeof (saddr_t)); + (void) memcpy(newaddr, psaddr, sizeof (saddr_t)); + insert_addr(&merged_list, newaddr, YESDUPS); + /* + * For raw advice that is given without size, try to default + * size to size of mapping (only allowed if raw adv addr is + * equal to beginning of mapping). Don't change the entry + * in rawadv_list, only in the merged_list as the mappings + * (and thus the default sizes) will be different for + * different processes. + */ + if ((pmp->pr_vaddr == psaddr->addr) && (psaddr->length == 0)) + newaddr->length = pmp->pr_size; + psaddr = psaddr->next; + } + + /* + * Put mapping into merged list with no advice, then + * check to see if any generic advice applies. + */ + newaddr = malloc(sizeof (saddr_t)); + newaddr->addr = pmp->pr_vaddr; + newaddr->length = pmp->pr_size; + newaddr->adv = NO_ADVICE; + insert_addr(&merged_list, newaddr, YESDUPS); + + newmap->mtypes &= at_map; + for (i = AT_STACK; i >= AT_PRIVM; i--) { + if (newmap->mtypes & (1 << i)) { + assert(generic_adv[i] != NO_ADVICE); + newaddr->adv = generic_adv[i]; + break; + } + } + + /* + * Add to linked list of mappings + */ + if (maplist_tail == NULL) { + maplist_head = maplist_tail = newmap; + } else { + maplist_tail->next = newmap; + maplist_tail = newmap; + } + + + return (0); +} + +/* + * Traverse advice list and apply all applicable advice to each region + */ +static int +apply_advice(saddr_t **advicelist) +{ + saddr_t *psaddr = *advicelist; + saddr_t *next; + int i; + + + while (!interrupt && psaddr != NULL) { + /* + * Save next pointer since element may be removed before + * we get a chance to advance psaddr. + */ + next = psaddr->next; + + /* + * Since mappings have been added to the merged list + * even if no generic advice was given for the map, + * check to make sure advice exists before bothering + * with the for loop. + */ + if (psaddr->adv != NO_ADVICE) { + for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) { + if ((psaddr->adv & (1 << i)) && + (pr_madvise(Pr, (caddr_t)psaddr->addr, + psaddr->length, i) < 0)) { + /* + * madvise(3C) call failed trying to + * apply advice output error and remove + * from advice list + */ + (void) fprintf(stderr, + gettext("Error applying " + "advice (%s) to memory range " + "[%lx, %lx):\n"), + advicestr[i], (ulong_t)psaddr->addr, + (ulong_t)psaddr->addr + + psaddr->length); + perror("madvise"); + /* + * Clear this advice from the advice + * mask. If no more advice is given + * for this element, remove element + * from list. + */ + psaddr->adv &= ~(1 << i); + if (psaddr->adv == 0) { + delete_addr(advicelist, psaddr); + break; + } + } + } + } + psaddr = next; + } + return (0); +} + +/* + * Set advice but keep mutual exclusive property of advice groupings + */ +static void +set_advice(int *combined_adv, int new_adv) { + /* + * Since advice falls in 3 groups of mutually exclusive options, + * clear previous value if new advice overwrites that group. + */ + + /* + * If this is the first advice to be applied, clear invalid value (-1) + */ + if (*combined_adv == -1) + *combined_adv = 0; + + if (new_adv & GRP1_ADV) + *combined_adv &= ~GRP1_ADV; + else if (new_adv & GRP2_ADV) + *combined_adv &= ~GRP2_ADV; + else + *combined_adv &= ~GRP3_ADV; + + *combined_adv |= new_adv; +} + +/* + * Create chopped list from merged list for use with verbose output + */ +static void +create_choplist(saddr_t **choppedlist, saddr_t *mergedlist) +{ + saddr_t *mlptr, *clptr; + + for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) { + clptr = malloc(sizeof (saddr_t)); + clptr->addr = mlptr->addr; + clptr->length = 0; + /* + * Initialize the adv to -1 as an indicator for invalid + * elements in the chopped list (created from gaps between + * memory maps). + */ + clptr->adv = -1; + clptr->next = NULL; + insert_addr(choppedlist, clptr, NODUPS); + + clptr = malloc(sizeof (saddr_t)); + clptr->addr = mlptr->addr + mlptr->length; + clptr->length = 0; + /* + * Again, initialize to -1 as an indicatorfor invalid elements + */ + clptr->adv = -1; + clptr->next = NULL; + insert_addr(choppedlist, clptr, NODUPS); + } + + for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) { + if (clptr->next) + clptr->length = clptr->next->addr - clptr->addr; + else { + /* + * must be last element, now that we've calculated + * all segment lengths, we can remove this node + */ + delete_addr(choppedlist, clptr); + } + } + + for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) { + for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) { + if (mlptr->addr <= clptr->addr && + mlptr->addr + mlptr->length >= + clptr->addr + clptr->length) + /* + * set_advice() will take care of conflicting + * advice by taking only the last advice + * applied for each of the 3 groups of advice. + */ + set_advice(&clptr->adv, mlptr->adv); + if (mlptr->addr + mlptr->length < + clptr->addr) + break; + } + } +} + +/* + * Print advice in pmap style for verbose output + */ +static void +print_advice(saddr_t *advlist, mapnode_t *maplist) +{ + saddr_t *psaddr = advlist; + mapnode_t *pmapnode; + char *advice; + + pmapnode = maplist; + + while (psaddr) { + /* + * Using indicator flag from create_choppedlist, we know + * which entries in the chopped_list are gaps and should + * not be printed. + */ + if (psaddr->adv == -1) { + psaddr = psaddr->next; + continue; + } + + while (pmapnode && (pmapnode->pmp->pr_vaddr + + pmapnode->pmp->pr_size <= psaddr->addr)) + pmapnode = pmapnode->next; + + advice = advtostr(psaddr->adv); + + /* + * Print segment mapping and advice if there is any, or just a + * segment mapping. + */ + if (strlen(advice) > 0) { + (void) printf("%.*lX %*uK %6s %s\t%s\n", + addr_width, (ulong_t)psaddr->addr, size_width - 1, + (int)ROUNDUP_KB(psaddr->length), + mflags(pmapnode->pmp->pr_mflags), pmapnode->label, + advice); + } else { + (void) printf("%.*lX %*uK %6s %s\n", + addr_width, (ulong_t)psaddr->addr, size_width - 1, + (int)ROUNDUP_KB(psaddr->length), + mflags(pmapnode->pmp->pr_mflags), pmapnode->label); + } + psaddr = psaddr->next; + + } +} + +/* + * Call madvise(3c) in the context of the target process + */ +static int +pr_madvise(struct ps_prochandle *Pr, caddr_t addr, size_t len, int advice) +{ + return (pr_memcntl(Pr, addr, len, MC_ADVISE, + (caddr_t)(uintptr_t)advice, 0, 0)); +} + +static char * +mflags(uint_t arg) +{ + static char code_buf[80]; + + /* + * rwxsR + * + * r - segment is readable + * w - segment is writable + * x - segment is executable + * s - segment is shared + * R - segment is mapped MAP_NORESERVE + * + */ + (void) snprintf(code_buf, sizeof (code_buf), "%c%c%c%c%c ", + arg & MA_READ ? 'r' : '-', + arg & MA_WRITE ? 'w' : '-', + arg & MA_EXEC ? 'x' : '-', + arg & MA_SHARED ? 's' : '-', + arg & MA_NORESERVE ? 'R' : '-'); + + return (code_buf); +} + +/* + * Convert advice to a string containing a commented list of applicable advice + */ +static char * +advtostr(int adv) +{ + static char buf[50]; + int i; + + *buf = '\0'; + + if (adv != NO_ADVICE) { + for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) { + if (adv & (1 << i)) { + /* + * check if it's the first advice entry + */ + if (*buf == '\0') + (void) snprintf(buf, sizeof (buf) - 1, + "<= %s", advicestr[i]); + else + (void) snprintf(buf, sizeof (buf) - 1, + "%s,%s", buf, advicestr[i]); + } + } + } + + return (buf); +} + +/* + * Handler for catching signals from terminal + */ +/* ARGSUSED */ +static void +intr(int sig) +{ + interrupt++; +} + +int +main(int argc, char **argv) +{ + int Fflag = 0; + int rc = 0; + int opt, subopt; + int tmpadv; + char *options, *value; + saddr_t *psaddr; + mapnode_t *pmapnode, *tempmapnode; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + /* + * Get name of program for error messages + */ + progname = basename(argv[0]); + + /* + * Not much to do when only name of program given + */ + if (argc == 1) + usage(); + + /* + * Catch signals from terminal, so they can be handled asynchronously + * when we're ready instead of when we're not (;-) + */ + if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) + (void) sigset(SIGHUP, intr); + if (sigset(SIGINT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGINT, intr); + if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGQUIT, intr); + (void) sigset(SIGPIPE, intr); + (void) sigset(SIGTERM, intr); + + /* + * Parse options, record generic advice if any and create + * rawadv_list from specific address advice. + */ + + while ((opt = getopt(argc, argv, "Fo:v")) != EOF) { + switch (opt) { + case 'o': + options = optarg; + while (*options != '\0') { + subopt = getsubopt(&options, suboptstr, + &value); + switch (subopt) { + case AT_PRIVM: + case AT_HEAP: + case AT_SHARED: + case AT_STACK: + at_map |= (1 << subopt); + tmpadv = get_advice(value); + set_advice(&generic_adv[subopt], + tmpadv); + break; + default: + at_map |= (1 << AT_SEG); + psaddr = parse_suboptions(value); + if (psaddr == NULL) { + usage(); + } else { + insert_addr(&rawadv_list, + psaddr, YESDUPS); + } + break; + } + } + break; + case 'v': + opt_verbose = 1; + break; + case 'F': /* force grabbing (no O_EXCL) */ + Fflag = PGRAB_FORCE; + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (argc <= 0) { + usage(); + } + + /* + * Iterate through all pid arguments, create new merged_list, maplist, + * (and chopped_list if using verbose output) based on each process' + * memory map. + */ + + while (!interrupt && argc-- > 0) { + char *arg; + int gcode; + psinfo_t psinfo; + + if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_PIDS, + PGRAB_RETAIN | Fflag, &gcode)) == NULL) { + (void) fprintf(stderr, + gettext("%s: cannot examine %s: %s\n"), + progname, arg, Pgrab_error(gcode)); + rc++; + continue; + } + + + addr_width = + (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8; + size_width = + (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8; + (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t)); + + if (opt_verbose) { + proc_unctrl_psinfo(&psinfo); + (void) printf("%d:\t%.70s\n", + (int)psinfo.pr_pid, psinfo.pr_psargs); + } + + /* + * Get mappings for a process unless it is a system process. + */ + if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) { + nstacks = psinfo.pr_nlwp * 2; + stacks = calloc(nstacks, sizeof (stacks[0])); + if (stacks != NULL) { + int n = 0; + (void) Plwp_iter(Pr, getstack, &n); + qsort(stacks, nstacks, sizeof (stacks[0]), + cmpstacks); + } + + if (Pgetauxval(Pr, AT_BASE) != -1L && + Prd_agent(Pr) == NULL) { + (void) fprintf(stderr, + gettext("%s: warning: " + "librtld_db failed to initialize; " + "shared library information will not " + "be available\n"), + progname); + } + + /* + * Create linked list of mappings for current process + * In addition, add generic advice and raw advice + * entries to merged_list. + * e.g. if rawadv_list contains: + * [0x38000,0x3a000) = adv1 + * [0x3a000,0x3c000) = adv2 + * and there is generic advice: + * heap = adv3 + * where heap corresponds to 0x38000, then merged_list + * will contain: + * ... (include all other mappings from process) + * [0x38000,0x3c000) = adv3 + * [0x38000,0x3a000) = adv1 + * [0x3a000,0x3c000) = adv2 + * ... (include all other mappings from process) + */ + assert(merged_list == NULL); + maplist_head = maplist_tail = NULL; + rc += Pmapping_iter(Pr, (proc_map_f *)create_maplist, + NULL); + + /* + * Apply advice by iterating through merged list + */ + (void) apply_advice(&merged_list); + + if (opt_verbose) { + assert(chopped_list == NULL); + /* + * Create chopped_list from merged_list + */ + create_choplist(&chopped_list, merged_list); + + /* + * Iterate through maplist and output as + * given by chopped_list + */ + print_advice(chopped_list, maplist_head); + delete_list(&chopped_list); + } + + delete_list(&merged_list); + + /* + * Clear maplist + */ + pmapnode = maplist_head; + while (pmapnode) { + tempmapnode = pmapnode; + pmapnode = pmapnode->next; + free(tempmapnode); + } + + if (stacks != NULL) { + free(stacks); + stacks = NULL; + } + } + + Prelease(Pr, 0); + } + + return (rc); +} diff --git a/usr/src/cmd/ptools/pmap/pmap.c b/usr/src/cmd/ptools/pmap/pmap.c index 7d2234aced..866964ef3a 100644 --- a/usr/src/cmd/ptools/pmap/pmap.c +++ b/usr/src/cmd/ptools/pmap/pmap.c @@ -18,6 +18,7 @@ * * CDDL HEADER END */ + /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -37,10 +38,29 @@ #include <link.h> #include <libelf.h> #include <sys/types.h> +#include <signal.h> #include <sys/stat.h> #include <sys/mkdev.h> +#include <sys/mman.h> +#include <sys/lgrp_user.h> #include <libproc.h> +#define KILOBYTE 1024 +#define MEGABYTE (KILOBYTE * KILOBYTE) +#define GIGABYTE (KILOBYTE * KILOBYTE * KILOBYTE) + +/* + * Round up the value to the nearest kilobyte + */ +#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE) + +/* + * The alignment should be a power of 2. + */ +#define P2ALIGN(x, align) ((x) & -(align)) + +#define INVALID_ADDRESS (uintptr_t)(-1) + struct totals { ulong_t total_size; ulong_t total_swap; @@ -49,6 +69,35 @@ struct totals { ulong_t total_locked; }; +/* + * -L option requires per-page information. The information is presented in an + * array of page_descr structures. + */ +typedef struct page_descr { + uintptr_t pd_start; /* start address of a page */ + size_t pd_pagesize; /* page size in bytes */ + lgrp_id_t pd_lgrp; /* lgroup of memory backing the page */ + int pd_valid; /* valid page description if non-zero */ +} page_descr_t; + +/* + * Per-page information for a memory chunk. + * The meminfo(2) system call accepts up to MAX_MEMINFO_CNT pages at once. + * When we need to scan larger ranges we divide them in MAX_MEMINFO_CNT sized + * chunks. The chunk information is stored in the memory_chunk structure. + */ +typedef struct memory_chunk { + page_descr_t page_info[MAX_MEMINFO_CNT]; + uintptr_t end_addr; + uintptr_t chunk_start; /* Starting address */ + uintptr_t chunk_end; /* chunk_end is always <= end_addr */ + size_t page_size; + int page_index; /* Current page */ + int page_count; /* Number of pages */ +} memory_chunk_t; + +static volatile int interrupt; + typedef int proc_xmap_f(void *, const prxmap_t *, const char *, int, int); static int xmapping_iter(struct ps_prochandle *, proc_xmap_f *, void *, @@ -65,18 +114,43 @@ static int gather_map(void *, const prmap_t *, const char *); static int gather_xmap(void *, const prxmap_t *, const char *, int, int); static int iter_map(proc_map_f *, void *); static int iter_xmap(proc_xmap_f *, void *); +static int parse_addr_range(char *, uintptr_t *, uintptr_t *); +static void mem_chunk_init(memory_chunk_t *, uintptr_t, size_t); static int perr(char *); static void printK(long, int); static char *mflags(uint_t); +static size_t get_contiguous_region(memory_chunk_t *, uintptr_t, + uintptr_t, size_t, lgrp_id_t *); +static void mem_chunk_get(memory_chunk_t *, uintptr_t); +static lgrp_id_t addr_to_lgrp(memory_chunk_t *, uintptr_t, size_t *); +static char *lgrp2str(lgrp_id_t); + +static int address_in_range(uintptr_t, uintptr_t, size_t); +static size_t adjust_addr_range(uintptr_t, uintptr_t, size_t, + uintptr_t *, uintptr_t *); + static int lflag = 0; +static int Lflag = 0; static int aflag = 0; + +/* + * The -A address range is represented as a pair of addresses + * <start_addr, end_addr>. Either one of these may be unspecified (set to + * INVALID_ADDRESS). If both are unspecified, no address range restrictions are + * in place. + */ +static uintptr_t start_addr = INVALID_ADDRESS; +static uintptr_t end_addr = INVALID_ADDRESS; + static int addr_width, size_width; static char *command; static char *procname; static struct ps_prochandle *Pr; +static void intr(int); + typedef struct lwpstack { lwpid_t lwps_lwpid; stack_t lwps_stack; @@ -86,7 +160,7 @@ typedef struct { prxmap_t md_xmap; prmap_t md_map; char *md_objname; - int md_last; + boolean_t md_last; int md_doswap; } mapdata_t; @@ -143,7 +217,7 @@ cmpstacks(const void *ap, const void *bp) int main(int argc, char **argv) { - int rflag = 0, sflag = 0, xflag = 0; + int rflag = 0, sflag = 0, xflag = 0, Fflag = 0; int errflg = 0, Sflag = 0; int rc = 0; int opt; @@ -160,7 +234,7 @@ main(int argc, char **argv) else command = argv[0]; - while ((opt = getopt(argc, argv, "arsxSlF")) != EOF) { + while ((opt = getopt(argc, argv, "arsxSlLFA:")) != EOF) { switch (opt) { case 'a': /* include shared mappings in -[xS] */ aflag = 1; @@ -180,12 +254,16 @@ main(int argc, char **argv) case 'l': /* show unresolved link map names */ lflag = 1; break; - case 'F': - /* - * Since we grab the process readonly now, the -F flag - * is meaningless. Consume it anyway it for backwards - * compatbility. - */ + case 'L': /* show lgroup information */ + Lflag = 1; + break; + case 'F': /* force grabbing (no O_EXCL) */ + Fflag = PGRAB_FORCE; + break; + case 'A': + if (parse_addr_range(optarg, &start_addr, &end_addr) + != 0) + errflg++; break; default: errflg = 1; @@ -197,21 +275,28 @@ main(int argc, char **argv) argv += optind; if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) || - (aflag && (!xflag && !Sflag))) { + (aflag && (!xflag && !Sflag)) || + (Lflag && (xflag || Sflag))) { errflg = 1; } if (errflg || argc <= 0) { (void) fprintf(stderr, - "usage:\t%s [-rslF] { pid | core } ...\n", command); + "usage:\t%s [-rslF] [-A start[,end]] { pid | core } ...\n", + command); (void) fprintf(stderr, "\t\t(report process address maps)\n"); (void) fprintf(stderr, - "\t%s -x [-aslF] pid ...\n", command); + "\t%s -L [-rslF] [-A start[,end]] pid ...\n", command); + (void) fprintf(stderr, + "\t\t(report process address maps lgroups mappings)\n"); + (void) fprintf(stderr, + "\t%s -x [-aslF] [-A start[,end]] pid ...\n", command); (void) fprintf(stderr, "\t\t(show resident/anon/locked mapping details)\n"); (void) fprintf(stderr, - "\t%s -S [-alF] { pid | core } ...\n", command); + "\t%s -S [-alF] [-A start[,end]] { pid | core } ...\n", + command); (void) fprintf(stderr, "\t\t(show swap reservations)\n\n"); (void) fprintf(stderr, @@ -224,6 +309,10 @@ main(int argc, char **argv) "\t-l: show unresolved dynamic linker map names\n"); (void) fprintf(stderr, "\t-F: force grabbing of the target process\n"); + (void) fprintf(stderr, + "\t-L: show lgroup mappings\n"); + (void) fprintf(stderr, + "\t-A start,end: limit output to the specified range\n"); return (2); } @@ -242,9 +331,16 @@ main(int argc, char **argv) int gcode; psinfo_t psinfo; int tries = 0; + int prg_gflags = PGRAB_RDONLY; + int prr_flags = 0; + + if (Lflag) { + prg_gflags = PGRAB_RETAIN | Fflag; + prr_flags = PRELEASE_RETAIN; + } if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY, - PGRAB_RDONLY, &gcode)) == NULL) { + prg_gflags, &gcode)) == NULL) { (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gcode)); rc++; @@ -267,7 +363,7 @@ main(int argc, char **argv) "examine %s: lost control of " "process\n", command, arg); rc++; - Prelease(Pr, 0); + Prelease(Pr, prr_flags); continue; } } else { @@ -281,11 +377,12 @@ again: (void) printf("core '%s' of %d:\t%.70s\n", arg, (int)psinfo.pr_pid, psinfo.pr_psargs); - if (rflag || sflag || xflag || Sflag) { + if (rflag || sflag || xflag || Sflag || Lflag) { (void) printf(" -%c option is not compatible " "with core files\n", xflag ? 'x' : - sflag ? 's' : rflag ? 'r' : 'S'); - Prelease(Pr, 0); + sflag ? 's' : rflag ? 'r' : + Lflag ? 'L' : 'S'); + Prelease(Pr, prr_flags); rc++; continue; } @@ -295,6 +392,27 @@ again: (int)psinfo.pr_pid, psinfo.pr_psargs); } + if (Lflag) { + /* + * The implementation of -L option creates an agent LWP + * in the target process address space. The agent LWP + * issues meminfo(2) system calls on behalf of the + * target process. If we are interrupted prematurely, + * the target process remains in the stopped state with + * the agent still attached to it. To prevent such + * situation we catch signals from terminal and + * terminate gracefully. + */ + if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) + (void) sigset(SIGHUP, intr); + if (sigset(SIGINT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGINT, intr); + if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) + (void) sigset(SIGQUIT, intr); + (void) sigset(SIGPIPE, intr); + (void) sigset(SIGTERM, intr); + } + if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) { struct totals t; @@ -305,7 +423,7 @@ again: */ if (Pstate(Pr) != PS_DEAD) { if (tries++ == MAX_TRIES) { - Prelease(Pr, 0); + Prelease(Pr, prr_flags); (void) close(mapfd); (void) fprintf(stderr, "%s: cannot " "examine %s: address space is " @@ -314,7 +432,7 @@ again: } if (fstat64(mapfd, &statbuf) != 0) { - Prelease(Pr, 0); + Prelease(Pr, prr_flags); (void) close(mapfd); (void) fprintf(stderr, "%s: cannot " "examine %s: lost control of " @@ -409,7 +527,8 @@ again: (void) printf("\n"); } else if (Sflag) { - (void) printf("%*s%*s%*s Mode Mapped File\n", + (void) printf("%*s%*s%*s Mode" + " Mapped File\n", addr_width, "Address", size_width, "Kbytes", size_width, "Swap"); @@ -433,11 +552,23 @@ again: if (rflag) { rc += iter_map(look_map, &t); } else if (sflag) { - (void) printf("%*s %*s %4s %-6s %s\n", - addr_width, "Address", size_width, - "Bytes", "Pgsz", "Mode ", - "Mapped File"); - rc += iter_xmap(look_smap, &t); + if (Lflag) { + (void) printf("%*s %*s %4s" + " %-6s %s %s\n", + addr_width, "Address", + size_width, + "Bytes", "Pgsz", "Mode ", + "Lgrp", "Mapped File"); + rc += iter_xmap(look_smap, &t); + } else { + (void) printf("%*s %*s %4s" + " %-6s %s\n", + addr_width, "Address", + size_width, + "Bytes", "Pgsz", "Mode ", + "Mapped File"); + rc += iter_xmap(look_smap, &t); + } } else { rc += iter_map(look_map, &t); } @@ -455,7 +586,7 @@ again: } - Prelease(Pr, 0); + Prelease(Pr, prr_flags); if (mapfd != -1) (void) close(mapfd); } @@ -631,6 +762,12 @@ again: } } + /* + * Mark the last element. + */ + if (map_count > 0) + maps[map_count - 1].md_last = B_TRUE; + free(prmapp); return (0); } @@ -641,16 +778,21 @@ look_map(void *data, const prmap_t *pmp, const char *object_name) { struct totals *t = data; const pstatus_t *Psp = Pstatus(Pr); - size_t size = (pmp->pr_size + 1023) / 1024; + size_t size; char mname[PATH_MAX]; char *lname = NULL; + size_t psz = pmp->pr_pagesize; + uintptr_t vaddr = pmp->pr_vaddr; + uintptr_t segment_end = vaddr + pmp->pr_size; + lgrp_id_t lgrp; + memory_chunk_t mchunk; /* * If the mapping is not anon or not part of the heap, make a name * for it. We don't want to report the heap as a.out's data. */ if (!(pmp->pr_mflags & MA_ANON) || - pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase || + segment_end <= Psp->pr_brkbase || pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) { lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname, mname, sizeof (mname)); @@ -662,11 +804,70 @@ look_map(void *data, const prmap_t *pmp, const char *object_name) pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid); } - (void) printf(lname ? "%.*lX %*luK %-6s %s\n" : "%.*lX %*luK %s\n", - addr_width, (uintptr_t)pmp->pr_vaddr, - size_width - 1, size, mflags(pmp->pr_mflags), lname); + /* + * Adjust the address range if -A is specified. + */ + size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz, + &vaddr, &segment_end); + + if (size == 0) + return (0); + + if (!Lflag) { + /* + * Display the whole mapping + */ + size = ROUNDUP_KB(size); + + (void) printf(lname ? + "%.*lX %*luK %-6s %s\n" : + "%.*lX %*luK %s\n", + addr_width, vaddr, + size_width - 1, size, mflags(pmp->pr_mflags), lname); + + t->total_size += size; + return (0); + } + + /* + * We need to display lgroups backing physical memory, so we break the + * segment into individual pages and coalesce pages with the same lgroup + * into one "segment". + */ + + /* + * Initialize address descriptions for the mapping. + */ + mem_chunk_init(&mchunk, segment_end, psz); + size = 0; - t->total_size += size; + /* + * Walk mapping (page by page) and display contiguous ranges of memory + * allocated to same lgroup. + */ + do { + size_t size_contig; + + /* + * Get contiguous region of memory starting from vaddr allocated + * from the same lgroup. + */ + size_contig = get_contiguous_region(&mchunk, vaddr, + segment_end, pmp->pr_pagesize, &lgrp); + + (void) printf(lname ? "%.*lX %*luK %-6s%s %s\n" : + "%.*lX %*luK %s %s\n", + addr_width, vaddr, + size_width - 1, size_contig / KILOBYTE, + mflags(pmp->pr_mflags), + lgrp2str(lgrp), lname); + + vaddr += size_contig; + size += size_contig; + } while (vaddr < segment_end && !interrupt); + + /* Update the total size */ + t->total_size += ROUNDUP_KB(size); return (0); } @@ -689,16 +890,16 @@ pagesize(const prxmap_t *pmp) return ("-"); /* no underlying HAT mapping */ } - if (pagesize >= 1024 && (pagesize % 1024) == 0) { - if ((pagesize % (1024 * 1024 * 1024)) == 0) + if (pagesize >= KILOBYTE && (pagesize % KILOBYTE) == 0) { + if ((pagesize % GIGABYTE) == 0) (void) snprintf(buf, sizeof (buf), "%dG", - pagesize / (1024 * 1024 * 1024)); - else if ((pagesize % (1024 * 1024)) == 0) + pagesize / GIGABYTE); + else if ((pagesize % MEGABYTE) == 0) (void) snprintf(buf, sizeof (buf), "%dM", - pagesize / (1024 * 1024)); + pagesize / MEGABYTE); else (void) snprintf(buf, sizeof (buf), "%dK", - pagesize / 1024); + pagesize / KILOBYTE); } else (void) snprintf(buf, sizeof (buf), "%db", pagesize); @@ -714,10 +915,15 @@ look_smap(void *data, { struct totals *t = data; const pstatus_t *Psp = Pstatus(Pr); - size_t size = (pmp->pr_size + 1023) / 1024; + size_t size; char mname[PATH_MAX]; char *lname = NULL; const char *format; + size_t psz = pmp->pr_pagesize; + uintptr_t vaddr = pmp->pr_vaddr; + uintptr_t segment_end = vaddr + pmp->pr_size; + lgrp_id_t lgrp; + memory_chunk_t mchunk; /* * If the mapping is not anon or not part of the heap, make a name @@ -736,16 +942,74 @@ look_smap(void *data, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid); } + /* + * Adjust the address range if -A is specified. + */ + size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz, + &vaddr, &segment_end); + + if (size == 0) + return (0); + + if (!Lflag) { + /* + * Display the whole mapping + */ + if (lname != NULL) + format = "%.*lX %*luK %4s %-6s %s\n"; + else + format = "%.*lX %*luK %4s %s\n"; + + size = ROUNDUP_KB(size); + + (void) printf(format, addr_width, vaddr, size_width - 1, size, + pagesize(pmp), mflags(pmp->pr_mflags), lname); + + t->total_size += size; + return (0); + } + if (lname != NULL) - format = "%.*lX %*luK %4s %-6s %s\n"; + format = "%.*lX %*luK %4s %-6s%s %s\n"; else - format = "%.*lX %*luK %4s %s\n"; + format = "%.*lX %*luK %4s%s %s\n"; - (void) printf(format, addr_width, (uintptr_t)pmp->pr_vaddr, - size_width - 1, size, - pagesize(pmp), mflags(pmp->pr_mflags), lname); + /* + * We need to display lgroups backing physical memory, so we break the + * segment into individual pages and coalesce pages with the same lgroup + * into one "segment". + */ + + /* + * Initialize address descriptions for the mapping. + */ + mem_chunk_init(&mchunk, segment_end, psz); + size = 0; - t->total_size += size; + /* + * Walk mapping (page by page) and display contiguous ranges of memory + * allocated to same lgroup. + */ + do { + size_t size_contig; + + /* + * Get contiguous region of memory starting from vaddr allocated + * from the same lgroup. + */ + size_contig = get_contiguous_region(&mchunk, vaddr, + segment_end, pmp->pr_pagesize, &lgrp); + + (void) printf(format, addr_width, vaddr, + size_width - 1, size_contig / KILOBYTE, + pagesize(pmp), mflags(pmp->pr_mflags), + lgrp2str(lgrp), lname); + + vaddr += size_contig; + size += size_contig; + } while (vaddr < segment_end && !interrupt); + + t->total_size += ROUNDUP_KB(size); return (0); } @@ -786,17 +1050,17 @@ look_xmap(void *data, (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr); - printK((pmp->pr_size + 1023) / 1024, size_width); - printK(pmp->pr_rss * (pmp->pr_pagesize / 1024), size_width); - printK(ANON(pmp) * (pmp->pr_pagesize / 1024), size_width); - printK(pmp->pr_locked * (pmp->pr_pagesize / 1024), size_width); + printK(ROUNDUP_KB(pmp->pr_size), size_width); + printK(pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE), size_width); + printK(ANON(pmp) * (pmp->pr_pagesize / KILOBYTE), size_width); + printK(pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE), size_width); (void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n", pagesize(pmp), mflags(pmp->pr_mflags), lname); - t->total_size += (pmp->pr_size + 1023) / 1024; - t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / 1024); - t->total_anon += ANON(pmp) * (pmp->pr_pagesize / 1024); - t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / 1024)); + t->total_size += ROUNDUP_KB(pmp->pr_size); + t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE); + t->total_anon += ANON(pmp) * (pmp->pr_pagesize / KILOBYTE); + t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE)); return (0); } @@ -863,9 +1127,9 @@ look_xmap_nopgsz(void *data, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid); } - kperpage = pmp->pr_pagesize / 1024; + kperpage = pmp->pr_pagesize / KILOBYTE; - t->total_size += (pmp->pr_size + 1023) / 1024; + t->total_size += ROUNDUP_KB(pmp->pr_size); t->total_rss += pmp->pr_rss * kperpage; t->total_anon += ANON(pmp) * kperpage; t->total_locked += pmp->pr_locked * kperpage; @@ -910,7 +1174,7 @@ look_xmap_nopgsz(void *data, } (void) printf("%.*lX", addr_width, (ulong_t)prev_vaddr); - printK((prev_size + 1023) / 1024, size_width); + printK(ROUNDUP_KB(prev_size), size_width); if (doswap) printK(prev_swap, size_width); @@ -919,7 +1183,7 @@ look_xmap_nopgsz(void *data, printK(prev_anon, size_width); printK(prev_locked, size_width); } - (void) printf(prev_lname ? " %-6s %s\n" : " %s\n", + (void) printf(prev_lname ? " %-6s %s\n" : "%s\n", mflags(prev_mflags), prev_lname); if (last == 0) { @@ -939,7 +1203,7 @@ look_xmap_nopgsz(void *data, prev_swap = swap * kperpage; } else if (merged == 0) { (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr); - printK((pmp->pr_size + 1023) / 1024, size_width); + printK(ROUNDUP_KB(pmp->pr_size), size_width); if (doswap) printK(swap * kperpage, size_width); else { @@ -1026,8 +1290,14 @@ nextmap(void) static int gather_map(void *ignored, const prmap_t *map, const char *objname) { - mapdata_t *data = nextmap(); + mapdata_t *data; + /* Skip mappings which are outside the range specified by -A */ + if (!address_in_range(map->pr_vaddr, + map->pr_vaddr + map->pr_size, map->pr_pagesize)) + return (0); + + data = nextmap(); data->md_map = *map; if (data->md_objname != NULL) free(data->md_objname); @@ -1041,8 +1311,14 @@ static int gather_xmap(void *ignored, const prxmap_t *xmap, const char *objname, int last, int doswap) { - mapdata_t *data = nextmap(); + mapdata_t *data; + + /* Skip mappings which are outside the range specified by -A */ + if (!address_in_range(xmap->pr_vaddr, + xmap->pr_vaddr + xmap->pr_size, xmap->pr_pagesize)) + return (0); + data = nextmap(); data->md_xmap = *xmap; if (data->md_objname != NULL) free(data->md_objname); @@ -1060,6 +1336,8 @@ iter_map(proc_map_f *func, void *data) int ret; for (i = 0; i < map_count; i++) { + if (interrupt) + break; if ((ret = func(data, &maps[i].md_map, maps[i].md_objname)) != 0) return (ret); @@ -1075,6 +1353,8 @@ iter_xmap(proc_xmap_f *func, void *data) int ret; for (i = 0; i < map_count; i++) { + if (interrupt) + break; if ((ret = func(data, &maps[i].md_xmap, maps[i].md_objname, maps[i].md_last, maps[i].md_doswap)) != 0) return (ret); @@ -1082,3 +1362,401 @@ iter_xmap(proc_xmap_f *func, void *data) return (0); } + +/* + * Convert lgroup ID to string. + * returns dash when lgroup ID is invalid. + */ +static char * +lgrp2str(lgrp_id_t lgrp) +{ + static char lgrp_buf[20]; + char *str = lgrp_buf; + + (void) sprintf(str, lgrp == LGRP_NONE ? " -" : "%4d", lgrp); + return (str); +} + +/* + * Parse address range specification for -A option. + * The address range may have the following forms: + * + * address + * start and end is set to address + * address, + * start is set to address, end is set to INVALID_ADDRESS + * ,address + * start is set to 0, end is set to address + * address1,address2 + * start is set to address1, end is set to address2 + * + */ +static int +parse_addr_range(char *input_str, uintptr_t *start, uintptr_t *end) +{ + char *startp = input_str; + char *endp = strchr(input_str, ','); + ulong_t s = (ulong_t)INVALID_ADDRESS; + ulong_t e = (ulong_t)INVALID_ADDRESS; + + if (endp != NULL) { + /* + * Comma is present. If there is nothing after comma, the end + * remains set at INVALID_ADDRESS. Otherwise it is set to the + * value after comma. + */ + *endp = '\0'; + endp++; + + if ((*endp != '\0') && sscanf(endp, "%lx", &e) != 1) + return (1); + } + + if (startp != NULL) { + /* + * Read the start address, if it is specified. If the address is + * missing, start will be set to INVALID_ADDRESS. + */ + if ((*startp != '\0') && sscanf(startp, "%lx", &s) != 1) + return (1); + } + + /* If there is no comma, end becomes equal to start */ + if (endp == NULL) + e = s; + + /* + * ,end implies 0..end range + */ + if (e != INVALID_ADDRESS && s == INVALID_ADDRESS) + s = 0; + + *start = (uintptr_t)s; + *end = (uintptr_t)e; + + /* Return error if neither start nor end address were specified */ + return (! (s != INVALID_ADDRESS || e != INVALID_ADDRESS)); +} + +/* + * Check whether any portion of [start, end] segment is within the + * [start_addr, end_addr] range. + * + * Return values: + * 0 - address is outside the range + * 1 - address is within the range + */ +static int +address_in_range(uintptr_t start, uintptr_t end, size_t psz) +{ + int rc = 1; + + /* + * Nothing to do if there is no address range specified with -A + */ + if (start_addr != INVALID_ADDRESS || end_addr != INVALID_ADDRESS) { + /* The segment end is below the range start */ + if ((start_addr != INVALID_ADDRESS) && + (end < P2ALIGN(start_addr, psz))) + rc = 0; + + /* The segment start is above the range end */ + if ((end_addr != INVALID_ADDRESS) && + (start > P2ALIGN(end_addr + psz, psz))) + rc = 0; + } + return (rc); +} + +/* + * Returns an intersection of the [start, end] interval and the range specified + * by -A flag [start_addr, end_addr]. Unspecified parts of the address range + * have value INVALID_ADDRESS. + * + * The start_addr address is rounded down to the beginning of page and end_addr + * is rounded up to the end of page. + * + * Returns the size of the resulting interval or zero if the interval is empty + * or invalid. + */ +static size_t +adjust_addr_range(uintptr_t start, uintptr_t end, size_t psz, + uintptr_t *new_start, uintptr_t *new_end) +{ + uintptr_t from; /* start_addr rounded down */ + uintptr_t to; /* end_addr rounded up */ + + /* + * Round down the lower address of the range to the beginning of page. + */ + if (start_addr == INVALID_ADDRESS) { + /* + * No start_addr specified by -A, the lower part of the interval + * does not change. + */ + *new_start = start; + } else { + from = P2ALIGN(start_addr, psz); + /* + * If end address is outside the range, return an empty + * interval + */ + if (end < from) { + *new_start = *new_end = 0; + return (0); + } + /* + * The adjusted start address is the maximum of requested start + * and the aligned start_addr of the -A range. + */ + *new_start = start < from ? from : start; + } + + /* + * Round up the higher address of the range to the end of page. + */ + if (end_addr == INVALID_ADDRESS) { + /* + * No end_addr specified by -A, the upper part of the interval + * does not change. + */ + *new_end = end; + } else { + /* + * If only one address is specified and it is the beginning of a + * segment, get information about the whole segment. This + * function is called once per segment and the 'end' argument is + * always the end of a segment, so just use the 'end' value. + */ + to = (end_addr == start_addr && start == start_addr) ? + end : + P2ALIGN(end_addr + psz, psz); + /* + * If start address is outside the range, return an empty + * interval + */ + if (start > to) { + *new_start = *new_end = 0; + return (0); + } + /* + * The adjusted end address is the minimum of requested end + * and the aligned end_addr of the -A range. + */ + *new_end = end > to ? to : end; + } + + /* + * Make sure that the resulting interval is legal. + */ + if (*new_end < *new_start) + *new_start = *new_end = 0; + + /* Return the size of the interval */ + return (*new_end - *new_start); +} + +/* + * Initialize memory_info data structure with information about a new segment. + */ +static void +mem_chunk_init(memory_chunk_t *chunk, uintptr_t end, size_t psz) +{ + chunk->end_addr = end; + chunk->page_size = psz; + chunk->page_index = 0; + chunk->chunk_start = chunk->chunk_end = 0; +} + +/* + * Create a new chunk of addresses starting from vaddr. + * Pass the whole chunk to pr_meminfo to collect lgroup and page size + * information for each page in the chunk. + */ +static void +mem_chunk_get(memory_chunk_t *chunk, uintptr_t vaddr) +{ + page_descr_t *pdp = chunk->page_info; + size_t psz = chunk->page_size; + uintptr_t addr = vaddr; + uint64_t inaddr[MAX_MEMINFO_CNT]; + uint64_t outdata[2 * MAX_MEMINFO_CNT]; + uint_t info[2] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE }; + uint_t validity[MAX_MEMINFO_CNT]; + uint64_t *dataptr = inaddr; + uint64_t *outptr = outdata; + uint_t *valptr = validity; + int i, j, rc; + + chunk->chunk_start = vaddr; + chunk->page_index = 0; /* reset index for the new chunk */ + + /* + * Fill in MAX_MEMINFO_CNT wotrh of pages starting from vaddr. Also, + * copy starting address of each page to inaddr array for pr_meminfo. + */ + for (i = 0, pdp = chunk->page_info; + (i < MAX_MEMINFO_CNT) && (addr <= chunk->end_addr); + i++, pdp++, dataptr++, addr += psz) { + *dataptr = (uint64_t)addr; + pdp->pd_start = addr; + pdp->pd_lgrp = LGRP_NONE; + pdp->pd_valid = 0; + pdp->pd_pagesize = 0; + } + + /* Mark the number of entries in the chunk and the last address */ + chunk->page_count = i; + chunk->chunk_end = addr - psz; + + if (interrupt) + return; + + /* Call meminfo for all collected addresses */ + rc = pr_meminfo(Pr, inaddr, i, info, 2, outdata, validity); + if (rc < 0) { + (void) perr("can not get memory information"); + return; + } + + /* Verify validity of each result and fill in the addrs array */ + pdp = chunk->page_info; + for (j = 0; j < i; j++, pdp++, valptr++, outptr += 2) { + /* Skip invalid address pointers */ + if ((*valptr & 1) == 0) { + continue; + } + + /* Is lgroup information available? */ + if ((*valptr & 2) != 0) { + pdp->pd_lgrp = (lgrp_id_t)*outptr; + pdp->pd_valid = 1; + } + + /* Is page size informaion available? */ + if ((*valptr & 4) != 0) { + pdp->pd_pagesize = *(outptr + 1); + } + } +} + +/* + * Starting from address 'vaddr' find the region with pages allocated from the + * same lgroup. + * + * Arguments: + * mchunk Initialized memory chunk structure + * vaddr Starting address of the region + * maxaddr Upper bound of the region + * pagesize Default page size to use + * ret_lgrp On exit contains the lgroup ID of all pages in the + * region. + * + * Returns: + * Size of the contiguous region in bytes + * The lgroup ID of all pages in the region in ret_lgrp argument. + */ +static size_t +get_contiguous_region(memory_chunk_t *mchunk, uintptr_t vaddr, + uintptr_t maxaddr, size_t pagesize, lgrp_id_t *ret_lgrp) +{ + size_t size_contig = 0; + lgrp_id_t lgrp; /* Lgroup of the region start */ + lgrp_id_t curr_lgrp; /* Lgroup of the current page */ + size_t psz = pagesize; /* Pagesize to use */ + + /* Set both lgroup IDs to the lgroup of the first page */ + curr_lgrp = lgrp = addr_to_lgrp(mchunk, vaddr, &psz); + + /* + * Starting from vaddr, walk page by page until either the end + * of the segment is reached or a page is allocated from a different + * lgroup. Also stop if interrupted from keyboard. + */ + while ((vaddr < maxaddr) && (curr_lgrp == lgrp) && !interrupt) { + /* + * Get lgroup ID and the page size of the current page. + */ + curr_lgrp = addr_to_lgrp(mchunk, vaddr, &psz); + /* If there is no page size information, use the default */ + if (psz == 0) + psz = pagesize; + + if (curr_lgrp == lgrp) { + /* + * This page belongs to the contiguous region. + * Increase the region size and advance to the new page. + */ + size_contig += psz; + vaddr += psz; + } + } + + /* Return the region lgroup ID and the size */ + *ret_lgrp = lgrp; + return (size_contig); +} + +/* + * Given a virtual address, return its lgroup and page size. If there is meminfo + * information for an address, use it, otherwise shift the chunk window to the + * vaddr and create a new chunk with known meminfo information. + */ +static lgrp_id_t +addr_to_lgrp(memory_chunk_t *chunk, uintptr_t vaddr, size_t *psz) +{ + page_descr_t *pdp; + lgrp_id_t lgrp = LGRP_NONE; + int i; + + *psz = chunk->page_size; + + if (interrupt) + return (0); + + /* + * Is there information about this address? If not, create a new chunk + * starting from vaddr and apply pr_meminfo() to the whole chunk. + */ + if (vaddr < chunk->chunk_start || vaddr > chunk->chunk_end) { + /* + * This address is outside the chunk, get the new chunk and + * collect meminfo information for it. + */ + mem_chunk_get(chunk, vaddr); + } + + /* + * Find information about the address. + */ + pdp = &chunk->page_info[chunk->page_index]; + for (i = chunk->page_index; i < chunk->page_count; i++, pdp++) { + if (pdp->pd_start == vaddr) { + if (pdp->pd_valid) { + lgrp = pdp->pd_lgrp; + /* + * Override page size information if it is + * present. + */ + if (pdp->pd_pagesize > 0) + *psz = pdp->pd_pagesize; + } + break; + } + } + /* + * Remember where we ended - the next search will start here. + * We can query for the lgrp for the same address again, so do not + * advance index past the current value. + */ + chunk->page_index = i; + + return (lgrp); +} + +/* ARGSUSED */ +static void +intr(int sig) +{ + interrupt = 1; +} diff --git a/usr/src/lib/libdtrace/common/procfs.d.in b/usr/src/lib/libdtrace/common/procfs.d.in index 58dbeca9c4..5d97197c31 100644 --- a/usr/src/lib/libdtrace/common/procfs.d.in +++ b/usr/src/lib/libdtrace/common/procfs.d.in @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -330,6 +330,7 @@ translator lwpsinfo_t < kthread_t *T > { pr_onpro = T->t_cpu->cpu_id; pr_bindpro = T->t_bind_cpu; pr_bindpset = T->t_bind_pset; + pr_lgrp = T->t_lpl->lpl_lgrpid; }; inline psinfo_t *curpsinfo = xlate <psinfo_t *> (curthread->t_procp); diff --git a/usr/src/lib/libproc/common/P32ton.c b/usr/src/lib/libproc/common/P32ton.c index c6b4b8fbcc..78219550c8 100644 --- a/usr/src/lib/libproc/common/P32ton.c +++ b/usr/src/lib/libproc/common/P32ton.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -401,6 +400,7 @@ lwpsinfo_32_to_n(const lwpsinfo32_t *src, lwpsinfo_t *dst) dst->pr_onpro = src->pr_onpro; dst->pr_bindpro = src->pr_bindpro; dst->pr_bindpset = src->pr_bindpset; + dst->pr_lgrp = src->pr_lgrp; } void @@ -773,6 +773,7 @@ lwpsinfo_n_to_32(const lwpsinfo_t *src, lwpsinfo32_t *dst) dst->pr_onpro = src->pr_onpro; dst->pr_bindpro = src->pr_bindpro; dst->pr_bindpset = src->pr_bindpset; + dst->pr_lgrp = src->pr_lgrp; } void diff --git a/usr/src/pkgdefs/SUNWesu/prototype_com b/usr/src/pkgdefs/SUNWesu/prototype_com index e53cf18680..170e58bc6e 100644 --- a/usr/src/pkgdefs/SUNWesu/prototype_com +++ b/usr/src/pkgdefs/SUNWesu/prototype_com @@ -72,6 +72,7 @@ f none usr/bin/last 555 root bin f none usr/bin/lastcomm 555 root bin f none usr/bin/logname 555 root bin f none usr/bin/look 755 root bin +f none usr/bin/lgrpinfo 555 root bin f none usr/bin/mkfifo 555 root bin f none usr/bin/nawk 555 root bin f none usr/bin/newform 555 root bin @@ -86,7 +87,9 @@ l none usr/bin/pcred=../../usr/lib/isaexec l none usr/bin/pfiles=../../usr/lib/isaexec l none usr/bin/pflags=../../usr/lib/isaexec l none usr/bin/pldd=../../usr/lib/isaexec +l none usr/bin/plgrp=../../usr/lib/isaexec l none usr/bin/pmap=../../usr/lib/isaexec +l none usr/bin/pmadvise=../../usr/lib/isaexec l none usr/bin/ppgsz=../../usr/lib/isaexec l none usr/bin/ppriv=../../usr/lib/isaexec l none usr/bin/preap=../../usr/lib/isaexec diff --git a/usr/src/pkgdefs/SUNWesu/prototype_i386 b/usr/src/pkgdefs/SUNWesu/prototype_i386 index 008d9a9834..fca1c84679 100644 --- a/usr/src/pkgdefs/SUNWesu/prototype_i386 +++ b/usr/src/pkgdefs/SUNWesu/prototype_i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ # # CDDL HEADER END # + # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -55,7 +55,9 @@ f none usr/bin/i86/pcred 555 root bin f none usr/bin/i86/pfiles 555 root bin f none usr/bin/i86/pflags 555 root bin f none usr/bin/i86/pldd 555 root bin +f none usr/bin/i86/plgrp 555 root bin f none usr/bin/i86/pmap 555 root bin +f none usr/bin/i86/pmadvise 555 root bin f none usr/bin/i86/ppgsz 555 root bin f none usr/bin/i86/ppriv 555 root bin f none usr/bin/i86/preap 555 root bin @@ -74,7 +76,9 @@ f none usr/bin/amd64/pcred 555 root bin f none usr/bin/amd64/pfiles 555 root bin f none usr/bin/amd64/pflags 555 root bin f none usr/bin/amd64/pldd 555 root bin +f none usr/bin/amd64/plgrp 555 root bin f none usr/bin/amd64/pmap 555 root bin +f none usr/bin/amd64/pmadvise 555 root bin f none usr/bin/amd64/ppgsz 555 root bin f none usr/bin/amd64/ppriv 555 root bin f none usr/bin/amd64/preap 555 root bin diff --git a/usr/src/pkgdefs/SUNWesu/prototype_sparc b/usr/src/pkgdefs/SUNWesu/prototype_sparc index 0f150a9c61..ea686e295e 100644 --- a/usr/src/pkgdefs/SUNWesu/prototype_sparc +++ b/usr/src/pkgdefs/SUNWesu/prototype_sparc @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,7 +18,9 @@ # # CDDL HEADER END # -# Copyright 2003 Sun Microsystems, Inc. All rights reserved. + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -54,7 +55,9 @@ f none usr/bin/sparcv9/pcred 555 root bin f none usr/bin/sparcv9/pfiles 555 root bin f none usr/bin/sparcv9/pflags 555 root bin f none usr/bin/sparcv9/pldd 555 root bin +f none usr/bin/sparcv9/plgrp 555 root bin f none usr/bin/sparcv9/pmap 555 root bin +f none usr/bin/sparcv9/pmadvise 555 root bin f none usr/bin/sparcv9/ppgsz 555 root bin f none usr/bin/sparcv9/ppriv 555 root bin f none usr/bin/sparcv9/preap 555 root bin diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_com b/usr/src/pkgdefs/SUNWperl584core/prototype_com index c2ddcda34f..491ac02f9f 100644 --- a/usr/src/pkgdefs/SUNWperl584core/prototype_com +++ b/usr/src/pkgdefs/SUNWperl584core/prototype_com @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -210,6 +210,7 @@ f none usr/perl5/5.8.4/lib/Sun/Solaris/Project.pm 0444 root bin f none usr/perl5/5.8.4/lib/Sun/Solaris/Task.pm 0444 root bin f none usr/perl5/5.8.4/lib/Sun/Solaris/Ucred.pm 0444 root bin f none usr/perl5/5.8.4/lib/Sun/Solaris/Utils.pm 0444 root bin +f none usr/perl5/5.8.4/lib/Sun/Solaris/Lgrp.pm 0444 root bin f none usr/perl5/5.8.4/lib/Switch.pm 0444 root bin f none usr/perl5/5.8.4/lib/Symbol.pm 0444 root bin d none usr/perl5/5.8.4/lib/Term 0755 root bin diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_i386 b/usr/src/pkgdefs/SUNWperl584core/prototype_i386 index fe9f20046a..587739c185 100644 --- a/usr/src/pkgdefs/SUNWperl584core/prototype_i386 +++ b/usr/src/pkgdefs/SUNWperl584core/prototype_i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,11 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -330,6 +330,9 @@ f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Privilege/Privil d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project 0755 root bin f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.bs 0444 root bin f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.so 0555 root bin +d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp 0755 root bin +f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 0444 root bin +f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 0555 root bin d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task 0755 root bin f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.bs 0444 root bin f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.so 0555 root bin diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_sparc b/usr/src/pkgdefs/SUNWperl584core/prototype_sparc index 5e66587b08..ac78b1c8ff 100644 --- a/usr/src/pkgdefs/SUNWperl584core/prototype_sparc +++ b/usr/src/pkgdefs/SUNWperl584core/prototype_sparc @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,11 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -324,6 +324,9 @@ f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Intrs/Intrs.so 05 d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat 0755 root bin f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.bs 0444 root bin f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.so 0555 root bin +d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp 0755 root bin +f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 0444 root bin +f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 0555 root bin d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege 0755 root bin f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privilege.bs 0444 root bin f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privilege.so 0555 root bin diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_com b/usr/src/pkgdefs/SUNWpl5u/prototype_com index 38066b45d2..1544efe0e4 100644 --- a/usr/src/pkgdefs/SUNWpl5u/prototype_com +++ b/usr/src/pkgdefs/SUNWpl5u/prototype_com @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,11 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -132,6 +132,7 @@ f none usr/perl5/5.6.1/lib/Sun/Solaris/Intrs.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Kstat.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Privilege.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Project.pm 444 root bin +f none usr/perl5/5.6.1/lib/Sun/Solaris/Lgrp.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Task.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Ucred.pm 444 root bin f none usr/perl5/5.6.1/lib/Sun/Solaris/Utils.pm 444 root bin diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_i386 b/usr/src/pkgdefs/SUNWpl5u/prototype_i386 index 5356eb4591..a0ffecf76e 100644 --- a/usr/src/pkgdefs/SUNWpl5u/prototype_i386 +++ b/usr/src/pkgdefs/SUNWpl5u/prototype_i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,11 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -275,6 +275,9 @@ f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Privilege/Privil d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project 755 root bin f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.bs 444 root bin f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.so 555 root bin +d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp 755 root bin +f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 444 root bin +f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 555 root bin d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task 755 root bin f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.bs 444 root bin f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.so 555 root bin diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_sparc b/usr/src/pkgdefs/SUNWpl5u/prototype_sparc index 1492575773..623711e70f 100644 --- a/usr/src/pkgdefs/SUNWpl5u/prototype_sparc +++ b/usr/src/pkgdefs/SUNWpl5u/prototype_sparc @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -275,6 +275,9 @@ f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privile d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project 755 root bin f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project/Project.bs 444 root bin f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project/Project.so 555 root bin +d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp 755 root bin +f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 444 root bin +f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 555 root bin d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task 755 root bin f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task/Task.bs 444 root bin f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task/Task.so 555 root bin diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index 2c26ee77f3..e1931414ec 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -2449,6 +2449,7 @@ prgetlwpsinfo(kthread_t *t, lwpsinfo_t *psp) psp->pr_onpro = t->t_cpu->cpu_id; psp->pr_bindpro = t->t_bind_cpu; psp->pr_bindpset = t->t_bind_pset; + psp->pr_lgrp = t->t_lpl->lpl_lgrpid; } #ifdef _SYSCALL32_IMPL @@ -2517,6 +2518,7 @@ prgetlwpsinfo32(kthread_t *t, lwpsinfo32_t *psp) psp->pr_onpro = t->t_cpu->cpu_id; psp->pr_bindpro = t->t_bind_cpu; psp->pr_bindpset = t->t_bind_pset; + psp->pr_lgrp = t->t_lpl->lpl_lgrpid; } #endif /* _SYSCALL32_IMPL */ diff --git a/usr/src/uts/common/os/lgrp.c b/usr/src/uts/common/os/lgrp.c index 3a2a6f0a17..315c0ac873 100644 --- a/usr/src/uts/common/os/lgrp.c +++ b/usr/src/uts/common/os/lgrp.c @@ -168,7 +168,6 @@ lpl_t *lpl_bootstrap; static lgrp_t lroot; - /* * Size, in bytes, beyond which random memory allocation policy is applied * to non-shared memory. Default is the maximum size, so random memory @@ -176,6 +175,12 @@ static lgrp_t lroot; */ size_t lgrp_privm_random_thresh = (size_t)(-1); +/* the maximum effect that a single thread can have on it's lgroup's load */ +#define LGRP_LOADAVG_MAX_EFFECT(ncpu) \ + ((lgrp_loadavg_max_effect) / (ncpu)) +uint32_t lgrp_loadavg_max_effect = LGRP_LOADAVG_THREAD_MAX; + + /* * Size, in bytes, beyond which random memory allocation policy is applied to * shared memory. Default is 8MB (2 ISM pages). @@ -1765,6 +1770,8 @@ lgrp_kstat_extract(kstat_t *ksp, int rw) ksd[stat + LGRP_NUM_PG_FREE].value.i64 = lgrp_mem_size(lgrpid, LGRP_MEM_SIZE_FREE); ksd[stat + LGRP_LOADAVG].value.i64 = lgrp_sum_loadavgs(lgrp); + ksd[stat + LGRP_LOADAVG_SCALE].value.i64 = + lgrp_loadavg_max_effect; } else { lgrp_kstat_reset(lgrpid); } @@ -2945,11 +2952,6 @@ lpl_topo_bootstrap(lpl_t *target, int size) bzero(lpl_bootstrap_list, sizeof (lpl_bootstrap_list)); } -/* the maximum effect that a single thread can have on it's lgroup's load */ -#define LGRP_LOADAVG_MAX_EFFECT(ncpu) \ - ((lgrp_loadavg_max_effect) / (ncpu)) -uint32_t lgrp_loadavg_max_effect = LGRP_LOADAVG_THREAD_MAX; - /* * If the lowest load among the lgroups a process' threads are currently * spread across is greater than lgrp_expand_proc_thresh, we'll consider diff --git a/usr/src/uts/common/sys/lgrp.h b/usr/src/uts/common/sys/lgrp.h index d8f26f787c..840a2cb564 100644 --- a/usr/src/uts/common/sys/lgrp.h +++ b/usr/src/uts/common/sys/lgrp.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -170,7 +170,8 @@ typedef enum lgrp_snap_stat_types { LGRP_NUM_PG_FREE, /* # of free pages */ LGRP_NUM_PG_AVAIL, /* # of allocatable physical pages */ LGRP_NUM_PG_INSTALL, /* # of installed physical pages */ - LGRP_LOADAVG, /* "load average" of this lgrp */ + LGRP_LOADAVG, /* unscaled load average of this lgrp */ + LGRP_LOADAVG_SCALE, /* load unit of one CPU bound thread */ LGRP_NUM_SNAPSHOT_STATS /* always last */ } lgrp_snap_stat_t; @@ -198,7 +199,8 @@ static char *lgrp_kstat_names[] = { \ "pages free", \ "pages avail", \ "pages installed", \ - "load average" \ + "load average", \ + "loadscale" \ } #define LGRP_NUM_STATS ((int)LGRP_NUM_COUNTER_STATS + \ diff --git a/usr/src/uts/common/sys/procfs.h b/usr/src/uts/common/sys/procfs.h index 995870f7fc..6f8e1a663d 100644 --- a/usr/src/uts/common/sys/procfs.h +++ b/usr/src/uts/common/sys/procfs.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -61,6 +61,7 @@ extern "C" { #include <sys/pset.h> #include <sys/procfs_isa.h> #include <sys/priv.h> +#include <sys/lgrp.h> #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 #error "Cannot use procfs in the large file compilation environment" @@ -262,7 +263,8 @@ typedef struct lwpsinfo { processorid_t pr_onpro; /* processor which last ran this lwp */ processorid_t pr_bindpro; /* processor to which lwp is bound */ psetid_t pr_bindpset; /* processor set to which lwp is bound */ - int pr_filler[5]; /* reserved for future use */ + lgrp_id_t pr_lgrp; /* lwp home lgroup */ + int pr_filler[4]; /* reserved for future use */ } lwpsinfo_t; /* @@ -633,7 +635,8 @@ typedef struct lwpsinfo32 { processorid_t pr_onpro; /* processor which last ran this lwp */ processorid_t pr_bindpro; /* processor to which lwp is bound */ psetid_t pr_bindpset; /* processor set to which lwp is bound */ - int pr_filler[5]; /* reserved for future use */ + lgrp_id_t pr_lgrp; /* lwp home lgroup */ + int pr_filler[4]; /* reserved for future use */ } lwpsinfo32_t; /* diff --git a/usr/src/uts/common/syscall/lgrpsys.c b/usr/src/uts/common/syscall/lgrpsys.c index 09b9818ad6..17fa2e1ff9 100644 --- a/usr/src/uts/common/syscall/lgrpsys.c +++ b/usr/src/uts/common/syscall/lgrpsys.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -502,14 +502,13 @@ lgrp_affinity_best(kthread_t *t, struct cpupart *cpupart, lgrp_id_t start) lgrpid = start; /* - * Begin with home as best lgroup if it's root or in this pset - * Otherwise, use starting lgroup given above as best first. + * Use starting lgroup given above as best first */ home = t->t_lpl->lpl_lgrpid; - if (LGRP_CPUS_IN_PART(home, cpupart)) - best_lpl = &cpupart->cp_lgrploads[home]; - else + if (LGRP_CPUS_IN_PART(lgrpid, cpupart)) best_lpl = &cpupart->cp_lgrploads[lgrpid]; + else + best_lpl = &cpupart->cp_lgrploads[home]; best_aff = affs[best_lpl->lpl_lgrpid]; @@ -529,7 +528,7 @@ lgrp_affinity_best(kthread_t *t, struct cpupart *cpupart, lgrp_id_t start) */ lpl = &cpupart->cp_lgrploads[lgrpid]; if (affs[lgrpid] > best_aff) { - best_aff = affs[lgrpid]; + best_aff = affs[lgrpid]; best_lpl = lpl; } @@ -558,10 +557,11 @@ int lgrp_affinity_set_thread(kthread_t *t, lgrp_id_t lgrp, lgrp_affinity_t aff, lgrp_affinity_t **aff_buf) { + lgrp_affinity_t *affs; + lgrp_id_t best; lpl_t *best_lpl; lgrp_id_t home; int retval; - lgrp_id_t start; ASSERT(t != NULL); ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock)); @@ -589,33 +589,26 @@ lgrp_affinity_set_thread(kthread_t *t, lgrp_id_t lgrp, lgrp_affinity_t aff, *aff_buf = NULL; } - t->t_lgrp_affinity[lgrp] = aff; - - /* - * Select a new home if the thread's affinity is being cleared - */ - if (aff == LGRP_AFF_NONE) { - lgrp_move_thread(t, lgrp_choose(t, t->t_cpupart), 1); - thread_unlock(t); - return (retval); - } + affs = t->t_lgrp_affinity; + affs[lgrp] = aff; /* * Find lgroup for which thread has most affinity, - * starting after home + * starting with lgroup for which affinity being set */ - home = t->t_lpl->lpl_lgrpid; - start = home + 1; - if (start > lgrp_alloc_max) - start = 0; - - best_lpl = lgrp_affinity_best(t, t->t_cpupart, start); + best_lpl = lgrp_affinity_best(t, t->t_cpupart, lgrp); /* - * Rehome if found lgroup with more affinity than home + * Rehome if found lgroup with more affinity than home or lgroup for + * which affinity is being set has same affinity as home */ - if (best_lpl != NULL && best_lpl != t->t_lpl) - lgrp_move_thread(t, best_lpl, 1); + home = t->t_lpl->lpl_lgrpid; + if (best_lpl != NULL && best_lpl != t->t_lpl) { + best = best_lpl->lpl_lgrpid; + if (affs[best] > affs[home] || (affs[best] == affs[home] && + best == lgrp)) + lgrp_move_thread(t, best_lpl, 1); + } thread_unlock(t); |