Author: pgollucci
Date: Fri Jun 23 23:57:52 2006
New Revision: 416874
URL: http://svn.apache.org/viewvc?rev=416874&view=rev
Log:
o overwrite relavent files with https://svn.urth.org/svn/Apache-SizeLimit
o update MANIFEST to be correct
o append Chnages list
Added:
perl/Apache-SizeLimit/trunk/MANIFEST.SKIP
perl/Apache-SizeLimit/trunk/t/TEST.PL
perl/Apache-SizeLimit/trunk/t/pod.t
perl/Apache-SizeLimit/trunk/t/response/
perl/Apache-SizeLimit/trunk/t/response/TestApache/
perl/Apache-SizeLimit/trunk/t/response/TestApache/basic.pm
Modified:
perl/Apache-SizeLimit/trunk/Changes
perl/Apache-SizeLimit/trunk/MANIFEST
perl/Apache-SizeLimit/trunk/Makefile.PL
perl/Apache-SizeLimit/trunk/lib/Apache/SizeLimit.pm
Modified: perl/Apache-SizeLimit/trunk/Changes
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/Changes?rev=416874&r1=416873&r2=416874&view=diff
==============================================================================
--- perl/Apache-SizeLimit/trunk/Changes (original)
+++ perl/Apache-SizeLimit/trunk/Changes Fri Jun 23 23:57:52 2006
@@ -1,6 +1,40 @@
-Revision history for Perl extension Apache-SizeLimit.
+=head1 NAME
-0.01 Fri Jun 23 22:46:53 2006
- - original version; created by h2xs 1.23 with options
- -X -A -n Apache-SizeLimit
+Changes - Apache::SizeLimit change logfile
+
+=head1 CHANGES
+
+=over 2
+
+=item 0.04-dev
+Copied from the mod_perl 1 core for an independent CPAN release.
+[Philip M. Gollucci <[EMAIL PROTECTED]>]
+
+Added support for using Linux::Smaps (on Linux only, obviously) to
+get much more accurate shared memory numbers on 2.6.x kernels. Taken
+from Apache2::SizeLimit.
+[Dave Rolsky <[EMAIL PROTECTED]>
+
+Added support for using Linux::Pid to get the parent pid on
+Linux. This fixes a long-standing bug that caused this module to never
+actually kill a process when using Perl 5.8.1+ on Linux.
+[Dave Rolsky <[EMAIL PROTECTED]>
+
+Lots of work on the docs.
+[Dave Rolsky <[EMAIL PROTECTED]>
+
+When calling C<setmax()>, C<setmin()>, and C<setmax_unshared()>,
+only add Apache::SizeLimit as a cleanup handler once, not once for
+each function call. Taken from Apache2::SizeLimit.
+[Dave Rolsky <[EMAIL PROTECTED]>
+
+Deprecated direct use of globals to set memory limits.
+[Dave Rolsky <[EMAIL PROTECTED]>
+
+=item 0.01 Fri Jun 23 22:46:53 2006
+original version; created by h2xs 1.23 with options
+-X -A -n Apache-SizeLimit
+[Philip M. Gollucci <[EMAIL PROTECTED]>]
+
+=back
Modified: perl/Apache-SizeLimit/trunk/MANIFEST
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/MANIFEST?rev=416874&r1=416873&r2=416874&view=diff
==============================================================================
--- perl/Apache-SizeLimit/trunk/MANIFEST (original)
+++ perl/Apache-SizeLimit/trunk/MANIFEST Fri Jun 23 23:57:52 2006
@@ -1,6 +1,13 @@
-Changes
-Makefile.PL
-MANIFEST
-README
-t/Apache-SizeLimit.t
-lib/Apache-SizeLimit.pm
+./Changes
+./INSTALL
+./LICENSE
+./MANIFEST
+./MANIFEST.SKIP
+./Makefile.PL
+./README
+./RELEASE
+./SUPPORT
+./lib/Apache/SizeLimit.pm
+./t/TEST.PL
+./t/pod.t
+./t/response/TestApache/basic.pm
Added: perl/Apache-SizeLimit/trunk/MANIFEST.SKIP
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/MANIFEST.SKIP?rev=416874&view=auto
==============================================================================
--- perl/Apache-SizeLimit/trunk/MANIFEST.SKIP (added)
+++ perl/Apache-SizeLimit/trunk/MANIFEST.SKIP Fri Jun 23 23:57:52 2006
@@ -0,0 +1,16 @@
+.*CVS.*
+^Makefile$
+\.\#.*
+~$
+MANIFEST.SKIP
+.*\.tar\.gz
+.bak$
+^blib/
+.*\.svn.*
+\#.*
+t/TEST.*
+t/apache.*
+t/conf.*
+t/htdocs.*
+t/logs.*
+t/response.*
\ No newline at end of file
Modified: perl/Apache-SizeLimit/trunk/Makefile.PL
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/Makefile.PL?rev=416874&r1=416873&r2=416874&view=diff
==============================================================================
--- perl/Apache-SizeLimit/trunk/Makefile.PL (original)
+++ perl/Apache-SizeLimit/trunk/Makefile.PL Fri Jun 23 23:57:52 2006
@@ -1,12 +1,37 @@
-use 5.008008;
+use strict;
+
+use Config;
use ExtUtils::MakeMaker;
-# See lib/ExtUtils/MakeMaker.pm for details of how to influence
-# the contents of the Makefile that is written.
-WriteMakefile(
- NAME => 'Apache-SizeLimit',
- VERSION_FROM => 'lib/Apache-SizeLimit.pm', # finds $VERSION
- PREREQ_PM => {}, # e.g., Module::Name => 1.1
- ($] >= 5.005 ? ## Add these new keywords supported since 5.005
- (ABSTRACT_FROM => 'lib/Apache-SizeLimit.pm', # retrieve abstract from
module
- AUTHOR => 'Philip M. Gollucci <[EMAIL PROTECTED]>') : ()),
-);
+
+my %prereqs = ( mod_perl => 0 );
+
+unless ( $ARGV[0] eq '--dist' ) {
+ if ( $Config{'osname'} eq 'linux' ) {
+ $prereqs{'Linux::Pid'} = 0;
+ if ( -e '/proc/self/smaps' ) {
+ $prereqs{'Linux::Smaps'} = 0;
+ }
+ }
+ elsif ( $Config{'osname'} =~ /(bsd|aix|darwin)/i ) {
+ $prereqs{'BSD::Resource'} = 0;
+ }
+ elsif ( $Config{'osname'} eq 'MSWin32' ) {
+ $prereqs{'Win32::API'} = 0;
+ }
+
+ if ( -f 't/apache/basic.t'
+ && eval { require Apache::TestMM } ) {
+ $prereqs{'Apache::Test'} = 0;
+
+ Apache::TestMM::filter_args();
+ Apache::TestMM::generate_script('t/TEST');
+ }
+}
+
+WriteMakefile( VERSION_FROM => "lib/Apache/SizeLimit.pm",
+ NAME => "Apache::SizeLimit",
+ PREREQ_PM => \%prereqs,
+ ABSTRACT_FROM => 'lib/Apache/SizeLimit.pm',
+ AUTHOR => 'Dave Rolsky, <[EMAIL PROTECTED]>',
+ clean => { FILES => 't/TEST t/logs t/htdocs t/conf' },
+ );
Modified: perl/Apache-SizeLimit/trunk/lib/Apache/SizeLimit.pm
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/lib/Apache/SizeLimit.pm?rev=416874&r1=416873&r2=416874&view=diff
==============================================================================
--- perl/Apache-SizeLimit/trunk/lib/Apache/SizeLimit.pm (original)
+++ perl/Apache-SizeLimit/trunk/lib/Apache/SizeLimit.pm Fri Jun 23 23:57:52 2006
@@ -1,248 +1,149 @@
package Apache::SizeLimit;
-=head1 NAME
-
-Apache::SizeLimit - Because size does matter.
-
-=head1 SYNOPSIS
-
-This module allows you to kill off Apache httpd processes if they grow too
-large. You can choose to set up the process size limiter to check the
-process size on every request:
-
- # in your startup.pl:
- use Apache::SizeLimit;
- # sizes are in KB
- $Apache::SizeLimit::MAX_PROCESS_SIZE = 10000; # 10MB
- $Apache::SizeLimit::MIN_SHARE_SIZE = 1000; # 1MB
- $Apache::SizeLimit::MAX_UNSHARED_SIZE = 12000; # 12MB
-
- # in your httpd.conf:
- PerlCleanupHandler Apache::SizeLimit
-
-Or you can just check those requests that are likely to get big, such as
-CGI requests. This way of checking is also easier for those who are mostly
-just running CGI.pm/Registry scripts:
-
- # in your CGI:
- use Apache::SizeLimit;
- &Apache::SizeLimit::setmax(10000); # Max size in KB
- &Apache::SizeLimit::setmin(1000); # Min share in KB
- &Apache::SizeLimit::setmax_unshared(12000); # Max unshared size in KB
-
-Since checking the process size can take a few system calls on some
-platforms (e.g. linux), you may want to only check the process size every
-N times. To do so, put this in your startup.pl or CGI:
-
- $Apache::SizeLimit::CHECK_EVERY_N_REQUESTS = 2;
-
-This will only check the process size every other time the process size
-checker is called.
-
-=head1 DESCRIPTION
-
-This module is highly platform dependent, please read the CAVEATS section.
-
-This module was written in response to questions on the mod_perl mailing
-list on how to tell the httpd process to exit if it gets too big.
-
-Actually there are two big reasons your httpd children will grow. First,
-it could have a bug that causes the process to increase in size
-dramatically, until your system starts swapping. Second, your process just
-does stuff that requires a lot of memory, and the more different kinds of
-requests your server handles, the larger the httpd processes grow over
-time.
-
-This module will not really help you with the first problem. For that you
-should probably look into Apache::Resource or some other means of setting a
-limit on the data size of your program. BSD-ish systems have setrlimit()
-which will croak your memory gobbling processes. However it is a little
-violent, terminating your process in mid-request.
-
-This module attempts to solve the second situation where your process
-slowly grows over time. The idea is to check the memory usage after every
-request, and if it exceeds a threshold, exit gracefully.
-
-By using this module, you should be able to discontinue using the Apache
-configuration directive B<MaxRequestsPerChild>, although for some folks,
-using both in combination does the job. Personally, I just use the
-technique shown in this module and set my MaxRequestsPerChild value to
-6000.
-
-=head1 SHARED MEMORY OPTIONS
-
-In addition to simply checking the total size of a process, this
-module can factor in how much of the memory used by the process is
-actually being shared by copy-on-write. If you don't understand how
-memory is shared in this way, take a look at the mod_perl Guide at
-http://perl.apache.org/guide/.
-
-You can take advantage of the shared memory information by setting a
-minimum shared size and/or a maximum unshared size. Experience on one
-heavily trafficked mod_perl site showed that setting maximum unshared
-size and leaving the others unset is the most effective policy. This
-is because it only kills off processes that are truly using too much
-physical RAM, allowing most processes to live longer and reducing the
-process churn rate.
-
-=head1 CAVEATS
-
-This module is platform dependent, since finding the size of a process
-is pretty different from OS to OS, and some platforms may not be
-supported. In particular, the limits on minimum shared memory and
-maximum shared memory are currently only supported on Linux and BSD.
-If you can contribute support for another OS, please do.
-
-Currently supported OSes:
-
-=over 4
-
-=item linux
-
-For linux we read the process size out of /proc/self/status. This is
-a little slow, but usually not too bad. If you are worried about
-performance, try only setting up the the exit handler inside CGIs
-(with the C<setmax> function), and see if the CHECK_EVERY_N_REQUESTS
-option is of benefit.
-
-=item solaris 2.6 and above
-
-For solaris we simply retrieve the size of /proc/self/as, which
-contains the address-space image of the process, and convert to KB.
-Shared memory calculations are not supported.
-
-NOTE: This is only known to work for solaris 2.6 and above. Evidently
-the /proc filesystem has changed between 2.5.1 and 2.6. Can anyone
-confirm or deny?
-
-=item *bsd*
-
-Uses BSD::Resource::getrusage() to determine process size. This is pretty
-efficient (a lot more efficient than reading it from the /proc fs anyway).
+use Apache::Constants qw(DECLINED OK);
+use Config;
+use strict;
+use vars qw(
+ $VERSION
+ $MAX_PROCESS_SIZE
+ $REQUEST_COUNT
+ $CHECK_EVERY_N_REQUESTS
+ $MIN_SHARE_SIZE
+ $MAX_UNSHARED_SIZE
+ $START_TIME
+ $IS_WIN32
+ $USE_SMAPS
+);
-=item AIX?
+$VERSION = '0.04';
+$CHECK_EVERY_N_REQUESTS = 1;
+$REQUEST_COUNT = 1;
+$MAX_PROCESS_SIZE = 0;
+$MIN_SHARE_SIZE = 0;
+$MAX_UNSHARED_SIZE = 0;
+$IS_WIN32 = 0;
+$USE_SMAPS = 1;
-Uses BSD::Resource::getrusage() to determine process size. Not sure if the
-shared memory calculations will work or not. AIX users?
+BEGIN {
-=item Win32
+ # decide at compile time how to check for a process' memory size.
+ if ( $Config{'osname'} eq 'solaris'
+ && $Config{'osvers'} >= 2.6 ) {
+ *check_size = \&_solaris_2_6_size_check;
+ *real_getppid = \&_perl_getppid;
+ }
+ elsif ( $Config{'osname'} eq 'linux' ) {
+ eval { require Linux::Pid }
+ or die "You must install Linux::Pid for Apache::SizeLimit to work
on your platform.";
-Uses Win32::API to access process memory information. Win32::API can be
-installed under ActiveState perl using the supplied ppm utility.
+ *real_getppid = \&_linux_getppid;
-=back
+ if ( eval { require Linux::Smaps } && Linux::Smaps->new($$) ) {
+ *check_size = \&_linux_smaps_size_check;
+ }
+ else {
+ $USE_SMAPS = 0;
+ *check_size = \&_linux_size_check;
+ }
+ }
+ elsif ( $Config{'osname'} =~ /(?:bsd|aix|darwin)/i ) {
-If your platform is not supported, and if you can tell me how to check for
-the size of a process under your OS (in KB), then I will add it to the list.
-The more portable/efficient the solution, the better, of course.
+ # will getrusage work on all BSDs? I should hope so.
+ eval "require BSD::Resource;"
+ or die
+ "You must install BSD::Resource for Apache::SizeLimit to work on
your platform.";
-=head1 TODO
+ *check_size = \&_bsd_size_check;
+ *real_getppid = \&_perl_getppid;
+ }
+ elsif ( $Config{'osname'} eq 'MSWin32' ) {
+ eval { require Win32::API }
+ or die
+ "You must install Win32::API for Apache::SizeLimit to work on your
platform.";
-Possibly provide a perl make/install so that the SizeLimit.pm is created at
-build time with only the code you need on your platform.
+ $IS_WIN32 = 1;
-If Apache was started in non-forking mode, should hitting the size limit
-cause the process to exit?
+ *check_size = \&_win32_size_check;
+ *real_getppid = \&_perl_getppid;
+ }
+ else {
+ die "Apache::SizeLimit is not implemented on your platform.";
+ }
+}
-=cut
+sub _linux_smaps_size_check {
+ goto &linux_size_check unless $USE_SMAPS;
-use Apache::Constants qw(:common);
-use Config;
-use strict;
-use vars qw($VERSION $HOW_BIG_IS_IT $MAX_PROCESS_SIZE
- $REQUEST_COUNT $CHECK_EVERY_N_REQUESTS
- $MIN_SHARE_SIZE $MAX_UNSHARED_SIZE $START_TIME $WIN32);
+ my $s = Linux::Smaps->new($$)->all;
+ return ($s->size, $s->shared_clean + $s->shared_dirty);
+}
-$VERSION = '0.03';
-$CHECK_EVERY_N_REQUESTS = 1;
-$REQUEST_COUNT = 1;
-$MAX_PROCESS_SIZE = 0;
-$MIN_SHARE_SIZE = 0;
-$MAX_UNSHARED_SIZE = 0;
+sub _linux_size_check {
+ my ( $size, $resident, $share ) = ( 0, 0, 0 );
-BEGIN {
- $WIN32 = 0;
- # decide at compile time how to check for a process' memory size.
- if (($Config{'osname'} eq 'solaris') &&
- ($Config{'osvers'} >= 2.6)) {
- $HOW_BIG_IS_IT = \&solaris_2_6_size_check;
- } elsif ($Config{'osname'} eq 'linux') {
- $HOW_BIG_IS_IT = \&linux_size_check;
- } elsif ($Config{'osname'} =~ /(bsd|aix|darwin)/i) {
- # will getrusage work on all BSDs? I should hope so.
- if (eval("require BSD::Resource;")) {
- $HOW_BIG_IS_IT = \&bsd_size_check;
- } else {
- die "you must install BSD::Resource for Apache::SizeLimit to work
on your platform.";
- }
- } elsif ($Config{'osname'} eq 'MSWin32') {
- $WIN32 = 1;
- if (eval("require Win32::API")) {
- $HOW_BIG_IS_IT = \&win32_size_check;
- } else {
- die "you must install Win32::API for Apache::SizeLimit to work on
your platform.";
- }
- } else {
- die "Apache::SizeLimit not implemented on your platform.";
+ if ( open my $fh, '<', '/proc/self/statm' ) {
+ ( $size, $resident, $share ) = split /\s/, scalar <$fh>;
+ close $fh;
}
-}
-
-# return process size (in KB)
-sub linux_size_check {
- my ($size, $resident, $share) = (0,0,0);
- local(*FH);
- if (open(FH, "</proc/self/statm")) {
- ($size, $resident, $share) = split(/\s/, scalar <FH>);
- close(FH);
- } else {
- &error_log("Fatal Error: couldn't access /proc/self/status");
+ else {
+ _error_log("Fatal Error: couldn't access /proc/self/status");
}
+
# linux on intel x86 has 4KB page size...
- return($size*4, $share*4);
+ return ( $size * 4, $share * 4 );
}
-sub solaris_2_6_size_check {
- my $size = -s "/proc/self/as" or
- &error_log("Fatal Error: /proc/self/as doesn't exist or is empty");
- $size = int($size/1024); # to get it into kb
- return($size, 0); # return 0 for share, to avoid undef warnings
+sub _solaris_2_6_size_check {
+ my $size = -s "/proc/self/as"
+ or _error_log("Fatal Error: /proc/self/as doesn't exist or is empty");
+ $size = int( $size / 1024 );
+
+ # return 0 for share, to avoid undef warnings
+ return ( $size, 0 );
}
-sub bsd_size_check {
- return (&BSD::Resource::getrusage())[2,3];
+sub _bsd_size_check {
+ return ( BSD::Resource::getrusage() )[ 2, 3 ];
}
-sub win32_size_check {
+sub _win32_size_check {
# get handle on current process
- my $GetCurrentProcess = new Win32::API('kernel32',
- 'GetCurrentProcess',
- [],
- 'I');
+ my $GetCurrentProcess = Win32::API->new(
+ 'kernel32',
+ 'GetCurrentProcess',
+ [],
+ 'I'
+ );
my $hProcess = $GetCurrentProcess->Call();
-
# memory usage is bundled up in ProcessMemoryCounters structure
# populated by GetProcessMemoryInfo() win32 call
- my $DWORD = 'B32'; # 32 bits
- my $SIZE_T = 'I'; # unsigned integer
+ my $DWORD = 'B32'; # 32 bits
+ my $SIZE_T = 'I'; # unsigned integer
# build a buffer structure to populate
my $pmem_struct = "$DWORD" x 2 . "$SIZE_T" x 8;
- my $pProcessMemoryCounters = pack($pmem_struct, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0);
-
+ my $pProcessMemoryCounters
+ = pack( $pmem_struct, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+
# GetProcessMemoryInfo is in "psapi.dll"
- my $GetProcessMemoryInfo = new Win32::API('psapi',
- 'GetProcessMemoryInfo',
- ['I', 'P', 'I'],
- 'I');
-
- my $bool = $GetProcessMemoryInfo->Call($hProcess,
- $pProcessMemoryCounters,
- length($pProcessMemoryCounters));
+ my $GetProcessMemoryInfo = new Win32::API(
+ 'psapi',
+ 'GetProcessMemoryInfo',
+ [ 'I', 'P', 'I' ],
+ 'I'
+ );
+
+ my $bool = $GetProcessMemoryInfo->Call(
+ $hProcess,
+ $pProcessMemoryCounters,
+ length($pProcessMemoryCounters)
+ );
# unpack ProcessMemoryCounters structure
- my ($cb,
- $PageFaultCount,
+ my (
+ $cb,
+ $PageFaultCount,
$PeakWorkingSetSize,
$WorkingSetSize,
$QuotaPeakPagedPoolUsage,
@@ -250,49 +151,56 @@
$QuotaPeakNonPagedPoolUsage,
$QuotaNonPagedPoolUsage,
$PagefileUsage,
- $PeakPagefileUsage) = unpack($pmem_struct, $pProcessMemoryCounters);
+ $PeakPagefileUsage
+ ) = unpack( $pmem_struct, $pProcessMemoryCounters );
# only care about peak working set size
- my $size = int($PeakWorkingSetSize / 1024);
+ my $size = int( $PeakWorkingSetSize / 1024 );
- return ($size, 0);
+ return ( $size, 0 );
}
+sub _perl_getppid { return getppid }
+sub _linux_getppid { return Linux::Pid::getppid() }
-sub exit_if_too_big {
+sub _exit_if_too_big {
my $r = shift;
- return DECLINED if ($CHECK_EVERY_N_REQUESTS &&
- ($REQUEST_COUNT++ % $CHECK_EVERY_N_REQUESTS));
+
+ return DECLINED
+ if ( $CHECK_EVERY_N_REQUESTS
+ && ( $REQUEST_COUNT++ % $CHECK_EVERY_N_REQUESTS ) );
$START_TIME ||= time;
- my($size, $share) = &$HOW_BIG_IS_IT();
+ my ( $size, $share ) = check_size();
+ my $unshared = $size - $share;
- if (($MAX_PROCESS_SIZE && $size > $MAX_PROCESS_SIZE)
- ||
- ($MIN_SHARE_SIZE && $share < $MIN_SHARE_SIZE)
- ||
- ($MAX_UNSHARED_SIZE && ($size - $share) > $MAX_UNSHARED_SIZE)) {
-
- # wake up! time to die.
- if ($WIN32 || (getppid > 1)) { # this is a child httpd
- my $e = time - $START_TIME;
- my $msg = "httpd process too big, exiting at SIZE=$size KB ";
- $msg .= " SHARE=$share KB " if ($share);
- $msg .= " REQUESTS=$REQUEST_COUNT LIFETIME=$e seconds";
- error_log($msg);
-
- if ($WIN32) {
- CORE::exit(-2); # child_terminate() is disabled in win32
Apache
- } else {
- $r->child_terminate();
- }
-
- } else { # this is the main httpd, whose parent is init?
- my $msg = "main process too big, SIZE=$size KB ";
- $msg .= " SHARE=$share KB" if ($share);
- error_log($msg);
- }
+ if ( ( $MAX_PROCESS_SIZE && $size > $MAX_PROCESS_SIZE )
+ || ( $MIN_SHARE_SIZE && $share < $MIN_SHARE_SIZE )
+ || ( $MAX_UNSHARED_SIZE && $unshared > $MAX_UNSHARED_SIZE ) ) {
+ # wake up! time to die.
+ if ( $IS_WIN32 || real_getppid() > 1 ) {
+ # this is a child httpd
+ my $e = time - $START_TIME;
+ my $msg = "httpd process too big, exiting at SIZE=$size KB";
+ $msg .= " SHARE=$share KB UNSHARED=$unshared" if ($share);
+ $msg .= " REQUESTS=$REQUEST_COUNT LIFETIME=$e seconds";
+ _error_log($msg);
+
+ if ($IS_WIN32) {
+ # child_terminate() is disabled in win32 Apache
+ CORE::exit(-2);
+ }
+ else {
+ $r->child_terminate();
+ }
+ }
+ else {
+ # this is the main httpd, whose parent is init?
+ my $msg = "main process too big, SIZE=$size KB ";
+ $msg .= " SHARE=$share KB" if ($share);
+ _error_log($msg);
+ }
}
return OK;
}
@@ -301,38 +209,345 @@
# to exit if the CGI causes the process to grow too big.
sub setmax {
$MAX_PROCESS_SIZE = shift;
- Apache->request->post_connection(\&exit_if_too_big);
+
+ _set_post_conn();
}
sub setmin {
$MIN_SHARE_SIZE = shift;
- Apache->request->post_connection(\&exit_if_too_big);
+
+ _set_post_conn();
}
sub setmax_unshared {
$MAX_UNSHARED_SIZE = shift;
- Apache->request->post_connection(\&exit_if_too_big);
+
+ _set_post_conn();
+}
+
+sub _set_post_conn {
+ my $r = Apache->request
+ or return;
+
+ return if $Apache::Server::Starting || $Apache::Server::ReStarting;
+ return if $r->pnotes('size_limit_cleanup');
+
+ $r->post_connection( \&_exit_if_too_big );
+ $r->pnotes( size_limit_cleanup => 1 );
}
sub handler {
my $r = shift || Apache->request;
- if ($r->is_main()) {
- # we want to operate in a cleanup handler
- if ($r->current_callback eq 'PerlCleanupHandler') {
- exit_if_too_big($r);
- } else {
- $r->post_connection(\&exit_if_too_big);
- }
+
+ return DECLINED unless $r->is_main();
+
+ # we want to operate in a cleanup handler
+ if ( $r->current_callback eq 'PerlCleanupHandler' ) {
+ return _exit_if_too_big($r);
+ }
+ else {
+ $r->post_connection( \&_exit_if_too_big );
}
- return(DECLINED);
+
+ return DECLINED;
}
-sub error_log {
- print STDERR "[", scalar(localtime(time)), "] ($$) Apache::SizeLimit @_\n";
+sub _error_log {
+ print STDERR "[", scalar( localtime(time) ),
+ "] ($$) Apache::SizeLimit @_\n";
}
1;
+
+__END__
+
+=head1 NAME
+
+Apache::SizeLimit - Because size does matter.
+
+=head1 SYNOPSIS
+
+ <Perl>
+ $Apache::SizeLimit::MAX_UNSHARED_SIZE = 120000; # 120MB
+ </Perl>
+
+ PerlCleanupHandler Apache::SizeLimit
+
+=head1 DESCRIPTION
+
+This module allows you to kill off Apache httpd processes if they grow
+too large. You can make the decision to kill a process based on its
+overall size, by setting a minimum limit on shared memory, or a
+maximum on unshared memory.
+
+You can set limits for each of these sizes, and if any limit is not
+met, the process will be killed.
+
+You can also limit the frequency that these sizes are checked so that
+this module only checks every N requests.
+
+This module is highly platform dependent, please read the CAVEATS
+section.
+
+=head1 API
+
+You can set set the size limits from a Perl module or script loaded by
+Apache:
+
+ use Apache::SizeLimit;
+
+ Apache::SizeLimit::setmax(150_000); # Max size in KB
+ Apache::SizeLimit::setmin(10_000); # Min share in KB
+ Apache::SizeLimit::setmax_unshared(120_000); # Max unshared size in KB
+
+Then in your Apache configuration, make Apache::SizeLimit a
+C<PerlCleanupHandler>:
+
+ PerlCleanupHandler Apache::SizeLimit
+
+If you want to use C<Apache::SizeLimit> from a registry script, you
+must call one of the above functions for every request:
+
+ use Apache::SizeLimit
+
+ main();
+
+ sub {
+ Apache::SizeLimit::setmax(150_000);
+
+ # handle request
+ };
+
+Calling any one of C<setmax()>, C<setmin()>, or C<setmax_unshared()>
+will install C<Apache::SizeLimit> as a cleanup handler, if it's not
+already installed.
+
+If you want to combine this module with a cleanup handler of your own,
+make sure that C<Apache::SizeLimit> is the last handler run:
+
+ PerlCleanupHandler Apache::SizeLimit My::CleanupHandler
+
+Remember, mod_perl will run stacked handlers from right to left, as
+they're defined in your configuration.
+
+You can explicitly call the C<Apache::SizeLimit::handler()> function
+from your own handler:
+
+ package My::CleanupHandler
+
+ sub handler {
+ my $r = shift;
+
+ # do my thing
+
+ return Apache::SizeLimit::handler($r);
+ }
+
+Since checking the process size can take a few system calls on some
+platforms (e.g. linux), you may want to only check the process size
+every N times. To do so, simple set the
+C<$Apache::SizeLimit::CHECK_EVERY_N_REQUESTS> global.
+
+ $Apache::SizeLimit::CHECK_EVERY_N_REQUESTS = 2;
+
+Now C<Apache::SizeLimit> will only check the process size on every
+other request.
+
+=head2 Deprecated API
+
+Previous versions of this module documented three globals for defining
+memory size limits:
+
+=over 4
+
+=item * $Apache::SizeLimit::MAX_PROCESS_SIZE
+
+=item * $Apache::SizeLimit::MIN_SHARE_SIZE
+
+=item * $Apache::SizeLimit::MAX_UNSHARED_SIZE
+
+=back
+
+Direct use of these globals is deprecated, but will continue to work
+for the foreseeable future.
+
+=head1 ABOUT THIS MODULE
+
+This module was written in response to questions on the mod_perl
+mailing list on how to tell the httpd process to exit if it gets too
+big.
+
+Actually, there are two big reasons your httpd children will grow.
+First, your code could have a bug that causes the process to increase
+in size very quickly. Second, you could just be doing operations that
+require a lot of memory for each request. Since Perl does not give
+memory back to the system after using it, the process size can grow
+quite large.
+
+This module will not really help you with the first problem. For that
+you should probably look into C<Apache::Resource> or some other means
+of setting a limit on the data size of your program. BSD-ish systems
+have C<setrlimit()>, which will kill your memory gobbling processes.
+However, it is a little violent, terminating your process in
+mid-request.
+
+This module attempts to solve the second situation, where your process
+slowly grows over time. It checks memory usage after every request,
+and if it exceeds a threshold, exits gracefully.
+
+By using this module, you should be able to discontinue using the
+Apache configuration directive B<MaxRequestsPerChild>, although for
+some folks, using both in combination does the job.
+
+=head1 SHARED MEMORY OPTIONS
+
+In addition to simply checking the total size of a process, this
+module can factor in how much of the memory used by the process is
+actually being shared by copy-on-write. If you don't understand how
+memory is shared in this way, take a look at the mod_perl Guide at
+http://perl.apache.org/guide/.
+
+You can take advantage of the shared memory information by setting a
+minimum shared size and/or a maximum unshared size. Experience on one
+heavily trafficked mod_perl site showed that setting maximum unshared
+size and leaving the others unset is the most effective policy. This
+is because it only kills off processes that are truly using too much
+physical RAM, allowing most processes to live longer and reducing the
+process churn rate.
+
+=head1 CAVEATS
+
+This module is highly platform dependent, since finding the size of a
+process is different for each OS, and some platforms may not be
+supported. In particular, the limits on minimum shared memory and
+maximum shared memory are currently only supported on Linux and BSD.
+If you can contribute support for another OS, patches are very
+welcome.
+
+Currently supported OSes:
+
+=over 4
+
+=item linux
+
+For linux we read the process size out of F</proc/self/statm>. This
+is a little slow, but usually not too bad. If you are worried about
+performance, try only setting up the the exit handler inside CGIs
+(with the C<setmax()> function), and see if the CHECK_EVERY_N_REQUESTS
+option is of benefit.
+
+Since linux 2.6 F</proc/self/statm> does not report the amount of
+memory shared by the copy-on-write mechanism as shared memory. Hence
+decisions made on the basis of C<MAX_UNSHARED_SIZE> or
+C<MIN_SHARE_SIZE> are inherently wrong.
+
+To correct this situation, as of the 2.6.14 release of the kernel,
+there is F</proc/self/smaps> entry for each
+process. F</proc/self/smaps> reports various sizes for each memory
+segment of a process and allows us to count the amount of shared
+memory correctly.
+
+If C<Apache::SizeLimit> detects a kernel that supports
+F</proc/self/smaps> and if the C<Linux::Smaps> module is installed it
+will use them instead of F</proc/self/statm>. You can prevent
+C<Apache::SizeLimit> from using F</proc/self/smaps> and turn on the
+old behaviour by setting C<$Apache::SizeLimit::USE_SMAPS> to 0.
+
+C<Apache::SizeLimit> itself will C<$Apache::SizeLimit::USE_SMAPS> to 0
+if it cannot load C<Linux::Smaps> or if your kernel does not support
+F</proc/self/smaps>. Thus, you can check it to determine what is
+actually used.
+
+NOTE: Reading F</proc/self/smaps> is expensive compared to
+F</proc/self/statm>. It must look at each page table entry of a process.
+Further, on multiprocessor systems the access is synchronized with
+spinlocks. Hence, you are encouraged to set the C<CHECK_EVERY_N_REQUESTS>
+option.
+
+The following example shows the effect of copy-on-write:
+
+ <Perl>
+ require Apache::SizeLimit;
+ package X;
+ use strict;
+ use Apache::Constants qw(OK);
+
+ my $x= "a" x (1024*1024);
+
+ sub handler {
+ my $r = shift;
+ my ($size, $shared) = $Apache::SizeLimit::check_size();
+ $x =~ tr/a/b/;
+ my ($size2, $shared2) = $Apache::SizeLimit::check_size();
+ $r->content_type('text/plain');
+ $r->print("1: size=$size shared=$shared\n");
+ $r->print("2: size=$size2 shared=$shared2\n");
+ return OK;
+ }
+ </Perl>
+
+ <Location /X>
+ SetHandler modperl
+ PerlResponseHandler X
+ </Location>
+
+The parent apache allocates a megabyte for the string in C<$x>. The
+C<tr>-command then overwrites all "a" with "b" if the handler is
+called with an argument. This write is done in place, thus, the
+process size doesn't change. Only C<$x> is not shared anymore by
+means of copy-on-write between the parent and the child.
+
+If F</proc/self/smaps> is available curl shows:
+
+ [EMAIL PROTECTED]:~/work/mp2> curl http://localhost:8181/X
+ 1: size=13452 shared=7456
+ 2: size=13452 shared=6432
+
+Shared memory has lost 1024 kB. The process' overall size remains unchanged.
+
+Without F</proc/self/smaps> it says:
+
+ [EMAIL PROTECTED]:~/work/mp2> curl http://localhost:8181/X
+ 1: size=13052 shared=3628
+ 2: size=13052 shared=3636
+
+One can see the kernel lies about the shared memory. It simply doesn't
+count copy-on-write pages as shared.
+
+=item solaris 2.6 and above
+
+For solaris we simply retrieve the size of F</proc/self/as>, which
+contains the address-space image of the process, and convert to KB.
+Shared memory calculations are not supported.
+
+NOTE: This is only known to work for solaris 2.6 and above. Evidently
+the F</proc> filesystem has changed between 2.5.1 and 2.6. Can anyone
+confirm or deny?
+
+=item *bsd*
+
+Uses C<BSD::Resource::getrusage()> to determine process size. This is
+pretty efficient (a lot more efficient than reading it from the
+F</proc> fs anyway).
+
+=item AIX?
+
+Uses C<BSD::Resource::getrusage()> to determine process size. Not
+sure if the shared memory calculations will work or not. AIX users?
+
+=item Win32
+
+Uses C<Win32::API> to access process memory information.
+C<Win32::API> can be installed under ActiveState perl using the
+supplied ppm utility.
+
+=back
+
+If your platform is not supported, then please send a patch to check
+the process size. The more portable/efficient/correct the solution the
+better, of course.
+
=head1 AUTHOR
Doug Bagley <[EMAIL PROTECTED]>, channeling Procrustes.
@@ -345,4 +560,8 @@
Matt Phillips <[EMAIL PROTECTED]> and Mohamed Hendawi
<[EMAIL PROTECTED]>: Win32 support
+Dave Rolsky <[EMAIL PROTECTED]>, maintenance and fixes outside of
+mod_perl tree (0.04+).
+
=cut
+
Added: perl/Apache-SizeLimit/trunk/t/TEST.PL
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/t/TEST.PL?rev=416874&view=auto
==============================================================================
--- perl/Apache-SizeLimit/trunk/t/TEST.PL (added)
+++ perl/Apache-SizeLimit/trunk/t/TEST.PL Fri Jun 23 23:57:52 2006
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use lib qw(lib);
+
+use Apache::TestRunPerl ();
+
+Apache::TestRunPerl->new->run(@ARGV);
Added: perl/Apache-SizeLimit/trunk/t/pod.t
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/t/pod.t?rev=416874&view=auto
==============================================================================
--- perl/Apache-SizeLimit/trunk/t/pod.t (added)
+++ perl/Apache-SizeLimit/trunk/t/pod.t Fri Jun 23 23:57:52 2006
@@ -0,0 +1,4 @@
+use Test::More;
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+all_pod_files_ok();
Added: perl/Apache-SizeLimit/trunk/t/response/TestApache/basic.pm
URL:
http://svn.apache.org/viewvc/perl/Apache-SizeLimit/trunk/t/response/TestApache/basic.pm?rev=416874&view=auto
==============================================================================
--- perl/Apache-SizeLimit/trunk/t/response/TestApache/basic.pm (added)
+++ perl/Apache-SizeLimit/trunk/t/response/TestApache/basic.pm Fri Jun 23
23:57:52 2006
@@ -0,0 +1,50 @@
+package TestApache::basic;
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Apache::Constants qw(OK);
+use Apache::SizeLimit;
+
+
+sub handler {
+ my $r = shift;
+
+ Test::Builder->new->output(*STDOUT);
+ Test::Builder->new->failure_output(*STDOUT);
+
+ $r->content_type('text/plain');
+ $r->send_http_header();
+
+ plan tests => 3;
+
+ Apache::SizeLimit::setmax( 100_000 );
+ Apache::SizeLimit::setmin( 1 );
+
+ ok( $r->pnotes('size_limit_cleanup'), 'Set size_limit_cleanup in pnotes'
);
+
+ my ( $size, $shared ) = Apache::SizeLimit::check_size();
+ cmp_ok( $size, '>', 0, 'proc size is reported > 0' );
+
+ cmp_ok( Apache::SizeLimit::real_getppid(), '>', 1,
+ 'real_getppid() > 1' );
+
+ return OK;
+}
+
+my $count = 1;
+sub _test {
+ my $ok = shift;
+ my $desc = shift;
+ my $r = shift;
+
+ my $string = $ok ? 'ok' : 'not ok';
+ $r->print( "$string $count - $desc\n" );
+
+ $count++;
+}
+
+
+1;