src/corelib/kernel/qcrashhandler.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/kernel/qcrashhandler.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,420 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*************************************************************************
+ *
+ * stacktrace.c 1.2 1998/12/21
+ *
+ * Copyright (c) 1998 by Bjorn Reese <breese@imada.ou.dk>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************/
+
+#include "qplatformdefs.h"
+#include "private/qcrashhandler_p.h"
+#include "qbytearray.h" // for qvsnprintf()
+
+#ifndef QT_NO_CRASHHANDLER
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QtCrashHandler QSegfaultHandler::callback = 0;
+
+#if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qstring.h"
+# include <execinfo.h>
+QT_END_INCLUDE_NAMESPACE
+
+static void print_backtrace(FILE *outb)
+{
+    void *stack[128];
+    int stack_size = backtrace(stack, sizeof(stack) / sizeof(void *));
+    char **stack_symbols = backtrace_symbols(stack, stack_size);
+    fprintf(outb, "Stack [%d]:\n", stack_size);
+    if(FILE *cppfilt = popen("c++filt", "rw")) {
+        dup2(fileno(outb), fileno(cppfilt));
+        for(int i = stack_size-1; i>=0; --i)
+            fwrite(stack_symbols[i], 1, strlen(stack_symbols[i]), cppfilt);
+        pclose(cppfilt);
+    } else {
+        for(int i = stack_size-1; i>=0; --i)
+            fprintf(outb, "#%d  %p [%s]\n", i, stack[i], stack_symbols[i]);
+    }
+}
+static void init_backtrace(char **, int)
+{
+}
+
+#else /* Don't use the GLIBC callback */
+/* Code sourced from: */
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#if defined(Q_OS_IRIX) && defined(USE_LIBEXC)
+# include <libexc.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+
+static char *globalProgName = NULL;
+static bool backtrace_command(FILE *outb, const char *format, ...)
+{
+
+    bool ret = false;
+    char buffer[50];
+
+    /*
+     * Please note that vsnprintf() is not ASync safe (ie. cannot safely
+     * be used from a signal handler.) If this proves to be a problem
+     * then the cmd string can be built by more basic functions such as
+     * strcpy, strcat, and a home-made integer-to-ascii function.
+     */
+    va_list args;
+    char cmd[512];
+    va_start(args, format);
+    qvsnprintf(cmd, 512, format, args);
+    va_end(args);
+
+    char *foo = cmd;
+#if 0
+    foo = "echo hi";
+#endif
+    if(FILE *inb = popen(foo, "r")) {
+        while(!feof(inb)) {
+            int len = fread(buffer, 1, sizeof(buffer), inb);
+            if(!len)
+                break;
+            if(!ret) {
+                fwrite("Output from ", 1, strlen("Output from "), outb);
+                strtok(cmd, " ");
+                fwrite(cmd, 1, strlen(cmd), outb);
+                fwrite("\n", 1, 1, outb);
+                ret = true;
+            }
+            fwrite(buffer, 1, len, outb);
+        }
+        fclose(inb);
+    }
+    return ret;
+}
+
+static void init_backtrace(char **argv, int argc)
+{
+    if(argc >= 1)
+        globalProgName = argv[0];
+}
+
+static void print_backtrace(FILE *outb)
+{
+    /*
+     * In general dbx seems to do a better job than gdb.
+     *
+     * Different dbx implementations require different flags/commands.
+     */
+#if defined(Q_OS_AIX)
+    if(backtrace_command(outb, "dbx -a %d 2>/dev/null <<EOF\n"
+                         "where\n"
+                         "detach\n"
+                         "EOF\n",
+                         (int)getpid()))
+        return;
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#elif defined(Q_OS_FREEBSD)
+    /*
+     * FreeBSD insists on sending a SIGSTOP to the process we
+     * attach to, so we let the debugger send a SIGCONT to that
+     * process after we have detached.
+     */
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "detach\n"
+                         "shell kill -CONT %d\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid(), (int)getpid()))
+        return;
+#elif defined(Q_OS_HPUX)
+    /*
+     * HP decided to call their debugger xdb.
+     *
+     * This does not seem to work properly yet. The debugger says
+     * "Note: Stack traces may not be possible until you are
+     *  stopped in user code." on HP-UX 09.01
+     *
+     * -L = line-oriented interface.
+     * "T [depth]" gives a stacktrace with local variables.
+     * The final "y" is confirmation to the quit command.
+     */
+    if(backtrace_command(outb, "xdb -P %d -L %s 2>&1 <<EOF\n"
+                         "T 50\n"
+                         "q\ny\n"
+                         "EOF\n",
+                         (int)getpid(), globalProgName))
+        return;
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#elif defined(Q_OS_IRIX)
+    /*
+     * "set $page=0" drops hold mode
+     * "dump ." displays the contents of the variables
+     */
+    if(backtrace_command(outb, "dbx -p %d 2>/dev/null <<EOF\n"
+                         "set \\$page=0\n"
+                         "where\n"
+# if !defined(__GNUC__)
+                         /* gcc does not generate this information */
+                         "dump .\n"
+# endif
+                         "detach\n"
+                         "EOF\n",
+                         (int)getpid()))
+        return;
+
+# if defined(USE_LIBEXC)
+    if(trace_back_stack_and_print())
+        return;
+# endif
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "echo ---\\n\n"
+                         "frame 5\n"      /* Skip signal handler frames */
+                         "set \\$x = 50\n"
+                         "while (\\$x)\n" /* Print local variables for each frame */
+                         "info locals\n"
+                         "up\n"
+                         "set \\$x--\n"
+                         "end\n"
+                         "echo ---\\n\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#elif defined(Q_OS_OSF)
+    if(backtrace_command(outb, "dbx -pid %d %s 2>/dev/null <<EOF\n"
+                         "where\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         (int)getpid(), globalProgName))
+        return;
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#elif defined(Q_OS_SCO)
+    /*
+     * SCO OpenServer dbx is like a catch-22. The 'detach' command
+     * depends on whether ptrace(S) support detaching or not. If it
+     * is supported then 'detach' must be used, otherwise the process
+     * will be killed upon dbx exit. If it isn't supported then 'detach'
+     * will cause the process to be killed. We do not want it to be
+     * killed.
+     *
+     * Out of two evils, the omission of 'detach' was chosen because
+     * it worked on our system.
+     */
+    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
+                         "where\n"
+                         "quit\nEOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#elif defined(Q_OS_SOLARIS)
+    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
+                         "where\n"
+                         "detach\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+                         "echo ---\\n\n"
+                         "frame 5\n"      /* Skip signal handler frames */
+                         "set \\$x = 50\n"
+                         "while (\\$x)\n" /* Print local variables for each frame */
+                         "info locals\n"
+                         "up\n"
+                         "set \\$x--\n"
+                         "end\n"
+                         "echo ---\\n\n"
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+    if(backtrace_command(outb, "/usr/proc/bin/pstack %d",
+                         (int)getpid()))
+        return;
+    /*
+     * Other Unices (AIX, HPUX, SCO) also have adb, but
+     * they seem unable to attach to a running process.)
+     */
+    if(backtrace_command(outb, "adb %s 2>&1 <<EOF\n"
+                         "0t%d:A\n" /* Attach to pid */
+                         "\\$c\n"   /* print stacktrace */
+                         ":R\n"     /* Detach */
+                         "\\$q\n"   /* Quit */
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#else /* All other platforms */
+    /*
+     * TODO: SCO/UnixWare 7 must be something like (not tested)
+     *  debug -i c <pid> <<EOF\nstack -f 4\nquit\nEOF\n
+     */
+# if !defined(__GNUC__)
+    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
+                         "where\n"
+                         "detach\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+# endif
+    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
+                         "set prompt\n"
+                         "where\n"
+#if 0
+                         "echo ---\\n\n"
+                         "frame 4\n"
+                         "set \\$x = 50\n"
+                         "while (\\$x)\n"
+                         "info locals\n"
+                         "up\n"
+                         "set \\$x--\n"
+                         "end\n"
+                         "echo ---\\n\n"
+#endif
+                         "detach\n"
+                         "quit\n"
+                         "EOF\n",
+                         globalProgName, (int)getpid()))
+        return;
+#endif
+    const char debug_err[] = "No debugger found\n";
+    fwrite(debug_err, strlen(debug_err), 1, outb);
+}
+/* end of copied code */
+#endif
+
+
+void qt_signal_handler(int sig)
+{
+    signal(sig, SIG_DFL);
+    if(QSegfaultHandler::callback) {
+        (*QSegfaultHandler::callback)();
+        _exit(1);
+    }
+    FILE *outb = stderr;
+    if(char *crash_loc = ::getenv("QT_CRASH_OUTPUT")) {
+        if(FILE *new_outb = fopen(crash_loc, "w")) {
+            fprintf(stderr, "Crash (backtrace written to %s)!!!\n", crash_loc);
+            outb = new_outb;
+        }
+    } else {
+        fprintf(outb, "Crash!!!\n");
+    }
+    print_backtrace(outb);
+    if(outb != stderr)
+        fclose(outb);
+    _exit(1);
+}
+
+
+void
+QSegfaultHandler::initialize(char **argv, int argc)
+{
+    init_backtrace(argv, argc);
+
+    struct sigaction SignalAction;
+    SignalAction.sa_flags = 0;
+    SignalAction.sa_handler = qt_signal_handler;
+    sigemptyset(&SignalAction.sa_mask);
+    sigaction(SIGSEGV, &SignalAction, NULL);
+    sigaction(SIGBUS, &SignalAction, NULL);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CRASHHANDLER