Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/script/src/orxonox/core/SignalHandler.cc @ 969

Last change on this file since 969 was 871, checked in by landauf, 17 years ago

merged core branch to trunk

File size: 6.7 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *
4 *
5 *   License notice:
6 *
7 *   This program is free software; you can redistribute it and/or
8 *   modify it under the terms of the GNU General Public License
9 *   as published by the Free Software Foundation; either version 2
10 *   of the License, or (at your option) any later version.
11 *
12 *   This program is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *   GNU General Public License for more details.
16 *
17 *   You should have received a copy of the GNU General Public License
18 *   along with this program; if not, write to the Free Software
19 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 *
21 *   Author:
22 *      Christoph Renner
23 *   Co-authors:
24 *      ...
25 *
26 */
27
28/**
29    @file SignalHandler.cc
30    @brief Implementation of the SignalHandler class.
31*/
32
33#include <assert.h>
34
35#include "Debug.h"
36#include "SignalHandler.h"
37
38SignalHandler * SignalHandler::singletonRef = NULL;
39
40#ifndef __WIN32__
41
42#include <wait.h>
43
44SignalHandler::SignalHandler()
45{
46}
47
48/**
49 * register signal handlers for SIGSEGV and SIGABRT
50 * @param appName path to executable eg argv[0]
51 * @param fileName filename to append backtrace to
52 */
53void SignalHandler::doCatch( const std::string & appName, const std::string & fileName )
54{
55  this->appName = appName;
56  this->fileName = fileName;
57
58  // make sure doCatch is only called once without calling dontCatch
59  assert( sigRecList.size() == 0 );
60
61  catchSignal( SIGSEGV );
62  catchSignal( SIGABRT );
63  catchSignal( SIGILL );
64}
65
66/**
67 * restore previous signal handlers
68 */
69void SignalHandler::dontCatch()
70{
71  for ( SignalRecList::iterator it = sigRecList.begin(); it != sigRecList.end(); it++ )
72  {
73    signal( it->signal, it->handler );
74  }
75
76  sigRecList.clear();
77}
78
79/**
80 * catch signal sig
81 * @param sig signal to catch
82 */
83void SignalHandler::catchSignal( int sig )
84{
85  sig_t handler = signal( sig, SignalHandler::sigHandler );
86
87  assert( handler != SIG_ERR );
88
89  SignalRec rec;
90  rec.signal = sig;
91  rec.handler = handler;
92
93  sigRecList.push_front( rec );
94}
95
96/**
97 * sigHandler is called when receiving signals
98 * @param sig
99 */
100void SignalHandler::sigHandler( int sig )
101{
102  for ( SignalCallbackList::iterator it = SignalHandler::getInstance()->callbackList.begin(); it != SignalHandler::getInstance()->callbackList.end(); it++  )
103  {
104    (*(it->cb))( it->someData );
105  }
106
107  std::string sigName = "UNKNOWN";
108
109  switch ( sig )
110  {
111    case SIGSEGV:
112      sigName = "SIGSEGV";
113      break;
114    case SIGABRT:
115      sigName = "SIGABRT";
116      break;
117    case SIGILL:
118      sigName = "SIGILL";
119      break;
120  }
121
122  PRINTF(0)( "recieved signal %s\ntry to write backtrace to file orxonox.log\n", sigName.c_str() );
123
124  int sigPipe[2];
125  if ( pipe(sigPipe) == -1 )
126  {
127    perror("pipe failed!\n");
128    exit(EXIT_FAILURE);
129  }
130
131  int sigPid = fork();
132
133  if ( sigPid == -1 )
134  {
135    perror("fork failed!\n");
136    exit(EXIT_FAILURE);
137  }
138
139  // gdb will be attached to this process
140  if ( sigPid == 0 )
141  {
142    getInstance()->dontCatch();
143    // wait for message from parent when it has attached gdb
144    int someData;
145
146    read( sigPipe[0], &someData, sizeof(someData) );
147
148    if ( someData != 0x12345678 )
149    {
150      PRINTF(0)("something went wrong :(\n");
151    }
152
153    return;
154  }
155
156  int gdbIn[2];
157  int gdbOut[2];
158  int gdbErr[2];
159
160  if ( pipe(gdbIn) == -1 || pipe(gdbOut) == -1 || pipe(gdbErr) == -1 )
161  {
162    perror("pipe failed!\n");
163    kill( sigPid, SIGTERM );
164    waitpid( sigPid, NULL, 0 );
165    exit(EXIT_FAILURE);
166  }
167
168  int gdbPid = fork();
169  // this process will run gdb
170
171  if ( gdbPid == -1 )
172  {
173    perror("fork failed\n");
174    kill( sigPid, SIGTERM );
175    waitpid( sigPid, NULL, 0 );
176    exit(EXIT_FAILURE);
177  }
178
179  if ( gdbPid == 0 )
180  {
181    // start gdb
182
183    close(gdbIn[1]);
184    close(gdbOut[0]);
185    close(gdbErr[0]);
186
187    dup2( gdbIn[0], STDIN_FILENO );
188    dup2( gdbOut[1], STDOUT_FILENO );
189    dup2( gdbErr[1], STDERR_FILENO );
190
191    execlp( "sh", "sh", "-c", "gdb", (void*)NULL);
192  }
193
194  char cmd[256];
195  snprintf( cmd, 256, "file %s\nattach %d\nc\n", getInstance()->appName.c_str(), sigPid );
196  write( gdbIn[1], cmd, strlen(cmd) );
197
198  int charsFound = 0;
199  int promptFound = 0;
200  char byte;
201  while ( read( gdbOut[0], &byte, 1 ) == 1 )
202  {
203    if (
204      charsFound == 0 && byte == '(' ||
205      charsFound == 1 && byte == 'g' ||
206      charsFound == 2 && byte == 'd' ||
207      charsFound == 3 && byte == 'b' ||
208      charsFound == 4 && byte == ')' ||
209      charsFound == 5 && byte == ' '
210        )
211          charsFound++;
212    else
213      charsFound = 0;
214
215    if ( charsFound == 6 )
216    {
217      promptFound++;
218      charsFound = 0;
219    }
220
221    if ( promptFound == 3 )
222    {
223      break;
224    }
225  }
226
227  int someData = 0x12345678;
228  write( sigPipe[1], &someData, sizeof(someData) );
229
230  write( gdbIn[1], "bt\nk\nq\n", 7 );
231
232
233  charsFound = 0;
234  promptFound = 0;
235  std::string bt;
236  while ( read( gdbOut[0], &byte, 1 ) == 1 )
237  {
238    bt += std::string( &byte, 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      bt += "\n";
257    }
258
259    if ( promptFound == 3 )
260    {
261      break;
262    }
263  }
264
265
266  waitpid( sigPid, NULL, 0 );
267  waitpid( gdbPid, NULL, 0 );
268
269  int wsRemoved = 0;
270
271  while ( wsRemoved < 2 && bt.length() > 0 )
272  {
273    if ( bt[1] == '\n' )
274      wsRemoved++;
275    bt.erase(0, 1);
276  }
277
278  if ( bt.length() > 0 )
279    bt.erase(0, 1);
280
281  time_t now = time(NULL);
282
283  std::string timeString = "\n\n\n\n"
284                           "=======================================================\n"
285                           "= time: " + std::string(ctime(&now)) +
286                           "=======================================================\n";
287  bt.insert(0, timeString);
288
289  FILE * f = fopen( getInstance()->fileName.c_str(), "a" );
290
291  if ( !f )
292  {
293    perror( ( std::string( "could not append to " ) + getInstance()->fileName ).c_str() );
294    exit(EXIT_FAILURE);
295  }
296
297  if ( fwrite( bt.c_str(), 1, bt.length(), f ) != bt.length() )
298  {
299    PRINTF(0)( ( std::string("could not write %d byte to ") + getInstance()->fileName ).c_str(), bt.length());
300    exit(EXIT_FAILURE);
301  }
302
303  exit(EXIT_FAILURE);
304}
305
306void SignalHandler::registerCallback( SignalCallback cb, void * someData )
307{
308  SignalCallbackRec rec;
309  rec.cb = cb;
310  rec.someData = someData;
311
312  callbackList.push_back(rec);
313}
314
315#endif /* __WIN32__ */
Note: See TracBrowser for help on using the repository browser.