File Coverage

blib/lib/Sys/Statistics/Linux/PgSwStats.pm
Criterion Covered Total %
statement 62 78 79.4
branch 19 34 55.8
condition 5 12 41.6
subroutine 9 10 90.0
pod 4 4 100.0
total 99 138 71.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Sys::Statistics::Linux::PgSwStats - Collect linux paging and swapping statistics.
4              
5             =head1 SYNOPSIS
6              
7             use Sys::Statistics::Linux::PgSwStats;
8              
9             my $lxs = Sys::Statistics::Linux::PgSwStats->new;
10             $lxs->init;
11             sleep 1;
12             my $stat = $lxs->get;
13              
14             Or
15              
16             my $lxs = Sys::Statistics::Linux::PgSwStats->new(initfile => $file);
17             $lxs->init;
18             my $stat = $lxs->get;
19              
20             =head1 DESCRIPTION
21              
22             Sys::Statistics::Linux::PgSwStats gathers paging and swapping statistics from the virtual F filesystem (procfs).
23              
24             For more information read the documentation of the front-end module L.
25              
26             =head1 PAGING AND SWAPPING STATISTICS
27              
28             Generated by F or F.
29              
30             pgpgin - Number of pages the system has paged in from disk per second.
31             pgpgout - Number of pages the system has paged out to disk per second.
32             pswpin - Number of pages the system has swapped in from disk per second.
33             pswpout - Number of pages the system has swapped out to disk per second.
34              
35             The following statistics are only available by kernels from 2.6.
36              
37             pgfault - Number of page faults the system has made per second (minor + major).
38             pgmajfault - Number of major faults per second the system required loading a memory page from disk.
39              
40             =head1 METHODS
41              
42             =head2 new()
43              
44             Call C to create a new object.
45              
46             my $lxs = Sys::Statistics::Linux::PgSwStats->new;
47              
48             Maybe you want to store/load the initial statistics to/from a file:
49              
50             my $lxs = Sys::Statistics::Linux::PgSwStats->new(initfile => '/tmp/pgswstats.yml');
51              
52             If you set C it's not necessary to call sleep before C.
53              
54             It's also possible to set the path to the proc filesystem.
55              
56             Sys::Statistics::Linux::PgSwStats->new(
57             files => {
58             # This is the default
59             path => '/proc',
60             stat => 'stat',
61             vmstat => 'vmstat',
62             }
63             );
64              
65             =head2 init()
66              
67             Call C to initialize the statistics.
68              
69             $lxs->init;
70              
71             =head2 get()
72              
73             Call C to get the statistics. C returns the statistics as a hash reference.
74              
75             my $stat = $lxs->get;
76              
77             =head2 raw()
78              
79             Get raw values.
80              
81             =head1 EXPORTS
82              
83             No exports.
84              
85             =head1 SEE ALSO
86              
87             B
88              
89             =head1 REPORTING BUGS
90              
91             Please report all bugs to .
92              
93             =head1 AUTHOR
94              
95             Jonny Schulz .
96              
97             =head1 COPYRIGHT
98              
99             Copyright (c) 2006, 2007 by Jonny Schulz. All rights reserved.
100              
101             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
102              
103             =cut
104              
105             package Sys::Statistics::Linux::PgSwStats;
106              
107 1     1   6 use strict;
  1         2  
  1         39  
108 1     1   6 use warnings;
  1         2  
  1         39  
109 1     1   6 use Carp qw(croak);
  1         2  
  1         71  
110 1     1   1104 use Time::HiRes;
  1         2055  
  1         5  
111              
112             our $VERSION = '0.18';
113              
114             sub new {
115 1     1 1 2 my $class = shift;
116 1 50       4 my $opts = ref($_[0]) ? shift : {@_};
117              
118 1         6 my %self = (
119             files => {
120             path => '/proc',
121             stat => 'stat',
122             vmstat => 'vmstat',
123             }
124             );
125              
126 1 50       8 if (defined $opts->{initfile}) {
127 0         0 require YAML::Syck;
128 0         0 $self{initfile} = $opts->{initfile};
129             }
130              
131 1         2 foreach my $file (keys %{ $opts->{files} }) {
  1         4  
132 0         0 $self{files}{$file} = $opts->{files}->{$file};
133             }
134              
135 1         8 return bless \%self, $class;
136             }
137              
138             sub init {
139 1     1 1 2 my $self = shift;
140              
141 1 50 33     15 if ($self->{initfile} && -r $self->{initfile}) {
142 0         0 $self->{init} = YAML::Syck::LoadFile($self->{initfile});
143 0         0 $self->{time} = delete $self->{init}->{time};
144             } else {
145 1         8 $self->{time} = Time::HiRes::gettimeofday();
146 1         3 $self->{init} = $self->_load;
147             }
148             }
149              
150             sub get {
151 1     1 1 3 my $self = shift;
152 1         5 my $class = ref $self;
153              
154 1 50       7 if (!exists $self->{init}) {
155 0         0 croak "$class: there are no initial statistics defined";
156             }
157              
158 1         5 $self->{stats} = $self->_load;
159 1         5 $self->_deltas;
160              
161 1 50       5 if ($self->{initfile}) {
162 0         0 $self->{init}->{time} = $self->{time};
163 0         0 YAML::Syck::DumpFile($self->{initfile}, $self->{init});
164             }
165              
166 1         5 return $self->{stats};
167             }
168              
169             sub raw {
170 0     0 1 0 my $self = shift;
171 0         0 my $stat = $self->_load;
172              
173 0         0 return $stat;
174             }
175              
176             #
177             # private stuff
178             #
179              
180             sub _load {
181 2     2   5 my $self = shift;
182 2         7 my $class = ref $self;
183 2         5 my $file = $self->{files};
184 2         5 my %stats = ();
185              
186 2 50       12 my $filename = $file->{path} ? "$file->{path}/$file->{stat}" : $file->{stat};
187 2 50       117 open my $fh, '<', $filename or croak "$class: unable to open $filename ($!)";
188              
189 2         338 while (my $line = <$fh>) {
190 48 50       208 if ($line =~ /^page\s+(\d+)\s+(\d+)$/) {
    50          
191 0         0 @stats{qw(pgpgin pgpgout)} = ($1, $2);
192             } elsif ($line =~ /^swap\s+(\d+)\s+(\d+)$/) {
193 0         0 @stats{qw(pswpin pswpout)} = ($1, $2);
194             }
195             }
196              
197 2         27 close($fh);
198              
199             # if paging and swapping are not found in /proc/stat
200             # then let's try a look into /proc/vmstat (since 2.6)
201              
202 2 50       10 if (!defined $stats{pswpout}) {
203 2 50       12 my $filename = $file->{path} ? "$file->{path}/$file->{vmstat}" : $file->{vmstat};
204 2 50       69 open my $fh, '<', $filename or croak "$class: unable to open $filename ($!)";
205 2         115 while (my $line = <$fh>) {
206 226 100       836 next unless $line =~ /^(pgpgin|pgpgout|pswpin|pswpout|pgfault|pgmajfault)\s+(\d+)/;
207 12         92 $stats{$1} = $2;
208             }
209 2         24 close($fh);
210             }
211              
212 2         18 return \%stats;
213             }
214              
215             sub _deltas {
216 1     1   2 my $self = shift;
217 1         3 my $class = ref $self;
218 1         3 my $istat = $self->{init};
219 1         2 my $lstat = $self->{stats};
220 1         6 my $time = Time::HiRes::gettimeofday();
221 1         16 my $delta = sprintf('%.2f', $time - $self->{time});
222 1         3 $self->{time} = $time;
223              
224 1         4 while (my ($k, $v) = each %{$lstat}) {
  7         26  
225 6 50 33     33 if (!defined $istat->{$k} || !defined $lstat->{$k}) {
226 0         0 croak "$class: not defined key found '$k'";
227             }
228              
229 6 50 33     36 if ($v !~ /^\d+\z/ || $istat->{$k} !~ /^\d+\z/) {
230 0         0 croak "$class: invalid value for key '$k'";
231             }
232              
233 6 100 66     41 if ($lstat->{$k} == $istat->{$k} || $istat->{$k} > $lstat->{$k}) {
    50          
234 1         3 $lstat->{$k} = sprintf('%.2f', 0);
235             } elsif ($delta > 0) {
236 5         31 $lstat->{$k} = sprintf('%.2f', ($lstat->{$k} - $istat->{$k}) / $delta);
237             } else {
238 0         0 $lstat->{$k} = sprintf('%.2f', $lstat->{$k} - $istat->{$k});
239             }
240              
241 6         16 $istat->{$k} = $v;
242             }
243             }
244              
245             1;