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 <