Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Sep 13, 2010, 11:12:21 PM (14 years ago)
Author:
landauf
Message:

added windows implementation of SignalHandler.

Location:
code/trunk/src/libraries/util
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • code/trunk/src/libraries/util/CMakeLists.txt

    r7284 r7449  
    5959    ${UTIL_SRC_FILES}
    6060)
     61
     62IF (DBGHELP_FOUND)
     63  TARGET_LINK_LIBRARIES(util ${DBGHELP_LIBRARY})
     64ENDIF()
  • code/trunk/src/libraries/util/SignalHandler.cc

    r7163 r7449  
    4444}
    4545
    46 #ifdef ORXONOX_PLATFORM_LINUX
     46#if defined(ORXONOX_PLATFORM_LINUX)
    4747
    4848#include <wait.h>
     
    124124      if( SignalHandler::singletonPtr_s == 0 )
    125125      {
    126         COUT(0) << "received signal " << sigName.c_str() << std::endl << "can't write backtrace because SignalHandler already destroyed" << std::endl;
     126        COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl;
    127127        exit(EXIT_FAILURE);
    128128      }
     
    134134
    135135
    136       COUT(0) << "received signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox_crash.log" << std::endl;
     136      COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Try to write backtrace to file orxonox_crash.log" << std::endl;
    137137
    138138      int sigPipe[2];
     
    328328}
    329329
    330 #endif /* ORXONOX_PLATFORM_LINUX */
     330#elif defined(ORXONOX_PLATFORM_WINDOWS) && defined(DBGHELP_FOUND)
     331
     332#include <iostream>
     333#include <iomanip>
     334#include <fstream>
     335#include <dbghelp.h>
     336
     337namespace orxonox
     338{
     339    /// Constructor: Initializes the values, but doesn't register the exception handler.
     340    SignalHandler::SignalHandler()
     341    {
     342        this->prevExceptionFilter_ = NULL;
     343    }
     344
     345    /// Destructor: Removes the exception handler.
     346    SignalHandler::~SignalHandler()
     347    {
     348        if (this->prevExceptionFilter_ != NULL)
     349        {
     350            // Remove the unhandled exception filter function
     351            SetUnhandledExceptionFilter(this->prevExceptionFilter_);
     352            this->prevExceptionFilter_ = NULL;
     353        }
     354    }
     355
     356    /// Registers an exception handler and initializes the filename of the crash log.
     357    void SignalHandler::doCatch(const std::string&, const std::string& filename)
     358    {
     359        this->filename_ = filename;
     360
     361        // don't register twice
     362        assert(this->prevExceptionFilter_ == NULL);
     363
     364        if (this->prevExceptionFilter_ == NULL)
     365        {
     366            // Install the unhandled exception filter function
     367            this->prevExceptionFilter_ = SetUnhandledExceptionFilter(&SignalHandler::exceptionFilter);
     368        }
     369    }
     370
     371    /// Exception handler: Will be called by Windows if an unhandled exceptions occurs.
     372    /* static */ LONG WINAPI SignalHandler::exceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
     373    {
     374        // avoid loops
     375        static bool bExecuting = false;
     376
     377        if (!bExecuting)
     378        {
     379            bExecuting = true;
     380
     381
     382            // if the signalhandler has already been destroyed then don't do anything
     383            if (SignalHandler::singletonPtr_s == 0)
     384            {
     385                COUT(1) << "Catched an unhandled exception" << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl;
     386                exit(EXIT_FAILURE);
     387            }
     388
     389            COUT(1) << "Catched an unhandled exception" << std::endl << "Try to write backtrace to orxonox_crash.log..." << std::endl;
     390
     391            // write the crash log
     392            std::ofstream crashlog(SignalHandler::getInstance().filename_.c_str());
     393
     394            time_t now = time(NULL);
     395
     396            crashlog << "=======================================================" << std::endl;
     397            crashlog << "= Time: " << std::string(ctime(&now));
     398            crashlog << "=======================================================" << std::endl;
     399            crashlog << std::endl;
     400
     401            const std::string& error = SignalHandler::getExceptionType(pExceptionInfo);
     402
     403            crashlog << error << std::endl;
     404            crashlog << std::endl;
     405
     406            const std::string& callstack = SignalHandler::getStackTrace(pExceptionInfo);
     407
     408            crashlog << "Call stack:" << std::endl;
     409            crashlog << callstack << std::endl;
     410
     411            crashlog.close();
     412
     413            // print the same information also to the console
     414            COUT(1) << std::endl;
     415            COUT(1) << error << std::endl;
     416            COUT(1) << std::endl;
     417            COUT(1) << "Call stack:" << std::endl;
     418            COUT(1) << callstack << std::endl;
     419
     420            bExecuting = false;
     421        }
     422        else
     423        {
     424            COUT(1) << "An error occurred while writing the backtrace" << std::endl;
     425        }
     426
     427        if (SignalHandler::getInstance().prevExceptionFilter_)
     428            return SignalHandler::getInstance().prevExceptionFilter_(pExceptionInfo);
     429        else
     430            return EXCEPTION_CONTINUE_SEARCH;
     431    }
     432
     433    /// Returns the stack trace for either the current function, or, if @a pExceptionInfo is not NULL, for the given exception context.
     434    /* static */ std::string SignalHandler::getStackTrace(PEXCEPTION_POINTERS pExceptionInfo)
     435    {
     436        // Initialise the symbol table to get function names:
     437        SymInitialize(GetCurrentProcess(), 0, true);
     438
     439        // Store the current stack frame here:
     440        STACKFRAME frame;
     441        memset(&frame, 0, sizeof(STACKFRAME));
     442
     443        // Get processor information for the current thread:
     444        CONTEXT context;
     445        memset(&context, 0, sizeof(CONTEXT));
     446
     447        if (pExceptionInfo)
     448        {
     449            // get the context of the exception
     450            context = *pExceptionInfo->ContextRecord;
     451        }
     452        else
     453        {
     454            context.ContextFlags = CONTEXT_FULL;
     455
     456            // Load the RTLCapture context function:
     457            HINSTANCE kernel32 = LoadLibrary("Kernel32.dll");
     458            typedef void (*RtlCaptureContextFunc) (CONTEXT* ContextRecord);
     459            RtlCaptureContextFunc rtlCaptureContext = (RtlCaptureContextFunc) GetProcAddress(kernel32, "RtlCaptureContext");
     460
     461            // Capture the thread context
     462            rtlCaptureContext(&context);
     463        }
     464
     465        DWORD type;
     466
     467        // set the flags and initialize the stackframe struct
     468    #ifdef _M_IX86
     469        type = IMAGE_FILE_MACHINE_I386;
     470
     471        frame.AddrPC.Offset         = context.Eip;      // Current location in program
     472        frame.AddrPC.Mode           = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     473        frame.AddrStack.Offset      = context.Esp;      // Stack pointers current value
     474        frame.AddrStack.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     475        frame.AddrFrame.Offset      = context.Ebp;      // Value of register used to access local function variables.
     476        frame.AddrFrame.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     477    #elif _M_X64
     478        type = IMAGE_FILE_MACHINE_AMD64;
     479
     480        frame.AddrPC.Offset         = context.Rip;      // Current location in program
     481        frame.AddrPC.Mode           = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     482        frame.AddrStack.Offset      = context.Rsp;      // Stack pointers current value
     483        frame.AddrStack.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     484        frame.AddrFrame.Offset      = context.Rsp;      // Value of register used to access local function variables.
     485        frame.AddrFrame.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     486    #elif _M_IA64
     487        type = IMAGE_FILE_MACHINE_IA64;
     488
     489        frame.AddrPC.Offset         = context.StIIP;    // Current location in program
     490        frame.AddrPC.Mode           = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     491        frame.AddrStack.Offset      = context.IntSp;    // Stack pointers current value
     492        frame.AddrStack.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     493        frame.AddrFrame.Offset      = context.IntSp;    // Value of register used to access local function variables.
     494        frame.AddrFrame.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
     495        frame.AddrBStore.Offset     = context.RsBSP;    //
     496        frame.AddrBStore.Mode       = AddrModeFlat;     //
     497    #else
     498        return
     499    #endif
     500
     501        std::string output;
     502
     503        // Keep getting stack frames from windows till there are no more left:
     504        for (int i = 0;
     505            StackWalk
     506            (
     507                type                     ,      // MachineType
     508                GetCurrentProcess()      ,      // Process to get stack trace for
     509                GetCurrentThread()       ,      // Thread to get stack trace for
     510                &frame                   ,      // Where to store next stack frame
     511                &context                 ,      // Pointer to processor context record
     512                0                        ,      // Routine to read process memory: use the default ReadProcessMemory
     513                &SymFunctionTableAccess  ,      // Routine to access the modules symbol table
     514                &SymGetModuleBase        ,      // Routine to access the modules base address
     515                0                               // Something to do with 16-bit code. Not needed.
     516            );
     517            ++i
     518        )
     519        {
     520            //------------------------------------------------------------------
     521            // Declare an image help symbol structure to hold symbol info and
     522            // name up to 256 chars This struct is of variable lenght though so
     523            // it must be declared as a raw byte buffer.
     524            //------------------------------------------------------------------
     525            static char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
     526            memset(symbolBuffer, 0, sizeof(IMAGEHLP_SYMBOL) + 255);
     527
     528            // Cast it to a symbol struct:
     529            IMAGEHLP_SYMBOL * symbol = (IMAGEHLP_SYMBOL*) symbolBuffer;
     530
     531            // Need to set the first two fields of this symbol before obtaining name info:
     532            symbol->SizeOfStruct    = sizeof(IMAGEHLP_SYMBOL) + 255;
     533            symbol->MaxNameLength   = 254;
     534
     535            // The displacement from the beginning of the symbol is stored here: pretty useless
     536            unsigned displacement = 0;
     537
     538            output += multi_cast<std::string>(i) + ": ";
     539
     540            // Get the symbol information from the address of the instruction pointer register:
     541            if
     542            (
     543                SymGetSymFromAddr
     544                (
     545                    GetCurrentProcess()    ,   // Process to get symbol information for
     546                    frame.AddrPC.Offset    ,   // Address to get symbol for: instruction pointer register
     547                    (DWORD*) &displacement ,   // Displacement from the beginning of the symbol: what is this for ?
     548                    symbol                     // Where to save the symbol
     549                )
     550            )
     551            {
     552                // Add the name of the function to the function list:
     553                output += SignalHandler::pointerToString(frame.AddrPC.Offset) + " " + symbol->Name;
     554            }
     555            else
     556            {
     557                // Print an unknown location:
     558                output += SignalHandler::pointerToString(frame.AddrPC.Offset);
     559            }
     560
     561//            output += " (" + SignalHandler::pointerToString(displacement) + ")";
     562            output += "\n";
     563        }
     564
     565        // Cleanup the symbol table:
     566        SymCleanup(GetCurrentProcess());
     567
     568        return output;
     569    }
     570
     571    /// Returns a description of the given exception.
     572    // Based on code from Dr. Mingw by José Fonseca
     573    /* static */ std::string SignalHandler::getExceptionType(PEXCEPTION_POINTERS pExceptionInfo)
     574    {
     575        PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
     576        TCHAR szModule[MAX_PATH];
     577        HMODULE hModule;
     578
     579        std::string output = (GetModuleFileName(NULL, szModule, MAX_PATH) ? SignalHandler::getModuleName(szModule) : "Application");
     580        output += " caused ";
     581
     582        switch(pExceptionRecord->ExceptionCode)
     583        {
     584            case EXCEPTION_ACCESS_VIOLATION:            output += "an Access Violation";        break;
     585            case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:       output += "an Array Bound Exceeded";    break;
     586            case EXCEPTION_BREAKPOINT:                  output += "a Breakpoint";               break;
     587            case EXCEPTION_DATATYPE_MISALIGNMENT:       output += "a Datatype Misalignment";    break;
     588            case EXCEPTION_FLT_DENORMAL_OPERAND:        output += "a Float Denormal Operand";   break;
     589            case EXCEPTION_FLT_DIVIDE_BY_ZERO:          output += "a Float Divide By Zero";     break;
     590            case EXCEPTION_FLT_INEXACT_RESULT:          output += "a Float Inexact Result";     break;
     591            case EXCEPTION_FLT_INVALID_OPERATION:       output += "a Float Invalid Operation";  break;
     592            case EXCEPTION_FLT_OVERFLOW:                output += "a Float Overflow";           break;
     593            case EXCEPTION_FLT_STACK_CHECK:             output += "a Float Stack Check";        break;
     594            case EXCEPTION_FLT_UNDERFLOW:               output += "a Float Underflow";          break;
     595            case EXCEPTION_GUARD_PAGE:                  output += "a Guard Page";               break;
     596            case EXCEPTION_ILLEGAL_INSTRUCTION:         output += "an Illegal Instruction";     break;
     597            case EXCEPTION_IN_PAGE_ERROR:               output += "an In Page Error";           break;
     598            case EXCEPTION_INT_DIVIDE_BY_ZERO:          output += "an Integer Divide By Zero";  break;
     599            case EXCEPTION_INT_OVERFLOW:                output += "an Integer Overflow";        break;
     600            case EXCEPTION_INVALID_DISPOSITION:         output += "an Invalid Disposition";     break;
     601            case EXCEPTION_INVALID_HANDLE:              output += "an Invalid Handle";          break;
     602            case EXCEPTION_NONCONTINUABLE_EXCEPTION:    output += "a Noncontinuable Exception"; break;
     603            case EXCEPTION_PRIV_INSTRUCTION:            output += "a Privileged Instruction";   break;
     604            case EXCEPTION_SINGLE_STEP:                 output += "a Single Step";              break;
     605            case EXCEPTION_STACK_OVERFLOW:              output += "a Stack Overflow";           break;
     606            case DBG_CONTROL_C:                         output += "a Control+C";                break;
     607            case DBG_CONTROL_BREAK:                     output += "a Control+Break";            break;
     608            case DBG_TERMINATE_THREAD:                  output += "a Terminate Thread";         break;
     609            case DBG_TERMINATE_PROCESS:                 output += "a Terminate Process";        break;
     610            case RPC_S_UNKNOWN_IF:                      output += "an Unknown Interface";       break;
     611            case RPC_S_SERVER_UNAVAILABLE:              output += "a Server Unavailable";       break;
     612            default:                                    output += "an Unknown Exception (" + SignalHandler::pointerToString(pExceptionRecord->ExceptionCode) + ")"; break;
     613        }
     614
     615        // Now print information about where the fault occured
     616        output += " at location " + SignalHandler::pointerToString(pExceptionRecord->ExceptionAddress);
     617        if ((hModule = (HMODULE) SignalHandler::getModuleBase((DWORD) pExceptionRecord->ExceptionAddress)) && GetModuleFileName(hModule, szModule, MAX_PATH))
     618        {
     619            output += " in module ";
     620            output += SignalHandler::getModuleName(szModule);
     621        }
     622
     623        // If the exception was an access violation, print out some additional information, to the error log and the debugger.
     624        if(pExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && pExceptionRecord->NumberParameters >= 2)
     625        {
     626            output += " ";
     627            output += pExceptionRecord->ExceptionInformation[0] ? "writing to" : "reading from";
     628            output += " location ";
     629            output += SignalHandler::pointerToString(pExceptionRecord->ExceptionInformation[1]);
     630        }
     631
     632        return output;
     633    }
     634
     635    /// Strips the directories from the path to the module and returns the module's name only.
     636    /* static */ std::string SignalHandler::getModuleName(const std::string& path)
     637    {
     638        return path.substr(path.find_last_of('\\') + 1);
     639    }
     640
     641    /// Retrieves the base address of the module that contains the specified address.
     642    // Code from Dr. Mingw by José Fonseca
     643    /* static */ DWORD SignalHandler::getModuleBase(DWORD dwAddress)
     644    {
     645        MEMORY_BASIC_INFORMATION Buffer;
     646
     647        return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer)) ? (DWORD) Buffer.AllocationBase : 0;
     648    }
     649
     650    /// Converts a value to string, formatted as pointer.
     651    template <typename T>
     652    /* static */ std::string SignalHandler::pointerToString(T pointer)
     653    {
     654        std::ostringstream oss;
     655
     656        oss << std::setw(8) << std::setfill('0') << std::hex << pointer;
     657
     658        return std::string("0x") + oss.str();
     659    }
     660
     661    /// Converts a pointer to string.
     662    template <typename T>
     663    /* static */ std::string SignalHandler::pointerToString(T* pointer)
     664    {
     665        std::ostringstream oss;
     666
     667        oss << pointer;
     668
     669        return oss.str();
     670    }
     671}
     672
     673#endif
  • code/trunk/src/libraries/util/SignalHandler.h

    r7401 r7449  
    3939
    4040#include <cassert>
    41 #include <list>
    4241#include <string>
    4342#include "Singleton.h"
     43#include "SpecialConfig.h"
    4444
    4545namespace orxonox
     
    4848}
    4949
    50 #ifdef ORXONOX_PLATFORM_LINUX
     50#if defined(ORXONOX_PLATFORM_LINUX)
     51
     52#include <list>
    5153#include <signal.h>
    5254
     
    9799}
    98100
    99 #else /* ORXONOX_PLATFORM_LINUX */
     101#elif defined(ORXONOX_PLATFORM_WINDOWS) && defined(DBGHELP_FOUND)
     102
     103#include <windows.h>
    100104
    101105namespace orxonox
    102106{
    103     /// The SignalHandler is used to catch signals like SIGSEGV and write a backtrace to the logfile. Not implemented on Windows.
     107    /// The SignalHandler is used to catch unhandled exceptions like access violations and write a backtrace to the logfile.
    104108    class _UtilExport SignalHandler : public Singleton<SignalHandler>
    105109    {
    106110        friend class Singleton<SignalHandler>;
    107111    public:
    108         SignalHandler()  { }
    109         ~SignalHandler() { }
     112        SignalHandler();
     113        ~SignalHandler();
     114
     115        void doCatch( const std::string & appName, const std::string & filename );
     116
     117    private:
     118        static LONG WINAPI exceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
     119
     120        static std::string getStackTrace(PEXCEPTION_POINTERS pExceptionInfo = NULL);
     121        static std::string getExceptionType(PEXCEPTION_POINTERS pExceptionInfo);
     122        static std::string getModuleName(const std::string& path);
     123        static DWORD getModuleBase(DWORD dwAddress);
     124
     125        template <typename T>
     126        static std::string pointerToString(T pointer);
     127        template <typename T>
     128        static std::string pointerToString(T* pointer);
     129
     130        static SignalHandler* singletonPtr_s;
     131
     132        std::string filename_;
     133        LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter_;
     134    };
     135}
     136
     137#else
     138
     139namespace orxonox
     140{
     141    /// The SignalHandler is used to catch signals like SIGSEGV and write a backtrace to the logfile. Not implemented on systems except Linux and Windows.
     142    class _UtilExport SignalHandler : public Singleton<SignalHandler>
     143    {
     144        friend class Singleton<SignalHandler>;
     145    public:
    110146        void doCatch( const std::string & appName, const std::string & filename ) {}
    111         void dontCatch() {}
    112         void registerCallback( SignalCallback cb, void * someData ) {}
    113147
    114148    private:
     
    117151}
    118152
    119 #endif /* ORXONOX_PLATFORM_LINUX */
     153#endif
    120154
    121155#endif /* _SignalHandler_H__ */
Note: See TracChangeset for help on using the changeset viewer.