File: //proc/self/root/usr/local/bin/vpsstat
#!/usr/bin/perl -w
#
# $Id: vpsstat,v 1.13 2010/05/06 13:52:38 vzcvs Exp $
#
# Copyright (C) 2004-2008 by ServInt Corporation
# All rights reserved.
#
# Author: Matt Loschert <loschert@servint.com>
#
use strict;
use Getopt::Std ();
use constant DEFAULT_TIMEOUT => 5; ## default seconds between polls
use constant PROCFILE => '/proc/user_beancounters';
use constant PRINT_FMT => "| %-12s | %10s | %10s | %10s | %10s | %8s |\n";
use constant DIV_FMT => "+" . join("+", "-" x 14, "-" x 12, "-" x 12,
"-" x 12, "-" x 12, "-" x 10) . "+\n";
use constant DESCRIP_FMT => "%-12s - %s\n";
use constant NA_STR => 'n/a';
use constant HUGE_VAL => 1024 ** 4; ## 1 TB
use constant SLM_MEM_KEY => 'all memory';
my @DATA_TYPES = ('held', 'maxheld', 'barrier', 'limit', 'failcnt');
my @FIELD_NAMES = ("Resource", "Current", "Recent Max", "Barrier", "Limit", "Failures");
my $RESOURCE_ORDER = [['Primary Parameters',SLM_MEM_KEY,'vmguarpages','oomguarpages','privvmpages','physpages'],
['Secondary Parameters','kmemsize','tcpsndbuf','tcprcvbuf','othersockbuf',
'dgramrcvbuf','shmpages','lockedpages'],
['Auxiliary Parameters','numproc','numtcpsock','numothersock','numfile',
'numflock','numpty','numiptent']];
my $SHORT_ORDER = [['',SLM_MEM_KEY,'vmguarpages','oomguarpages','privvmpages',
'numproc','numtcpsock','numothersock','numfile']];
my $RESOURCE_TYPES = {
&SLM_MEM_KEY => [[1,0,1,0,0], 'bytes', 'SLM total memory utilization'],
'vmguarpages' => [[0,0,1,0,1], '4KB pages', 'Memory allocation guarantee'],
'oomguarpages' => [[1,1,1,0,1], '4KB pages', 'Memory over-commitment kill guarantee'],
'privvmpages' => [[1,1,1,1,1], '4KB pages', 'Memory allocation limit'],
'physpages' => [[1,1,0,0,1], '4KB pages', 'Process memory usage (accounting only)'],
'shmpages' => [[1,1,0,1,1], '4KB pages', 'Size of shared memory'],
'lockedpages' => [[1,1,1,1,1], '4KB pages', 'Unswappable process memory (via mlock(2))'],
'kmemsize' => [[1,1,1,1,1], 'bytes', 'Size of unswappable kernel memory'],
'tcpsndbuf' => [[1,1,1,1,1], 'bytes', 'Size of TCP send buffers'],
'tcprcvbuf' => [[1,1,1,1,1], 'bytes', 'Size of TCP receive buffers'],
'othersockbuf' => [[1,1,1,1,1], 'bytes', 'Size of non-TCP/UDP socket buffers'],
'dgramrcvbuf' => [[1,1,1,1,1], 'bytes', 'Size of datagram receive buffers (e.g. UDP)'],
'dcachesize' => [[1,1,1,1,1], 'bytes', "Size of locked 'dentry' and 'inode' structures"],
'numproc' => [[1,1,0,1,1], '', 'Number of processes and threads'],
'numfile' => [[1,1,0,1,1], '', 'Number of open files'],
'numflock' => [[1,1,1,1,1], '', 'Number of file locks'],
'numiptent' => [[1,1,0,1,1], '', 'Number of firewall rules'],
'numpty' => [[1,1,0,1,1], '', 'Number of pseudo-terminals'],
'numtcpsock' => [[1,1,0,1,1], '', 'Number of TCP sockets'],
'numothersock' => [[1,1,0,1,1], '', 'Number of non-TCP sockets'],
'numsiginfo' => [[1,1,0,1,1], '', "Number of 'siginfo' structures"],
};
main();
exit;
sub main
{
my %options;
Getopt::Std::getopts('sD1t:', \%options);
my $one_poll = $options{'1'};
my $short_view = $options{'s'} ? 1 : 0;
my $no_descrip = $options{'D'};
if (defined $options{'t'}) {
usage("the -t and -1 flags are mutually exclusive") if $one_poll;
usage("timeout must be numeric") if $options{'t'} !~ /^\d+$/;
usage("timeout must be at least one second") if $options{'t'} < 1;
}
my $timeout = $options{'t'} || DEFAULT_TIMEOUT;
die("this script is only for use in a VE") if -e "/etc/sysconfig/vz-scripts";
die("this is not a Virtuozzo VE") unless -e PROCFILE;
if ($one_poll) {
my $stats = poll_counters();
print_stats($stats, $short_view, $no_descrip);
return;
}
for (;;) {
system("clear");
my $stats = poll_counters();
print_stats($stats, $short_view, $no_descrip);
sleep $timeout;
}
}
sub print_stats
{
my $stats = shift;
my $short_view = shift;
my $no_descrip = shift;
print DIV_FMT, sprintf(PRINT_FMT, @FIELD_NAMES), DIV_FMT;
my $order = $short_view ? $SHORT_ORDER : $RESOURCE_ORDER;
for my $section (@$order) {
my ($sec_name, @this_section) = @$section;
print "$sec_name\n", DIV_FMT if $sec_name;
for my $resource (@this_section) {
next unless $stats->{$resource};
my $data = $stats->{$resource};
my $dtype = $RESOURCE_TYPES->{$resource};
next unless $dtype;
my ($meaningful, $units) = @$dtype;
my @display = ($resource);
for (my $i = 0; defined $DATA_TYPES[$i]; $i++) {
unless ($meaningful->[$i]) {
push(@display, NA_STR);
next;
}
my $val = $data->{$DATA_TYPES[$i]};
if ($units eq '4KB pages' && $DATA_TYPES[$i] ne 'failcnt') {
$val *= 4096;
}
if ($val > HUGE_VAL) {
push(@display, NA_STR);
next;
}
if ($units && $DATA_TYPES[$i] ne 'failcnt') {
if ($units) {
$val = nice_size($val, 1);
} else {
$val .= " $units";
}
}
push(@display, $val) if defined $val;
}
printf PRINT_FMT, @display;
}
print DIV_FMT;
}
unless ($no_descrip) {
print "\nResource Descriptions\n\n";
for my $section (@$order) {
my ($sec_name, @this_section) = @$section;
for my $resource (@this_section) {
my $descrip = $RESOURCE_TYPES->{$resource}->[2];
printf DESCRIP_FMT, $resource, $descrip;
}
}
}
}
sub poll_counters
{
open(STATS, PROCFILE) or die "could not poll Virtuozzo resource statistics";
chomp(my @stats = <STATS>);
close STATS;
my $stats = {};
for (@stats) {
s|^\s+||;
my @data = split(/\s+/);
shift(@data) if scalar @data == 7;
next if scalar @data != 6;
my ($resource, $held, $maxheld, $barrier, $limit, $failcnt) = @data;
next if $resource eq 'resource' || $resource eq 'dummy';
$stats->{$data[0]}->{'held'} = $data[1];
$stats->{$data[0]}->{'maxheld'} = $data[2];
$stats->{$data[0]}->{'barrier'} = $data[3];
$stats->{$data[0]}->{'limit'} = $data[4];
$stats->{$data[0]}->{'failcnt'} = $data[5];
}
my @free = `/usr/bin/free -b`;
if (@free) {
my ($mem, $swap);
for (@free) {
chomp;
if (/^Mem:/) { $mem = $_; }
elsif (/^Swap:/) { $swap = $_; }
}
if ($mem && $swap && $swap =~ /^Swap:\s+0\s+0\s+0$/) {
my ($total, $used) = (split(/\s+/, $mem))[1,2];
$stats->{ &SLM_MEM_KEY }->{'held'} = $used;
$stats->{ &SLM_MEM_KEY }->{'barrier'} = $total;
}
}
return $stats;
}
sub nice_size
{
my $size = shift;
my $as_scalar = shift;
return unless defined $size;
my %sizes = (1024 ** 6 => 'EB',
1024 ** 5 => 'PB',
1024 ** 4 => 'TB',
1024 ** 3 => 'GB',
1024 ** 2 => 'MB',
1024 ** 1 => 'kB',
1024 ** 0 => ' B');
for (sort { $b <=> $a} keys %sizes) {
if ($size > $_) {
return sprintf("%.1f %s", $size / $_, $sizes{$_}) if $as_scalar;
return (sprintf("%.1f", $size / $_), $sizes{$_});
}
}
return '0 B';
}
sub usage
{
my $emsg = shift;
print STDERR "error: $emsg\n" if $emsg;
print STDERR <<EOF;
usage: $0 [-sD] [-1 | -t timeout]
options:
-s : show summary view instead of full view
-D : don't show resource descriptions
-1 : exit after one poll
-t : timeout in seconds between polls
EOF
exit 1;
}