Qt 4.8
qcrashhandler.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
70 
72 
73 #if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
75 # include "qstring.h"
76 # include <execinfo.h>
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: */
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
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);
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 
422 
423 #endif // QT_NO_CRASHHANDLER
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
static QtCrashHandler callback
#define QT_END_INCLUDE_NAMESPACE
This macro is equivalent to QT_BEGIN_NAMESPACE.
Definition: qglobal.h:92
static char * globalProgName
friend void qt_signal_handler(int)
static void initialize(char **, int)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
void(* QtCrashHandler)()
void qt_signal_handler(int sig)
static bool backtrace_command(FILE *outb, const char *format,...)
int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
A portable vsnprintf() function.
Definition: qvsnprintf.cpp:78
int sigaction(int, const struct sigaction *, struct sigaction *)
static void print_backtrace(FILE *outb)
static void init_backtrace(char **argv, int argc)
#define QT_BEGIN_INCLUDE_NAMESPACE
This macro is equivalent to QT_END_NAMESPACE.
Definition: qglobal.h:91