File Coverage

blib/lib/Sys/Statgrab.pm
Criterion Covered Total %
statement 27 69 39.1
branch 2 4 50.0
condition 0 24 0.0
subroutine 8 8 100.0
pod n/a
total 37 105 35.2


line stmt bran cond sub pod time code
1             package Sys::Statgrab;
2              
3             $VERSION = 0.01;
4              
5 1     1   29263 use strict;
  1         3  
  1         51  
6 1     1   6 use warnings;
  1         3  
  1         221  
7              
8 1         257 use constant STATGRAB_CONSTANTS => qw(
9             SG_ERROR_ASPRINTF
10             SG_ERROR_DEVSTAT_GETDEVS
11             SG_ERROR_DEVSTAT_SELECTDEVS
12             SG_ERROR_ENOENT
13             SG_ERROR_GETIFADDRS
14             SG_ERROR_GETMNTINFO
15             SG_ERROR_GETPAGESIZE
16             SG_ERROR_KSTAT_DATA_LOOKUP
17             SG_ERROR_KSTAT_LOOKUP
18             SG_ERROR_KSTAT_OPEN
19             SG_ERROR_KSTAT_READ
20             SG_ERROR_KVM_GETSWAPINFO
21             SG_ERROR_KVM_OPENFILES
22             SG_ERROR_MALLOC
23             SG_ERROR_NONE
24             SG_ERROR_OPEN
25             SG_ERROR_OPENDIR
26             SG_ERROR_PARSE
27             SG_ERROR_SETEGID
28             SG_ERROR_SETEUID
29             SG_ERROR_SETMNTENT
30             SG_ERROR_SOCKET
31             SG_ERROR_SWAPCTL
32             SG_ERROR_SYSCONF
33             SG_ERROR_SYSCTL
34             SG_ERROR_SYSCTLBYNAME
35             SG_ERROR_SYSCTLNAMETOMIB
36             SG_ERROR_UNAME
37             SG_ERROR_UNSUPPORTED
38             SG_ERROR_XSW_VER_MISMATCH
39             SG_IFACE_DUPLEX_FULL
40             SG_IFACE_DUPLEX_HALF
41             SG_IFACE_DUPLEX_UNKNOWN
42             SG_PROCESS_STATE_RUNNING
43             SG_PROCESS_STATE_SLEEPING
44             SG_PROCESS_STATE_STOPPED
45             SG_PROCESS_STATE_UNKNOWN
46             SG_PROCESS_STATE_ZOMBIE
47 1     1   7 );
  1         12  
48 1         86 use constant STATGRAB_BASE_FUNCTIONS => qw(
49             get_error drop_privileges
50             get_host_info
51             get_cpu_stats get_cpu_stats_diff get_cpu_percents
52             get_disk_io_stats get_disk_io_stats_diff
53             get_fs_stats
54             get_load_stats
55             get_mem_stats
56             get_swap_stats
57             get_network_io_stats get_network_io_stats_diff
58             get_network_iface_stats
59             get_page_stats get_page_stats_diff
60             get_user_stats
61             get_process_stats
62 1     1   6 );
  1         2  
63 1         64 use constant STATGRAB_SORT_FUNCTIONS => qw(
64             sort_procs_by_name
65             sort_procs_by_pid
66             sort_procs_by_uid
67             sort_procs_by_gid
68             sort_procs_by_size
69             sort_procs_by_res
70             sort_procs_by_cpu
71             sort_procs_by_time
72 1     1   7 );
  1         1  
73              
74             BEGIN {
75 1     1   5 use Exporter ();
  1         2  
  1         22  
76 1     1   6 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         1  
  1         1928  
77 1     1   22 @ISA = qw(Exporter);
78 1         21 @EXPORT = (STATGRAB_CONSTANTS, STATGRAB_BASE_FUNCTIONS, STATGRAB_SORT_FUNCTIONS);
79 1         9 @EXPORT_OK = (STATGRAB_CONSTANTS, STATGRAB_BASE_FUNCTIONS, STATGRAB_SORT_FUNCTIONS);
80 1         11 %EXPORT_TAGS = ( 'all' => [ STATGRAB_CONSTANTS, STATGRAB_BASE_FUNCTIONS, STATGRAB_SORT_FUNCTIONS ] );
81              
82 1 50       16 if ($^O eq 'cygwin') {
    50          
83 0         0 require Unix::Statgrab;
84 0         0 import Unix::Statgrab (STATGRAB_CONSTANTS);
85            
86             # Natively supported by Statgrab (>= 0.8) on cygwin
87 0         0 *drop_privileges = *drop_privileges = \&Unix::Statgrab::drop_privileges;
88 0         0 *get_host_info = *get_host_info = \&Unix::Statgrab::get_host_info;
89 0         0 *get_cpu_stats = *get_cpu_stats = \&Unix::Statgrab::get_cpu_stats;
90 0         0 *get_cpu_stats_diff = *get_cpu_stats_diff = \&Unix::Statgrab::get_cpu_stats_diff;
91 0         0 *get_cpu_percents = *get_cpu_percents = \&Unix::Statgrab::get_cpu_percents;
92 0         0 *get_mem_stats = *get_mem_stats = \&Unix::Statgrab::get_mem_stats;
93 0         0 *get_swap_stats = *get_swap_stats = \&Unix::Statgrab::get_swap_stats;
94 0         0 *get_page_stats = *get_page_stats = \&Unix::Statgrab::get_page_stats;
95 0         0 *get_page_stats_diff = *get_page_stats_diff = \&Unix::Statgrab::get_page_stats_diff;
96 0         0 *get_user_stats = *get_user_stats = \&Unix::Statgrab::get_user_stats;
97              
98             # Known as not supported by Statgrab (<= 0.13, at least) on cygwin
99 0   0     0 *get_disk_io_stats = *get_disk_io_stats = sub { return Unix::Statgrab::get_disk_io_stats(@_) || Sys::Statgrab::Cygwin::sg_disk_io_stats->new(); };
  0         0  
100 0   0     0 *get_disk_io_stats_diff = *get_disk_io_stats_diff = sub { return Unix::Statgrab::get_disk_io_stats_diff(@_) || Sys::Statgrab::Cygwin::sg_disk_io_stats->new('diff'); };
  0         0  
101 0   0     0 *get_fs_stats = *get_fs_stats = sub { return Unix::Statgrab::get_fs_stats(@_) || Sys::Statgrab::Cygwin::sg_fs_stats->new(); };
  0         0  
102 0   0     0 *get_load_stats = *get_load_stats = sub { return Unix::Statgrab::get_load_stats(@_) || Sys::Statgrab::Cygwin::sg_load_stats->new(); };
  0         0  
103 0   0     0 *get_network_io_stats = *get_network_io_stats = sub { return Unix::Statgrab::get_network_io_stats(@_) || Sys::Statgrab::Cygwin::sg_network_io_stats->new(); };
  0         0  
104 0   0     0 *get_network_io_stats_diff = *get_network_io_stats_diff = sub { return Unix::Statgrab::get_network_io_stats_diff(@_) || Sys::Statgrab::Cygwin::sg_network_io_stats->new('diff'); };
  0         0  
105 0   0     0 *get_network_iface_stats = *get_network_iface_stats = sub { return Unix::Statgrab::get_network_iface_stats(@_) || Sys::Statgrab::Cygwin::sg_network_iface_stats->new(); };
  0         0  
106 0   0     0 *get_process_stats = *get_process_stats = sub { return Unix::Statgrab::get_process_stats(@_) || Sys::Statgrab::Cygwin::sg_process_stats->new(); };
  0         0  
107            
108 0         0 *sort_procs_by_name = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_name;
109 0         0 *sort_procs_by_pid = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_pid;
110 0         0 *sort_procs_by_uid = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_uid;
111 0         0 *sort_procs_by_gid = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_gid;
112 0         0 *sort_procs_by_size = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_size;
113 0         0 *sort_procs_by_res = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_res;
114 0         0 *sort_procs_by_cpu = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_cpu;
115 0         0 *sort_procs_by_time = \&Sys::Statgrab::Cygwin::sg_process_stats::_sort_procs_by_time;
116             }
117             elsif ($^O eq 'MSWin32')
118             {
119 0         0 die "$^O not yet supported by ".__PACKAGE__;
120 0         0 require Unix::Statgrab;
121 0         0 import Unix::Statgrab (STATGRAB_CONSTANTS);
122 0         0 require Win32::Process::Info;
123 0         0 import Unix::Statgrab (STATGRAB_CONSTANTS);
124              
125             }
126             else {
127 1         2761 require Unix::Statgrab;
128 0           import Unix::Statgrab (STATGRAB_CONSTANTS, STATGRAB_BASE_FUNCTIONS, STATGRAB_SORT_FUNCTIONS);
129             }
130             }
131              
132              
133             package Sys::Statgrab::Cygwin::sg_disk_io_stats;
134             use strict;
135             use warnings;
136              
137             sub new {
138             my $type = shift;
139             my $class = ref($type) || $type;
140             my $diff = 1 if shift;
141            
142             warn "get_disk_io_stats not yet implemented";
143             return undef;
144             }
145              
146             package Sys::Statgrab::Cygwin::sg_fs_stats;
147             use strict;
148             use warnings;
149              
150             sub new {
151             my $type = shift;
152             my $class = ref($type) || $type;
153            
154             warn "get_fs_stats not yet implemented";
155             return undef;
156             }
157              
158             package Sys::Statgrab::Cygwin::sg_load_stats;
159             use strict;
160             use warnings;
161              
162             sub new {
163             my $type = shift;
164             my $class = ref($type) || $type;
165            
166             warn "get_load_stats not yet implemented";
167             return undef;
168             }
169              
170             package Sys::Statgrab::Cygwin::sg_network_io_stats;
171             use strict;
172             use warnings;
173              
174             sub new {
175             my $type = shift;
176             my $class = ref($type) || $type;
177             my $diff = 1 if shift;
178            
179             warn "get_network_io_stats not yet implemented";
180             return undef;
181             }
182              
183             package Sys::Statgrab::Cygwin::sg_network_iface_stats;
184             use strict;
185             use warnings;
186              
187             sub new {
188             my $type = shift;
189             my $class = ref($type) || $type;
190            
191             warn "get_network_iface_stats not yet implemented";
192             return undef;
193             }
194              
195             package Sys::Statgrab::Cygwin::sg_process_stats;
196             use strict;
197             use warnings;
198              
199             use constant SORT_METHOD_PREFIX => '_sort_procs_by_';
200             use constant SORT_METHODS => qw(
201             name
202             pid
203             uid
204             gid
205             size
206             res
207             cpu
208             time
209             );
210              
211             sub new {
212             my $type = shift;
213             my $class = ref($type) || $type;
214            
215             ### generate process stat objects ###
216             my @procs;
217             opendir(PROCDIR, '/proc') || die "Can't read dir /proc: $!";
218             push @procs, Sys::Statgrab::Cygwin::sg_process_stats::all_procs->new($_) foreach grep(/^\d+$/o, (readdir(PROCDIR)));
219             closedir PROCDIR;
220            
221             ### optimization for pcpu stat ###
222             my %cpu_map;
223             my @line;
224             if (open(IPCCMD, "procps -e -opid -opcpu |")) {
225             foreach my $l () {
226             $l =~ s/^\s+//o;
227             @line = split(/\s+/o, $l);
228             chomp @line;
229             $cpu_map{$line[0]} = $line[1];
230             }
231             close IPCCMD;
232             foreach my $proc (@procs) {
233             $proc->{cpu_percent} = $cpu_map{$proc->{pid}};
234             }
235             }
236             else {
237             warn "Can't obtain cpu_percent stats: Can't execute procps: $!";
238             }
239            
240             return bless(\@procs, $class);
241             }
242              
243             sub all_procs {
244             my $self = shift;
245             return @{$self};
246             }
247              
248             sub sort_by {
249             my $self = shift;
250             my $meth = shift;
251             die "Usage: ".__PACKAGE__."::sort_by(obj, meth)" unless defined $meth;
252            
253             my $regex = quotemeta $meth;
254             my $sort_method = SORT_METHOD_PREFIX.$meth;
255             @{$self} = sort $sort_method @{$self} if grep(/^$regex$/, SORT_METHODS);
256             return $self;
257             }
258              
259             sub _sort_procs_by_name ($$) { shift->proc_name cmp shift->proc_name }
260             sub _sort_procs_by_pid ($$) { shift->pid <=> shift->pid }
261             sub _sort_procs_by_uid ($$) { shift->uid <=> shift->uid }
262             sub _sort_procs_by_gid ($$) { shift->gid <=> shift->gid }
263             sub _sort_procs_by_size ($$) { shift->proc_size <=> shift->proc_size }
264             sub _sort_procs_by_res ($$) { shift->proc_resident <=> shift->proc_resident }
265             sub _sort_procs_by_cpu ($$) { shift->cpu_percent <=> shift->cpu_percent }
266             sub _sort_procs_by_time ($$) { shift->time_spent <=> shift->time_spent }
267              
268             package Sys::Statgrab::Cygwin::sg_process_stats::all_procs;
269             use strict;
270             use warnings;
271              
272             our $AUTOLOAD;
273              
274             sub new {
275             my $type = shift;
276             my $class = ref($type) || $type;
277             my $pid = shift;
278            
279             my $o = bless(\$pid, $class);
280             my $self = {
281             proc_name => $o->_proc_name,
282             proc_title => $o->_proc_title,
283             pid => $o->_pid,
284             parent_pid => $o->_parent_pid,
285             pgid => $o->_pgid,
286             uid => $o->_uid,
287             euid => $o->_euid,
288             gid => $o->_gid,
289             egid => $o->_egid,
290             proc_size => $o->_proc_size,
291             proc_resident => $o->_proc_resident,
292             time_spent => $o->_time_spent,
293             cpu_percent => undef, #efficiently calculated later by caller class
294             nice => $o->_nice,
295             state => $o->_state,
296             };
297             return bless($self, $class);
298             }
299              
300             sub AUTOLOAD { #read-only
301             my $self = shift;
302             my $class = ref($self) || $self;
303             my $name = $AUTOLOAD;
304             $name =~ s/.*://o; # strip fully-qualified portion
305             no strict 'refs';
306             Carp::confess "Can't access '$name' field in class $class" unless (exists $self->{$name});
307             return $self->{$name};
308             }
309             sub DESTROY {}
310             sub CLONE {}
311              
312             sub _proc_name {
313             my $self = shift;
314             return Sys::Statgrab::Util::get_hash_value("/proc/${$self}/status", ':', 'Name', 1);
315             }
316             sub _proc_title {
317             my $self = shift;
318             my $cmdline = Sys::Statgrab::Util::get_value("/proc/${$self}/cmdline");
319             $cmdline =~ s/\x0/ /go;
320             return $cmdline;
321             }
322             sub _pid {
323             my $self = shift;
324             return ${$self};
325             }
326             sub _parent_pid {
327             my $self = shift;
328             return Sys::Statgrab::Util::get_value("/proc/${$self}/ppid");
329             }
330             sub _pgid {
331             my $self = shift;
332             return Sys::Statgrab::Util::get_value("/proc/${$self}/pgid");
333             }
334             sub _uid {
335             my $self = shift;
336             return Sys::Statgrab::Util::get_value("/proc/${$self}/uid");
337             }
338             sub _euid { return _uid(@_); } #bug: is euid accessable on cygwin?
339             sub _gid {
340             my $self = shift;
341             return Sys::Statgrab::Util::get_value("/proc/${$self}/gid");
342             }
343             sub _egid { return _gid(@_); } #bug: is egid accessable on cygwin?
344             sub _proc_size { #note: approximated to nearest unit (default is kB)
345             my $self = shift;
346             my @size = split(/ /, Sys::Statgrab::Util::get_hash_value("/proc/${$self}/status", ':', 'VmSize', 1));
347             return $size[0] * (lc $size[1] eq 'kb' ? 1000 : lc $size[1] eq 'mb' ? 1000000 : lc $size[1] eq 'gb' ? 1000000000 : 1);
348             }
349             sub _proc_resident { #note: approximated to nearest unit (default is kB)
350             my $self = shift;
351             my @rss = split(/ /, Sys::Statgrab::Util::get_hash_value("/proc/${$self}/status", ':', 'VmRSS', 1));
352             return $rss[0] * (lc $rss[1] eq 'kb' ? 1000 : lc $rss[1] eq 'mb' ? 1000000 : lc $rss[1] eq 'gb' ? 1000000000 : 1);
353             }
354             sub _time_spent {
355             my $self = shift;
356             my ($utime, $stime) = (split(/ /, Sys::Statgrab::Util::get_value("/proc/${$self}/stat")))[13..14];
357             return $utime + $stime;
358             }
359             sub _cpu_percent { #note: using more efficient method
360             my $self = shift;
361             return Sys::Statgrab::Util::get_procps_value(${$self}, 'pcpu');
362             return undef;
363             }
364             sub _nice {
365             my $self = shift;
366             # return Sys::Statgrab::Util::get_procps_value(${$self}, 'ni');
367             return (split(/ /, Sys::Statgrab::Util::get_value("/proc/${$self}/stat")))[18];
368             }
369             sub _state {
370             my $self = shift;
371             my $state = (split(/ /, Sys::Statgrab::Util::get_value("/proc/${$self}/stat")))[2];
372             # return $state eq 'R' ? Sys::Statgrab::SG_PROCESS_STATE_RUNNING
373             # : $state eq 'S' ? Sys::Statgrab::SG_PROCESS_STATE_SLEEPING
374             # : $state eq 'T' ? Sys::Statgrab::SG_PROCESS_STATE_STOPPED
375             # : $state eq 'Z' ? Sys::Statgrab::SG_PROCESS_STATE_ZOMBIE #kludge: not sure if this is the correct letter
376             # : Sys::Statgrab::SG_PROCESS_STATE_UNKNOWN
377             }
378              
379             package Sys::Statgrab::Util;
380              
381             use strict;
382             use warnings;
383              
384             sub get_value ($) {
385             my $file = shift;
386             open(PROCFILE, "<$file") || die "Can't open file $file";
387             my @line = ;
388             close PROCFILE;
389             chomp @line;
390             return $line[0];
391             }
392              
393             sub get_array_index ($$$) {
394             my $file = shift;
395             my $delimiter = shift;
396             my $idx = shift;
397             open(PROCFILE, "<$file") || die "Can't open file $file";
398             my @line = split(/\s*$delimiter\s*/, );
399             close PROCFILE;
400             chomp @line;
401             return $line[$idx];
402             }
403              
404             sub get_hash_value ($$$;$) {
405             my $file = shift;
406             my $delimiter = shift;
407             my $key = shift;
408             my $idx = shift;
409             open(PROCFILE, "<$file") || die "Can't open file $file";
410             my @line;
411             while (@line = split(/\s*$delimiter\s*/, )) {
412             last if $line[0] eq $key;
413             }
414             chomp @line;
415             close PROCFILE;
416             return defined $idx ? $line[$idx] : wantarray ? @line : $line[1];
417             }
418              
419             sub get_procps_value ($$) {
420             my $pid = shift;
421             my $format = shift;
422             if (open(IPCCMD, "procps -e -opid -o$format |")) {
423             my @line;
424             foreach my $l () {
425             $l =~ s/^\s+//o;
426             @line = split(/\s+/o, $l);
427             last if $line[0] eq $pid;
428             }
429             close IPCCMD;
430             chomp @line;
431             return $line[1];
432             }
433             else {
434             warn "Can't obtain cpu_percent stats: Can't execute procps: $!";
435             return undef;
436             }
437             }
438              
439             1;
440              
441             __END__