#!/usr/bin/perl -w # # nicstat - print network traffic, Kb/s read and written. # Solaris 8+, Perl (Sun::Solaris::Kstat). # # "netstat -i" only gives a packet count, this program gives Kbytes. # # 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. # It should be impossible to reach 100% as there are overheads due to bus # negotiation and timing. # - 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] # # VERSION: 0.60 # # HISTORY # 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. # 22-Oct-2008 Stefan Parvu Modified for SDR/RRD collection. 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:szv') or usage(); usage() if defined $main::opt_h; revision() if defined $main::opt_v; 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; $main::opt_v = 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 %5s %7s %7s %7s %7s %7s %7s %7s %7s\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 = sprintf("%.2f", ($rbytes - $old_rbytes) / $tdiff); my $wbps = sprintf("%.2f", ($wbytes - $old_wbytes) / $tdiff); my $rkps = sprintf("%.2f", $rbps / 1024); my $wkps = sprintf("%.2f", $wbps / 1024); my $rpps = sprintf("%.2f", ($rpackets - $old_rpackets) / $tdiff); my $wpps = sprintf("%.2f", ($wpackets - $old_wpackets) / $tdiff); my $ravs = sprintf("%.2f", ($rpps > 0 ? $rbps / $rpps : 0)); my $wavs = sprintf("%.2f", ($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; } $util = sprintf ("%.2f", $util); # saturation per sec my $sats = sprintf ("%.2f", ($sat - $old_sat) / $tdiff); # # Print statistics # if ($rbps ne "") { # my @Time = localtime(); my $time = time(); if ($STYLE == 0) { #printf "10%d:%5s:%.2f:%.2f:%.2f:%.2f:%.2f:%.2f:%.2f:%.2f\n", # time, $int, $rkps, $wkps, $rpps, $wpps, $ravs, $wavs, $util, $sats; print time . ":$int:$rkps:$wkps:$rpps:$wpps:$ravs:$wavs:$util:$sats\n"; #printf "%02d:%02d:%02d %5s " . # "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", # $Time[2], $Time[1], $Time[0], $int, $rkps, $wkps, # $rpps, $wpps, $ravs, $wavs, $util, $sats; } elsif ($STYLE == 1) { #printf "%02d:%02d:%02d %8s %14.3f %14.3f\n", # $Time[2], $Time[1], $Time[0], $int, $rkps, $wkps; print time . ":$int:$rkps:$wkps\n"; } $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}; } my $Names = $Instances->{$name}; # Check this is a network device. # Matching on ifspeed has been more reliable than "class" if (defined $$Names{ifspeed}) { ### 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{obytes} or defined $$Names{obytes64}) { ### Fetch write bytes if (defined $$Names{obytes64}) { $rbytes = $$Names{rbytes64}; $wbytes = $$Names{obytes64}; } else { $rbytes = $$Names{rbytes}; $wbytes = $$Names{obytes}; } ### 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; } # usage - print usage and exit. # sub usage { print STDERR <