Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

don't panic, no codechanges!
added a link to www.orxonox.net

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