Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2721 was 2710, checked in by rgrieder, 16 years ago

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

File size: 8.5 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    bool SignalHandler::bXAutoKeyRepeatOn_ = false;
56
57    /**
58     * register signal handlers for SIGSEGV and SIGABRT
59     * @param appName path to executable eg argv[0]
60     * @param filename filename to append backtrace to
61     */
62    void SignalHandler::doCatch( const std::string & appName, const std::string & filename )
63    {
64      this->appName = appName;
65      this->filename = filename;
66
67      // prepare for restoring XAutoKeyRepeat
68      Display* display;
69      if ((display = XOpenDisplay(0)))
70      {
71        XKeyboardState oldState;
72        XGetKeyboardControl(display, &oldState);
73        if (oldState.global_auto_repeat == AutoRepeatModeOn)
74          bXAutoKeyRepeatOn_ = true;
75        else
76          bXAutoKeyRepeatOn_ = false;
77        XCloseDisplay(display);
78      }
79      else
80      {
81        std::cout << "Warning: couldn't open X display to restore XAutoKeyRepeat." << std::endl;
82        bXAutoKeyRepeatOn_ = false;
83      }
84
85
86      // make sure doCatch is only called once without calling dontCatch
87      assert( sigRecList.size() == 0 );
88
89      catchSignal( SIGSEGV );
90      catchSignal( SIGABRT );
91      catchSignal( SIGILL );
92    }
93
94    /**
95     * restore previous signal handlers
96     */
97    void SignalHandler::dontCatch()
98    {
99      for ( SignalRecList::iterator it = sigRecList.begin(); it != sigRecList.end(); it++ )
100      {
101        signal( it->signal, it->handler );
102      }
103
104      sigRecList.clear();
105    }
106
107    /**
108     * catch signal sig
109     * @param sig signal to catch
110     */
111    void SignalHandler::catchSignal( int sig )
112    {
113      sig_t handler = signal( sig, SignalHandler::sigHandler );
114
115      assert( handler != SIG_ERR );
116
117      SignalRec rec;
118      rec.signal = sig;
119      rec.handler = handler;
120
121      sigRecList.push_front( rec );
122    }
123
124    /**
125     * sigHandler is called when receiving signals
126     * @param sig
127     */
128    void SignalHandler::sigHandler( int sig )
129    {
130      for ( SignalCallbackList::iterator it = SignalHandler::getInstance().callbackList.begin(); it != SignalHandler::getInstance().callbackList.end(); it++  )
131      {
132        (*(it->cb))( it->someData );
133      }
134
135      std::string sigName = "UNKNOWN";
136
137      switch ( sig )
138      {
139        case SIGSEGV:
140          sigName = "SIGSEGV";
141          break;
142        case SIGABRT:
143          sigName = "SIGABRT";
144          break;
145        case SIGILL:
146          sigName = "SIGILL";
147          break;
148      }
149
150      if (bXAutoKeyRepeatOn_)
151      {
152        std::cout << "Trying to restore XAutoKeyRepeat" << std::endl;
153        Display* display;
154        if ((display = XOpenDisplay(0)))
155        {
156          XAutoRepeatOn(display);
157          XCloseDisplay(display);
158        }
159      }
160
161      COUT(0) << "recieved signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox_crash.log" << std::endl;
162
163      int sigPipe[2];
164      if ( pipe(sigPipe) == -1 )
165      {
166        perror("pipe failed!\n");
167        exit(EXIT_FAILURE);
168      }
169
170      int sigPid = fork();
171
172      if ( sigPid == -1 )
173      {
174        perror("fork failed!\n");
175        exit(EXIT_FAILURE);
176      }
177
178      // gdb will be attached to this process
179      if ( sigPid == 0 )
180      {
181        getInstance().dontCatch();
182        // wait for message from parent when it has attached gdb
183        int someData;
184
185        read( sigPipe[0], &someData, sizeof(someData) );
186
187        if ( someData != 0x12345678 )
188        {
189          COUT(0) << "something went wrong :(" << std::endl;
190        }
191
192        return;
193      }
194
195      int gdbIn[2];
196      int gdbOut[2];
197      int gdbErr[2];
198
199      if ( pipe(gdbIn) == -1 || pipe(gdbOut) == -1 || pipe(gdbErr) == -1 )
200      {
201        perror("pipe failed!\n");
202        kill( sigPid, SIGTERM );
203        waitpid( sigPid, NULL, 0 );
204        exit(EXIT_FAILURE);
205      }
206
207      int gdbPid = fork();
208      // this process will run gdb
209
210      if ( gdbPid == -1 )
211      {
212        perror("fork failed\n");
213        kill( sigPid, SIGTERM );
214        waitpid( sigPid, NULL, 0 );
215        exit(EXIT_FAILURE);
216      }
217
218      if ( gdbPid == 0 )
219      {
220        // start gdb
221
222        close(gdbIn[1]);
223        close(gdbOut[0]);
224        close(gdbErr[0]);
225
226        dup2( gdbIn[0], STDIN_FILENO );
227        dup2( gdbOut[1], STDOUT_FILENO );
228        dup2( gdbErr[1], STDERR_FILENO );
229
230        execlp( "sh", "sh", "-c", "gdb", (void*)NULL);
231      }
232
233      char cmd[256];
234      snprintf( cmd, 256, "file %s\nattach %d\nc\n", getInstance().appName.c_str(), sigPid );
235      write( gdbIn[1], cmd, strlen(cmd) );
236
237      int charsFound = 0;
238      int promptFound = 0;
239      char byte;
240      while ( read( gdbOut[0], &byte, 1 ) == 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        }
259
260        if ( promptFound == 3 )
261        {
262          break;
263        }
264      }
265
266      int someData = 0x12345678;
267      write( sigPipe[1], &someData, sizeof(someData) );
268
269      write( gdbIn[1], "bt\nk\nq\n", 7 );
270
271
272      charsFound = 0;
273      promptFound = 0;
274      std::string bt;
275      while ( read( gdbOut[0], &byte, 1 ) == 1 )
276      {
277        bt += std::string( &byte, 1 );
278
279        if (
280          charsFound == 0 && byte == '(' ||
281          charsFound == 1 && byte == 'g' ||
282          charsFound == 2 && byte == 'd' ||
283          charsFound == 3 && byte == 'b' ||
284          charsFound == 4 && byte == ')' ||
285          charsFound == 5 && byte == ' '
286            )
287              charsFound++;
288        else
289          charsFound = 0;
290
291        if ( charsFound == 6 )
292        {
293          promptFound++;
294          charsFound = 0;
295          bt += "\n";
296        }
297
298        if ( promptFound == 3 )
299        {
300          break;
301        }
302      }
303
304
305      waitpid( sigPid, NULL, 0 );
306      waitpid( gdbPid, NULL, 0 );
307
308      int wsRemoved = 0;
309
310      while ( wsRemoved < 2 && bt.length() > 0 )
311      {
312        if ( bt[1] == '\n' )
313          wsRemoved++;
314        bt.erase(0, 1);
315      }
316
317      if ( bt.length() > 0 )
318        bt.erase(0, 1);
319
320      time_t now = time(NULL);
321
322      std::string timeString = "\n\n\n\n"
323                         "=======================================================\n"
324                         "= time: " + std::string(ctime(&now)) +
325             "=======================================================\n";
326      bt.insert(0, timeString);
327
328      FILE * f = fopen( getInstance().filename.c_str(), "a" );
329
330      if ( !f )
331      {
332        perror( ( std::string( "could not append to " ) + getInstance().filename ).c_str() );
333        exit(EXIT_FAILURE);
334      }
335
336      if ( fwrite( bt.c_str(), 1, bt.length(), f ) != bt.length() )
337      {
338        COUT(0) << "could not write " << bt.length() << " byte to " << getInstance().filename << std::endl;
339        exit(EXIT_FAILURE);
340      }
341
342      exit(EXIT_FAILURE);
343    }
344
345    void SignalHandler::registerCallback( SignalCallback cb, void * someData )
346    {
347      SignalCallbackRec rec;
348      rec.cb = cb;
349      rec.someData = someData;
350
351      callbackList.push_back(rec);
352    }
353}
354
355#endif /* ORXONOX_PLATFORM_LINUX */
Note: See TracBrowser for help on using the repository browser.