[PATCH-V2] xserver: Optional backtrace handler

Simon Farnsworth simon.farnsworth at onelan.co.uk
Fri Sep 23 02:40:35 PDT 2011


Review inline. Commit message is fine this time, so snipped.

On Thursday 22 September 2011, Barry Scott <barry.scott at onelan.co.uk> wrote:
<snip>
> diff --git a/dix/main.c b/dix/main.c
> index 16575ce..96b3f37 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,8 @@ int main(int argc, char *argv[], char *envp[])
>  	free(ConnectionInfo);
>  	ConnectionInfo = NULL;
>      }
> +
> +    xorg_cleanup_backtrace();
> +
>      return 0;
>  }
> -

Nitpick: why remove this blank line?

> diff --git a/include/os.h b/include/os.h
> index a553f57..48debe9 100644
> --- a/include/os.h
> +++ b/include/os.h
> @@ -542,5 +542,8 @@ extern _X_EXPORT void Error(const char *str);
>  extern _X_EXPORT void LogPrintMarkers(void);
> 
>  extern _X_EXPORT void xorg_backtrace(void);
> +extern _X_EXPORT void xorg_init_backtrace(void);
> +extern _X_EXPORT void xorg_cleanup_backtrace(void);
> +extern const char *BacktraceHandlerPath;
>

I still think these ought to be _X_HIDDEN or _X_INTERNAL, rather than
_X_EXPORT - they shouldn't be called by modules, for example.

>  #endif /* OS_H */

<snip>
> diff --git a/os/backtrace.c b/os/backtrace.c
> index 7ca6dab..c399a1f 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,114 @@ 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_stdout);
> +            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;
> +
> +        /* tell backtrace handler xorg is about to exit cleanly */
> +        while (write(backtrace_handler_stdin, cmd_exit, sizeof(cmd_exit ) - 
1) < 0)
> +            if (errno != EAGAIN && errno != EINTR)
> +                break;
> +
> +        while (waitpid(child_pid, &status, 0) < 0)
> +            if (errno != EINTR)
> +                break;
> +
> +        BacktraceHandlerPath = NULL;

Nitpick: I'd set BacktraceHandlerPath to NULL at the beginning of this
code block (before the write and waitpid). I know it makes very little
difference (since X is supposed to be single-threaded), but given that
we're in trouble if xorg_backtrace is ever called, I'd rather do it as:

if (BacktraceHandlerPath) {
	int status;

	BacktraceHandlerPath = NULL;

to maximise the chance of getting a backtrace if
xorg_cleanup_backtrace is called immediately before a signal or
unexpected thread (maybe one from AIGLX) calls xorg_backtrace.

Also, it took me a few seconds to convince myself that we'd be able to
escape an endless loop if the writes never succeeded - a quick comment
to point out that we are guaranteed EPIPE on the reads and writes, and
ECHILD on the waitpids if the handler dies would be nice.

> +    }
> +}
<snip>

Apart from the nitpicks above, it looks good to me; with them fixed, feel free 
to add my reviewed-by:

Reviewed-by: Simon Farnsworth <simon.farnsworth at onelan.co.uk>

-- 
Simon Farnsworth
Software Engineer
ONELAN Limited
http://www.onelan.com/


More information about the xorg-devel mailing list