Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/FICN/src/orxonox/core/SignalHandler.cc @ 668

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