Created
July 20, 2010 14:36
-
-
Save res0nat0r/483036 to your computer and use it in GitHub Desktop.
nicstat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 <<END; | |
USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]] | |
eg, nicstat # print summary since boot | |
nicstat 1 # print continually every 1 second | |
nicstat 1 5 # print 5 times, every 1 second | |
nicstat -s # summary output | |
nicstat -i hme0 # print hme0 only | |
END | |
exit 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment