Skip Menu |

This queue is for tickets about the Sys-Statistics-Linux CPAN distribution.

Report information
The Basics
Id: 86737
Status: open
Priority: 0/
Queue: Sys-Statistics-Linux

People
Owner: jschulz.cpan [...] bloonix.de
Requestors: thatsafunnyname [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.66
Fixed in: (no value)



Subject: PATCH To add support for /proc/<pid>/limits data to Sys/Statistics/Linux/Processes.pm
Hello Johnny and thank you for Sys-Statistics-Linux 0.66, I found myself needing to read the (u)limits for another process in a pure perl script (without XS). I added support for providing a 'limits' file to Sys/Statistics/Linux/Processes.pm (to read the limits file), but as this file is typically (I think) only readable by the self process or root I set the default to the empty string and only make the calls to populate the data if it is set to a true value. I also know the 'io' file has had permissions changed over linux kernels, and on recent RHEL6 can now also only be read by the self process and root, I know there is the option to not collect 'processes' data at the Sys::Statistics::Linux level, but I thought it might also be useful to be able to efficiently turn off attempts to read the 'io' file at the Sys::Statistics::Linux::Processes level, by setting the file to a false value. Rather than have the open calls be made to files that non root users can not read I changed _init() and _load() to only make the calls to _get_io() and _get_limits() if the $self->{files}->{*} are set. Hopefully this makes sense, the same could be done for the other files/directories, or a different approach could be to skip the open(dir) calls if the $file->{thing} is false, for example: sub _get_fd { my ($self, $pid) = @_; my $file = $self->{files}; my %stat = (); Show quoted text
>> if( $file->{fd} ){
if (opendir my $dh, "$file->{path}/$pid/$file->{fd}") { ... Show quoted text
>> }
so a consumer could set files => { fd => q{} } , and avoid the "opendir" system calls if they were not interested in collecting the fd data, this approach still incurs the cost of the method calls which may be significant if called frequently over many pids. Many thanks, Peter (Stig) Edwards Here is the patch for Sys-Statistics-Linux-0.66/lib/Sys/Statistics/Linux/Processes.pm @@ -82,6 +82,9 @@ Generated by F</proc/E<lt>pidE<gt>/io>. + Permissions on this file have changed with versions of the kernel, opt out from + trying to read the file by setting the file to a false value, files => { io => q{} } + rchar - Bytes read from storage (might have been from pagecache). wchar - Bytes written. syscr - Number of read syscalls. @@ -90,6 +93,19 @@ write_bytes - Bytes sent to the storage layer. cancelled_write_bytes - Refer to docs. +Generated by F</proc/E<lt>pidE<gt>/limits>. + + Often only readable by self and root, opt in to trying to read the file by setting + the file to 'limits', files => { limits => 'limits' } + + An array with ( soft_limit , hard_limit , units ) is provided for the limits listed. + This may vary between kernels. Some examples are: + + max_address_space - The maximum amount of virtual memory available to the shell + max_core_file_size - The maximum size of core files created + max_processes - The maximum number of processes available to a single user + max_open_files - The maximum number of open file descriptors + See Documentation/filesystems/proc.txt for more (from kernel 2.6.20) =head1 METHODS @@ -118,6 +134,7 @@ wchan => 'wchan', fd => 'fd', io => 'io', + limits => '', } ); @@ -195,6 +212,7 @@ wchan => 'wchan', fd => 'fd', io => 'io', + limits => '', }, ); @@ -259,6 +277,9 @@ $stats->{time} = Time::HiRes::gettimeofday(); + my $collecting_io = $file->{'io'}; + my $collecting_limits = $file->{'limits'}; + foreach my $pid (@$pids) { my $stat = $self->_get_stat($pid); @@ -266,7 +287,13 @@ foreach my $key (qw/minflt cminflt mayflt cmayflt utime stime cutime cstime sttime/) { $stats->{$pid}->{$key} = $stat->{$key}; } - $stats->{$pid}->{io} = $self->_get_io($pid); + + if( $collecting_io ){ + $stats->{$pid}->{io} = $self->_get_io($pid); + } + if( $collecting_limits ){ + $stats->{$pid}->{limits} = $self->_get_limits($pid); + } } } @@ -282,8 +309,17 @@ $stats->{time} = Time::HiRes::gettimeofday(); + my @keys = qw/statm stat owner cmdline wchan fd/; + if( $file->{'io'} ){ + push @keys,'io'; + } + + if( $file->{'limits'} ){ + push @keys,'limits'; + } + PID: foreach my $pid (@$pids) { - foreach my $key (qw/statm stat io owner cmdline wchan fd/) { + foreach my $key (@keys) { my $method = "_get_$key"; my $data = $self->$method($pid); @@ -527,6 +563,29 @@ return \%stat; } +sub _get_limits { + my ($self, $pid) = @_; + my $file = $self->{files}; + my %stat = (); + my ( $line , $limit ); + + if (open my $fh, '<', "$file->{path}/$pid/$file->{limits}") { + while ($line = <$fh>) { + if ($line =~ /^([Ma-z ]+[a-z]) +(\d+|unlimited) +(\d+|unlimited) +([a-z]*)/) { + $limit = $1; + $limit =~ tr/M /m_/; + # soft hard units + $stat{$limit} = [ $2 , $3 , $4 ]; + } + } + + close($fh); + } + + return \%stat; +} + + sub _get_fd { my ($self, $pid) = @_; my $file = $self->{files}; __END__OF__PATCH__ Here is an example strace on the open calls as non root user, the errors when providing io=>q{} are from trying to open /proc/<pid>/fd, while the code could check if the effective user is root or the owner of the PID this *could* break existing behaviour on some (older) kernels, so that was why I was thinking being able provide fd=>q{} might be a nice option, just to note that if you provide fd=>q{} now the errors from permission denied go away but the /proc/<pid>/ directory ends up being read. strace -e trace=open -c perl -Mblib -e 'use Sys::Statistics::Linux;$s=Sys::Statistics::Linux->new();$s->set( processes => {} );my $t=$s->get' > /dev/null % time seconds usecs/call calls errors syscall 100.00 0.000234 0 8465 2300 open strace -e trace=open -c perl -Mblib -e 'use Sys::Statistics::Linux;$s=Sys::Statistics::Linux->new();$s->set( processes => { files => { io=>q{} } } );my $t=$s->get' > /dev/null % time seconds usecs/call calls errors syscall 100.00 0.000129 0 6943 796 open
Sorry for the mistake in your name. s/Johnny/Jonny/