File Coverage

blib/lib/Linux/Proc/Maps.pm
Criterion Covered Total %
statement 40 62 64.5
branch 13 36 36.1
condition 4 11 36.3
subroutine 9 11 81.8
pod 4 4 100.0
total 70 124 56.4


line stmt bran cond sub pod time code
1             package Linux::Proc::Maps;
2             # ABSTRACT: Read and write /proc/[pid]/maps files
3             # KEYWORDS: linux proc procfs
4              
5 1     1   30865 use warnings;
  1         2  
  1         37  
6 1     1   4 use strict;
  1         2  
  1         61  
7              
8             our $VERSION = '0.001'; # VERSION
9              
10 1     1   7 use Carp qw(croak);
  1         3  
  1         80  
11 1     1   5 use Exporter qw(import);
  1         2  
  1         30  
12 1     1   494 use namespace::clean -except => [qw(import)];
  1         14584  
  1         7  
13              
14             our @EXPORT_OK = qw(read_maps write_maps parse_maps_single_line format_maps_single_line);
15              
16              
17             sub read_maps {
18 3 100   3 1 12432 my %args = @_ == 1 ? (pid => $_[0]) : @_;
19              
20 3         5 my $file = $args{file};
21              
22 3 100 66     27 if (!$file and my $pid = $args{pid}) {
23 2 50       11 if ($pid =~ /^\d+$/) {
24 0         0 require File::Spec::Functions;
25             my $procfs = $args{mnt} || $ENV{PERL_LINUX_PROC_MAPS_MOUNT} ||
26 0   0     0 File::Spec::Functions::catdir(File::Spec::Functions::rootdir(), 'proc');
27 0         0 $file = File::Spec::Functions::catfile($procfs, $pid, 'maps');
28             }
29             else {
30 2         10 $file = $args{pid};
31             }
32             }
33              
34 3 100       161 $file or croak 'Filename or PID required';
35 1 100   1   9 open(my $fh, '<:encoding(UTF-8)', $file) or croak "Open failed ($file): $!";
  1         2  
  1         5  
  2         50  
36              
37 1         208 my @regions;
38              
39 1         24 while (my $line = <$fh>) {
40 20         76 chomp $line;
41              
42 20         28 my $region = parse_maps_single_line($line);
43 20 50       34 next if !$region;
44              
45 20         69 push @regions, $region;
46             }
47              
48 1         19 return \@regions;
49             }
50              
51              
52             sub write_maps {
53 0 0   0 1 0 my $regions = shift or croak 'Regions required';
54 0         0 my %args = @_;
55              
56 0 0       0 ref $regions eq 'ARRAY' or croak 'Regions must be an arrayref';
57              
58 0         0 my $out = '';
59              
60 0         0 for my $region (@$regions) {
61 0         0 $out .= format_maps_single_line($region);
62             }
63              
64             # maybe print out the memory regions to a filehandle
65 0         0 my $fh = $args{fh};
66 0 0 0     0 if (!$fh and my $file = $args{file}) {
67 0 0       0 open($fh, '>:encoding(UTF-8)', $file) or croak "Open failed ($file): $!";
68             }
69 0 0       0 print $fh $out if $fh;
70              
71 0         0 return $out;
72             }
73              
74              
75             sub parse_maps_single_line {
76 24 50   24 1 3659 my $line = shift or croak 'Line from a maps file required';
77              
78 24         24 chomp $line;
79              
80 24         170 my ($addr1, $addr2, $read, $write, $exec, $shared, $offset, $device, $inode, $pathname) = $line =~ m{
81             ^
82             ([[:xdigit:]]+)-([[:xdigit:]]+)
83             \s+ ([r-])([w-])([x-])([sp])
84             \s+ ([[:xdigit:]]+)
85             \s+ ([[:xdigit:]]+:[[:xdigit:]]+)
86             \s+ (\d+)
87             (?: \s+ (.*))?
88             }x;
89              
90 24 100       49 return if !$addr1;
91              
92 1     1   723 no warnings 'portable'; # for hex() on 64-bit perls
  1         1  
  1         229  
93              
94             return {
95 23   100     173 address_start => hex($addr1),
96             address_end => hex($addr2),
97             read => 'r' eq $read,
98             write => 'w' eq $write,
99             execute => 'x' eq $exec,
100             shared => 's' eq $shared,
101             offset => hex($offset),
102             device => $device,
103             inode => $inode,
104             pathname => $pathname || '',
105             };
106             }
107              
108              
109             sub format_maps_single_line {
110 0 0   0 1   my $region = shift or croak 'Region required';
111              
112 0           my @args = @{$region}{qw(address_start address_end read write execute shared offset device inode)};
  0            
113 0 0         $args[2] = $args[2] ? 'r' : '-';
114 0 0         $args[3] = $args[3] ? 'w' : '-';
115 0 0         $args[4] = $args[4] ? 'x' : '-';
116 0 0         $args[5] = $args[5] ? 's' : 'p';
117              
118 0           return sprintf("%-72s %s\n", sprintf("%x-%x %s%s%s%s %08x %s %d", @args), $region->{pathname});
119             }
120              
121              
122             1;
123              
124             __END__