File Coverage

blib/lib/Sys/Statistics/Linux/ProcStats.pm
Criterion Covered Total %
statement 64 79 81.0
branch 17 30 56.6
condition 4 12 33.3
subroutine 10 11 90.9
pod 4 4 100.0
total 99 136 72.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Sys::Statistics::Linux::ProcStats - Collect linux process statistics.
4              
5             =head1 SYNOPSIS
6              
7             use Sys::Statistics::Linux::ProcStats;
8              
9             my $lxs = Sys::Statistics::Linux::ProcStats->new;
10             $lxs->init;
11             sleep 1;
12             my $stat = $lxs->get;
13              
14             Or
15              
16             my $lxs = Sys::Statistics::Linux::ProcStats->new(initfile => $file);
17             $lxs->init;
18             my $stat = $lxs->get;
19              
20             =head1 DESCRIPTION
21              
22             Sys::Statistics::Linux::ProcStats gathers process statistics from the virtual F filesystem (procfs).
23              
24             For more information read the documentation of the front-end module L.
25              
26             =head1 IMPORTANT
27              
28             I renamed key C to C!
29              
30             =head1 LOAD AVERAGE STATISTICS
31              
32             Generated by F and F.
33              
34             new - Number of new processes that were produced per second.
35             runqueue - The number of currently executing kernel scheduling entities (processes, threads).
36             count - The number of kernel scheduling entities that currently exist on the system (processes, threads).
37             blocked - Number of processes blocked waiting for I/O to complete (Linux 2.5.45 onwards).
38             running - Number of processes in runnable state (Linux 2.5.45 onwards).
39              
40             =head1 METHODS
41              
42             =head2 new()
43              
44             Call C to create a new object.
45              
46             my $lxs = Sys::Statistics::Linux::ProcStats->new;
47              
48             Maybe you want to store/load the initial statistics to/from a file:
49              
50             my $lxs = Sys::Statistics::Linux::ProcStats->new(initfile => '/tmp/procstats.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::ProcStats->new(
57             files => {
58             # This is the default
59             path => '/proc',
60             loadavg => 'loadavg',
61             stat => 'stat',
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::ProcStats;
106              
107 2     2   12 use strict;
  2         4  
  2         89  
108 2     2   14 use warnings;
  2         4  
  2         72  
109 2     2   12 use Carp qw(croak);
  2         3  
  2         128  
110 2     2   2190 use Time::HiRes;
  2         4520  
  2         10  
111              
112             our $VERSION = '0.20';
113              
114             sub new {
115 2     2 1 6 my $class = shift;
116 2 50       9 my $opts = ref($_[0]) ? shift : {@_};
117              
118 2         11 my %self = (
119             files => {
120             path => '/proc',
121             loadavg => 'loadavg',
122             stat => 'stat',
123             }
124             );
125              
126 2 50       11 if (defined $opts->{initfile}) {
127 0         0 require YAML::Syck;
128 0         0 $self{initfile} = $opts->{initfile};
129             }
130              
131 2         7 foreach my $file (keys %{ $opts->{files} }) {
  2         10  
132 0         0 $self{files}{$file} = $opts->{files}->{$file};
133             }
134              
135 2         15 return bless \%self, $class;
136             }
137              
138             sub init {
139 2     2 1 7 my $self = shift;
140              
141 2 50 33     24 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 2         18 $self->{time} = Time::HiRes::gettimeofday();
146 2         8 $self->{init} = $self->_load;
147             }
148             }
149              
150             sub get {
151 2     2 1 10 my $self = shift;
152 2         9 my $class = ref $self;
153              
154 2 50       17 if (!exists $self->{init}) {
155 0         0 croak "$class: there are no initial statistics defined";
156             }
157              
158 2         14 $self->{stats} = $self->_load;
159 2         15 $self->_deltas;
160              
161 2 50       11 if ($self->{initfile}) {
162 0         0 $self->{init}->{time} = $self->{time};
163 0         0 YAML::Syck::DumpFile($self->{initfile}, $self->{init});
164             }
165              
166 2         15 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 4     4   11 my $self = shift;
182 4         13 my $class = ref $self;
183 4         10 my $file = $self->{files};
184 4         18 my $lavg = $self->_procs;
185              
186 4 50       25 my $filename = $file->{path} ? "$file->{path}/$file->{loadavg}" : $file->{loadavg};
187 4 50       148 open my $fh, '<', $filename or croak "$class: unable to open $filename ($!)";
188 4         129 ($lavg->{runqueue}, $lavg->{count}) = (split m@/@, (split /\s+/, <$fh>)[3]);
189 4         48 close($fh);
190              
191 4         33 return $lavg;
192             }
193              
194             sub _procs {
195 4     4   9 my $self = shift;
196 4         11 my $class = ref $self;
197 4         10 my $file = $self->{files};
198 4         13 my %stat = ();
199              
200 4 50       31 my $filename = $file->{path} ? "$file->{path}/$file->{stat}" : $file->{stat};
201 4 50       349 open my $fh, '<', $filename or croak "$class: unable to open $filename ($!)";
202              
203 4         510 while (my $line = <$fh>) {
204 96 100       452 if ($line =~ /^processes\s+(\d+)/) {
    100          
205 4         36 $stat{new} = $1;
206             } elsif ($line =~ /^procs_(blocked|running)\s+(\d+)/) {
207 8         46 $stat{$1} = $2;
208             }
209             }
210              
211 4         169 close($fh);
212 4         30 return \%stat;
213             }
214              
215             sub _deltas {
216 2     2   12 my $self = shift;
217 2         7 my $class = ref $self;
218 2         7 my $istat = $self->{init};
219 2         7 my $lstat = $self->{stats};
220 2         17 my $time = Time::HiRes::gettimeofday();
221 2         56 my $delta = sprintf('%.2f', $time - $self->{time});
222 2         9 $self->{time} = $time;
223              
224 2 50 33     24 if (!defined $istat->{new} || !defined $lstat->{new}) {
225 0         0 croak "$class: not defined key found 'new'";
226             }
227 2 50 33     35 if ($istat->{new} !~ /^\d+\z/ || $lstat->{new} !~ /^\d+\z/) {
228 0         0 croak "$class: invalid value for key 'new'";
229             }
230              
231 2         8 my $new_init = $lstat->{new};
232              
233 2 50 33     43 if ($lstat->{new} == $istat->{new} || $istat->{new} > $lstat->{new}) {
    50          
234 0         0 $lstat->{new} = sprintf('%.2f', 0);
235             } elsif ($delta > 0) {
236 2         22 $lstat->{new} = sprintf('%.2f', ($new_init - $istat->{new}) / $delta );
237             } else {
238 0         0 $lstat->{new} = sprintf('%.2f', $new_init - $istat->{new});
239             }
240              
241 2         9 $istat->{new} = $new_init;
242             }
243              
244             1;