File Coverage

blib/lib/Test2/Plugin/MemUsage.pm
Criterion Covered Total %
statement 88 88 100.0
branch 56 62 90.3
condition 29 33 87.8
subroutine 15 15 100.0
pod 0 3 0.0
total 188 201 93.5


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