Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7627 was 7457, checked in by landauf, 14 years ago

show file name and line number in the call stack (works only with msvc)
hack-fix for wrong call stack when calling some "noreturn" functions (e.g. _assert())
some cleanup

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