You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by tr...@apache.org on 2004/02/20 12:41:05 UTC
cvs commit: apache-1.3/src/modules/experimental mod_backtrace.c mod_whatkilledus.c mod_whatkilledus.exp
trawick 2004/02/20 03:41:05
Modified: src CHANGES
Added: src/modules/experimental mod_backtrace.c mod_whatkilledus.c
mod_whatkilledus.exp
Log:
Add mod_whatkilledus and mod_backtrace (experimental) for
reporting diagnostic information after a child process crash.
See source code for documentation.
Revision Changes Path
1.1928 +5 -0 apache-1.3/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /home/cvs/apache-1.3/src/CHANGES,v
retrieving revision 1.1927
retrieving revision 1.1928
diff -u -r1.1927 -r1.1928
--- CHANGES 29 Jan 2004 21:46:50 -0000 1.1927
+++ CHANGES 20 Feb 2004 11:41:04 -0000 1.1928
@@ -1,5 +1,10 @@
Changes with Apache 1.3.30
+ *) Add mod_whatkilledus and mod_backtrace (experimental) for
+ reporting diagnostic information after a child process crash.
+ See source code for documentation.
+ [Jeff Trawick, with help from mod_log_forensic]
+
*) mod_usertrack no longer inspects the Cookie2 header for
the cookie name. PR 11475. [Chris Darrochi <chrisd pearsoncmg.com>]
1.1 apache-1.3/src/modules/experimental/mod_backtrace.c
Index: mod_backtrace.c
===================================================================
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(__linux__) && !defined(__FreeBSD__)
#error This module is currently only implemented for Linux and FreeBSD.
#endif
/*
* Documentation:
*
* mod_backtrace is an experimental module for Apache httpd 1.3 which
* collects backtraces when a child process crashes. Currently it is
* implemented only on Linux and FreeBSD, but other platforms could be
* supported in the future. You should verify that it works reasonably
* on your system before putting it in production.
*
* It implements a fatal exception hook that will be called when a child
* process crashes. In the exception hook it uses system library routines
* to obtain information about the call stack, and it writes the call
* stack to a log file or the web server error log. The backtrace is a
* critical piece of information when determining the failing software
* component that caused the crash. Note that the backtrace written by
* mod_backtrace may not have as much information as a debugger can
* display from a core dump.
*
* Apache httpd requirements for mod_backtrace:
*
* Apache httpd >= 1.3.30 must be built with the AP_ENABLE_EXCEPTION_HOOK
* symbol defined and mod_so enabled. AP_ENABLE_EXCEPTION_HOOK is already
* defined in ap_config.h for some platforms, including AIX, Linux,
* Solaris, and HP-UX. It can be enabled for other platforms by including
* -DAP_ENABLE_EXCEPTION_HOOK in CFLAGS when the configure script is
* invoked.
*
* Compiling mod_backtrace:
*
* Linux:
* apxs -ci mod_backtrace.c
*
* FreeBSD:
* install libexecinfo from the Ports system then
* apxs -ci -L/usr/local/lib -lexecinfo mod_backtrace.c
*
* Activating mod_backtrace:
*
* 1. Load it like any other DSO:
* LoadModule backtrace_module libexec/mod_backtrace.so
* ...
* AddModule mod_backtrace.c
*
* 2. Enable exception hooks for modules like mod_backtrace:
* EnableExceptionHook On
*
* 3. Choose where backtrace information should be written.
* If you want backtraces from crashes to be reported some place other
* than the error log, use the BacktraceLog directive to specify a
* fully-qualified filename for the log to which backtraces will be
* written. Note that the web server user id (e.g., "nobody") must
* be able to create or append to this log file, as the log file is
* not opened until a crash occurs.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include <fcntl.h>
#include <unistd.h>
#include <execinfo.h>
static char *log_fname;
static void bt_show_backtrace(int sig)
{
char msgbuf[128];
size_t size;
void *array[20];
extern int main();
int logfd;
time_t now;
char msg_prefix[60];
char *newline;
int using_errorlog = 1;
time(&now);
ap_snprintf(msg_prefix, sizeof msg_prefix,
"[%s pid %ld mod_backtrace",
asctime(localtime(&now)),
(long)getpid());
newline = strchr(msg_prefix, '\n'); /* dang asctime() */
if (newline) { /* silly we are */
*newline = ']';
}
if (log_fname) {
logfd = open(log_fname, O_WRONLY|O_APPEND|O_CREAT, 0644);
if (logfd == -1) {
logfd = 2; /* unix, so fd 2 is the web server error log */
ap_snprintf(msgbuf, sizeof msgbuf,
"%s error %d opening %s\n",
msg_prefix, errno, log_fname);
write(logfd, msgbuf, strlen(msgbuf));
}
else {
using_errorlog = 0;
}
}
else {
logfd = 2;
}
ap_snprintf(msgbuf, sizeof msgbuf,
"%s backtrace for signal %d\n",
msg_prefix, sig);
write(logfd, msgbuf, strlen(msgbuf));
/* the address of main() can be useful if we're on old
* glibc and get only addresses for stack frames... knowing
* where main() is then is a useful clue
*/
ap_snprintf(msgbuf, sizeof msgbuf,
"%s main() is at %pp\n",
msg_prefix,
main);/* don't you DARE put parens after "main" */
write(logfd, msgbuf, strlen(msgbuf));
size = backtrace(array, sizeof array / sizeof array[0]);
backtrace_symbols_fd(array, size, logfd);
ap_snprintf(msgbuf, sizeof msgbuf,
"%s end of report\n",
msg_prefix);
write(logfd, msgbuf, strlen(msgbuf));
if (!using_errorlog) {
close(logfd);
}
}
static void bt_exception_hook(ap_exception_info_t *ei)
{
bt_show_backtrace(ei->sig);
}
static void bt_init(server_rec *s, pool *p)
{
int rc = ap_add_fatal_exception_hook(bt_exception_hook);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s,
"fatal exception hooks are not enabled; please "
"enable them with the EnableExceptionHook directive "
"or disable mod_backtrace");
}
}
static const char *bt_cmd_file(cmd_parms *cmd, void *dconf, char *fname)
{
log_fname = ap_pstrdup(cmd->pool, fname);
return NULL;
}
static const command_rec bt_command_table[] = {
{
"BacktraceLog", bt_cmd_file, NULL, RSRC_CONF, TAKE1, "the fully-qualified filename of the mod_backtrace logfile"
}
,
{
NULL
}
};
module MODULE_VAR_EXPORT backtrace_module = {
STANDARD_MODULE_STUFF,
bt_init, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
bt_command_table, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child init */
NULL, /* child exit */
NULL /* post read request */
};
1.1 apache-1.3/src/modules/experimental/mod_whatkilledus.c
Index: mod_whatkilledus.c
===================================================================
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Documentation:
*
* mod_whatkilledus is an experimental module for Apache httpd 1.3 which
* tracks the current request and logs a report of the active request
* when a child process crashes. You should verify that it works reasonably
* on your system before putting it in production.
*
* mod_whatkilledus is called during request processing to save information
* about the current request. It also implements a fatal exception hook
* that will be called when a child process crashes.
*
* Apache httpd requirements for mod_whatkilledus:
*
* Apache httpd >= 1.3.30 must be built with the AP_ENABLE_EXCEPTION_HOOK
* symbol defined and mod_so enabled. AP_ENABLE_EXCEPTION_HOOK is already
* defined in ap_config.h for some platforms, including AIX, Linux,
* Solaris, and HP-UX. It can be enabled for other platforms by including
* -DAP_ENABLE_EXCEPTION_HOOK in CFLAGS when the configure script is
* invoked.
*
* Compiling mod_whatkilledus:
*
* AIX:
* apxs -ci -I/path/to/apache/src/main -Wl,-bE:mod_whatkilledus.exp mod_whatkilledus.c
*
* other:
* apxs -ci -I/path/to/apache/src/main mod_whatkilledus.c
*
* Activating mod_whatkilledus:
*
* 1. Load it like any other DSO, but the AddModule should come
* last so that if another module causes a crash early in
* request processing mod_whatkilledus will have already
* had a chance to save information about the request.
*
* LoadModule whatkilledus_module libexec/mod_whatkilledus.so
* ...
* AddModule mod_whatkilledus.c
*
* 2. Enable exception hooks for modules like mod_whatkilledus:
* EnableExceptionHook On
*
* 3. Choose where the report on current activity should be written. If
* you want it reported to some place other than the error log, use the
* WhatKilledUsLog directive to specify a fully-qualified filename for
* the log. Note that the web server user id (e.g., "nobody") must
* be able to create or append to this log file, as the log file is
* not opened until a crash occurs.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "test_char.h" /* an odd one since it is not installed */
/* this module is not thread-safe; it is intended for Apache 1.3 on
* platforms that use single-threaded child processes for handling
* client connections
*/
static char *log_fname;
static char *local_addr;
static char *remote_addr;
static char *request_plus_headers;
static char buffer[2048];
static void exception_hook(ap_exception_info_t *ei)
{
int msg_len;
int logfd;
char msg_prefix[60];
time_t now;
char *newline;
int using_errorlog = 1;
time(&now);
ap_snprintf(msg_prefix, sizeof msg_prefix,
"[%s pid %ld mod_whatkilledus",
asctime(localtime(&now)),
(long)getpid());
newline = strchr(msg_prefix, '\n'); /* dang asctime() */
if (newline) { /* silly we are */
*newline = ']';
}
if (log_fname) {
logfd = open(log_fname, O_WRONLY|O_APPEND|O_CREAT, 0644);
if (logfd == -1) {
logfd = 2; /* unix, so fd 2 is the web server error log */
ap_snprintf(buffer, sizeof buffer,
"%s error %d opening %s\n",
msg_prefix, errno, log_fname);
write(logfd, buffer, strlen(buffer));
}
else {
using_errorlog = 0;
}
}
else {
logfd = 2;
}
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s sig %d crash\n",
msg_prefix, ei->sig);
write(logfd, buffer, msg_len);
if (local_addr) {
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s active connection: %s->%s\n",
msg_prefix, remote_addr, local_addr);
}
else {
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s no active connection at crash\n",
msg_prefix);
}
write(logfd, buffer, msg_len);
if (request_plus_headers) {
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s active request:\n",
msg_prefix);
write(logfd, buffer, msg_len);
write(logfd, request_plus_headers, strlen(request_plus_headers));
}
else {
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s no request active at crash\n",
msg_prefix);
write(logfd, buffer, msg_len);
}
msg_len = ap_snprintf(buffer, sizeof buffer,
"%s end of report\n",
msg_prefix);
write(logfd, buffer, msg_len);
if (!using_errorlog) {
close(logfd);
}
}
static void init(server_rec *s, pool *p)
{
int rc = ap_add_fatal_exception_hook(exception_hook);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s,
"fatal exception hooks are not enabled; please "
"enable them with the EnableExceptionHook directive "
"or disable mod_whatkilledus");
}
}
static void clear_conn_info(void *ignored)
{
local_addr = remote_addr = NULL;
}
static void save_conn_info(request_rec *r)
{
conn_rec *c = r->connection;
local_addr = ap_psprintf(c->pool, "%pI", &c->local_addr);
remote_addr = ap_psprintf(c->pool, "%pI", &c->remote_addr);
ap_register_cleanup(c->pool, NULL, clear_conn_info, ap_null_cleanup);
}
static void clear_req_info(void *ignored)
{
request_plus_headers = NULL;
}
#define FIELD_SEPARATOR "|"
#define KEYVAL_SEPARATOR ":"
/* graciously lifted from mod_log_forensic */
static int count_string(const char *p)
{
int n;
for (n = 0; *p; ++p, ++n) {
if (test_char_table[*(unsigned char *)p] & T_ESCAPE_FORENSIC) {
n += 2;
}
}
return n;
}
static int count_headers(void *in_len, const char *key, const char *value)
{
int *len = in_len;
*len += strlen(FIELD_SEPARATOR);
*len += count_string(key);
*len += strlen(KEYVAL_SEPARATOR);
*len += count_string(value);
return 1;
}
static char *copy_and_escape(char *loc, const char *str) {
/* mod_log_forensic will SIGABRT here if it messed up the count
* and overflowed; mod_whatkilledus will segfault here or will
* SIGABRT back in the caller if that happens
*/
for ( ; *str; ++str) {
if (test_char_table[*(unsigned char *)str] & T_ESCAPE_FORENSIC) {
*loc++ = '%';
sprintf(loc, "%02x", *(unsigned char *)str);
loc += 2;
}
else {
*loc++ = *str;
}
}
*loc = '\0';
return loc;
}
static int copy_headers(void *in_ch, const char *key, const char *value)
{
char **ch = in_ch;
strcpy(*ch, FIELD_SEPARATOR);
*ch += strlen(FIELD_SEPARATOR);
*ch = copy_and_escape(*ch, key);
strcpy(*ch, KEYVAL_SEPARATOR);
*ch += strlen(KEYVAL_SEPARATOR);
*ch = copy_and_escape(*ch, value);
return 1;
}
static void save_req_info(request_rec *r)
{
/* to save for the request:
* r->the_request +
* foreach header:
* '|' + header field
*/
int len = strlen(r->the_request);
char *ch;
ap_table_do(count_headers, &len, r->headers_in, NULL);
request_plus_headers = ap_palloc(r->pool, len + 2 /* 2 for the '\n' + '\0' at end */);
ch = request_plus_headers;
strcpy(ch, r->the_request);
ch += strlen(ch);
ap_table_do(copy_headers, &ch, r->headers_in, NULL);
*ch = '\n';
*(ch + 1) = '\0';
ap_assert(ch == request_plus_headers + len);
ap_register_cleanup(r->pool, NULL, clear_req_info, ap_null_cleanup);
}
static int post_read(request_rec *r)
{
if (r->prev) { /* we were already called for this internal redirect */
return DECLINED;
}
/* save whatever info, like client, which vhost, which port
* (to know SSL or not), etc.
*/
if (!local_addr) { /* first request on this connection */
save_conn_info(r);
}
save_req_info(r);
return DECLINED;
}
static const char *cmd_file(cmd_parms *cmd, void *dconf, char *fname)
{
log_fname = ap_pstrdup(cmd->pool, fname);
return NULL;
}
static const command_rec command_table[] = {
{
"WhatKilledUsLog", cmd_file, NULL, RSRC_CONF, TAKE1, "the fully-qualified filename of the mod_whatkilledus logfile"
}
,
{
NULL
}
};
module MODULE_VAR_EXPORT whatkilledus_module = {
STANDARD_MODULE_STUFF,
init, /* initializer */
NULL, /* create per-dir config */
NULL, /* merge per-dir config */
NULL, /* server config */
NULL, /* merge server config */
command_table, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
post_read /* post read-request */
};
1.1 apache-1.3/src/modules/experimental/mod_whatkilledus.exp
Index: mod_whatkilledus.exp
===================================================================
whatkilledus_module