File Coverage

blib/lib/Linux/GetPidstat/Reader.pm
Criterion Covered Total %
statement 62 64 96.8
branch 17 18 94.4
condition 5 6 83.3
subroutine 11 12 91.6
pod 0 3 0.0
total 95 103 92.2


line stmt bran cond sub pod time code
1             package Linux::GetPidstat::Reader;
2 14     14   80799 use 5.008001;
  14         42  
3 14     14   71 use strict;
  14         29  
  14         256  
4 14     14   82 use warnings;
  14         26  
  14         336  
5              
6 14     14   110 use Carp;
  14         32  
  14         759  
7 14     14   634 use Capture::Tiny qw/capture/;
  14         34071  
  14         626  
8 14     14   1517 use Path::Tiny qw/path/;
  14         24373  
  14         6713  
9              
10             sub new {
11 54     54 0 26896 my ( $class, %opt ) = @_;
12 54         248 bless \%opt, $class;
13             }
14              
15             sub get_program_pid_mapping {
16 53     53 0 4106 my $self = shift;
17              
18 53         631 my $pid_dir = path($self->{pid_dir});
19              
20 53         1843 my @program_pid_mapping;
21 53         273 for my $pid_file ($pid_dir->children) {
22             # Skip processing if there are no more files after directory scanning
23 105 50       11233 next unless -e $pid_file;
24              
25 105         2089 my $pid = $pid_file->slurp;
26             # Skip processing if it could not read anything from the file
27 105 100       17272 next unless length($pid);
28              
29 104         265 chomp($pid);
30 104 100       331 unless (_is_valid_pid($pid)) {
31 26         55 next;
32             }
33              
34 78         190 my @pids;
35 78         222 push @pids, $pid;
36              
37 78 100       305 if ($self->{include_child}) {
38 76         394 my $child_pids = $self->search_child_pids($pid);
39 76         312 push @pids, @$child_pids;
40             }
41              
42 78         603 push @program_pid_mapping, {
43             program_name => $pid_file->basename,
44             pids => \@pids,
45             };
46             }
47              
48 53         2462 return \@program_pid_mapping;
49             }
50              
51             sub search_child_pids {
52 76     76 0 217 my ($self, $pid) = @_;
53 76         408 my $command = _command_search_child_pids($pid);
54 76     76   4476 my ($stdout, $stderr, $exit) = capture { system $command };
  76         273512  
55              
56 76 100 66     63613 if (length $stderr or $exit != 0) {
57 1         5 chomp ($stderr);
58 1         130 carp "Failed a command: $command, stdout=$stdout, stderr=$stderr, exit=$exit";
59             }
60 76 100       322 unless (length $stdout) {
61 1         7 return [];
62             }
63              
64 75         135 my @child_pids;
65              
66 75         918 my @lines = split '\n', $stdout;
67 75         201 for (@lines) {
68 1925         5075 while (/[^}]\((\d+)\)/g) {
69 260         806 my $child_pid = $1;
70 260 100       796 next if $child_pid == $pid;
71              
72             # TODO: Remove the limit.
73             ## FIXME: Replace calling pidstat with reading /proc manually
74 185         397 my $max = $self->{max_child_limit};
75 185 100 100     424 if ($max && $max <= scalar @child_pids) {
76 2         256 carp "Stop searching child pids. max_child_limit is too little. pid=$pid";
77 2         99 last;
78             }
79 183         617 push @child_pids, $child_pid;
80             }
81             }
82 75         569 return \@child_pids;
83             }
84              
85             # for mock in tests
86             sub _command_search_child_pids {
87 0     0   0 my $pid = shift;
88 0         0 return "pstree -pn $pid";
89             }
90              
91             sub _is_valid_pid {
92 104     104   253 my $pid = shift;
93 104 100       858 unless ($pid =~ /^[0-9]+$/) {
94 26         2157 carp "invalid pid: $pid";
95 26         1191 return 0;
96             }
97 78         252 return 1;
98             }
99              
100             1;
101             __END__