Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/orxonox/SignalHandler.cc @ 1607

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

moved SignalHandler from core to orxonox

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