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