perl-modperl-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pgollu...@apache.org
Subject svn commit: r416874 - in /perl/Apache-SizeLimit/trunk: Changes MANIFEST MANIFEST.SKIP Makefile.PL lib/Apache/SizeLimit.pm t/TEST.PL t/pod.t t/response/ t/response/TestApache/ t/response/TestApache/basic.pm
Date Sat, 24 Jun 2006 06:57:53 GMT
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 <pgollucci@p6m7g8.com>]
+
+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 <autarch@urth.org>
+
+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 <autarch@urth.org>
+
+Lots of work on the docs.
+[Dave Rolsky <autarch@urth.org>
+
+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 <autarch@urth.org>
+
+Deprecated direct use of globals to set memory limits.
+[Dave Rolsky <autarch@urth.org>
+
+=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 <pgollucci@p6m7g8.com>]
+
+=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 <pgollucci@p6m7g8.net>') : ()),
-);
+
+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, <autarch@urth.org>',
+               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:
+
+  r2@s93:~/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:
+
+  r2@s93:~/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 <doug+modperl@bagley.org>, channeling Procrustes.
@@ -345,4 +560,8 @@
 Matt Phillips <mphillips@virage.com> and Mohamed Hendawi
 <mhendawi@virage.com>: Win32 support
 
+Dave Rolsky <autarch@urth.org>, 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;



Mime
View raw message