Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/util/SignalHandler.cc @ 8516

Last change on this file since 8516 was 8351, checked in by rgrieder, 13 years ago

Merged kicklib2 branch back to trunk (includes former branches ois_update, mac_osx and kicklib).

Notes for updating

Linux:
You don't need an extra package for CEGUILua and Tolua, it's already shipped with CEGUI.
However you do need to make sure that the OgreRenderer is installed too with CEGUI 0.7 (may be a separate package).
Also, Orxonox now recognises if you install the CgProgramManager (a separate package available on newer Ubuntu on Debian systems).

Windows:
Download the new dependency packages versioned 6.0 and use these. If you have problems with that or if you don't like the in game console problem mentioned below, you can download the new 4.3 version of the packages (only available for Visual Studio 2005/2008).

Key new features:

  • *Support for Mac OS X*
  • Visual Studio 2010 support
  • Bullet library update to 2.77
  • OIS library update to 1.3
  • Support for CEGUI 0.7 —> Support for Arch Linux and even SuSE
  • Improved install target
  • Compiles now with GCC 4.6
  • Ogre Cg Shader plugin activated for Linux if available
  • And of course lots of bug fixes

There are also some regressions:

  • No support for CEGUI 0.5, Ogre 1.4 and boost 1.35 - 1.39 any more
  • In game console is not working in main menu for CEGUI 0.7
  • Tolua (just the C lib, not the application) and CEGUILua libraries are no longer in our repository. —> You will need to get these as well when compiling Orxonox
  • And of course lots of new bugs we don't yet know about
  • Property svn:eol-style set to native
File size: 26.2 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>
[8351]39#include <cstdio>
[7457]40
[3196]41#include "Debug.h"
[1505]42
[2171]43namespace orxonox
44{
[3370]45    SignalHandler* SignalHandler::singletonPtr_s = NULL;
[2171]46}
[1505]47
[7449]48#if defined(ORXONOX_PLATFORM_LINUX)
[1505]49
50#include <wait.h>
51#include <X11/Xlib.h>
52#include <X11/Xutil.h>
53#include <X11/keysym.h>
[7801]54#include <sys/prctl.h>
[1505]55
[2171]56namespace orxonox
[1505]57{
[2171]58    /**
59     * register signal handlers for SIGSEGV and SIGABRT
60     * @param appName path to executable eg argv[0]
61     * @param filename filename to append backtrace to
62     */
63    void SignalHandler::doCatch( const std::string & appName, const std::string & filename )
64    {
65      this->appName = appName;
66      this->filename = filename;
[1505]67
[2171]68      // make sure doCatch is only called once without calling dontCatch
69      assert( sigRecList.size() == 0 );
[1505]70
[2171]71      catchSignal( SIGSEGV );
72      catchSignal( SIGABRT );
73      catchSignal( SIGILL );
74    }
[1505]75
[2171]76    /**
77     * restore previous signal handlers
78     */
79    void SignalHandler::dontCatch()
80    {
81      for ( SignalRecList::iterator it = sigRecList.begin(); it != sigRecList.end(); it++ )
82      {
83        signal( it->signal, it->handler );
84      }
[1505]85
[2171]86      sigRecList.clear();
87    }
[1505]88
[2171]89    /**
90     * catch signal sig
91     * @param sig signal to catch
92     */
93    void SignalHandler::catchSignal( int sig )
94    {
95      sig_t handler = signal( sig, SignalHandler::sigHandler );
[1505]96
[2171]97      assert( handler != SIG_ERR );
[1505]98
[2171]99      SignalRec rec;
100      rec.signal = sig;
101      rec.handler = handler;
[1505]102
[2171]103      sigRecList.push_front( rec );
104    }
[1505]105
[2171]106    /**
107     * sigHandler is called when receiving signals
108     * @param sig
109     */
110    void SignalHandler::sigHandler( int sig )
111    {
112      std::string sigName = "UNKNOWN";
[1505]113
[2171]114      switch ( sig )
115      {
116        case SIGSEGV:
117          sigName = "SIGSEGV";
118          break;
119        case SIGABRT:
120          sigName = "SIGABRT";
121          break;
122        case SIGILL:
123          sigName = "SIGILL";
124          break;
125      }
[3198]126      // if the signalhandler has already been destroyed then don't do anything
[3370]127      if( SignalHandler::singletonPtr_s == 0 )
[3198]128      {
[7449]129        COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl;
[3198]130        exit(EXIT_FAILURE);
131      }
[1505]132
[3198]133      for ( SignalCallbackList::iterator it = SignalHandler::getInstance().callbackList.begin(); it != SignalHandler::getInstance().callbackList.end(); it++  )
134      {
135        (*(it->cb))( it->someData );
136      }
137
138
[7449]139      COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Try to write backtrace to file orxonox_crash.log" << std::endl;
[1505]140
[7801]141     
142      // First start GDB which will be attached to this process later on
143     
144      int gdbIn[2];
145      int gdbOut[2];
146      int gdbErr[2];
147     
148      if ( pipe(gdbIn) == -1 || pipe(gdbOut) == -1 || pipe(gdbErr) == -1 )
149      {
150        perror("pipe failed!\n");
151        exit(EXIT_FAILURE);
152      }
153     
154      int gdbPid = fork();
155      // this process will run gdb
156     
157      if ( gdbPid == -1 )
158      {
159        perror("fork failed\n");
160        exit(EXIT_FAILURE);
161      }
162     
163      if ( gdbPid == 0 )
164      {
165        // start gdb
166       
167        close(gdbIn[1]);
168        close(gdbOut[0]);
169        close(gdbErr[0]);
170       
171        dup2( gdbIn[0], STDIN_FILENO );
172        dup2( gdbOut[1], STDOUT_FILENO );
173        dup2( gdbErr[1], STDERR_FILENO );
174       
175        execlp( "sh", "sh", "-c", "gdb", static_cast<void*>(NULL));
176      }
177     
178     
179      // Now start a fork of this process on which GDB will be attached on
180     
[2171]181      int sigPipe[2];
182      if ( pipe(sigPipe) == -1 )
183      {
184        perror("pipe failed!\n");
[7801]185        kill( gdbPid, SIGTERM );
186        waitpid( gdbPid, NULL, 0 );
[2171]187        exit(EXIT_FAILURE);
188      }
[1505]189
[2171]190      int sigPid = fork();
[1505]191
[2171]192      if ( sigPid == -1 )
193      {
194        perror("fork failed!\n");
[7801]195        kill( gdbPid, SIGTERM );
196        waitpid( gdbPid, NULL, 0 );
[2171]197        exit(EXIT_FAILURE);
198      }
[1505]199
[2171]200      // gdb will be attached to this process
201      if ( sigPid == 0 )
202      {
[2662]203        getInstance().dontCatch();
[7801]204       
205        // make sure gdb is allowed to attach to our PID even if there are some system restrictions
206#ifdef PR_SET_PTRACER
207        if( prctl(PR_SET_PTRACER, gdbPid, 0, 0, 0) == -1 )
208          COUT(0) << "could not set proper permissions for GDB to attach to process..." << endl;
209#endif
210       
[2171]211        // wait for message from parent when it has attached gdb
212        int someData;
[1505]213
[7801]214        if( read( sigPipe[0], &someData, sizeof(someData) ) != sizeof(someData) )
215          COUT(0) << "something went wrong :(" << std::endl;
[1505]216
[2171]217        if ( someData != 0x12345678 )
218        {
219          COUT(0) << "something went wrong :(" << std::endl;
220        }
[1505]221
[2171]222        return;
223      }
[1505]224
[2171]225      char cmd[256];
[2662]226      snprintf( cmd, 256, "file %s\nattach %d\nc\n", getInstance().appName.c_str(), sigPid );
[2171]227      write( gdbIn[1], cmd, strlen(cmd) );
[1505]228
[2171]229      int charsFound = 0;
230      int promptFound = 0;
231      char byte;
232      while ( read( gdbOut[0], &byte, 1 ) == 1 )
233      {
234        if (
[3196]235          (charsFound == 0 && byte == '(') ||
236          (charsFound == 1 && byte == 'g') ||
237          (charsFound == 2 && byte == 'd') ||
238          (charsFound == 3 && byte == 'b') ||
239          (charsFound == 4 && byte == ')') ||
240          (charsFound == 5 && byte == ' ')
[2171]241            )
242              charsFound++;
243        else
244          charsFound = 0;
[1505]245
[2171]246        if ( charsFound == 6 )
247        {
248          promptFound++;
249          charsFound = 0;
250        }
[1505]251
[2171]252        if ( promptFound == 3 )
253        {
254          break;
255        }
256      }
[1505]257
[2171]258      int someData = 0x12345678;
259      write( sigPipe[1], &someData, sizeof(someData) );
[1505]260
[2171]261      write( gdbIn[1], "bt\nk\nq\n", 7 );
[1505]262
263
264      charsFound = 0;
[2171]265      promptFound = 0;
266      std::string bt;
267      while ( read( gdbOut[0], &byte, 1 ) == 1 )
268      {
269        bt += std::string( &byte, 1 );
[1505]270
[2171]271        if (
[3196]272          (charsFound == 0 && byte == '(') ||
273          (charsFound == 1 && byte == 'g') ||
274          (charsFound == 2 && byte == 'd') ||
275          (charsFound == 3 && byte == 'b') ||
276          (charsFound == 4 && byte == ')') ||
277          (charsFound == 5 && byte == ' ')
[2171]278            )
279              charsFound++;
280        else
281          charsFound = 0;
[1505]282
[2171]283        if ( charsFound == 6 )
284        {
285          promptFound++;
286          charsFound = 0;
287          bt += "\n";
288        }
[1505]289
[2171]290        if ( promptFound == 3 )
291        {
292          break;
293        }
294      }
[1505]295
296
[2171]297      waitpid( sigPid, NULL, 0 );
298      waitpid( gdbPid, NULL, 0 );
[1505]299
[2171]300      int wsRemoved = 0;
[1505]301
[2171]302      while ( wsRemoved < 2 && bt.length() > 0 )
303      {
304        if ( bt[1] == '\n' )
305          wsRemoved++;
306        bt.erase(0, 1);
307      }
[1505]308
[2171]309      if ( bt.length() > 0 )
310        bt.erase(0, 1);
[1505]311
[2171]312      time_t now = time(NULL);
[1505]313
[2731]314      std::string timeString =
[2171]315                         "=======================================================\n"
316                         "= time: " + std::string(ctime(&now)) +
[2731]317                         "=======================================================\n";
[2171]318      bt.insert(0, timeString);
[1505]319
[2753]320      FILE * f = fopen( getInstance().filename.c_str(), "w" );
[1505]321
[2171]322      if ( !f )
323      {
[2662]324        perror( ( std::string( "could not append to " ) + getInstance().filename ).c_str() );
[2171]325        exit(EXIT_FAILURE);
326      }
[1505]327
[2171]328      if ( fwrite( bt.c_str(), 1, bt.length(), f ) != bt.length() )
329      {
[2662]330        COUT(0) << "could not write " << bt.length() << " byte to " << getInstance().filename << std::endl;
[2171]331        exit(EXIT_FAILURE);
332      }
[1505]333
[2171]334      exit(EXIT_FAILURE);
335    }
[1505]336
[2171]337    void SignalHandler::registerCallback( SignalCallback cb, void * someData )
338    {
339      SignalCallbackRec rec;
340      rec.cb = cb;
341      rec.someData = someData;
342
343      callbackList.push_back(rec);
344    }
[1505]345}
346
[7449]347#elif defined(ORXONOX_PLATFORM_WINDOWS) && defined(DBGHELP_FOUND)
348
349#include <iostream>
350#include <iomanip>
351#include <fstream>
[7455]352#include <ctime>
[7449]353#include <dbghelp.h>
[7455]354#include <tlhelp32.h>
[7449]355
[7455]356#include "Convert.h"
357
[7452]358#ifdef ORXONOX_COMPILER_GCC
359#   include <cxxabi.h>
360#endif
361
[7455]362#ifdef ORXONOX_COMPILER_GCC
[7457]363/// Overwrite the original abort() function in MinGW to enable a break point.
[7455]364_UtilExport void __cdecl abort()
365{
366    COUT(1) << "This application has requested the Runtime to terminate it in an unusual way." << std::endl;
367    COUT(1) << "Please contact the application's support team for more information." << std::endl;
368    DebugBreak();
369    exit(0x3);
370}
371
[7457]372/// Overwrite the original _abort() function in MinGW to enable a break point.
[7455]373_UtilExport void __cdecl _assert(const char* expression, const char* file, int line)
374{
375    COUT(1) << "Assertion failed: " << expression << ", file " << file << ", line " << line << std::endl;
376    COUT(1) << std::endl;
377    abort();
378}
379#endif
380
[7449]381namespace orxonox
382{
383    /// Constructor: Initializes the values, but doesn't register the exception handler.
384    SignalHandler::SignalHandler()
385    {
386        this->prevExceptionFilter_ = NULL;
387    }
388
389    /// Destructor: Removes the exception handler.
390    SignalHandler::~SignalHandler()
391    {
392        if (this->prevExceptionFilter_ != NULL)
393        {
394            // Remove the unhandled exception filter function
395            SetUnhandledExceptionFilter(this->prevExceptionFilter_);
396            this->prevExceptionFilter_ = NULL;
397        }
398    }
399
400    /// Registers an exception handler and initializes the filename of the crash log.
401    void SignalHandler::doCatch(const std::string&, const std::string& filename)
402    {
403        this->filename_ = filename;
404
405        // don't register twice
406        assert(this->prevExceptionFilter_ == NULL);
407
408        if (this->prevExceptionFilter_ == NULL)
409        {
410            // Install the unhandled exception filter function
411            this->prevExceptionFilter_ = SetUnhandledExceptionFilter(&SignalHandler::exceptionFilter);
412        }
413    }
414
415    /// Exception handler: Will be called by Windows if an unhandled exceptions occurs.
416    /* static */ LONG WINAPI SignalHandler::exceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
417    {
418        // avoid loops
419        static bool bExecuting = false;
420
421        if (!bExecuting)
422        {
423            bExecuting = true;
424
[7455]425            COUT(1) << std::endl;
[7449]426
427            // if the signalhandler has already been destroyed then don't do anything
428            if (SignalHandler::singletonPtr_s == 0)
429            {
[7450]430                COUT(1) << "Caught an unhandled exception" << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl;
[7449]431                exit(EXIT_FAILURE);
432            }
433
[7450]434            COUT(1) << "Caught an unhandled exception" << std::endl << "Try to write backtrace to orxonox_crash.log..." << std::endl;
[7449]435
436            // write the crash log
437            std::ofstream crashlog(SignalHandler::getInstance().filename_.c_str());
438
439            time_t now = time(NULL);
440
441            crashlog << "=======================================================" << std::endl;
442            crashlog << "= Time: " << std::string(ctime(&now));
443            crashlog << "=======================================================" << std::endl;
444            crashlog << std::endl;
445
446            const std::string& error = SignalHandler::getExceptionType(pExceptionInfo);
447
448            crashlog << error << std::endl;
449            crashlog << std::endl;
450
451            const std::string& callstack = SignalHandler::getStackTrace(pExceptionInfo);
452
453            crashlog << "Call stack:" << std::endl;
454            crashlog << callstack << std::endl;
455
456            crashlog.close();
457
458            // print the same information also to the console
459            COUT(1) << std::endl;
460            COUT(1) << error << std::endl;
461            COUT(1) << std::endl;
462            COUT(1) << "Call stack:" << std::endl;
463            COUT(1) << callstack << std::endl;
464
465            bExecuting = false;
466        }
467        else
468        {
469            COUT(1) << "An error occurred while writing the backtrace" << std::endl;
470        }
471
472        if (SignalHandler::getInstance().prevExceptionFilter_)
473            return SignalHandler::getInstance().prevExceptionFilter_(pExceptionInfo);
474        else
475            return EXCEPTION_CONTINUE_SEARCH;
476    }
477
478    /// Returns the stack trace for either the current function, or, if @a pExceptionInfo is not NULL, for the given exception context.
479    /* static */ std::string SignalHandler::getStackTrace(PEXCEPTION_POINTERS pExceptionInfo)
480    {
481        // Initialise the symbol table to get function names:
[7452]482        SymSetOptions
483        (
484            SYMOPT_DEFERRED_LOADS
485#ifndef ORXONOX_COMPILER_GCC
486            | SYMOPT_UNDNAME
487#endif
488        );
[7449]489        SymInitialize(GetCurrentProcess(), 0, true);
490
491        // Store the current stack frame here:
[7452]492        STACKFRAME64 frame;
493        memset(&frame, 0, sizeof(STACKFRAME64));
[7449]494
495        // Get processor information for the current thread:
496        CONTEXT context;
497        memset(&context, 0, sizeof(CONTEXT));
498
499        if (pExceptionInfo)
500        {
501            // get the context of the exception
502            context = *pExceptionInfo->ContextRecord;
503        }
504        else
505        {
506            context.ContextFlags = CONTEXT_FULL;
507
508            // Load the RTLCapture context function:
[7455]509            HMODULE kernel32 = LoadLibrary("Kernel32.dll");
[7449]510            typedef void (*RtlCaptureContextFunc) (CONTEXT* ContextRecord);
511            RtlCaptureContextFunc rtlCaptureContext = (RtlCaptureContextFunc) GetProcAddress(kernel32, "RtlCaptureContext");
512
513            // Capture the thread context
514            rtlCaptureContext(&context);
515        }
516
517        DWORD type;
518
519        // set the flags and initialize the stackframe struct
[7452]520#ifdef _M_IX86
[7449]521        type = IMAGE_FILE_MACHINE_I386;
522
[7452]523        frame.AddrPC.Offset         = context.Eip;              // program counter
524        frame.AddrFrame.Offset      = context.Ebp;              // frame pointer (for function arguments)
525        frame.AddrStack.Offset      = context.Esp;              // stack pointer
526#elif _M_X64
[7449]527        type = IMAGE_FILE_MACHINE_AMD64;
528
[7452]529        frame.AddrPC.Offset         = context.Rip;              // program counter
530        frame.AddrFrame.Offset      = context.Rbp; // (or Rdi)  // frame pointer (for function arguments)
531        frame.AddrStack.Offset      = context.Rsp;              // stack pointer
532#elif _M_IA64
[7449]533        type = IMAGE_FILE_MACHINE_IA64;
534
[7452]535        frame.AddrPC.Offset         = context.StIIP;            // program counter
536        frame.AddrFrame.Offset      = context.RsBSP;            // frame pointer (for function arguments) // <-- unneeded on Intel IPF, may be removed
537        frame.AddrStack.Offset      = context.IntSp;            // stack pointer
538        frame.AddrBStore.Offset     = context.RsBSP;            // backing store
539        frame.AddrBStore.Mode       = AddrModeFlat;
540#else
[7455]541        return "";
[7452]542#endif
[7449]543
[7455]544        frame.AddrPC.Mode           = AddrModeFlat;
545        frame.AddrFrame.Mode        = AddrModeFlat;
546        frame.AddrStack.Mode        = AddrModeFlat;
547
[7449]548        std::string output;
549
550        // Keep getting stack frames from windows till there are no more left:
551        for (int i = 0;
[7452]552            StackWalk64
[7449]553            (
[7452]554                type                      ,      // MachineType
555                GetCurrentProcess()       ,      // Process to get stack trace for
556                GetCurrentThread()        ,      // Thread to get stack trace for
557                &frame                    ,      // Where to store next stack frame
558                &context                  ,      // Pointer to processor context record
559                0                         ,      // Routine to read process memory: use the default ReadProcessMemory
560                &SymFunctionTableAccess64 ,      // Routine to access the modules symbol table
561                &SymGetModuleBase64       ,      // Routine to access the modules base address
562                0                                // Something to do with 16-bit code. Not needed.
[7449]563            );
564            ++i
565        )
566        {
567            //------------------------------------------------------------------
568            // Declare an image help symbol structure to hold symbol info and
569            // name up to 256 chars This struct is of variable lenght though so
570            // it must be declared as a raw byte buffer.
571            //------------------------------------------------------------------
[7452]572            static char symbolBuffer[sizeof(SYMBOL_INFO) + 255];
573            memset(symbolBuffer, 0, sizeof(symbolBuffer));
[7449]574
575            // Cast it to a symbol struct:
[7452]576            SYMBOL_INFO* symbol = (SYMBOL_INFO*)symbolBuffer;
[7449]577
[7452]578            // Need to set two fields of this symbol before obtaining name info:
579            symbol->SizeOfStruct    = sizeof(SYMBOL_INFO);
580            symbol->MaxNameLen      = 255;
[7449]581
582            // The displacement from the beginning of the symbol is stored here: pretty useless
[7452]583            long long unsigned int displacement = 0;
[7449]584
[7452]585            if (i < 10)
586                output += " ";
[7449]587            output += multi_cast<std::string>(i) + ": ";
588
[7452]589            // Print the function's address:
590            output += SignalHandler::pointerToString(frame.AddrPC.Offset);
591
[7449]592            // Get the symbol information from the address of the instruction pointer register:
[7457]593            bool bCorrected = false;
594            BOOL result = SymFromAddr
[7449]595            (
[7457]596                GetCurrentProcess() ,   // Process to get symbol information for
597                frame.AddrPC.Offset ,   // Address to get symbol for: instruction pointer register
598                &displacement       ,   // Displacement from the beginning of the symbol
599                symbol                  // Where to save the symbol
600            );
601
602            // If the symbol was found, but the displacement is 0, we likely got the wrong symbol - decrease the program counter and try again
603            if (result && displacement == 0)
604            {
605                bCorrected = true;
606                result = SymFromAddr
[7449]607                (
[7457]608                    GetCurrentProcess()     ,
609                    frame.AddrPC.Offset - 1 ,
610                    &displacement           ,
611                    symbol
612                );
613            }
614
615            // Display the function name + offset
616            if (result)
[7449]617            {
618                // Add the name of the function to the function list:
[7452]619                output += " ";
620
621#ifdef ORXONOX_COMPILER_GCC
622                int status;
623                char* demangled = __cxxabiv1::__cxa_demangle(symbol->Name, NULL, NULL, &status);
624                if (demangled)
625                {
626                    output += demangled;
627                    free(demangled);
628                }
629                else
630#endif
631                {
632                    output += symbol->Name;
633                }
[7455]634
635                output += " +" + SignalHandler::pointerToString(displacement, false);
[7457]636                if (bCorrected)
637                    output += " (?)";
[7449]638            }
639
[7457]640            output += "\n";
[7452]641
[7457]642            // Get the file name and line number
643            IMAGEHLP_LINE64 line;
644            memset(&line, 0, sizeof(IMAGEHLP_LINE64));
645            line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
646
647            DWORD displacement2 = 0;
648
[7455]649            if
650            (
[7457]651                SymGetLineFromAddr64
[7455]652                (
653                    GetCurrentProcess(),
[7457]654                    frame.AddrPC.Offset - bCorrected ? 1 : 0,
655                    &displacement2,
656                    &line
[7455]657                )
658            )
659            {
[7457]660                output += "               ";
661                output += line.FileName;
662                output += ":";
663                output += multi_cast<std::string>(line.LineNumber);
664                output += "\n";
[7455]665            }
[7449]666        }
667
668        // Cleanup the symbol table:
669        SymCleanup(GetCurrentProcess());
670
671        return output;
672    }
673
674    /// Returns a description of the given exception.
675    // Based on code from Dr. Mingw by José Fonseca
676    /* static */ std::string SignalHandler::getExceptionType(PEXCEPTION_POINTERS pExceptionInfo)
677    {
678        PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
679        TCHAR szModule[MAX_PATH];
680        HMODULE hModule;
681
682        std::string output = (GetModuleFileName(NULL, szModule, MAX_PATH) ? SignalHandler::getModuleName(szModule) : "Application");
683        output += " caused ";
684
685        switch(pExceptionRecord->ExceptionCode)
686        {
687            case EXCEPTION_ACCESS_VIOLATION:            output += "an Access Violation";        break;
688            case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:       output += "an Array Bound Exceeded";    break;
689            case EXCEPTION_BREAKPOINT:                  output += "a Breakpoint";               break;
690            case EXCEPTION_DATATYPE_MISALIGNMENT:       output += "a Datatype Misalignment";    break;
691            case EXCEPTION_FLT_DENORMAL_OPERAND:        output += "a Float Denormal Operand";   break;
692            case EXCEPTION_FLT_DIVIDE_BY_ZERO:          output += "a Float Divide By Zero";     break;
693            case EXCEPTION_FLT_INEXACT_RESULT:          output += "a Float Inexact Result";     break;
694            case EXCEPTION_FLT_INVALID_OPERATION:       output += "a Float Invalid Operation";  break;
695            case EXCEPTION_FLT_OVERFLOW:                output += "a Float Overflow";           break;
696            case EXCEPTION_FLT_STACK_CHECK:             output += "a Float Stack Check";        break;
697            case EXCEPTION_FLT_UNDERFLOW:               output += "a Float Underflow";          break;
698            case EXCEPTION_GUARD_PAGE:                  output += "a Guard Page";               break;
699            case EXCEPTION_ILLEGAL_INSTRUCTION:         output += "an Illegal Instruction";     break;
700            case EXCEPTION_IN_PAGE_ERROR:               output += "an In Page Error";           break;
701            case EXCEPTION_INT_DIVIDE_BY_ZERO:          output += "an Integer Divide By Zero";  break;
702            case EXCEPTION_INT_OVERFLOW:                output += "an Integer Overflow";        break;
703            case EXCEPTION_INVALID_DISPOSITION:         output += "an Invalid Disposition";     break;
704            case EXCEPTION_INVALID_HANDLE:              output += "an Invalid Handle";          break;
705            case EXCEPTION_NONCONTINUABLE_EXCEPTION:    output += "a Noncontinuable Exception"; break;
706            case EXCEPTION_PRIV_INSTRUCTION:            output += "a Privileged Instruction";   break;
707            case EXCEPTION_SINGLE_STEP:                 output += "a Single Step";              break;
708            case EXCEPTION_STACK_OVERFLOW:              output += "a Stack Overflow";           break;
709            case DBG_CONTROL_C:                         output += "a Control+C";                break;
710            case DBG_CONTROL_BREAK:                     output += "a Control+Break";            break;
711            case DBG_TERMINATE_THREAD:                  output += "a Terminate Thread";         break;
712            case DBG_TERMINATE_PROCESS:                 output += "a Terminate Process";        break;
713            case RPC_S_UNKNOWN_IF:                      output += "an Unknown Interface";       break;
714            case RPC_S_SERVER_UNAVAILABLE:              output += "a Server Unavailable";       break;
715            default:                                    output += "an Unknown Exception (" + SignalHandler::pointerToString(pExceptionRecord->ExceptionCode) + ")"; break;
716        }
717
718        // Now print information about where the fault occured
719        output += " at location " + SignalHandler::pointerToString(pExceptionRecord->ExceptionAddress);
720        if ((hModule = (HMODULE) SignalHandler::getModuleBase((DWORD) pExceptionRecord->ExceptionAddress)) && GetModuleFileName(hModule, szModule, MAX_PATH))
721        {
722            output += " in module ";
723            output += SignalHandler::getModuleName(szModule);
724        }
725
726        // If the exception was an access violation, print out some additional information, to the error log and the debugger.
727        if(pExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && pExceptionRecord->NumberParameters >= 2)
728        {
729            output += " ";
730            output += pExceptionRecord->ExceptionInformation[0] ? "writing to" : "reading from";
731            output += " location ";
732            output += SignalHandler::pointerToString(pExceptionRecord->ExceptionInformation[1]);
733        }
734
735        return output;
736    }
737
738    /// Strips the directories from the path to the module and returns the module's name only.
739    /* static */ std::string SignalHandler::getModuleName(const std::string& path)
740    {
741        return path.substr(path.find_last_of('\\') + 1);
742    }
743
744    /// Retrieves the base address of the module that contains the specified address.
745    // Code from Dr. Mingw by José Fonseca
746    /* static */ DWORD SignalHandler::getModuleBase(DWORD dwAddress)
747    {
748        MEMORY_BASIC_INFORMATION Buffer;
749
750        return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer)) ? (DWORD) Buffer.AllocationBase : 0;
751    }
752
753    /// Converts a value to string, formatted as pointer.
754    template <typename T>
[7455]755    /* static */ std::string SignalHandler::pointerToString(T pointer, bool bFillZeros)
[7449]756    {
757        std::ostringstream oss;
758
[7455]759        if (bFillZeros)
760            oss << std::setw(8) << std::setfill('0');
[7449]761
[7455]762        oss << std::hex << pointer;
763
[7449]764        return std::string("0x") + oss.str();
765    }
766
767    /// Converts a pointer to string.
768    template <typename T>
769    /* static */ std::string SignalHandler::pointerToString(T* pointer)
770    {
771        std::ostringstream oss;
772
773        oss << pointer;
774
775        return oss.str();
776    }
777}
778
779#endif
Note: See TracBrowser for help on using the repository browser.