~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/corelib/kernel/qcrashhandler.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the core module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
/*************************************************************************
 
30
 *
 
31
 * Original copyright notice:
 
32
 *
 
33
 * stacktrace.c 1.2 1998/12/21
 
34
 *
 
35
 * Copyright (c) 1998 by Bjorn Reese <breese@imada.ou.dk>
 
36
 *
 
37
 * Permission to use, copy, modify, and distribute this software for any
 
38
 * purpose with or without fee is hereby granted, provided that the above
 
39
 * copyright notice and this permission notice appear in all copies.
 
40
 *
 
41
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 
42
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 
43
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
 
44
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
 
45
 *
 
46
 ************************************************************************/
 
47
 
 
48
#include "qplatformdefs.h"
 
49
#include "private/qcrashhandler_p.h"
 
50
#include <stdio.h>
 
51
#include <signal.h>
 
52
#include <stdlib.h>
 
53
 
 
54
QtCrashHandler QSegfaultHandler::callback = 0;
 
55
 
 
56
#if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__)
 
57
# include "qstring.h"
 
58
# include <execinfo.h>
 
59
static void print_backtrace(FILE *outb)
 
60
{
 
61
    void *stack[128];
 
62
    int stack_size = backtrace(stack, sizeof(stack) / sizeof(void *));
 
63
    char **stack_symbols = backtrace_symbols(stack, stack_size);
 
64
    fprintf(outb, "Stack [%d]:\n", stack_size);
 
65
    if(FILE *cppfilt = popen("c++filt", "rw")) {
 
66
        dup2(fileno(outb), fileno(cppfilt));
 
67
        for(int i = stack_size-1; i>=0; --i)
 
68
            fwrite(stack_symbols[i], 1, strlen(stack_symbols[i]), cppfilt);
 
69
        pclose(cppfilt);
 
70
    } else {
 
71
        for(int i = stack_size-1; i>=0; --i)
 
72
            fprintf(outb, "#%d  %p [%s]\n", i, stack[i], stack_symbols[i]);
 
73
    }
 
74
}
 
75
static void init_backtrace(char **, int)
 
76
{
 
77
}
 
78
#else /* Don't use the GLIBC callback */
 
79
/* Code sourced from: */
 
80
#include <stdarg.h>
 
81
#include <string.h>
 
82
#include <errno.h>
 
83
#include <sys/types.h>
 
84
#include <sys/wait.h>
 
85
#if defined(Q_OS_IRIX) && defined(USE_LIBEXC)
 
86
# include <libexc.h>
 
87
#endif
 
88
 
 
89
static char *globalProgName = NULL;
 
90
static bool backtrace_command(FILE *outb, const char *format, ...)
 
91
{
 
92
 
 
93
    bool ret = false;
 
94
    char buffer[50];
 
95
 
 
96
    /*
 
97
     * Please note that vsprintf() is not ASync safe (ie. cannot safely
 
98
     * be used from a signal handler.) If this proves to be a problem
 
99
     * then the cmd string can be built by more basic functions such as
 
100
     * strcpy, strcat, and a homemade integer-to-ascii function.
 
101
     */
 
102
    va_list args;
 
103
    char cmd[512];
 
104
    va_start(args, format);
 
105
    vsprintf(cmd, format, args);
 
106
    va_end(args);
 
107
 
 
108
    char *foo = cmd;
 
109
#if 0
 
110
    foo = "echo hi";
 
111
#endif
 
112
    if(FILE *inb = popen(foo, "r")) {
 
113
        while(!feof(inb)) {
 
114
            int len = fread(buffer, 1, sizeof(buffer), inb);
 
115
            if(!len)
 
116
                break;
 
117
            if(!ret) {
 
118
                fwrite("Output from ", 1, strlen("Output from "), outb);
 
119
                strtok(cmd, " ");
 
120
                fwrite(cmd, 1, strlen(cmd), outb);
 
121
                fwrite("\n", 1, 1, outb);
 
122
                ret = true;
 
123
            }
 
124
            fwrite(buffer, 1, len, outb);
 
125
        }
 
126
        fclose(inb);
 
127
    }
 
128
    return ret;
 
129
}
 
130
 
 
131
static void init_backtrace(char **argv, int argc)
 
132
{
 
133
    if(argc >= 1)
 
134
        globalProgName = argv[0];
 
135
}
 
136
 
 
137
static void print_backtrace(FILE *outb)
 
138
{
 
139
    /*
 
140
     * In general dbx seems to do a better job than gdb.
 
141
     *
 
142
     * Different dbx implementations require different flags/commands.
 
143
     */
 
144
#if defined(Q_OS_AIX)
 
145
    if(backtrace_command(outb, "dbx -a %d 2>/dev/null <<EOF\n"
 
146
                         "where\n"
 
147
                         "detach\n"
 
148
                         "EOF\n",
 
149
                         (int)getpid()))
 
150
        return;
 
151
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
152
                         "set prompt\n"
 
153
                         "where\n"
 
154
                         "detach\n"
 
155
                         "quit\n"
 
156
                         "EOF\n",
 
157
                         globalProgName, (int)getpid()))
 
158
        return;
 
159
#elif defined(Q_OS_FREEBSD)
 
160
    /*
 
161
     * FreeBSD insists on sending a SIGSTOP to the process we
 
162
     * attach to, so we let the debugger send a SIGCONT to that
 
163
     * process after we have detached.
 
164
     */
 
165
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
166
                         "set prompt\n"
 
167
                         "where\n"
 
168
                         "detach\n"
 
169
                         "shell kill -CONT %d\n"
 
170
                         "quit\n"
 
171
                         "EOF\n",
 
172
                         globalProgName, (int)getpid(), (int)getpid()))
 
173
        return;
 
174
#elif defined(Q_OS_HPUX)
 
175
    /*
 
176
     * HP decided to call their debugger xdb.
 
177
     *
 
178
     * This does not seem to work properly yet. The debugger says
 
179
     * "Note: Stack traces may not be possible until you are
 
180
     *  stopped in user code." on HP-UX 09.01
 
181
     *
 
182
     * -L = line-oriented interface.
 
183
     * "T [depth]" gives a stacktrace with local variables.
 
184
     * The final "y" is confirmation to the quit command.
 
185
     */
 
186
    if(backtrace_command(outb, "xdb -P %d -L %s 2>&1 <<EOF\n"
 
187
                         "T 50\n"
 
188
                         "q\ny\n"
 
189
                         "EOF\n",
 
190
                         (int)getpid(), globalProgName))
 
191
        return;
 
192
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
193
                         "set prompt\n"
 
194
                         "where\n"
 
195
                         "detach\n"
 
196
                         "quit\n"
 
197
                         "EOF\n",
 
198
                         globalProgName, (int)getpid()))
 
199
        return;
 
200
#elif defined(Q_OS_IRIX)
 
201
    /*
 
202
     * "set $page=0" drops hold mode
 
203
     * "dump ." displays the contents of the variables
 
204
     */
 
205
    if(backtrace_command(outb, "dbx -p %d 2>/dev/null <<EOF\n"
 
206
                         "set \\$page=0\n"
 
207
                         "where\n"
 
208
# if !defined(__GNUC__)
 
209
                         /* gcc does not generate this information */
 
210
                         "dump .\n"
 
211
# endif
 
212
                         "detach\n"
 
213
                         "EOF\n",
 
214
                         (int)getpid()))
 
215
        return;
 
216
 
 
217
# if defined(USE_LIBEXC)
 
218
    if(trace_back_stack_and_print())
 
219
        return;
 
220
# endif
 
221
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
222
                         "set prompt\n"
 
223
                         "where\n"
 
224
                         "echo ---\\n\n"
 
225
                         "frame 5\n"      /* Skip signal handler frames */
 
226
                         "set \\$x = 50\n"
 
227
                         "while (\\$x)\n" /* Print local variables for each frame */
 
228
                         "info locals\n"
 
229
                         "up\n"
 
230
                         "set \\$x--\n"
 
231
                         "end\n"
 
232
                         "echo ---\\n\n"
 
233
                         "detach\n"
 
234
                         "quit\n"
 
235
                         "EOF\n",
 
236
                         globalProgName, (int)getpid()))
 
237
        return;
 
238
#elif defined(Q_OS_OSF)
 
239
    if(backtrace_command(outb, "dbx -pid %d %s 2>/dev/null <<EOF\n"
 
240
                         "where\n"
 
241
                         "detach\n"
 
242
                         "quit\n"
 
243
                         "EOF\n",
 
244
                         (int)getpid(), globalProgName))
 
245
        return;
 
246
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
247
                         "set prompt\n"
 
248
                         "where\n"
 
249
                         "detach\n"
 
250
                         "quit\n"
 
251
                         "EOF\n",
 
252
                         globalProgName, (int)getpid()))
 
253
        return;
 
254
#elif defined(Q_OS_SCO)
 
255
    /*
 
256
     * SCO OpenServer dbx is like a catch-22. The 'detach' command
 
257
     * depends on whether ptrace(S) support detaching or not. If it
 
258
     * is supported then 'detach' must be used, otherwise the process
 
259
     * will be killed upon dbx exit. If it isn't supported then 'detach'
 
260
     * will cause the process to be killed. We do not want it to be
 
261
     * killed.
 
262
     *
 
263
     * Out of two evils, the omission of 'detach' was chosen because
 
264
     * it worked on our system.
 
265
     */
 
266
    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
 
267
                         "where\n"
 
268
                         "quit\nEOF\n",
 
269
                         globalProgName, (int)getpid()))
 
270
        return;
 
271
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
272
                         "set prompt\n"
 
273
                         "where\n"
 
274
                         "detach\n"
 
275
                         "quit\n"
 
276
                         "EOF\n",
 
277
                         globalProgName, (int)getpid()))
 
278
        return;
 
279
#elif defined(Q_OS_SOLARIS)
 
280
    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
 
281
                         "where\n"
 
282
                         "detach\n"
 
283
                         "EOF\n",
 
284
                         globalProgName, (int)getpid()))
 
285
        return;
 
286
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
287
                         "set prompt\n"
 
288
                         "where\n"
 
289
                         "echo ---\\n\n"
 
290
                         "frame 5\n"      /* Skip signal handler frames */
 
291
                         "set \\$x = 50\n"
 
292
                         "while (\\$x)\n" /* Print local variables for each frame */
 
293
                         "info locals\n"
 
294
                         "up\n"
 
295
                         "set \\$x--\n"
 
296
                         "end\n"
 
297
                         "echo ---\\n\n"
 
298
                         "detach\n"
 
299
                         "quit\n"
 
300
                         "EOF\n",
 
301
                         globalProgName, (int)getpid()))
 
302
        return;
 
303
    if(backtrace_command(outb, "/usr/proc/bin/pstack %d",
 
304
                         (int)getpid()))
 
305
        return;
 
306
    /*
 
307
     * Other Unices (AIX, HPUX, SCO) also have adb, but
 
308
     * they seem unable to attach to a running process.)
 
309
     */
 
310
    if(backtrace_command(outb, "adb %s 2>&1 <<EOF\n"
 
311
                         "0t%d:A\n" /* Attach to pid */
 
312
                         "\\$c\n"   /* print stacktrace */
 
313
                         ":R\n"     /* Detach */
 
314
                         "\\$q\n"   /* Quit */
 
315
                         "EOF\n",
 
316
                         globalProgName, (int)getpid()))
 
317
        return;
 
318
#else /* All other platforms */
 
319
    /*
 
320
     * TODO: SCO/UnixWare 7 must be something like (not tested)
 
321
     *  debug -i c <pid> <<EOF\nstack -f 4\nquit\nEOF\n
 
322
     */
 
323
# if !defined(__GNUC__)
 
324
    if(backtrace_command(outb, "dbx %s %d 2>/dev/null <<EOF\n"
 
325
                         "where\n"
 
326
                         "detach\n"
 
327
                         "EOF\n",
 
328
                         globalProgName, (int)getpid()))
 
329
        return;
 
330
# endif
 
331
    if(backtrace_command(outb, "gdb -q %s %d 2>/dev/null <<EOF\n"
 
332
                         "set prompt\n"
 
333
                         "where\n"
 
334
#if 0
 
335
                         "echo ---\\n\n"
 
336
                         "frame 4\n"
 
337
                         "set \\$x = 50\n"
 
338
                         "while (\\$x)\n"
 
339
                         "info locals\n"
 
340
                         "up\n"
 
341
                         "set \\$x--\n"
 
342
                         "end\n"
 
343
                         "echo ---\\n\n"
 
344
#endif
 
345
                         "detach\n"
 
346
                         "quit\n"
 
347
                         "EOF\n",
 
348
                         globalProgName, (int)getpid()))
 
349
        return;
 
350
#endif
 
351
    const char debug_err[] = "No debugger found\n";
 
352
    fwrite(debug_err, strlen(debug_err), 1, outb);
 
353
}
 
354
/* end of copied code */
 
355
#endif
 
356
 
 
357
 
 
358
void qt_signal_handler(int sig)
 
359
{
 
360
    signal(sig, SIG_DFL);
 
361
    if(QSegfaultHandler::callback) {
 
362
        (*QSegfaultHandler::callback)();
 
363
        _exit(1);
 
364
    }
 
365
    FILE *outb = stderr;
 
366
    if(char *crash_loc = ::getenv("QT_CRASH_OUTPUT")) {
 
367
        if(FILE *new_outb = fopen(crash_loc, "w")) {
 
368
            fprintf(stderr, "Crash (backtrace written to %s)!!!\n", crash_loc);
 
369
            outb = new_outb;
 
370
        }
 
371
    } else {
 
372
        fprintf(outb, "Crash!!!\n");
 
373
    }
 
374
    print_backtrace(outb);
 
375
    if(outb != stderr)
 
376
        fclose(outb);
 
377
    _exit(1);
 
378
}
 
379
 
 
380
 
 
381
void
 
382
QSegfaultHandler::initialize(char **argv, int argc)
 
383
{
 
384
    init_backtrace(argv, argc);
 
385
 
 
386
    struct sigaction SignalAction;
 
387
    SignalAction.sa_flags = 0;
 
388
    SignalAction.sa_handler = qt_signal_handler;
 
389
    sigemptyset(&SignalAction.sa_mask);
 
390
    sigaction(SIGSEGV, &SignalAction, NULL);
 
391
    sigaction(SIGBUS, &SignalAction, NULL);
 
392
}