Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2726 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
RevLine 
[1505]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/**
[2030]30    @file
[1505]31    @brief Implementation of the SignalHandler class.
32*/
33
34#include "SignalHandler.h"
[2030]35#include "Debug.h"
[1505]36
37#include <iostream>
[1837]38#include <cstdlib>
39#include <cstring>
[1505]40
[2171]41namespace orxonox
42{
[2662]43    SignalHandler* SignalHandler::singletonRef_s = NULL;
[2171]44}
[1505]45
[2710]46#ifdef ORXONOX_PLATFORM_LINUX
[1505]47
48#include <wait.h>
49#include <X11/Xlib.h>
50#include <X11/Xutil.h>
51#include <X11/keysym.h>
52
[2171]53namespace orxonox
[1505]54{
[2171]55    bool SignalHandler::bXAutoKeyRepeatOn_ = false;
[1505]56
[2171]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;
[1505]66
[2171]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      }
[1505]84
85
[2171]86      // make sure doCatch is only called once without calling dontCatch
87      assert( sigRecList.size() == 0 );
[1505]88
[2171]89      catchSignal( SIGSEGV );
90      catchSignal( SIGABRT );
91      catchSignal( SIGILL );
92    }
[1505]93
[2171]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      }
[1505]103
[2171]104      sigRecList.clear();
105    }
[1505]106
[2171]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 );
[1505]114
[2171]115      assert( handler != SIG_ERR );
[1505]116
[2171]117      SignalRec rec;
118      rec.signal = sig;
119      rec.handler = handler;
[1505]120
[2171]121      sigRecList.push_front( rec );
122    }
[1505]123
[2171]124    /**
125     * sigHandler is called when receiving signals
126     * @param sig
127     */
128    void SignalHandler::sigHandler( int sig )
129    {
[2662]130      for ( SignalCallbackList::iterator it = SignalHandler::getInstance().callbackList.begin(); it != SignalHandler::getInstance().callbackList.end(); it++  )
[2171]131      {
132        (*(it->cb))( it->someData );
133      }
[1505]134
[2171]135      std::string sigName = "UNKNOWN";
[1505]136
[2171]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      }
[1505]149
[2171]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      }
[1505]160
[2710]161      COUT(0) << "recieved signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox_crash.log" << std::endl;
[1505]162
[2171]163      int sigPipe[2];
164      if ( pipe(sigPipe) == -1 )
165      {
166        perror("pipe failed!\n");
167        exit(EXIT_FAILURE);
168      }
[1505]169
[2171]170      int sigPid = fork();
[1505]171
[2171]172      if ( sigPid == -1 )
173      {
174        perror("fork failed!\n");
175        exit(EXIT_FAILURE);
176      }
[1505]177
[2171]178      // gdb will be attached to this process
179      if ( sigPid == 0 )
180      {
[2662]181        getInstance().dontCatch();
[2171]182        // wait for message from parent when it has attached gdb
183        int someData;
[1505]184
[2171]185        read( sigPipe[0], &someData, sizeof(someData) );
[1505]186
[2171]187        if ( someData != 0x12345678 )
188        {
189          COUT(0) << "something went wrong :(" << std::endl;
190        }
[1505]191
[2171]192        return;
193      }
[1505]194
[2171]195      int gdbIn[2];
196      int gdbOut[2];
197      int gdbErr[2];
[1505]198
[2171]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      }
[1505]206
[2171]207      int gdbPid = fork();
208      // this process will run gdb
[1505]209
[2171]210      if ( gdbPid == -1 )
211      {
212        perror("fork failed\n");
213        kill( sigPid, SIGTERM );
214        waitpid( sigPid, NULL, 0 );
215        exit(EXIT_FAILURE);
216      }
[1505]217
[2171]218      if ( gdbPid == 0 )
219      {
220        // start gdb
[1505]221
[2171]222        close(gdbIn[1]);
223        close(gdbOut[0]);
224        close(gdbErr[0]);
[1505]225
[2171]226        dup2( gdbIn[0], STDIN_FILENO );
227        dup2( gdbOut[1], STDOUT_FILENO );
228        dup2( gdbErr[1], STDERR_FILENO );
[1505]229
[2171]230        execlp( "sh", "sh", "-c", "gdb", (void*)NULL);
231      }
[1505]232
[2171]233      char cmd[256];
[2662]234      snprintf( cmd, 256, "file %s\nattach %d\nc\n", getInstance().appName.c_str(), sigPid );
[2171]235      write( gdbIn[1], cmd, strlen(cmd) );
[1505]236
[2171]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;
[1505]253
[2171]254        if ( charsFound == 6 )
255        {
256          promptFound++;
257          charsFound = 0;
258        }
[1505]259
[2171]260        if ( promptFound == 3 )
261        {
262          break;
263        }
264      }
[1505]265
[2171]266      int someData = 0x12345678;
267      write( sigPipe[1], &someData, sizeof(someData) );
[1505]268
[2171]269      write( gdbIn[1], "bt\nk\nq\n", 7 );
[1505]270
271
272      charsFound = 0;
[2171]273      promptFound = 0;
274      std::string bt;
275      while ( read( gdbOut[0], &byte, 1 ) == 1 )
276      {
277        bt += std::string( &byte, 1 );
[1505]278
[2171]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;
[1505]290
[2171]291        if ( charsFound == 6 )
292        {
293          promptFound++;
294          charsFound = 0;
295          bt += "\n";
296        }
[1505]297
[2171]298        if ( promptFound == 3 )
299        {
300          break;
301        }
302      }
[1505]303
304
[2171]305      waitpid( sigPid, NULL, 0 );
306      waitpid( gdbPid, NULL, 0 );
[1505]307
[2171]308      int wsRemoved = 0;
[1505]309
[2171]310      while ( wsRemoved < 2 && bt.length() > 0 )
311      {
312        if ( bt[1] == '\n' )
313          wsRemoved++;
314        bt.erase(0, 1);
315      }
[1505]316
[2171]317      if ( bt.length() > 0 )
318        bt.erase(0, 1);
[1505]319
[2171]320      time_t now = time(NULL);
[1505]321
[2171]322      std::string timeString = "\n\n\n\n"
323                         "=======================================================\n"
324                         "= time: " + std::string(ctime(&now)) +
325             "=======================================================\n";
326      bt.insert(0, timeString);
[1505]327
[2662]328      FILE * f = fopen( getInstance().filename.c_str(), "a" );
[1505]329
[2171]330      if ( !f )
331      {
[2662]332        perror( ( std::string( "could not append to " ) + getInstance().filename ).c_str() );
[2171]333        exit(EXIT_FAILURE);
334      }
[1505]335
[2171]336      if ( fwrite( bt.c_str(), 1, bt.length(), f ) != bt.length() )
337      {
[2662]338        COUT(0) << "could not write " << bt.length() << " byte to " << getInstance().filename << std::endl;
[2171]339        exit(EXIT_FAILURE);
340      }
[1505]341
[2171]342      exit(EXIT_FAILURE);
343    }
[1505]344
[2171]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    }
[1505]353}
354
[2710]355#endif /* ORXONOX_PLATFORM_LINUX */
Note: See TracBrowser for help on using the repository browser.