[PATCH-V4] xserver: Optional backtrace handler

Jeremy Huddleston jeremyhu at apple.com
Thu Sep 29 15:26:56 PDT 2011


Reviewed-by: Jeremy Huddleston <jeremyhu at apple.com>

On Sep 29, 2011, at 03:21, Barry Scott wrote:

> It is useful to be able to run an external program to analyse
> a crashed server process. The server will run a user supplied
> program in a subprocess of the server.
> 
> The subprocess is created when the server starts up so that all
> resources needed to create the subprocess are available. It is not
> always possible to start up a process from within a crashed server.
> For example if the malloc heap is corrupt fork will not work.
> 
> If the server crashes a "crash" message is sent to the handler on
> its stdin. The handler can then respond in whatever way it wishes.
> If the handler wants the server to continue, return from
> xorg_backtrace, it must write a line to its stdout.
> 
> When then server exits normally the handler is sent the "exit"
> message just before the server exits.
> 
> Signed-off-by: Barry Scott <barry.scott at onelan.co.uk>
> Reviewed-by: Simon Farnsworth <simon.farnsworth at onelan.co.uk>
> ---
> change from V3:
> * close backtrace_handler_stdin in child process
> 
> change from V2:
> * restore trailing NL in main.c
> * beef up comments to make it clea how loops are exited
>  when the child process dies
> * Use _X_HIDDEN for new symbols that are not to be exported
> 
> change from V1:
> 
> * check for EAGAIN and EINTR on calls to read(), write() and waitpid()
> * put -backtrace into lexical position in the command parser and usage
> * fix coding style white space and condition ordering
> * update man page with more details
> * call xorg_cleanup_backtrace() from FatalError()
> 
> dix/main.c      |    5 ++
> include/os.h    |    3 +
> man/Xserver.man |   20 +++++++++
> os/backtrace.c  |  123 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> os/log.c        |    3 +
> os/utils.c      |    8 ++++
> 6 files changed, 159 insertions(+), 3 deletions(-)
> 
> diff --git a/dix/main.c b/dix/main.c
> index 16575ce..3326e78 100644
> --- a/dix/main.c
> +++ b/dix/main.c
> @@ -147,6 +147,8 @@ int main(int argc, char *argv[], char *envp[])
> 
>     ProcessCommandLine(argc, argv);
> 
> +    xorg_init_backtrace();
> +
>     alwaysCheckForInput[0] = 0;
>     alwaysCheckForInput[1] = 1;
>     while(1)
> @@ -354,6 +356,9 @@ int main(int argc, char *argv[], char *envp[])
> 	free(ConnectionInfo);
> 	ConnectionInfo = NULL;
>     }
> +
> +    xorg_cleanup_backtrace();
> +
>     return 0;
> }
> 
> diff --git a/include/os.h b/include/os.h
> index a553f57..00336e5 100644
> --- a/include/os.h
> +++ b/include/os.h
> @@ -541,6 +541,9 @@ extern _X_EXPORT void ErrorF(const char *f, ...) _X_ATTRIBUTE_PRINTF(1,2);
> extern _X_EXPORT void Error(const char *str);
> extern _X_EXPORT void LogPrintMarkers(void);
> 
> +extern _X_HIDDEN void xorg_init_backtrace(void);
> extern _X_EXPORT void xorg_backtrace(void);
> +extern _X_HIDDEN void xorg_cleanup_backtrace(void);
> +extern _X_HIDDEN const char *BacktraceHandlerPath;
> 
> #endif /* OS_H */
> diff --git a/man/Xserver.man b/man/Xserver.man
> index f743912..9def022 100644
> --- a/man/Xserver.man
> +++ b/man/Xserver.man
> @@ -100,6 +100,9 @@ specifies a file which contains a collection of authorization records used
> to authenticate access.  See also the \fIxdm\fP(1) and
> \fIXsecurity\fP(__miscmansuffix__) manual pages.
> .TP 8
> +.B \-backtrace \fIbacktrace-handler-path\fP
> +specifies the path to the optional backtrace handler program. See \fBBACKTRACE HANDLER\fP below.
> +.TP 8
> .B \-br
> sets the default root window to solid black instead of the standard root weave
> pattern.   This is the default unless -retro or -wr is specified.
> @@ -530,6 +533,23 @@ the following font path:
>     /usr/share/fonts/default/ghostscript
> .fi
> 
> +.SH BACKTRACE HANDLER
> +
> +The optional backtrace handler program will be run in a subprocess when
> +the server starts. The handler is invoked with argv[1] set to the PID
> +of the server.
> +.PP
> +The handler is expected to read a line from stdin that will tell the
> +handler what has happened to the server.
> +.PP
> +When the server is about to exit normally "exit\\n" is sent to the handler.
> +.PP
> +If the server crashes "crash\n" is sent to the handler and the server will
> +wait for a line to be written out on the handler's stdout. This causes
> +the server to return from the xorg_backtrace function. Typically after calling
> +xorg_backtrace additional error messages are logged and the server shuts down.
> +If you do not want the server to continue then kill it.
> +
> .SH FILES
> .TP 30
> .I /etc/X\fBn\fP.hosts
> diff --git a/os/backtrace.c b/os/backtrace.c
> index 7ca6dab..bcdcbf6 100644
> --- a/os/backtrace.c
> +++ b/os/backtrace.c
> @@ -28,6 +28,11 @@
> #include "os.h"
> #include "misc.h"
> 
> +#include <unistd.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +
> #ifdef HAVE_BACKTRACE
> #ifndef _GNU_SOURCE
> #define _GNU_SOURCE
> @@ -35,7 +40,7 @@
> #include <dlfcn.h>
> #include <execinfo.h>
> 
> -void xorg_backtrace(void)
> +void xorg_builtin_backtrace(void)
> {
>     void *array[64];
>     const char *mod;
> @@ -180,7 +185,7 @@ static int xorg_backtrace_pstack(void) {
> 
> # if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
> 
> -void xorg_backtrace(void) {
> +void xorg_builtin_backtrace(void) {
> 
>     ErrorF("\nBacktrace:\n");
> 
> @@ -207,7 +212,119 @@ void xorg_backtrace(void) {
> # else
> 
> /* Default fallback if we can't find any way to get a backtrace */
> -void xorg_backtrace(void) { return; }
> +void xorg_builtin_backtrace(void) { return; }
> 
> # endif
> #endif
> +
> +const char *BacktraceHandlerPath = NULL;
> +
> +static int backtrace_handler_stdin;
> +static int backtrace_handler_stdout;
> +static pid_t child_pid;
> +
> +static const char cmd_crash[] = "crash\n";
> +static const char cmd_exit[] = "exit\n";
> +
> +static void report_init_error(const char *msg) {
> +    /* prevent any further use of the handler */
> +    BacktraceHandlerPath = NULL;
> +    FatalError("%s - errno %d", msg, errno);
> +}
> +
> +void xorg_init_backtrace(void) {
> +    char pid_buffer[32];
> +    int pipe_fds[2];
> +    int child_stdin;
> +    int child_stdout;
> +
> +    if (!BacktraceHandlerPath)
> +        return;
> +
> +    if (pipe(pipe_fds) != 0)
> +        report_init_error("failed to create pipe");
> +    backtrace_handler_stdin = pipe_fds[1];
> +    child_stdin = pipe_fds[0];
> +
> +    if (pipe(pipe_fds) != 0)
> +        report_init_error("failed to create pipe");
> +    backtrace_handler_stdout = pipe_fds[0];
> +    child_stdout = pipe_fds[1];
> +
> +    /* server pid will be passed in argv[1] */
> +    snprintf( pid_buffer, sizeof(pid_buffer), "%d", getpid());
> +
> +    child_pid = fork();
> +    if (child_pid < 0) {
> +        report_init_error("failed to fork");
> +    } else {
> +        if (child_pid == 0) {
> +            /* setup stdin, stdout to point to the pipes */
> +            dup2(child_stdin, STDIN_FILENO);
> +            dup2(child_stdout, STDOUT_FILENO);
> +
> +            /* close all other fds to the pipes */
> +            close(child_stdin);
> +            close(child_stdout);
> +            close(backtrace_handler_stdin);
> +            close(backtrace_handler_stdout);
> +
> +            execl(BacktraceHandlerPath, BacktraceHandlerPath, pid_buffer, NULL);
> +            exit(1);
> +        } else {
> +            if (close(child_stdin) != 0)
> +                report_init_error("failed to close pipe fd");
> +            if (close(child_stdout) != 0)
> +                report_init_error("failed to close pipe fd");
> +        }
> +    }
> +}
> +
> +void xorg_backtrace(void) {
> +    if (BacktraceHandlerPath) {
> +        int status;
> +
> +        /* check that the handler process is still running */
> +        if (waitpid(child_pid, &status, WNOHANG) == 0) {
> +            char buffer[1];
> +
> +            /* tell backtrace handler xorg has crashed */
> +            while (write(backtrace_handler_stdin, cmd_crash, sizeof(cmd_crash ) - 1) < 0)
> +                if (errno != EAGAIN && errno != EINTR)
> +                    break;
> +
> +            /* exit to caller after backtrace handler writes anything to stdout */
> +            while (read(backtrace_handler_stdout, buffer, sizeof(buffer)) < 0)
> +                if (errno != EAGAIN && errno != EINTR)
> +                    break;
> +
> +        } else {
> +            /* handler process has quit? run builtin handler */
> +            xorg_builtin_backtrace();
> +        }
> +    } else {
> +        xorg_builtin_backtrace();
> +    }
> +}
> +
> +void xorg_cleanup_backtrace(void) {
> +    if (BacktraceHandlerPath) {
> +        int status;
> +
> +        BacktraceHandlerPath = NULL;
> +
> +        /* tell backtrace handler xorg is about to exit cleanly
> +           EPIPE is returned if the child process closes the pipe
> +           (which happens when the process exits) and the loop will exit */
> +        while (write(backtrace_handler_stdin, cmd_exit, sizeof(cmd_exit ) - 1) < 0)
> +            if (errno != EAGAIN && errno != EINTR)
> +                break;
> +
> +        /* If the child has already exited waitpid return with ECHILD
> +           and the loop will exit */
> +        while (waitpid(child_pid, &status, 0) < 0)
> +            if (errno != EINTR)
> +                break;
> +
> +    }
> +}
> diff --git a/os/log.c b/os/log.c
> index f519762..7b3409a 100644
> --- a/os/log.c
> +++ b/os/log.c
> @@ -523,6 +523,9 @@ FatalError(const char *f, ...)
>     va_list args;
>     static Bool beenhere = FALSE;
> 
> +    /* make sure any backtrace handler process is told to exit */
> +    xorg_cleanup_backtrace();
> +
>     if (beenhere)
> 	ErrorF("\nFatalError re-entered, aborting\n");
>     else
> diff --git a/os/utils.c b/os/utils.c
> index 36cb46f..78c39e6 100644
> --- a/os/utils.c
> +++ b/os/utils.c
> @@ -470,6 +470,7 @@ void UseMsg(void)
>     ErrorF("-ac                    disable access control restrictions\n");
>     ErrorF("-audit int             set audit trail level\n");	
>     ErrorF("-auth file             select authorization file\n");	
> +    ErrorF("-backtrace string      path of program to run to handle a server crash\n");
>     ErrorF("-br                    create root window with black background\n");
>     ErrorF("+bs                    enable any backing store support\n");
>     ErrorF("-bs                    disable any backing store support\n");
> @@ -616,6 +617,13 @@ ProcessCommandLine(int argc, char *argv[])
> 	    else
> 		UseMsg();
> 	}
> +        else if ( strcmp( argv[i], "-backtrace") == 0)
> +        {
> +	    if(++i < argc)
> +                BacktraceHandlerPath = argv[i];
> +	    else
> +		UseMsg();
> +        }
> 	else if ( strcmp( argv[i], "-br") == 0) ; /* default */
> 	else if ( strcmp( argv[i], "+bs") == 0)
> 	    enableBackingStore = TRUE;
> -- 
> 1.7.6
> 



More information about the xorg-devel mailing list