Apache-VMonitor
view release on metacpan or search on metacpan
lib/Apache/VMonitor.pm view on Meta::CPAN
package Apache::VMonitor;
$Apache::VMonitor::VERSION = '2.07';
require 5.006;
use strict;
use warnings;
use Template ();
BEGIN {
use constant MP2 => eval { require mod_perl2; $mod_perl2::VERSION >= 2.0 };
eval {require mod_perl} unless MP2;
die "mod_perl is required to run this module: $@" if $@;
if (MP2) {
require Apache2::ServerUtil;
require Apache2::RequestRec;
require Apache2::RequestIO;
require APR::Table;
require APR::Pool;
require Apache2::Const;
Apache2::Const->import('OK');
} else {
require Apache;
require Apache::Constants;
Apache::Constants->import('OK');
}
require Apache::Scoreboard;
require Time::HiRes;
}
# once 2.0 is released drop the Apache::MPM_IS_THREADED case
use constant APACHE_IS_THREADED => MP2 &&
require Apache2::MPM && Apache2::MPM->is_threaded();
# Apache::Scoreboard for 1.3 scoreboard has the vhost accessor only
# starting from version 0.12
use constant HAS_VHOSTS => (MP2 || $Apache::Scoreboard::VERSION > 0.11);
#use constant THREAD_LIMIT => MP2
# ? Apache::Const::THREAD_LIMIT
# : 0; # no threads in mp1
use constant SINGLE_PROCESS_MODE => MP2
? Apache2::ServerUtil::exists_config_define('ONE_PROCESS')
: Apache->define('X');
my $gtop;
eval {
require GTop;
$gtop = GTop->new;
};
my $tt;
%Apache::VMonitor::longflags = (
"_" => "Waiting for Connection",
"S" => "Starting up",
"R" => "Reading Request",
"W" => "Sending Reply",
"K" => "Keepalive (read)",
"D" => "DNS Lookup",
"C" => "Closing connection",
"L" => "Logging",
"G" => "Gracefully finishing",
"I" => "Idle cleanup of worker",
"." => "Open slot with no current process",
);
########################
# default config values
########################
%Apache::VMonitor::Config = (
# behavior
refresh => 0,
verbose => 0,
# sections to show
system => 1,
apache => 1,
procs => 0,
mount => 0,
fs_usage => 1,
# sorting
apache_sort_by => 'size',
apache_sort_by_ascend => 0,
);
my @sects = qw(system apache procs mount fs_usage verbose);
my %cfg = ();
sub handler_mp1 ($$) { &run }
sub handler_mp2 : method { &run }
*handler = MP2 ? \&handler_mp2 : \&handler_mp1;
my $counter = 0;
sub run {
my ($class, $r) = @_;
$class = ref($class)||$class;
#$tt = Template->new({});
my %params = MP2
? map({ split('=', $_, 2) } split /[&]/, $r->args)
: $r->args;
# modify the default args if requested
for (keys %Apache::VMonitor::Config) {
$cfg{$_} = exists $params{$_}
? $params{$_}
: $Apache::VMonitor::Config{$_};
}
my $pid = $params{pid} || 0;
# really just a worker index (in threaded mpm)
my $tid = $params{thread_num} || '';
# build the updated URL (append the pid k/v pair)
my $url = $r->uri . "?pid=$pid&" . join "&", map {"$_=$cfg{$_}"} keys %cfg;
# if refresh is non-null, set the refresh header
$r->headers_out->set(Refresh => "$cfg{refresh}; URL=$url")
if $cfg{refresh};
MP2 ? $r->content_type('text/html') : $r->send_http_header('text/html');
my $self = $class->new(
r => $r,
tt => $tt,
gtop => $gtop,
cfg => \%cfg,
url => $url,
pid => $pid,
tid => $tid,
);
$self->{tt} ||= Template->new({
BLOCKS => {
tmpl_start_html => $self->tmpl_start_html(),
tmpl_end_html => $self->tmpl_end_html(),
tmpl_procs => $self->tmpl_procs(),
tmpl_nav_bar => $self->tmpl_nav_bar(),
tmpl_fs_usage => $self->tmpl_fs_usage(),
tmpl_mount => $self->tmpl_mount(),
tmpl_apache => $self->tmpl_apache(),
tmpl_apache_single => $self->tmpl_apache_single(),
tmpl_system => $self->tmpl_system(),
tmpl_verbose => $self->tmpl_verbose(),
},
});
$self->generate;
return OK;
}
sub new {
my $class = shift;
my $self = bless {@_}, ref($class)||$class;
return $self;
}
sub generate {
my $self = shift;
my $cfg = $self->{cfg};
my $tt = $self->{tt};
my @items = 'start_html';
if ($self->{pid}) {
push @items, qw(apache_single);
}
else {
my @sects = qw(system apache procs fs_usage mount);
$cfg->{$_} && push @items, $_ for @sects;
push @items, qw(nav_bar);
lib/Apache/VMonitor.pm view on Meta::CPAN
uptime => format_time($gtop->uptime->uptime),
loadavg => \@{ $loadavg->loadavg },
};
if ($^O eq 'linux') {
$data->{tasks} = [ $loadavg->nr_tasks, $loadavg->nr_running ];
}
# total CPU stats
my $cpu = $gtop->cpu;
my $total = $cpu->total;
$data->{cpu} = {
map { $_ => ( $total ? ($cpu->$_() * 100 / $total) : 0 ) }
qw(user nice sys idle)
};
# total mem stats
my $mem = $gtop->mem;
$data->{mem} = {
map { $_ => size_string($mem->$_()) }
qw(total used free shared buffer)
};
# total swap stats
my $swap = $gtop->swap();
my $swap_total = $swap->total();
my $swap_used = $swap->used();
$data->{swap} = {
usage => ($swap_total ? ($swap_used * 100 / $swap_total) : 0),
used => $swap_used,
map({ ("f$_" => size_string($swap->$_)) }
qw(total used free)),
map({ ("f$_" => format_counts($swap->$_)) }
qw(pagein pageout)),
};
return $data;
}
sub tmpl_system {
return \ <<'EOT';
<hr>
<pre>
[%-
# date/time/load
USE format_date = format("%d/%.2d/%d");
fdate = format_date(date.month, date.day, date.year);
USE format_time = format("%d:%.2d%s");
pam = date.hour > 11 ? "pm" : "am";
date.hour = date.hour - 12 IF date.hour > 11;
ftime = format_time(date.hour, date.min, pam);
USE format_load = format("%.2f %.2f %.2f");
floadavg = format_load(loadavg.0, loadavg.1, loadavg.2,);
USE format_run_procs = format(", %d processes/threads: %d running");
frun_procs = tasks
? format_run_procs(tasks.0, tasks.1)
: "";
USE format_line_time_load =
format("<b>%s %s up %s, load average: %s%s</b>\n");
format_line_time_load(fdate, ftime, uptime, floadavg, frun_procs);
# CPU
USE format_line_cpu =
format("<b>CPU: %2.1f%% user, %2.1f%% nice, %2.1f%% sys, %2.1f%% idle</b>\n");
format_line_cpu(cpu.user, cpu.nice, cpu.sys, cpu.idle);
# Memory
USE format_line_mem =
format("<b>Mem: %5s av, %5s used, %5s free, %5s shared, %5s buff</b>\n");
format_line_mem(mem.total, mem.used, mem.free, mem.shared, mem.buffer);
# Swap
# visual alert on swap usage:
# 1) 5Mb < swap < 10 MB color: light red
# 2) 20% < swap (swapping is bad!) color: red
# 3) 70% < swap (swap almost used!) color: red
format_swap_data = "%5s av, %5s used, %5s free, %5s pagein, %5s pageout";
IF 5000 < swap.used AND swap.used < 10000;
USE format_line_swap = format("<b>Swap: <font color=\"#ff99cc\">$format_swap_data</font></b>\n");
ELSIF swap.usage >= 20;
USE format_line_swap = format("<b>Swap: <font color=\"#ff0000\">$format_swap_data</font></b>\n");
ELSIF swap.usage >= 70;
# swap on fire!
USE format_line_swap = format("<b>Swap: <font color=\"#ff0000\">$format_swap_data</font></b>\n");
ELSE;
USE format_line_swap = format("<b>Swap: $format_swap_data</b>\n");
END;
format_line_swap(swap.ftotal, swap.fused, swap.ffree, swap.fpagein, swap.fpageout);
-%]
</pre>
EOT
}
### apache ###
sub scoreboard_image {
MP2 ? Apache::Scoreboard->image(shift->{r}->pool)
: Apache::Scoreboard->image();
}
sub data_apache {
my $self = shift;
if (MP2 && $Apache::Scoreboard::VERSION < 2.0) {
lib/Apache/VMonitor.pm view on Meta::CPAN
my %mem_total = map { $_ => 0 } qw(size real max_shared);
my %cols = (
# WIDTH # LABEL # SORT
pid => [ 3, 'PID' , 'd'],
size => [ 5, 'Size' , 'd'],
share => [ 5, 'Share' , 'd'],
vsize => [ 5, 'VSize' , 'd'],
rss => [ 5, 'Rss' , 'd'],
mode => [ 1, 'M' , 's'],
elapsed => [ 7, 'Elapsed' , 'd'],
lastreq => [ 7, 'LastReq' , 'd'],
served => [ 4, 'Srvd' , 'd'],
client => [15, 'Client' , 's'],
vhost => [15, 'Virtual Host' , 's'],
request => [27, 'Request (first 64 chars)', 's'],
);
my @cols_sorted = qw(pid size share vsize rss mode elapsed lastreq served
client);
push @cols_sorted, "vhost" if HAS_VHOSTS;
push @cols_sorted, "request";
my $sort_field = lc($cfg{apache_sort_by}) || 'size';
$sort_field = 'size' unless $cols{$sort_field};
my $sort_ascend = $Apache::VMonitor::Config{apache_sort_by_ascend} || 0;
#warn "SORT field: $sort_field, ascending $sort_ascend\n";
for (@cols_sorted) {
if ($sort_field eq $_) {
$sort_ascend = $cfg{apache_sort_by_ascend} + 1;
$sort_ascend %= 2; # reverse sorting order
}
# add a link to sort by that field
$cols{$_}[3] = fixup_url($self->{url},
apache_sort_by => $cols{$_}[1],
apache_sort_by_ascend => $sort_ascend);
}
my %data = ();
# in a non-single server mode we want to show the parent process
# (so we can tell its memory usage)
unless (SINGLE_PROCESS_MODE) {
my $ppid = getppid();
#warn "ppid: $ppid\n";
my $pmem = $self->pid2mem($ppid, \%mem_total);
# XXX: mp1 gives us a wrong getppid (proc that has died
# already, is there another way to get to the parent proc?)
# handle the parent case
if ($pmem && $pmem->{size}) {
my $prec = {
id => 0,
pid => $ppid,
pid_link => fixup_url($self->{url}, pid => $ppid),
%$pmem,
};
$data{ $ppid }{process} = $prec;
# this parent has no worker threads
$data{ $ppid }{workers} = [];
}
}
my $SERVER_LIMIT = MP2 ? $image->server_limit : $Apache::Constants::HARD_SERVER_LIMIT;
my $i;
my $parent_count = 0;
my ($parent_score, $worker_score, $pid);
for ($i=0; $i < $SERVER_LIMIT; $i++) {
last if SINGLE_PROCESS_MODE && $i > 0;
$parent_score = MP2 ? $image->parent_score($i) : $image->servers($i);
next unless $parent_score;
$pid = SINGLE_PROCESS_MODE
? $$
: MP2 ? $parent_score->pid : $image->parent($i)->pid;
next unless $pid;
$worker_score = MP2 ? $parent_score->worker_score : $parent_score;
next unless $worker_score;
my $mem = $self->pid2mem($pid, \%mem_total);
next unless $mem && $mem->{size};
# good record
$parent_count++;
my %record = %$mem;
$record{pid} = $pid;
$record{id} = $parent_count;
$data{ $pid }{process} = \%record;
if (APACHE_IS_THREADED) {
do {
my $record = $self->score2record($worker_score);
my $thread_num = $worker_score->thread_num;
$record->{pid} = $thread_num;
$record->{pid_link} = fixup_url($self->{url}, pid => $pid);
$record->{pid_link} .= "&thread_num=$thread_num";
push @{ $data{$pid}{workers} }, $record;
$worker_score =
$parent_score->next_live_worker_score($worker_score);
} while $worker_score
}
else {
push @{ $data{$pid}{workers} },
$self->score2record($worker_score);
}
}
my @records = ();
my $count = 0;
my $max_client_len = 9;
my $max_vhost_len = 5;
my $max_request_len = 10;
my $max_pid_len = 0;
# sort strings alphabetically, numbers numerically reversed
my $sort_sub;
#warn "sort_field: $sort_field $cols{$sort_field}[2]\n";
# XXX: need to sort {workers} as well
if ($cols{$sort_field}[2] eq 's') {
$sort_sub = $sort_ascend
? sub { $data{$a}{process}{$sort_field} cmp $data{$b}{process}{$sort_field} }
: sub { $data{$b}{process}{$sort_field} cmp $data{$a}{process}{$sort_field} };
}
else {
$sort_sub = $sort_ascend
? sub { $data{$a}{process}{$sort_field} <=> $data{$b}{process}{$sort_field} }
: sub { $data{$b}{process}{$sort_field} <=> $data{$a}{process}{$sort_field} };
}
# it's a pity to waste display space on vhosts if none is configured
my $has_vhosts_entries = 0;
for my $pid (sort $sort_sub keys %data) {
my $rec = $data{$pid}{process};
# threads
my @workers = ();
my $tcount = 0;
for my $trec (@{ $data{$pid}{workers} || []}) {
$tcount++;
my $lastreq = $trec->{lastreq} ? $trec->{lastreq}/1000 : 0;
$has_vhosts_entries++ if exists $trec->{vhost} && length $trec->{vhost};
push @workers, {
id => sprintf("%03d", $tcount),
pid => $trec->{pid},
pid_link => $trec->{pid_link},
mode => $trec->{mode},
elapsed => $trec->{elapsed},
felapsed => format_time($trec->{elapsed}),
lastreq => $lastreq,
flastreq => format_time($lastreq),
fserved => format_counts($trec->{served}),
client => $trec->{client},
vhost => $trec->{vhost},
request => $trec->{request},
};
$max_client_len = length $trec->{client}
if $trec->{client} && length($trec->{client}) > $max_client_len;
$max_request_len = length $trec->{request}
if $trec->{request} && length($trec->{request}) > $max_request_len;
$max_vhost_len = length $trec->{vhost}
if exists $trec->{vhost} && length($trec->{vhost}) > $max_vhost_len;
# XXX: s/pid/tid/;
$max_pid_len = length $pid if length($pid) > $max_pid_len;
}
my $lastreq = $rec->{lastreq} ? $rec->{lastreq}/1000 : 0;
# print sorted
push @records, {
id => sprintf("%3d", $rec->{id}),
pid => $rec->{pid},
pid_link => $rec->{pid_link},
mode => $rec->{mode},
elapsed => $rec->{elapsed},
felapsed => format_time($rec->{elapsed}),
lastreq => $lastreq,
flastreq => format_time($lastreq),
fserved => format_counts($rec->{served}),
fsize => size_string($rec->{size}),
fshare => size_string($rec->{share}),
fvsize => size_string($rec->{vsize}),
frss => size_string($rec->{rss}),
client => $rec->{client},
vhost => $rec->{vhost},
request => $rec->{request},
workers => \@workers,
};
$has_vhosts_entries++ if exists $rec->{vhost} && length $rec->{vhost};
$max_client_len = length $rec->{client}
if $rec->{client} && length($rec->{client}) > $max_client_len;
$max_request_len = length $rec->{request}
if $rec->{request} && length($rec->{request}) > $max_request_len;
$max_vhost_len = length $rec->{vhost}
if $rec->{vhost} && length($rec->{vhost}) > $max_vhost_len;
$max_pid_len = length $pid if length($pid) > $max_pid_len;
}
$cols{client}[0] = $max_client_len;
$cols{request}[0] = $max_request_len;
$cols{vhost}[0] = $max_vhost_len;
$cols{pid}[0] = $max_pid_len;
# Summary of memory usage
# Note how do I calculate the approximate real usage of the memory:
# 1. For each process sum up the difference between shared and system
# memory 2. Now if we add the share size of the process with maximum
# shared memory, we will get all the memory that actually is being
# used by all httpd processes but the parent process.
my $total = {
size => $mem_total{size}/1000,
fsize => size_string($mem_total{size}),
shared => ($mem_total{real} + $mem_total{max_shared})/1000,
fshared => size_string($mem_total{real} + $mem_total{max_shared}),
};
# remove the vhost col if there are no vhosts to display
@cols_sorted = grep { $_ ne 'vhost' } @cols_sorted
unless $has_vhosts_entries;
return {
total => $total,
records => \@records,
cols_sorted => \@cols_sorted,
cols => \%cols,
has_vhosts_entries => $has_vhosts_entries,
threaded => (APACHE_IS_THREADED ? 1 : 0),
};
}
sub pid2mem {
my($self, $pid, $total) = @_;
return {} unless $gtop;
my $proc_mem = $gtop->proc_mem($pid);
my $size = $proc_mem ? $proc_mem->size($pid) : 0;
# dead process?
return {} unless $size;
my $share = $proc_mem->share($pid);
my $vsize = $proc_mem->vsize($pid);
my $rss = $proc_mem->rss($pid);
# total http size update
if ($total) {
$total->{size} += $size;
$total->{real} += $size-$share;
$total->{max_shared} = $share if $total->{max_shared} < $share;
}
return {
size => $size,
share => $share,
vsize => $vsize,
rss => $rss,
pid => $pid,
pid_link => fixup_url($self->{url}, pid => $pid),
};
}
sub score2record {
my($self, $worker_score) = @_;
# get absolute start and stop times in usecs since epoch
my ($start_sec, $start_usec) = $worker_score->start_time;
my $start = $start_sec * 1000000 + $start_usec;
my($stop_sec, $stop_usec) = $worker_score->stop_time;
my $stop = $stop_sec * 1000000 + $stop_usec;
#warn "time: $start_sec, $start_usec, $stop_sec, $stop_usec\n";
# measure running time till now if not idle
my $elapsed = $stop < $start
? Time::HiRes::tv_interval([$start_sec, $start_usec],
[Time::HiRes::gettimeofday()])
: 0;
my $vhost = HAS_VHOSTS ? $worker_score->vhost : '';
return {
mode => $worker_score->status,
elapsed => $elapsed,
lastreq => $worker_score->req_time || 0,
served => $worker_score->my_access_count,
client => $worker_score->client,
vhost => $vhost,
lib/Apache/VMonitor.pm view on Meta::CPAN
USE format_parent =
format("%s: %s %5s %5s %5s %5s %${spacing_len}s");
BLOCK present_parent_record;
times = max_pid_len - prec.pid.length;
spacing = times > 0 ? space.repeat(times) : "";
pid_link = "$spacing<a href=\"${prec.pid_link}\">${prec.pid}</a>";
format_parent(prec.id, pid_link, prec.fsize, prec.fshare, prec.fvsize,
prec.frss, space);
END;
fvhost = has_vhosts_entries
? " %${max_vhost_len}.${max_vhost_len}s"
: "%0.0s";
USE format_child =
format("%s: %s %5s %5s %5s %5s %1s %s %s %4s %${max_client_len}.${max_client_len}s${fvhost} %s%s");
# if prec is passed, that means that the parent process is also a worker
# so present the process data on the same line with the worker data
BLOCK present_worker_record;
IF prec;
wrec.id = prec.id;
wrec.pid = prec.pid;
wrec.pid_link = prec.pid_link;
wrec.fsize = prec.fsize;
wrec.fshare = prec.fshare;
wrec.fvsize = prec.fvsize;
wrec.frss = prec.frss;
END;
# alert on workers that are still at work for a single request
# for more than 15 secs
elapsed_class = wrec.elapsed > 15 ? "alert" : "normal";
wrec.felapsed = "<span class=\"$elapsed_class\">${wrec.felapsed}</span>";
# alert on workers that worked for a single request for more
# than 15 secs
lastreq_class = wrec.lastreq > 15 ? "alert" : "normal";
wrec.flastreq = "<span class=\"$lastreq_class\">${wrec.flastreq}</span>";
# escape HTML in request URI to prevent cross-site scripting attack
wrec.frequest = HTML.escape(wrec.request);
# line fill spacing (needed for coloured areas)
times = max_request_len - wrec.frequest.length;
line_fill = times > 0 ? space.repeat(times) : "";
# pid linked
times = max_pid_len - wrec.pid.length;
spacing = times > 0 ? space.repeat(times) : "";
pid_link = "$spacing<a href=\"${wrec.pid_link}\">${wrec.pid}</a>";
format_child(wrec.id, pid_link, wrec.fsize, wrec.fshare, wrec.fvsize,
wrec.frss, wrec.mode, wrec.felapsed, wrec.flastreq,
wrec.fserved, wrec.client, wrec.vhost, wrec.frequest, line_fill);
END;
IF threaded;
FOR rec = records;
item_class = loop.count % 2 ? "item_odd" : "item_even";
"<span class=\"$item_class\">";
IF rec.workers.size;
PROCESS present_parent_record prec = rec;
"\n";
FOR wrec = rec.workers;
PROCESS present_worker_record prec = 0, wrec = wrec;
"\n";
END;
ELSE;
PROCESS present_parent_record prec = rec;
END;
"</span>\n";
END;
ELSE;
FOR rec = records;
item_class = loop.count % 2 ? "item_odd" : "item_even";
"<span class=\"$item_class\">";
IF rec.workers.size;
PROCESS present_worker_record prec = rec, wrec => rec.workers.0;
ELSE;
PROCESS present_parent_record prec = rec;
END;
"</span>\n";
END;
END;
# total apache proc memory usage
USE format_total =
format("\n<b>Total: %5dK (%s) size, %6dK (%s) approx real size (-shared)</b>\n");
format_total(total.size, total.fsize, total.shared, total.fshared);
-%]
</pre>
EOT
}
### procs ###
sub data_procs {
my $self = shift;
unless ($Apache::VMonitor::PROC_REGEX) {
warn "Don't know what processes to display..." .
'int: set $Apache::VMonitor::PROC_REGEX' .
'e.g. \$Apache::VMonitor::PROC_REGEX = join "\|", qw(httpd mysql);';
return {};
}
my $gtop = $self->{gtop};
unless ($gtop) {
warn "GTop not installed, not displaying process data";
return {};
}
my($proclist, $entries) = $gtop->proclist;
lib/Apache/VMonitor.pm view on Meta::CPAN
</pre>
EOT
}
### verbose ###
%Apache::VMonitor::abbreviations =
(
verbose =>
qq{
<B>Verbose option</B>
Enables Verbose mode - displays an explanation and abbreviation
table for each enabled section.
},
refresh =>
qq{
<B>Refresh Section</B>
You can tune the automatic refresh rate by clicking on the
number of desired rate (in seconds). 0 (zero) means "no
automatic refresh".
},
system =>
qq{
<B>Top section</B>
Represents the emulation of top utility, while individually
reporting only on httpd processes, and provides information
specific to these processes.
<B>1st</B>: current date/time, uptime, load average: last 1, 5 and 15
minutes, total number of processes and how many are in the
running state.
<B>2nd</B>: CPU utilization in percents: by processes in user, nice,
sys and idle state
<B>3rd</B>: RAM utilization: total available, total used, free, shared
and buffered
<B>4th</B>: SWAP utilization: total available, total used, free, how
many paged in and out
},
apache =>
qq{
<B>Apache/mod_perl processes:</B>
The first row reports the status of parent process (mnemonic 'par').
Columns:
<pre>
<span class="item_even">Column Purpose</span>
<b>PID</b> Id (or Thread index for threaded httpd)
<b>Size</b> Total Size
<b>Share</b> Shared Size
<b>VSize</b> Virtual Size
<b>RSS</b> Resident Size
<b>M</b> Apache mode (See below a full table of abbreviations)
<b>Elapsed</b> Time since request was started if still in process (0 otherwise)
<b>LastReq</b> Time last request was served if idle now (0 otherwise)
<b>Srvd</b> How many requests were processed by this child
<b>Client</b> Client IP
<b>VHost</b> Virtual Hosts (httpd 2.0, if any configured)
<b>Request</b> Request (first 64 chars)
</pre>
<p> You can sort the report by clicking on any column (only
the parent process is outstanding and is not sorted)</p>
Last row reports:
<B>Total</B> = a total size of the httpd processes (by
summing the SIZE value of each process)
<B>Approximate real size (-shared)</B> =
1. For each process sum up the difference between shared and system
memory.
2. Now if we add the share size of the process with maximum
shared memory, we will get all the memory that actually is being
used by all httpd processes but the parent process.
Please note that this might be incorrect for your system, so you use
this number on your own risk. I have verified this number, by writing
it down and then killing all the servers. The system memory went down
by approximately this number. Again, use this number wisely!
The <B>modes</B> a process can be in:
<code><b>_</b></code> = Waiting for Connection<BR>
<code><b>S</b></code> = Starting up<BR>
<code><b>R</b></code> = Reading Request<BR>
<code><b>W</b></code> = Sending Reply<BR>
<code><b>K</b></code> = Keepalive (read)<BR>
<code><b>D</b></code> = DNS Lookup<BR>
<code><b>C</b></code> = Closing connection<BR>
<code><b>L</b></code> = Logging<BR>
<code><b>G</b></code> = Gracefully finishing<BR>
<code><b>I</b></code> = Idle cleanup of worker<BR>
<code><b>.</b></code> = Open slot with no current process<BR>
},
procs =>
qq{
<B> Processes matched by <CODE>\$Apache::VMonitor::PROC_REGEX</CODE> (PROCS)</B>
Setting:
<PRE>\$Apache::VMonitor::PROC_REGEX = join "\|", qw(httpd mysql squid);</PRE>
will display the processes that match /httpd|mysql|squid/ regex in a
top(1) fashion in groups of processes. After each group the report of
( run in 2.287 seconds using v1.01-cache-2.11-cpan-c966e8aa7e8 )