Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10860 was 9682, checked in by landauf, 11 years ago

fixed build on MSVC: unistd.h is not part of windows

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