The attached patch changes sig_coredump to call a hook. In the fullness of time, the ap_exception_info_t provided to the hook would contain any and all relevant information available to a signal/exception handler (e.g., siginfo_t on many Unix variants).

mod_whatkilledus.c is a pretty trim module, with no current ability to deal with multi-threaded MPMs, but with relatively little code it puts the hook to good use in order to log a synopsis of what was going on at the time of the fatal exception.

Here is an example of information written by mod_whatkilledus, followed by the real trace message written by the parent when it realizes that a child has died:

Fatal signal 11 received in pid 6518:
Active request: GET /silly?fn=govrr
Client IP: 127.0.0.1
Local port: 8080
Connection aborted? no
[Wed Feb 19 13:56:04 2003] [notice] child pid 6518 exit signal Segmentation fault (11), possible coredump in /home/trawick/apacheinst

Some of the obvious things to consider:

have common sig_coredump for Unix

let sig_coredump receive siginfo_t on platforms that support that flavor, and store it in ap_exception_info_t

recognize a recursive call to sig_coredump and don't call the hook since presumably a hook has itself segfaulted

have a way for module to know that the hook is available (I have no
plans to implement exception handlers and related hook on Win32 :) )

get some feeling that Win32 or other non-Unix platforms could represent relevant info in ap_exception_info_t

Thoughts/concerns?
Index: include/ap_mpm.h
===================================================================
RCS file: /home/cvs/httpd-2.0/include/ap_mpm.h,v
retrieving revision 1.34
diff -u -r1.34 ap_mpm.h
--- include/ap_mpm.h    3 Feb 2003 17:52:53 -0000       1.34
+++ include/ap_mpm.h    19 Feb 2003 18:46:54 -0000
@@ -197,4 +197,10 @@
 #define AP_MONCONTROL(x)
 #endif
 
+typedef struct ap_exception_info_tag {
+    int sig;
+} ap_exception_info_t;
+
+AP_DECLARE_HOOK(int,fatal_exception_received,(ap_exception_info_t *ei))
+    
 #endif
Index: server/mpm/prefork/prefork.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/prefork/prefork.c,v
retrieving revision 1.273
diff -u -r1.273 prefork.c
--- server/mpm/prefork/prefork.c        3 Feb 2003 17:53:24 -0000       1.273
+++ server/mpm/prefork/prefork.c        19 Feb 2003 18:46:55 -0000
@@ -353,9 +353,26 @@
 }
 #endif
 
+APR_HOOK_STRUCT(
+    APR_HOOK_LINK(fatal_exception_received)
+)
+
+AP_IMPLEMENT_HOOK_RUN_ALL(int,fatal_exception_received,
+                          (ap_exception_info_t *ei), (ei), OK, DECLINED)
+
+static int prefork_fatal_exception_received(ap_exception_info_t *ei)
+{
+    /* maybe prefork needs to do something to release any mutex it could be holding? 
+*/
+    return 0;
+}
+
 /* handle all varieties of core dumping signals */
 static void sig_coredump(int sig)
 {
+    ap_exception_info_t ei = {0};
+
+    ei.sig = sig;
+    ap_run_fatal_exception_received(&ei);
     chdir(ap_coredump_dir);
     apr_signal(sig, SIG_DFL);
     if (ap_my_pid == parent_pid) {
@@ -1285,6 +1302,7 @@
 
     ap_hook_open_logs(prefork_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
     ap_hook_pre_config(prefork_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_fatal_exception_received(prefork_fatal_exception_received, NULL, NULL, 
+APR_HOOK_MIDDLE);
 }
 
 static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg) 
#include <unistd.h>

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_connection.h"
#include "http_log.h"
#include "ap_mpm.h"

#include "apr_strings.h"
#include "apr_network_io.h"

module AP_MODULE_DECLARE_DATA whatkilledus_module;

typedef struct wku_req_info_tag {
    struct wku_req_info_tag *next;
    request_rec *r;
} wku_req_info_t;

typedef struct wku_conn_info_tag {
    int active;
    apr_pool_t *p;
    conn_rec *c;
    wku_req_info_t *reqs;
} wku_conn_info_t;

static wku_conn_info_t staticci;

static wku_req_info_t *get_new_ri(wku_conn_info_t *ci)
{
    wku_req_info_t *new = malloc(sizeof(*new));

    memset(new, 0, sizeof(*new));
    new->next = ci->reqs;
    ci->reqs = new;
    return new;
}

static wku_conn_info_t *get_new_ci(conn_rec *c)
{
    /* non-threaded MPM only :) */
    ap_assert(staticci.active == 0);
    memset(&staticci, 0, sizeof(staticci));
    staticci.active = 1;

    return &staticci;
}

static wku_conn_info_t *get_cur_ci(conn_rec *c)
{
    /* non-threaded MPM only :) */
    return &staticci;
}

static void free_ci(wku_conn_info_t *ci)
{
    /* todo: free any chained requests! */
    ci->active = 0;
}

static int wku_fatal_exception_received(ap_exception_info_t *ei)
{
    int sig = ei->sig;
    wku_conn_info_t *curconn;
    wku_req_info_t *curreq;
    char buf[4096];

    curconn = get_cur_ci(NULL); /* no conn_rec avail */

    if (!curconn) {
        const char *msg = "no active connection at time of fatal exception\n";
        
        write(2, msg, strlen(msg));
        return 0;
    }
    
    curreq = curconn->reqs; /* head of list */

    apr_snprintf(buf, sizeof(buf),
                 "Fatal signal %d received in pid %ld:\n"
                 "Active request: %s\n"
                 "Client IP: %s\n"
                 "Local port: %d\n"
                 "Connection aborted?  %s\n"
                 "",
                 sig,
                 (long)getpid(),
                 curreq ? curreq->r->the_request : "no active request",
                 curconn->c->remote_ip,
                 (int)curconn->c->local_addr->port,
                 curconn->c->aborted ? "yes" : "no"
        );

    write(2, buf, strlen(buf));
    
    return 0;
}

static apr_status_t wku_connection_end(void *void_ci)
{
    wku_conn_info_t *ci = void_ci;

    free_ci(ci);
    return APR_SUCCESS;
}

static int wku_pre_connection(conn_rec *c, void *vcsd)
{
    wku_conn_info_t *new;

    new = get_new_ci(c);
    new->c = c;
    new->p = c->pool;

    apr_pool_cleanup_register(c->pool, new, wku_connection_end, NULL);

    return DECLINED;
}

static int wku_post_read_request(request_rec *r)
{
    wku_conn_info_t *cur;
    wku_req_info_t *new;
    
    cur = get_cur_ci(r->connection);
    new = get_new_ri(cur);

    new->r = r;

    return DECLINED;
}

static void wku_error_log(const char *file, int line, int level,
                          apr_status_t status, const server_rec *s,
                          const request_rec *r, apr_pool_t *pool,
                          const char *errstr)
{
}

static void wku_register_hooks(apr_pool_t *p)
{
    ap_hook_pre_connection(wku_pre_connection, NULL, NULL, APR_HOOK_LAST);
    ap_hook_post_read_request(wku_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_error_log(wku_error_log, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_fatal_exception_received(wku_fatal_exception_received, NULL, NULL, 
APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA whatkilledus_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    wku_register_hooks
};

Reply via email to