Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5274 was 3370, checked in by rgrieder, 15 years ago

Merged resource branch back to the trunk. Changes:

  • Automated graphics loading by evaluating whether a GameState requires it
  • Using native Tcl library (x3n)

Windows users: Update your dependency package!

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