Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/util/SignalHandler.cc @ 2118

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

Merged r2101 (objecthierarchy) to trunk.

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