Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1535 was 1505, checked in by rgrieder, 16 years ago

f* svn: It doesn't even inform you if you attempt to set a non existing property. It is svn:eol-style and not eol-style when using the command by the way…

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