Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/testing/src/libraries/util/SignalHandler.cc @ 9437

Last change on this file since 9437 was 9114, checked in by landauf, 13 years ago

added tests for several classes and functions in util

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