/****************************************************************************
**
** Copyright (C) 2010 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