Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/SignalHandler.cc @ 2040

Last change on this file since 2040 was 1837, checked in by adrfried, 16 years ago

includes fixed

  • Property svn:eol-style set to native
File size: 7.7 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 "OrxonoxStableHeaders.h"
35#include "SignalHandler.h"
36#include "util/Debug.h"
37
38#include <assert.h>
39#include <iostream>
40#include <cstdlib>
41#include <cstring>
42
43SignalHandler * SignalHandler::singletonRef = NULL;
44
45#if ORXONOX_PLATFORM != ORXONOX_PLATFORM_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  COUT(0) << "recieved signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox.log" << std::endl;
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      COUT(0) << "something went wrong :(" << std::endl;
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    COUT(0) << "could not write " << bt.length() << " byte to " << getInstance()->fileName << std::endl;
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 /* ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32 */
Note: See TracBrowser for help on using the repository browser.