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.07'; # VERSION
5              
6             #############################################################################
7             # Modules
8              
9             # use sanity;
10 3     3   42829 use strict qw(subs vars);
  3         9  
  3         125  
11 3     3   18 no strict 'refs';
  3         7  
  3         86  
12 3     3   16 use warnings FATAL => 'all';
  3         6  
  3         147  
13 3     3   16 no warnings qw(uninitialized);
  3         7  
  3         103  
14              
15 3     3   17 use Moo::Role;
  3         13  
  3         21  
16              
17             requires 'table';
18             requires 'process';
19              
20 3     3   1388 use Path::Class;
  3         7  
  3         236  
21 3     3   17 use Config;
  3         6  
  3         145  
22 3     3   3458 use POSIX;
  3         20771  
  3         31  
23              
24 3     3   13565 use namespace::clean;
  3         9  
  3         43  
25 3     3   27250 no warnings 'uninitialized';
  3         9  
  3         7315  
26              
27             #############################################################################
28             # Methods
29              
30             sub list {
31 1     1 0 3 my $self = shift;
32              
33 1         2 my @list;
34 1         6 my $dir = dir('', 'proc');
35 1         214 while (my $pdir = $dir->next) {
36 71 100       14363 next unless ($pdir->is_dir);
37 24 100       190 next unless (-e $pdir->file('status'));
38 11 100       1489 next unless ($pdir->basename =~ /^\d+$/);
39              
40 10         85 push @list, $pdir->basename;
41             }
42              
43 1         98 return sort { $a <=> $b } @list;
  19         31  
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   38 my ($self, $pid) = @_;
64              
65 13         88 my $pdir = dir('', 'proc', $pid);
66 13 100       1780 return unless (-d $pdir);
67 12         825 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         6588 foreach my $ln (qw{cwd exe root}) {
76 36         2744 my $link = $pdir->file($ln);
77 36 50       21533 $hash->{$ln} = readlink $link if (-l $link);
78             }
79              
80             # process simple cats
81 12         1208 foreach my $fn (qw{cmdline}) {
82 12         52 my $file = $pdir->file($fn);
83 12 50       1330 next unless (-f $file);
84 12         601 $hash->{$fn} = $file->slurp;
85 12         3087 $hash->{$fn} =~ s/\0/ /g;
86 12         467 $hash->{$fn} =~ s/^\s+|\s+$//g;
87             }
88              
89             # process environment
90 12         58 my $env_file = $pdir->file('environ');
91 12 50       1068 if (-f $env_file) {
92 12         557 my $data;
93 12         33 eval { $data = $env_file->slurp; }; # skip permission failures
  12         46  
94 12 100       13078 unless ($@) {
95 2         318 $data =~ s/^\0+|\0+$//g;
96 2         20 $hash->{environ} = { map { split /\=/, $_, 2 } grep { /\=/ } split /\0/, $data };
  44         121  
  44         89  
97             }
98             }
99              
100 12         115 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         20 my $boot_time;
104 12         61 my $uptime_file = file('', 'proc', 'uptime');
105 12 50       1085 if ( -f $uptime_file ) {
106 12         657 my $time = time;
107 12         53 my $uptime = $uptime_file->slurp;
108 12 50       3200 $boot_time = $time - $1 if $uptime =~ /^([\d\.]+)/;
109             }
110              
111             # process main PID stats
112 12 50 33     67 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         14210 my $data = $pdir->file('stat')->slurp;
118 12         3725 my @data = split /\s+/, $data;
119              
120 12         124 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         250 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         41 foreach my $i (0 .. @data - 1) {
135 540 100       1157 next if $stat_loc->[$i] eq '.';
136 288 100       555 last if ($i >= @$stat_loc);
137 276         867 $hash->{ $stat_loc->[$i] } = $data[$i];
138             }
139              
140             # normalize clock ticks into seconds
141 12 50       35 if ($clock_ticks) {
142 12         110 $hash->{$_} /= $clock_ticks for (qw[ utime stime cutime cstime starttime ]);
143 12 50       58 $hash->{start} = $boot_time + $hash->{starttime} if $boot_time;
144             }
145 12         27 delete $hash->{starttime};
146              
147 12         86 $hash->{fname} =~ s/^\((.+)\)$/$1/;
148 12         45 $hash->{state} = $states->{ $hash->{state} };
149 12         48 $hash->{ time} = $hash->{ utime} + $hash->{ stime};
150 12         51 $hash->{ctime} = $hash->{cutime} + $hash->{cstime};
151              
152 12         61 $hash->{ ttlflt} = $hash->{ minflt} + $hash->{ majflt};
153 12         51 $hash->{cttlflt} = $hash->{cminflt} + $hash->{cmajflt};
154              
155 12         184 $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         202 return $hash;
286             }
287              
288             42;