File Coverage

blib/lib/Test2/Plugin/MemUsage.pm
Criterion Covered Total %
statement 85 85 100.0
branch 50 56 89.2
condition 35 40 87.5
subroutine 15 15 100.0
pod 0 3 0.0
total 185 199 92.9


line stmt bran cond sub pod time code
1             package Test2::Plugin::MemUsage;
2 6     6   901255 use strict;
  6         14  
  6         197  
3 6     6   27 use warnings;
  6         12  
  6         400  
4              
5             our $VERSION = '0.002004';
6              
7 6     6   32 use Test2::API qw/test2_add_callback_exit/;
  6         11  
  6         9029  
8              
9             my $ADDED_HOOK = 0;
10              
11             sub import {
12 7 100   7   150 return if $ADDED_HOOK++;
13              
14 6         30 test2_add_callback_exit(\&send_mem_event);
15             }
16              
17 9     9 0 212873 sub proc_file { "/proc/$$/status" }
18              
19 16     16   168 sub _empty_mem { (peak => ['NA', ''], size => ['NA', ''], rss => ['NA', '']) }
20              
21             sub _collect_proc {
22 13     13   197675 my $file = proc_file();
23 13 100       349 return unless -e $file;
24              
25 12         24 my $stats;
26             {
27 12 50       20 open(my $fh, '<', $file) or warn("Could not open file '$file' (<): $!"), return;
  12         382  
28 12         53 local $/;
29 12         393 $stats = <$fh>;
30 12 50       166 close($fh) or warn "Could not close file '$file': $!";
31             }
32              
33 12 100       30 return unless $stats;
34              
35 11         30 my %mem = _empty_mem();
36 11 100       121 $mem{peak} = [$1, $2] if $stats =~ m/VmPeak:\s+(\d+)\s+(\S+)/;
37 11 100       63 $mem{size} = [$1, $2] if $stats =~ m/VmSize:\s+(\d+)\s+(\S+)/;
38 11 100       68 $mem{rss} = [$1, $2] if $stats =~ m/VmRSS:\s+(\d+)\s+(\S+)/;
39              
40 11         49 return %mem;
41             }
42              
43 3     3 0 17755 sub ps_command { "ps -o rss=,vsz= -p $$" }
44              
45             sub _collect_ps {
46 4     4   175361 my $cmd = ps_command();
47 4         27280 my $out = `$cmd 2>/dev/null`;
48 4 100 66     116 return unless defined $out && length $out;
49              
50 3 100       110 my ($rss, $vsz) = $out =~ /^\s*(\d+)\s+(\d+)\s*$/m
51             or return;
52              
53 2         40 my %mem = _empty_mem();
54 2         28 $mem{rss} = [$rss, 'kB'];
55 2         21 $mem{size} = [$vsz, 'kB'];
56 2         36 return %mem;
57             }
58              
59             sub _collect_win32 {
60 4 50   4   152848 return unless eval { require Win32::Process::Memory; 1 };
  4         23  
  4         31  
61              
62 4 100       5 my $info = eval { Win32::Process::Memory::GetProcessMemoryInfo($$) }
  4         8  
63             or return;
64              
65 2   50     13 my $rss = $info->{WorkingSetSize} || 0;
66 2   100     8 my $peak = $info->{PeakWorkingSetSize} || 0;
67 2   100     8 my $size = $info->{PagefileUsage} || 0;
68              
69 2         4 my %mem = _empty_mem();
70 2 50       10 $mem{rss} = [int($rss / 1024), 'kB'] if $rss;
71 2 100       7 $mem{peak} = [int($peak / 1024), 'kB'] if $peak;
72 2 100       4 $mem{size} = [int($size / 1024), 'kB'] if $size;
73 2         7 return %mem;
74             }
75              
76             sub _collector_for_os {
77 19   66 19   181805 my $os = shift // $^O;
78 19 100 100     98 return \&_collect_proc if $os eq 'linux' || $os eq 'cygwin' || $os eq 'gnukfreebsd';
      100        
79 10 100 100     73 return \&_collect_ps if $os eq 'darwin' || $os =~ /bsd$/
      100        
      100        
      100        
80             || $os eq 'solaris' || $os eq 'aix' || $os eq 'hpux';
81 3 100       8 return \&_collect_win32 if $os eq 'MSWin32';
82 2         7 return undef;
83             }
84              
85             sub _maxrss_kb {
86 7 50   7   162052 return unless eval { require BSD::Resource; 1 };
  7         53  
  7         24  
87 7 100       20 my @ru = BSD::Resource::getrusage(BSD::Resource::RUSAGE_SELF()) or return;
88 6         82 my $maxrss = $ru[2];
89 6 100 66     32 return unless defined $maxrss && $maxrss > 0;
90 5 100       34 return $^O eq 'darwin' ? int($maxrss / 1024) : $maxrss;
91             }
92              
93             sub _augment_peak {
94 10     10   12256 my %mem = @_;
95 10 100 66     81 return %mem if !exists $mem{peak} || $mem{peak}->[0] ne 'NA';
96              
97 3   100     8 my $kb = _maxrss_kb() // return %mem;
98 2         10 $mem{peak} = [$kb, 'kB'];
99 2         9 return %mem;
100             }
101              
102             sub collect_mem {
103 8     8   4652 my $c = _collector_for_os();
104 8 100       44 my %mem = $c ? $c->() : ();
105              
106 8 100       37 unless (%mem) {
107 2   100     8 my $kb = _maxrss_kb() // return ();
108 1         28 %mem = _empty_mem();
109 1         5 $mem{peak} = [$kb, 'kB'];
110 1         7 return %mem;
111             }
112              
113 6         25 return _augment_peak(%mem);
114             }
115              
116             sub send_mem_event {
117 8     8 0 34208 my ($ctx, $real, $new) = @_;
118              
119 8         29 my %mem = collect_mem();
120 8 100       72 return unless %mem;
121 7 100       23 return unless grep { $_->[0] ne 'NA' } values %mem;
  21         89  
122              
123 6         34 $mem{details} = "rss: $mem{rss}->[0]$mem{rss}->[1]\nsize: $mem{size}->[0]$mem{size}->[1]\npeak: $mem{peak}->[0]$mem{peak}->[1]";
124              
125             $ctx->send_ev2(
126             memory => \%mem,
127             about => {package => __PACKAGE__, details => $mem{details}},
128             info => [{tag => 'MEMORY', details => $mem{details}}],
129              
130             harness_job_fields => [
131             map {
132 6         67 my $k = $_;
  18         28  
133 18         37 my ($v, $u) = @{$mem{$k}};
  18         34  
134             +{
135 18 50       127 name => "mem_$k",
136             details => "$v$u",
137             data => {value => ($v eq 'NA' ? undef : $v + 0), units => $u},
138             };
139             } qw/rss size peak/,
140             ],
141             );
142             }
143              
144             1;
145              
146             __END__