Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/SignalHandler.cc @ 1710

Last change on this file since 1710 was 1639, checked in by rgrieder, 16 years ago

merged nico's fixes for gcc 4.3 back to trunk.
I'm not going to delete branch yet.

  • Property svn:eol-style set to native
File size: 7.5 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Christoph Renner
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30    @file SignalHandler.cc
31    @brief Implementation of the SignalHandler class.
32*/
33
34#include "SignalHandler.h"
35
36#include <assert.h>
37#include <iostream>
38#include <cstdlib>
39#include <string.h>
40
41#include "Debug.h"
42
43SignalHandler * SignalHandler::singletonRef = NULL;
44
45#ifndef __WIN32__
46
47#include <wait.h>
48#include <X11/Xlib.h>
49#include <X11/Xutil.h>
50#include <X11/keysym.h>
51
52bool SignalHandler::bXAutoKeyRepeatOn_ = false;
53
54SignalHandler::SignalHandler()
55{
56}
57
58/**
59 * register signal handlers for SIGSEGV and SIGABRT
60 * @param appName path to executable eg argv[0]
61 * @param fileName filename to append backtrace to
62 */
63void SignalHandler::doCatch( const std::string & appName, const std::string & fileName )
64{
65  this->appName = appName;
66  this->fileName = fileName;
67
68  // prepare for restoring XAutoKeyRepeat
69  Display* display;
70  if ((display = XOpenDisplay(0)))
71  {
72    XKeyboardState oldState;
73    XGetKeyboardControl(display, &oldState);
74    if (oldState.global_auto_repeat == AutoRepeatModeOn)
75      bXAutoKeyRepeatOn_ = true;
76    else
77      bXAutoKeyRepeatOn_ = false;
78    XCloseDisplay(display);
79  }
80  else
81  {
82    std::cout << "Warning: couldn't open X display to restore XAutoKeyRepeat." << std::endl;
83    bXAutoKeyRepeatOn_ = false;
84  }
85
86
87  // make sure doCatch is only called once without calling dontCatch
88  assert( sigRecList.size() == 0 );
89
90  catchSignal( SIGSEGV );
91  catchSignal( SIGABRT );
92  catchSignal( SIGILL );
93}
94
95/**
96 * restore previous signal handlers
97 */
98void SignalHandler::dontCatch()
99{
100  for ( SignalRecList::iterator it = sigRecList.begin(); it != sigRecList.end(); it++ )
101  {
102    signal( it->signal, it->handler );
103  }
104
105  sigRecList.clear();
106}
107
108/**
109 * catch signal sig
110 * @param sig signal to catch
111 */
112void SignalHandler::catchSignal( int sig )
113{
114  sig_t handler = signal( sig, SignalHandler::sigHandler );
115
116  assert( handler != SIG_ERR );
117
118  SignalRec rec;
119  rec.signal = sig;
120  rec.handler = handler;
121
122  sigRecList.push_front( rec );
123}
124
125/**
126 * sigHandler is called when receiving signals
127 * @param sig
128 */
129void SignalHandler::sigHandler( int sig )
130{
131  for ( SignalCallbackList::iterator it = SignalHandler::getInstance()->callbackList.begin(); it != SignalHandler::getInstance()->callbackList.end(); it++  )
132  {
133    (*(it->cb))( it->someData );
134  }
135
136  std::string sigName = "UNKNOWN";
137
138  switch ( sig )
139  {
140    case SIGSEGV:
141      sigName = "SIGSEGV";
142      break;
143    case SIGABRT:
144      sigName = "SIGABRT";
145      break;
146    case SIGILL:
147      sigName = "SIGILL";
148      break;
149  }
150
151  if (bXAutoKeyRepeatOn_)
152  {
153    std::cout << "Trying to restore XAutoKeyRepeat" << std::endl;
154    Display* display;
155    if ((display = XOpenDisplay(0)))
156    {
157      XAutoRepeatOn(display);
158      XCloseDisplay(display);
159    }
160  }
161
162  PRINTF(0)( "recieved signal %s\ntry to write backtrace to file orxonox.log\n", sigName.c_str() );
163
164  int sigPipe[2];
165  if ( pipe(sigPipe) == -1 )
166  {
167    perror("pipe failed!\n");
168    exit(EXIT_FAILURE);
169  }
170
171  int sigPid = fork();
172
173  if ( sigPid == -1 )
174  {
175    perror("fork failed!\n");
176    exit(EXIT_FAILURE);
177  }
178
179  // gdb will be attached to this process
180  if ( sigPid == 0 )
181  {
182    getInstance()->dontCatch();
183    // wait for message from parent when it has attached gdb
184    int someData;
185
186    read( sigPipe[0], &someData, sizeof(someData) );
187
188    if ( someData != 0x12345678 )
189    {
190      PRINTF(0)("something went wrong :(\n");
191    }
192
193    return;
194  }
195
196  int gdbIn[2];
197  int gdbOut[2];
198  int gdbErr[2];
199
200  if ( pipe(gdbIn) == -1 || pipe(gdbOut) == -1 || pipe(gdbErr) == -1 )
201  {
202    perror("pipe failed!\n");
203    kill( sigPid, SIGTERM );
204    waitpid( sigPid, NULL, 0 );
205    exit(EXIT_FAILURE);
206  }
207
208  int gdbPid = fork();
209  // this process will run gdb
210
211  if ( gdbPid == -1 )
212  {
213    perror("fork failed\n");
214    kill( sigPid, SIGTERM );
215    waitpid( sigPid, NULL, 0 );
216    exit(EXIT_FAILURE);
217  }
218
219  if ( gdbPid == 0 )
220  {
221    // start gdb
222
223    close(gdbIn[1]);
224    close(gdbOut[0]);
225    close(gdbErr[0]);
226
227    dup2( gdbIn[0], STDIN_FILENO );
228    dup2( gdbOut[1], STDOUT_FILENO );
229    dup2( gdbErr[1], STDERR_FILENO );
230
231    execlp( "sh", "sh", "-c", "gdb", (void*)NULL);
232  }
233
234  char cmd[256];
235  snprintf( cmd, 256, "file %s\nattach %d\nc\n", getInstance()->appName.c_str(), sigPid );
236  write( gdbIn[1], cmd, strlen(cmd) );
237
238  int charsFound = 0;
239  int promptFound = 0;
240  char byte;
241  while ( read( gdbOut[0], &byte, 1 ) == 1 )
242  {
243    if (
244      charsFound == 0 && byte == '(' ||
245      charsFound == 1 && byte == 'g' ||
246      charsFound == 2 && byte == 'd' ||
247      charsFound == 3 && byte == 'b' ||
248      charsFound == 4 && byte == ')' ||
249      charsFound == 5 && byte == ' '
250        )
251          charsFound++;
252    else
253      charsFound = 0;
254
255    if ( charsFound == 6 )
256    {
257      promptFound++;
258      charsFound = 0;
259    }
260
261    if ( promptFound == 3 )
262    {
263      break;
264    }
265  }
266
267  int someData = 0x12345678;
268  write( sigPipe[1], &someData, sizeof(someData) );
269
270  write( gdbIn[1], "bt\nk\nq\n", 7 );
271
272
273  charsFound = 0;
274  promptFound = 0;
275  std::string bt;
276  while ( read( gdbOut[0], &byte, 1 ) == 1 )
277  {
278    bt += std::string( &byte, 1 );
279
280    if (
281      charsFound == 0 && byte == '(' ||
282      charsFound == 1 && byte == 'g' ||
283      charsFound == 2 && byte == 'd' ||
284      charsFound == 3 && byte == 'b' ||
285      charsFound == 4 && byte == ')' ||
286      charsFound == 5 && byte == ' '
287        )
288          charsFound++;
289    else
290      charsFound = 0;
291
292    if ( charsFound == 6 )
293    {
294      promptFound++;
295      charsFound = 0;
296      bt += "\n";
297    }
298
299    if ( promptFound == 3 )
300    {
301      break;
302    }
303  }
304
305
306  waitpid( sigPid, NULL, 0 );
307  waitpid( gdbPid, NULL, 0 );
308
309  int wsRemoved = 0;
310
311  while ( wsRemoved < 2 && bt.length() > 0 )
312  {
313    if ( bt[1] == '\n' )
314      wsRemoved++;
315    bt.erase(0, 1);
316  }
317
318  if ( bt.length() > 0 )
319    bt.erase(0, 1);
320
321  time_t now = time(NULL);
322
323  std::string timeString = "\n\n\n\n"
324                     "=======================================================\n"
325                     "= time: " + std::string(ctime(&now)) +
326         "=======================================================\n";
327  bt.insert(0, timeString);
328
329  FILE * f = fopen( getInstance()->fileName.c_str(), "a" );
330
331  if ( !f )
332  {
333    perror( ( std::string( "could not append to " ) + getInstance()->fileName ).c_str() );
334    exit(EXIT_FAILURE);
335  }
336
337  if ( fwrite( bt.c_str(), 1, bt.length(), f ) != bt.length() )
338  {
339    PRINTF(0)( ( std::string("could not write %d byte to ") + getInstance()->fileName ).c_str(), bt.length());
340    exit(EXIT_FAILURE);
341  }
342
343  exit(EXIT_FAILURE);
344}
345
346void SignalHandler::registerCallback( SignalCallback cb, void * someData )
347{
348  SignalCallbackRec rec;
349  rec.cb = cb;
350  rec.someData = someData;
351
352  callbackList.push_back(rec);
353}
354
355#endif /* __WIN32__ */
Note: See TracBrowser for help on using the repository browser.