Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/core/SignalHandler.cc @ 1494

Last change on this file since 1494 was 1494, checked in by rgrieder, 16 years ago
  • set the svn:eol-style property to all files so, that where ever you check out, you'll get the right line endings (had to change every file with mixed endings to windows in order to set the property)
  • 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.