Author: pgollucci
Date: Mon Mar 26 07:16:38 2007
New Revision: 522521

URL: http://svn.apache.org/viewvc?view=rev&rev=522521
Log:
pulled the seperate A-SL back out and restored
lib/Apache/[EMAIL PROTECTED]

(will restore this after the 1.30 security release)


Added:
    perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm
Modified:
    perl/modperl/branches/1.x/   (props changed)
    perl/modperl/branches/1.x/Changes
    perl/modperl/branches/1.x/Makefile.PL

Propchange: perl/modperl/branches/1.x/
------------------------------------------------------------------------------
--- svn:externals (original)
+++ svn:externals Mon Mar 26 07:16:38 2007
@@ -1,2 +1 @@
-Apache-SizeLimit https://svn.apache.org/repos/asf/perl/Apache-SizeLimit/trunk
 

Modified: perl/modperl/branches/1.x/Changes
URL: 
http://svn.apache.org/viewvc/perl/modperl/branches/1.x/Changes?view=diff&rev=522521&r1=522520&r2=522521
==============================================================================
--- perl/modperl/branches/1.x/Changes (original)
+++ perl/modperl/branches/1.x/Changes Mon Mar 26 07:16:38 2007
@@ -16,11 +16,6 @@
 reported by Alex Solovey
 [Randal L. Schwartz <merlyn@stonehenge.com>, Fred Moyer <[EMAIL PROTECTED]>]
 
-Pull in the new Apache-SizeLimit from
-http://svn.apache.org/repos/asf/perl/Apache-SizeLimit/trunk
-and obsolete the previous lib/Apache/SizeLimit.pm.
-[Philip M. Gollucci]
-
 Fix an Apache::(Registry|PerlRun) bug caused by special characters
 in the url [EMAIL PROTECTED]
 

Modified: perl/modperl/branches/1.x/Makefile.PL
URL: 
http://svn.apache.org/viewvc/perl/modperl/branches/1.x/Makefile.PL?view=diff&rev=522521&r1=522520&r2=522521
==============================================================================
--- perl/modperl/branches/1.x/Makefile.PL (original)
+++ perl/modperl/branches/1.x/Makefile.PL Mon Mar 26 07:16:38 2007
@@ -1531,7 +1531,7 @@
 
 q(
 
-test:  pure_all start_httpd run_tests kill_httpd run_apache_sizelimit_tests
+test:  pure_all start_httpd run_tests kill_httpd
 
 );
     my $have_so = $USE_DSO || ($APACI_ARGS =~ /--enable-shared=/);
@@ -1570,10 +1570,6 @@
 
 run_tests:
        $(FULLPERL) $(MP_TEST_SCRIPT) $(TEST_VERBOSE)
-
-run_apache_sizelimit_tests:
-       @(echo ; echo ; echo)
-       cd Apache-SizeLimit ; $(MAKE) test
 ),
 
 $my_test,

Added: perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm
URL: 
http://svn.apache.org/viewvc/perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm?view=auto&rev=522521
==============================================================================
--- perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm (added)
+++ perl/modperl/branches/1.x/lib/Apache/SizeLimit.pm Mon Mar 26 07:16:38 2007
@@ -0,0 +1,348 @@
+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).
+
+=item AIX?
+
+Uses BSD::Resource::getrusage() to determine process size.  Not sure if the
+shared memory calculations will work or not.  AIX users?
+
+=item Win32
+
+Uses Win32::API to access process memory information.  Win32::API can be 
+installed under ActiveState perl using the supplied ppm utility.
+
+=back
+
+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.
+
+=head1 TODO
+
+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.
+
+If Apache was started in non-forking mode, should hitting the size limit
+cause the process to exit?
+
+=cut
+
+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);
+
+$VERSION = '0.03';
+$CHECK_EVERY_N_REQUESTS = 1;
+$REQUEST_COUNT = 1;
+$MAX_PROCESS_SIZE  = 0;
+$MIN_SHARE_SIZE    = 0;
+$MAX_UNSHARED_SIZE = 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.";
+    }
+}
+
+# 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");
+    }
+    # linux on intel x86 has 4KB page size...
+    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 bsd_size_check {
+    return (&BSD::Resource::getrusage())[2,3];
+}
+
+sub win32_size_check {
+    # get handle on current process
+    my $GetCurrentProcess = new Win32::API('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
+
+    # 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);
+    
+    # GetProcessMemoryInfo is in "psapi.dll"
+    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, 
+        $PeakWorkingSetSize,
+        $WorkingSetSize,
+        $QuotaPeakPagedPoolUsage,
+        $QuotaPagedPoolUsage,
+        $QuotaPeakNonPagedPoolUsage,
+        $QuotaNonPagedPoolUsage,
+        $PagefileUsage,
+        $PeakPagefileUsage) = unpack($pmem_struct, $pProcessMemoryCounters);
+
+    # only care about peak working set size
+    my $size = int($PeakWorkingSetSize / 1024);
+
+    return ($size, 0);
+}
+
+
+sub exit_if_too_big {
+    my $r = shift;
+    return DECLINED if ($CHECK_EVERY_N_REQUESTS &&
+       ($REQUEST_COUNT++ % $CHECK_EVERY_N_REQUESTS));
+
+    $START_TIME ||= time;
+
+    my($size, $share) = &$HOW_BIG_IS_IT();
+
+    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);
+           }
+    }
+    return OK;
+}
+
+# setmax can be called from within a CGI/Registry script to tell the httpd
+# 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);
+}
+
+sub setmin {
+    $MIN_SHARE_SIZE = shift;
+    Apache->request->post_connection(\&exit_if_too_big);
+}
+
+sub setmax_unshared {
+    $MAX_UNSHARED_SIZE = shift;
+    Apache->request->post_connection(\&exit_if_too_big);
+}
+
+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);
+}
+
+sub error_log {
+    print STDERR "[", scalar(localtime(time)), "] ($$) Apache::SizeLimit @_\n";
+}
+
+1;
+
+=head1 AUTHOR
+
+Doug Bagley <[EMAIL PROTECTED]>, channeling Procrustes.
+
+Brian Moseley <[EMAIL PROTECTED]>: Solaris 2.6 support
+
+Doug Steinwand and Perrin Harkins <[EMAIL PROTECTED]>: added support 
+    for shared memory and additional diagnostic info
+
+Matt Phillips <[EMAIL PROTECTED]> and Mohamed Hendawi
+<[EMAIL PROTECTED]>: Win32 support
+
+=cut


Reply via email to