File Coverage

blib/lib/P9Y/ProcessTable/Role/Table/ProcFS.pm
Criterion Covered Total %
statement 95 138 68.8
branch 23 60 38.3
condition 2 6 33.3
subroutine 12 13 92.3
pod 0 2 0.0
total 132 219 60.2


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