Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/util/SignalHandler.cc @ 3014

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

Merged r1-2096 of questsystem5 back to trunk

I hope there weren't more "hidden merge changes" in r2909 than the one in OverlayGroup (removeElement) (and related to this the adjustments in NotificationQueue).

The corresponding media commit seems not yet to be done, but it doesn't break the build.

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