Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/input/src/core/SignalHandler.cc @ 1238

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