Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/core/SignalHandler.cc @ 861

Last change on this file since 861 was 790, checked in by nicolasc, 17 years ago

merged FICN back into trunk
awaiting release.

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