Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/core/SignalHandler.cc @ 1596

Last change on this file since 1596 was 1586, checked in by landauf, 16 years ago

moved Debug.h, OutputHandler and OutputBuffer to util, to make COUT(x) available everywhere

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