Section 11.3. Using Perl to Access kstats


11.3. Using Perl to Access kstats

The previous example illustrates how simple it is to extract the information you need from the kernel; however, it also shows how tedious it can be to format the output in a shell script. Fortunately, the Perl extension module that /usr/bin/ kstat uses is documented so that you can write custom Perl programs. Because Perl is a "real programming language" and is ideally suited for text formatting, you can write solutions that are quite robust and comprehensive.

11.3.1. The Tied-Hash Interface to the kstat Facility

Access to kstats is made through a Perl extension in the XSUB interface module called Sun::Solaris::Kstat. To access Solaris kernel statistics in a Perl program, you use Sun::Solaris::Kstat; to import the module

The module contains two methods, new() and update(), correlating with the libkstat C functions kstat_open() and kstat_chain_update(). The module provides kstat data through a tree of hashes based on a three-part key, consisting of the module, instance, and name (ks_module, ks_instance, and ks_name are members of the C-language kstat struct). Following is a synopsis.

Sun::Solaris::Kstat->new(); Sun::Solaris::Kstat->update(); Sun::Solaris::Kstat->{module}{instance}{name}{statistic} 


The lowest-level "statistic" member of the hierarchy is a tied hash implemented in the XSUB module and holds the following elements from struct kstat:

  • ks_crtime. Creation time, which is presented as the statistic crtime

  • ks_snaptime. Time of last data snapshot, which is presented as the statistic snaptime

  • ks_class. The kstat class, which is presented as the statistic class

  • ks_data. Kstat type-specific data decoded into individual statistics (the module produces one statistic per member of whatever structure is being decoded)

Because the module converts all kstat types, you need not worry about the different data structures for named and raw types. Most of the Solaris OS raw kstat entries are decoded by the module, giving you easy access to low-level data about things such as kernel memory allocation, swap, NFS performance, etc.

11.3.2. The update() Method

The update() method updates all the statistics you have accessed so far and adds a bit of functionality on top of the libkstat kstat_chain_update() function. If called in scalar context, it acts the same as kstat_chain_update(). It returns 0 if the kstat chain has not changed and 1 if it has. However, if update() is called in list context, it returns references to two arrays. The first array holds the keys of any kstats that have been added since the call to new() or the last call to update(); the second holds a list of entries that have been deleted. The entries in the arrays are strings of the form module:instance:name. This is useful for implementing programs that cache state information about devices, such as disks, that you can dynamically add or remove from a running system.

Once you access a kstat, it will always be read by subsequent calls to update(). To stop it from being reread, you can clear the appropriate hash. For example:

$kstat->{$module}{$instance}{$name} = (); 


11.3.3. 64-Bit Values

At the time the kstat tied-hash interface was first released on the Solaris 8 OS, Perl 5 could not yet internally support 64-bit integers, so the kstat module approximates these values.

  • Timer. Values ks_crtime and ks_snaptime in struct kstat are of type hrtime_t, as are values of timer kstats and the wtime, wlentime, wlastupdate, rtime, rlentime, and rlastupdate fields of the kstat I/O statistics structures. This is a C-type definition used for the Solaris high-resolution timer, which is a 64-bit integer value. These fields are measured by the kstat facility in nanoseconds, meaning that a 32-bit value would represent approximately four seconds. The alternative is to store the values as floating-point numbers, which offer approximately 53 bits of precision on present hardware. You can store 64-bit intervals and timers as floating-point values expressed in seconds, meaning that this module rounds up time-related kstats to approximately microsecond resolution.

  • Counters. Because it is not useful to store these values as 32-bit values and because floating-point values offer 53 bits of precision, all 64-bit counters are also stored as floating-point values.

11.3.4. Getting Started with Perl

As in our first example, the following example shows a Perl program that gives the same output as obtained by calling /usr/sbin/psrinfo without arguments.

#!/usr/bin/perl -w # psrinfo.perl: emulate the Solaris psrinfo command use strict; use Sun::Solaris::Kstat; my $kstat = Sun::Solaris::Kstat->new(); my $mh = $kstat->{cpu_info}; foreach my $cpu (keys(%$mh)) {     my ($state, $when) = @{$kstat->{cpu_info}{$cpu}        {"cpu_info".$cpu}}{qw(state state_begin)};     my ($sec,$min,$hour,$mday,$mon,$year) =        (localtime($when))[0..5];     printf("%d\t%-8s  since %.2d/%.2d/%.2d %.2d:%.2d:%.2d\n",         $cpu,$state,$mon + 1,$mday,$year - 100,$hour,$min,$sec); } 


This program produces the following output:

$ psrinfo.perl 0        on-line  since 07/09/01 08:29:00 1        on-line  since 07/09/01 08:29:07 


The psrinfo command has a -v (verbose) option that prints much more detail about the processors in the system. The output looks like the following example:

$ psrinfo -v Status of processor 0 as of: 08/17/01 16:52:44   Processor has been on-line since 08/14/01 16:27:56.   The sparcv9 processor operates at 400 MHz,         and has a sparcv9 floating point processor. Status of processor 1 as of: 08/17/01 16:52:44   Processor has been on-line since 08/14/01 16:28:03.   The sparcv9 processor operates at 400 MHz,         and has a sparcv9 floating point processor. 


All the information in the psrinfo command is accessible through the kstat interface. As an exercise, try modifying the simple psrinfo.perl example script to print the verbose information, as in this example.

11.3.5. netstatMulti Implemented in Perl

The Perl script in the following example has the same function as our previous example (in Section 11.2.2 ) that used the kstat and nawk commands. Note that we have to implement our own search methods to find the kstat entries that we want to work with. Although this script is not shorter than our first example, it is certainly easier to extend with new functionality. Without much work, you could create a generic search method, similar to how /usr/bin/kstat works, and import it into any Perl scripts that need to access Solaris kernel statistics.

#!/usr/bin/perl -w # netstatMulti.perl: print out netstat-like stats for multiple interfaces #  using the kstat tied hash facility use strict; use Sun::Solaris::Kstat; my $USAGE = "usage: $0  ... interval"; ###### # Main ###### sub interface_exists($); sub get_kstats(); sub print_kstats(); # process args my $argc = scalar(@ARGV); my @interfaces = (); my $fmt = "%-10s %-10u %-10u %-10u %-10u %-10u\n"; if ($argc < 2) {   print "$USAGE\n";   exit 1; } elsif  ( !($ARGV[-1] =~ /^\d+$/) ) {    print "$USAGE\n";    print "   interval must be an integer.\n";    exit 1; } # get kstat chain a la kstat_open() my $kstat = Sun::Solaris::Kstat->new(); # Check for interfaces foreach my $interface (@ARGV[-($argc)..-2]) {   my $iface;   if(! ($iface = interface_exists($interface)) ){     print "$USAGE\n";     print "   interface $interface not found.\n";     exit 1;   }   push @interfaces, $iface; } my $interval = $ARGV[-1]; # print header print "           input                output     \n"; print "    packets    errs       packets    errs       colls\n"; # loop forever printing stats while(1) {   get_kstats();   print_kstats();   sleep($interval);   $kstat->update(); } ############# # Subroutines ############# # search for the first kstat with given name sub interface_exists($) {   my ($name) = @_;   my ($mod, $inst) = $name =~ /^(.+?)(\d+)$/;   return(exists($kstat->{$mod}{$inst}{$name})          ? { module => $mod, instance => $inst, name => $name }          : undef); }                        # get kstats for given interface sub get_kstats() {   my (@statnames) = ('ipackets','ierrors','opackets',     'oerrors','collisions');   my ($m, $i, $n);   foreach my $interface (@interfaces) {     $m = $interface->{module};     $i = $interface->{instance};     $n = $interface->{name};     foreach my $statname (@statnames) {       my $stat = $kstat->{$m}{$i}{$n}{$statname};       die "kstat not found: $m:$i:$n:$statname" unless defined $stat;       my $begin_stat = "b_" . $statname; # name of first sample       if(exists $interface->{$begin_stat}) {         $interface->{$statname} = $stat -           $interface->{$begin_stat};       }else { # save first sample to calculate deltas         $interface->{$statname} =  $stat;         $interface->{$begin_stat} = $stat;       }     }   } }  # print out formatted information a la netstat  sub print_kstats() {    foreach my $i (@interfaces) {      printf($fmt,$i->{name},$i->{ipackets},$i->{ierrors},        $i->{opackets},$i->{oerrors},$i->{collisions});    }  } 


In the subroutine interface_exists(), you cache the members of the key if an entry is found. This way, you need not do another search in get_kstats(). You could fairly easily modify the script to display all network interfaces on the system (rather than take command-line arguments) and use the update() method to discover if interfaces are added or removed from the system (with ifconfig, for example). This exercise is left up to you.




Solaris Performance and Tools(c) Dtrace and Mdb Techniques for Solaris 10 and Opensolaris
Solaris Performance and Tools: DTrace and MDB Techniques for Solaris 10 and OpenSolaris
ISBN: 0131568191
EAN: 2147483647
Year: 2007
Pages: 180

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net