Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2677 was 2662, checked in by rgrieder, 16 years ago

Merged presentation branch back to trunk.

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
[1607]46#if ORXONOX_PLATFORM != ORXONOX_PLATFORM_WIN32
[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
[2171]161      COUT(0) << "recieved signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox.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
[1607]355#endif /* ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32 */
Note: See TracBrowser for help on using the repository browser.