File Coverage

blib/lib/P9Y/ProcessTable/Role/Table/ProcFS.pm
Criterion Covered Total %
statement 91 134 67.9
branch 22 58 37.9
condition 2 6 33.3
subroutine 12 13 92.3
pod 0 2 0.0
total 127 213 59.6


line stmt bran cond sub pod time code
1             package P9Y::ProcessTable::Role::Table::ProcFS;
2              
3             our $AUTHORITY = 'cpan:BBYRD'; # AUTHORITY
4             our $VERSION = '1.06_01'; # VERSION
5              
6             #############################################################################
7             # Modules
8              
9             # use sanity;
10 3     3   26412 use strict qw(subs vars);
  3         5  
  3         111  
11 3     3   13 no strict 'refs';
  3         4  
  3         82  
12 3     3   12 use warnings FATAL => 'all';
  3         3  
  3         131  
13 3     3   12 no warnings qw(uninitialized);
  3         3  
  3         95  
14              
15 3     3   14 use Moo::Role;
  3         4  
  3         17  
16              
17             requires 'table';
18             requires 'process';
19              
20 3     3   928 use Path::Class;
  3         4  
  3         193  
21 3     3   13 use Config;
  3         4  
  3         113  
22 3     3   1493 use POSIX;
  3         12973  
  3         18  
23              
24 3     3   7456 use namespace::clean;
  3         5  
  3         28  
25 3     3   13185 no warnings 'uninitialized';
  3         6  
  3         4326  
26              
27             #############################################################################
28             # Methods
29              
30             sub list {
31 1     1 0 1 my $self = shift;
32              
33 1         2 my @list;
34 1         5 my $dir = dir('', 'proc');
35 1         141 while (my $pdir = $dir->next) {
36 72 100       8565 next unless ($pdir->is_dir);
37 24 100       73 next unless (-e $pdir->file('status'));
38 11 100       1619 next unless ($pdir->basename =~ /^\d+$/);
39              
40 10         84 push @list, $pdir->basename;
41             }
42              
43 1         65 return sort { $a <=> $b } @list;
  19         23  
44             }
45              
46             sub fields {
47 0 0   0 0 0 return $^O eq /solaris|sunos/i ?
48             ( qw/
49             pid uid gid euid egid ppid pgrp sess
50             cwd exe root cmdline
51             utime stime cutime cstime start time ctime
52             fname ttynum flags threads size rss pctcpu pctmem
53             / ) :
54             ( qw/
55             pid uid gid ppid pgrp sess
56             cwd exe root cmdline environ
57             minflt cminflt majflt cmajflt ttlflt cttlflt utime stime cutime cstime start time ctime
58             priority fname state ttynum flags threads size rss wchan cpuid
59             / );
60             }
61              
62             sub _process_hash {
63 13     13   19 my ($self, $pid) = @_;
64              
65 13         57 my $pdir = dir('', 'proc', $pid);
66 13 100       1137 return unless (-d $pdir);
67 12         412 my $hash = {
68             pid => $pid,
69             uid => $pdir->stat->uid,
70             gid => $pdir->stat->gid,
71             start => $pdir->stat->mtime, # not reliable
72             };
73              
74             # process links
75 12         3970 foreach my $ln (qw{cwd exe root}) {
76 36         1699 my $link = $pdir->file($ln);
77 36 50       1837 $hash->{$ln} = readlink $link if (-l $link);
78             }
79              
80             # process simple cats
81 12         675 foreach my $fn (qw{cmdline}) {
82 12         25 my $file = $pdir->file($fn);
83 12 50       490 next unless (-f $file);
84 12         369 $hash->{$fn} = $file->slurp;
85 12         1870 $hash->{$fn} =~ s/\0/ /g;
86 12         220 $hash->{$fn} =~ s/^\s+|\s+$//g;
87             }
88              
89             # process environment
90 12         34 my $env_file = $pdir->file('environ');
91 12 50       673 if (-f $env_file) {
92 12         427 my $data;
93 12         21 eval { $data = $env_file->slurp; }; # skip permission failures
  12         31  
94 12 100       5948 unless ($@) {
95 2         171 $data =~ s/^\0+|\0+$//g;
96 2         15 $hash->{environ} = { map { split /\=/, $_, 2 } grep { /\=/ } split /\0/, $data };
  44         74  
  44         44  
97             }
98             }
99              
100 12         61 my $clock_ticks = POSIX::sysconf( &POSIX::_SC_CLK_TCK );
101              
102             # start time is measured in the number of clock ticks since boot, so we need the boot time
103 12         19 my $boot_time;
104 12         34 my $uptime_file = file('', 'proc', 'uptime');
105 12 50       770 if ( -f $uptime_file ) {
106 12         436 my $time = time;
107 12         26 my $uptime = $uptime_file->slurp;
108 12 50       1570 $boot_time = $time - $1 if $uptime =~ /^([\d\.]+)/;
109             }
110              
111             # process main PID stats
112 12 50 33     45 if ( -f $pdir->file('status') and -f $pdir->file('statm') and -f $pdir->file('stat') ) {
    0 33        
    0          
113             ### Linux ###
114             # stat has more needed information than the friendier status, so we'll use that file instead
115              
116             # stat
117 12         2911 my $data = $pdir->file('stat')->slurp;
118 12         2396 my @data = split /\s+/, $data;
119              
120 12         75 my $states = {
121             R => 'run',
122             S => 'sleep',
123             D => 'disk sleep',
124             Z => 'defunct',
125             T => 'stop',
126             W => 'paging',
127             };
128              
129 12         140 my $stat_loc = [ qw(
130             pid fname state ppid pgrp sess ttynum . flags minflt cminflt majflt cmajflt utime stime cutime cstime priority . threads .
131             starttime size rss . . . . . . . . . . wchan . . . cpuid . . . . .
132             ) ];
133              
134 12         36 foreach my $i (0 .. @data - 1) {
135 540 100       655 next if $stat_loc->[$i] eq '.';
136 288 100       351 last if ($i >= @$stat_loc);
137 276         449 $hash->{ $stat_loc->[$i] } = $data[$i];
138             }
139              
140             # normalize clock ticks into seconds
141 12 50       32 if ($clock_ticks) {
142 12         77 $hash->{$_} /= $clock_ticks for (qw[ utime stime cutime cstime starttime ]);
143 12 50       35 $hash->{start} = $boot_time + $hash->{starttime} if $boot_time;
144             }
145 12         18 delete $hash->{starttime};
146              
147 12         73 $hash->{fname} =~ s/^\((.+)\)$/$1/;
148 12         24 $hash->{state} = $states->{ $hash->{state} };
149 12         24 $hash->{ time} = $hash->{ utime} + $hash->{ stime};
150 12         25 $hash->{ctime} = $hash->{cutime} + $hash->{cstime};
151              
152 12         38 $hash->{ ttlflt} = $hash->{ minflt} + $hash->{ majflt};
153 12         18 $hash->{cttlflt} = $hash->{cminflt} + $hash->{cmajflt};
154              
155 12         116 $hash->{rss} *= POSIX::sysconf( &POSIX::_SC_PAGESIZE );
156             }
157             elsif ($^O =~ /solaris|sunos/i) {
158             ### Solaris ###
159 0 0       0 my $ptr = $Config{longsize} >= 8 ? 'Q' : 'I';
160              
161 0         0 my $data = '';
162 0         0 eval { $data = $pdir->file('status')->slurp; }; # skip permission failures
  0         0  
163 0 0       0 if (length $data) {
164 0         0 my @data = unpack 'I[10]'.$ptr.'[4]I[12]CI[4]', $data;
165              
166             # 1 int pr_flags; /* flags (see below) */
167             # 2 int pr_nlwp; /* number of active lwps in the process */
168             # 3 int pr_nzomb; /* number of zombie lwps in the process */
169             # 4 pid_tpr_pid; /* process id */
170             # 5 pid_tpr_ppid; /* parent process id */
171             # 6 pid_tpr_pgid; /* process group id */
172             # 7 pid_tpr_sid; /* session id */
173             # 8 id_t pr_aslwpid; /* obsolete */
174             # 9 id_t pr_agentid; /* lwp-id of the agent lwp, if any */
175             # 10 sigset_t pr_sigpend; /* set of process pending signals */
176             # 11 uintptr_t pr_brkbase; /* virtual address of the process heap */
177             # 12 size_t pr_brksize; /* size of the process heap, in bytes */
178             # 13 uintptr_t pr_stkbase; /* virtual address of the process stack */
179             # 14 size_tpr_stksize; /* size of the process stack, in bytes */
180             #
181             # 15 timestruc_t pr_utime; /* process user cpu time */
182             # 17 timestruc_t pr_stime; /* process system cpu time */
183             # 19 timestruc_t pr_cutime; /* sum of children's user times */
184             # 21 timestruc_t pr_cstime; /* sum of children's system times */
185              
186             # some Solaris versions don't have pr_nzomb
187 0 0       0 if ($data[2] == $pid) {
188 0         0 @data = unpack 'I[9]'.$ptr.'[4]I[12]CI[4]', $data;
189 0         0 splice @data, 2, 0, (0);
190             }
191              
192 0         0 my $stat_loc = [ qw(
193             flags threads . pid ppid pgrp sess . . . . . . . utime . stime . cutime . cstime .
194             ) ];
195              
196 0         0 foreach my $i (0 .. @data - 1) {
197 0 0       0 next if $stat_loc->[$i] eq '.';
198 0 0       0 last if ($i >= @$stat_loc);
199 0         0 $hash->{ $stat_loc->[$i] } = $data[$i];
200             }
201              
202 0         0 $hash->{time} = $hash->{utime} + $hash->{stime};
203 0         0 $hash->{ctime} = $hash->{cutime} + $hash->{stime};
204             }
205              
206 0         0 $data = '';
207 0         0 eval { $data = $pdir->file('psinfo')->slurp; }; # skip permission failures
  0         0  
208 0 0       0 if (length $data) {
209 0         0 my @data = unpack 'I[11]'.$ptr.'[3]IS[2]I[6]A[16]A[80]I', $data;
210              
211             #define PRFNSZ 16 /* Maximum size of execed filename */
212             #define PRARGSZ 80 /* number of chars of arguments */
213              
214             # 1 int pr_flag; /* process flags (DEPRECATED: see below) */
215             # 2 int pr_nlwp; /* number of active lwps in the process */
216             # 3 int pr_nzomb; /* number of zombie lwps in the process */
217             # 4 pid_t pr_pid; /* process id */
218             # 5 pid_t pr_ppid; /* process id of parent */
219             # 6 pid_t pr_pgid; /* process id of process group leader */
220             # 7 pid_t pr_sid; /* session id */
221             # 8 uid_t pr_uid; /* real user id */
222             # 9 uid_t pr_euid; /* effective user id */
223             # 10 gid_t pr_gid; /* real group id */
224             # 11 gid_t pr_egid; /* effective group id */
225             # 12 uintptr_t pr_addr; /* address of process */
226             # 13 size_t pr_size; /* size of process image in Kbytes */
227             # 14 size_t pr_rssize; /* resident set size in Kbytes */
228             # 15 dev_t pr_ttydev; /* controlling tty device (or PRNODEV) */
229             # 16 ushort_t pr_pctcpu; /* % of recent cpu time used by all lwps */
230             # 17 ushort_t pr_pctmem; /* % of system memory used by process */
231             # 18 timestruc_t pr_start; /* process start time, from the epoch */
232             # 20 timestruc_t pr_time; /* cpu time for this process */
233             # 22 timestruc_t pr_ctime; /* cpu time for reaped children */
234             # 23 char pr_fname[PRFNSZ]; /* name of exec'ed file */
235             # 24 char pr_psargs[PRARGSZ]; /* initial characters of arg list */
236             # 25 int pr_wstat; /* if zombie, the wait() status */
237              
238             # some Solaris versions don't have pr_nzomb
239 0 0       0 if ($data[2] == $pid) {
240 0         0 @data = unpack 'I[10]'.$ptr.'[3]IS[2]I[6]A[16]A[80]I', $data;
241 0         0 splice @data, 2, 0, (0);
242             }
243              
244 0         0 my $psinfo_loc = [ qw(
245             . threads . pid ppid pgrp sess uid euid gid egid . size rss ttynum pctcpu pctmem start time ctime fname cmdline .
246             ) ];
247              
248 0         0 foreach my $i (0 .. @data - 1) {
249 0 0       0 next if $psinfo_loc->[$i] eq '.';
250 0 0       0 last if ($i >= @$psinfo_loc);
251 0         0 $hash->{ $psinfo_loc->[$i] } = $data[$i];
252             }
253              
254 0         0 $hash->{size} *= 1024;
255 0         0 $hash->{rss} *= 1024;
256             }
257             }
258             elsif ($^O =~ /dragonfly|bsd/i) {
259             ### Dragonfly ###
260              
261             # stat
262 0         0 my $data = $pdir->file('status')->slurp;
263 0         0 my @data = split /\s+/, $data;
264              
265 0         0 my $stat_loc = [ qw(
266             fname pid ppid pgrp sess ttynum flags start utime stime state euid
267             ) ];
268              
269 0         0 foreach my $i (0 .. @data - 1) {
270 0 0       0 next if $stat_loc->[$i] eq '.';
271 0 0       0 last if ($i >= @$stat_loc);
272 0         0 $hash->{ $stat_loc->[$i] } = $data[$i];
273             }
274              
275 0         0 $hash->{fname} =~ s/^\((.+)\)$/$1/;
276 0         0 ($hash->{euid}, $hash->{egid}) = split(/,/, $hash->{euid}, 3);
277 0         0 $hash->{$_} =~ s!\,!.! for qw[start utime stime];
278              
279             ### TODO: State normalization, like $states in the Linux block ###
280             #$hash->{state} = $states->{ $hash->{state} };
281              
282 0         0 $hash->{ time} = $hash->{ utime} + $hash->{ stime};
283             }
284              
285 12         99 return $hash;
286             }
287              
288             42;