Solaris network statistics utility (only Solaris 10)

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

Capturing Network statistics on Solaris 10
Latest nicstat utility is available here.
The latest nicstat utility is compiled and might work better, below is the older perl version.
The latest version is also on sourceforge
Example script output

Time     Int   rKB/s   wKB/s   rPk/s   wPk/s    rAvs    wAvs   %Util    Sat
00:26:33   aggr1 60562.9  1521.4 41090.4 20494.1  1509.3   76.02   50.86   0.00
00:26:33   nxge3    0.47    0.29    5.23    3.17   92.39   93.85    0.00   0.00
00:26:33   nxge7    0.47    0.29    5.23    3.15   92.61   93.41    0.00   0.00
00:26:33     lo0    0.00    0.00  1883.0  1883.0    0.00    0.00    0.00   0.00

Perl script

#!/usr/bin/perl -w
#
# nicstat - print network traffic, Kbyte/s read and written. 
#           Solaris 8+, Perl (Sun::Solaris::Kstat).
#
# "netstat -i" only gives a packet count, this program gives Kbytes.
#
# 30-Sep-2006, ver 1.00  (check for new versions, http://www.brendangregg.com)
#
# USAGE:    nicstat [-hsz] [-i int[,int...]] | [interval [count]]
#
#           -h              # help
#           -s              # print summary output
#           -z              # skip zero lines
#           -i int[,int...] # print these instances only
#   eg,
#           nicstat         # print summary since boot
#           nicstat 1       # print continually, every 1 second
#           nicstat 1 5     # print 5 times, every 1 second
#           nicstat -i hme0 # only examine hme0
#
# This prints out the KB/s transferred for all the network cards (NICs),
# including packet counts and average sizes. The first line is the summary
# data since boot.
#
# FIELDS:
#           Int         Interface
#           rKB/s       read Kbytes/s
#           wKB/s       write Kbytes/s
#           rPk/s       read Packets/s
#           wPk/s       write Packets/s
#           rAvs        read Average size, bytes
#           wAvs        write Average size, bytes
#           %Util       %Utilisation (r+w/ifspeed)
#           Sat         Saturation (defer, nocanput, norecvbuf, noxmtbuf)
#
# NOTES:
#
# - Some unusual network cards may not provide all the details to Kstat,
#   (or provide different symbols). Check for newer versions of this program,
#   and the @Network array in the code below.
# - Utilisation is based on bytes transferred divided by speed of the interface
#   (if the speed is known). It should be impossible to reach 100% as there
#   are overheads due to bus negotiation and timing.
# - Loopback interfaces may only provide packet counts (if anything), and so
#   bytes and %util will always be zero. Newer versions of Solaris (newer than
#   Solaris 10 6/06) may provide loopback byte stats.
# - Saturation is determined by counting read and write errors caused by the 
#   interface running at saturation. This approach is not ideal, and the value
#   reported is often lower than it should be (eg, 0.0). Reading the rKB/s and
#   wKB/s fields may be more useful.
#
# SEE ALSO:
#           nicstat.c       # the C version, also on my website
#           kstat -n hme0 [interval [count]]       # or qfe0, ...
#           netstat -iI hme0 [interval [count]]
#           se netstat.se [interval]               # SE Toolkit
#           se nx.se [interval]                    # SE Toolkit
#
# COPYRIGHT: Copyright (c) 2006 Brendan Gregg.
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#  (http://www.gnu.org/copyleft/gpl.html)
#
# Author: Brendan Gregg  [Sydney, Australia]
#
# 18-Jul-2004   Brendan Gregg   Created this.
# 07-Jan-2005       "      "    added saturation value.
# 07-Jan-2005       "      "    added summary style (from Peter Tribble).
# 23-Jan-2006       "      "    Tweaked style.
# 11-Aug-2006       "      "    Improved output neatness.
# 30-Sep-2006       "      "    Added loopback, tweaked output.
 
use strict;
use Getopt::Std;
use Sun::Solaris::Kstat;
my $Kstat = Sun::Solaris::Kstat->new();
 
 
#
#  Process command line args
#
usage() if defined $ARGV[0] and $ARGV[0] eq "--help";
getopts('hi:sz') or usage();
usage() if defined $main::opt_h;
my $STYLE  = defined $main::opt_s ? $main::opt_s : 0;
my $SKIPZERO  = defined $main::opt_z ? $main::opt_z : 0;
 
# process [interval [count]],
my ($interval, $loop_max);
if (defined $ARGV[0]) {
    $interval = $ARGV[0];
    $loop_max = defined $ARGV[1] ? $ARGV[1] : 2**32;
    usage() if $interval == 0;
}
else {
    $interval = 1;
    $loop_max = 1; 
}
 
# check for -i,
my %NetworkOnly;             # network interfaces to print
my $NETWORKONLY = 0;         # match on network interfaces
if (defined $main::opt_i) {
    foreach my $net (split /,/, $main::opt_i) {
        $NetworkOnly{$net} = 1;
    }
    $NETWORKONLY = 1;
}
 
# globals,
my $loop = 0;                # current loop number
my $PAGESIZE = 20;           # max lines per header
my $line = $PAGESIZE;        # counter for lines printed
my %NetworkNames;            # Kstat network interfaces
my %NetworkData;             # network interface data
my %NetworkDataOld;          # network interface data
$main::opt_h = 0;
$| = 1;                      # autoflush
 
### Determine network interfaces
unless (find_nets()) {
    if ($NETWORKONLY) {
        print STDERR "ERROR1: $main::opt_i matched no network interfaces.\n";
    }
    else {
        print STDERR "ERROR1: No network interfaces found!\n";
    }
    exit 1;
}
 
 
#
#  Main
#
while (1) {
 
    ### Print Header
    if ($line >= $PAGESIZE) {
        if ($STYLE == 0) {
            printf "%8s %7s %7s %7s %7s %7s %7s %7s %7s %6s\n",
                   "Time", "Int", "rKB/s", "wKB/s", "rPk/s", "wPk/s", "rAvs",
                   "wAvs", "%Util", "Sat";
        }
        elsif ($STYLE == 1) {
            printf "%8s %8s %14s %14s\n", "Time", "Int", "rKB/s", "wKB/s";
        }
 
        $line = 0;
    }
 
    ### Get new data
    my (@NetworkData) = fetch_net_data();
 
    foreach my $network_data (@NetworkData) {
 
        ### Extract values
        my ($int, $rbytes, $wbytes, $rpackets, $wpackets, $speed, $sat, $time)
            = split /:/, $network_data;
 
        ### Retrieve old values
        my ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets, $old_sat,
            $old_time);
        if (defined $NetworkDataOld{$int}) {
            ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets,
             $old_sat, $old_time) = split /:/, $NetworkDataOld{$int};
        }
        else {
            $old_rbytes = $old_wbytes = $old_rpackets = $old_wpackets
                = $old_sat = $old_time = 0;
        }
 
        #
        #  Calculate statistics
        #
 
        # delta time
        my $tdiff = $time - $old_time;
 
        # per second values
        my $rbps = ($rbytes - $old_rbytes) / $tdiff;
        my $wbps = ($wbytes - $old_wbytes) / $tdiff;
        my $rkps = $rbps / 1024;
        my $wkps = $wbps / 1024;
        my $rpps = ($rpackets - $old_rpackets) / $tdiff;
        my $wpps = ($wpackets - $old_wpackets) / $tdiff;
        my $ravs = $rpps > 0 ? $rbps / $rpps : 0;
        my $wavs = $wpps > 0 ? $wbps / $wpps : 0;
 
        # skip zero lines if asked
        next if $SKIPZERO and ($rbps + $wbps) == 0;
 
        # % utilisation
        my $util;
        if ($speed > 0) {
            # the following has a mysterious "800", it is 100 
            # for the % conversion, and 8 for bytes2bits.
            $util = ($rbps + $wbps) * 800 / $speed;
            $util = 100 if $util > 100;
        }
        else {
            $util = 0;
        }
 
        # saturation per sec
        my $sats = ($sat - $old_sat) / $tdiff;
 
        #
        #  Print statistics
        #
        if ($rbps ne "") {
            my @Time = localtime();
 
            if ($STYLE == 0) {
                printf "%02d:%02d:%02d %7s ",
                       $Time[2], $Time[1], $Time[0], $int;
                print_neat($rkps);
                print_neat($wkps);
                print_neat($rpps);
                print_neat($wpps);
                print_neat($ravs);
                print_neat($wavs);
                printf "%7.2f %6.2f\n", $util, $sats;
            }
            elsif ($STYLE == 1) {
                printf "%02d:%02d:%02d %8s %14.3f %14.3f\n",
                       $Time[2], $Time[1], $Time[0], $int, $rkps, $wkps;
            }
 
            $line++;
 
            # for multiple interfaces, always print the header
            $line += $PAGESIZE if @NetworkData > 1;
        }
 
        ### Store old values
        $NetworkDataOld{$int}
            = "$rbytes:$wbytes:$rpackets:$wpackets:$sat:$time";
    }
 
    ### Check for end
    last if ++$loop == $loop_max;
 
    ### Interval
    sleep $interval;
}
 
 
# find_nets - walk Kstat to discover network interfaces.
#
# This walks %Kstat and populates a %NetworkNames with discovered
# network interfaces.
#
sub find_nets {
    my $found = 0;
 
    ### Loop over all Kstat modules
    foreach my $module (keys %$Kstat) {
        my $Modules = $Kstat->{$module};
 
        foreach my $instance (keys %$Modules) {
            my $Instances = $Modules->{$instance};
 
            foreach my $name (keys %$Instances) {
 
                ### Skip interface if asked
                if ($NETWORKONLY) {
                    next unless $NetworkOnly{$name};
                }
 
                ### Skip if not the regular statistic set
                next unless $name =~ /^$module/;
 
                my $Names = $Instances->{$name};
 
                # Check this is a network device.
                # Matching on ifspeed has been more reliable than "class"
                # we also match loopback interfaces.
                if (defined $$Names{ifspeed} || $module eq "lo") {
 
                    ### Save network interface
                    $NetworkNames{$name} = $Names;
                    $found++;
                }
            }
        }
    }
 
    return $found;
}
 
# fetch - fetch Kstat data for the network interfaces.
#
# This uses the interfaces in %NetworkNames and returns useful Kstat data.
# The Kstat values used are rbytes64, obytes64, ipackets64, opackets64
# (or the 32 bit versions if the 64 bit values are not there).
#
sub fetch_net_data {
    my ($rbytes, $wbytes, $rpackets, $wpackets, $speed, $time);
    my @NetworkData = ();
 
    $Kstat->update();
 
    ### Loop over previously found network interfaces
    foreach my $name (keys %NetworkNames) {
        my $Names = $NetworkNames{$name};
 
        if (defined $$Names{opackets}) {
 
            ### Fetch write bytes
            if (defined $$Names{obytes64}) {
                $rbytes = $$Names{rbytes64};
                $wbytes = $$Names{obytes64};
            }
            elsif (defined $$Names{obytes}) {
                $rbytes = $$Names{rbytes};
                $wbytes = $$Names{obytes};
            } else {
                $rbytes = $wbytes = 0;
            }
 
            ### Fetch read bytes
            if (defined $$Names{opackets64}) {
                $rpackets = $$Names{ipackets64};
                $wpackets = $$Names{opackets64};
            }
            else {
                $rpackets = $$Names{ipackets};
                $wpackets = $$Names{opackets};
            }
 
            ### Fetch interface speed
            if (defined $$Names{ifspeed}) {
                $speed = $$Names{ifspeed};
            }
            else {
                # if we can't fetch the speed, print the
                # %Util as 0.0 . To do this we,
                $speed = 2 ** 48; 
            }
 
            ### Determine saturation value
            my $sat = 0;
            if (defined $$Names{nocanput} or defined $$Names{norcvbuf}) {
                $sat += defined $$Names{defer} ? $$Names{defer} : 0;
                $sat += defined $$Names{nocanput} ? $$Names{nocanput} : 0;
                $sat += defined $$Names{norcvbuf} ? $$Names{norcvbuf} : 0;
                $sat += defined $$Names{noxmtbuf} ? $$Names{noxmtbuf} : 0;
            }
 
            ### use the last snaptime value,
            $time = $$Names{snaptime};
 
            ### store data
            push @NetworkData, "$name:$rbytes:$wbytes:" . 
             "$rpackets:$wpackets:$speed:$sat:$time";
        }
    }
 
    return @NetworkData;
}
 
# print_neat - print a float with decimal places if appropriate.
#
# This specifically keeps the width to 7 characters, if possible, plus
# a trailing space.
#
sub print_neat {
    my $num = shift;
    if ($num >= 100000) {
        printf "%7d ", $num;
    } elsif ($num >= 100) {
        printf "%7.1f ", $num;
    } else {
        printf "%7.2f ", $num;
    }
}
 
# usage - print usage and exit.
#
sub usage {
        print STDERR <