Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/SignalHandler.cc @ 1152

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