Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8953 was 8858, checked in by landauf, 13 years ago

merged output branch back to trunk.

Changes:

  • you have to include util/Output.h instead of util/Debug.h
  • COUT(x) is now called orxout(level)
  • output levels are now defined by an enum instead of numbers. see util/Output.h for the definition
  • it's possible to use output contexts with orxout(level, context). see util/Output.h for some common contexts. you can define more contexts
  • you must use 'endl' at the end of an output message, '\n' does not flush the message

Output levels:

  • instead of COUT(0) use orxout()
  • instead of COUT(1) use orxout(user_error) or orxout(internal_error)
  • instead of COUT(2) use orxout(user_warning) or orxout(internal_warning)
  • instead of COUT(3) use orxout(user_status/user_info) or orxout(internal_status/internal_info)
  • instead of COUT(4) use orxout(verbose)
  • instead of COUT(5) use orxout(verbose_more)
  • instead of COUT(6) use orxout(verbose_ultra)

Guidelines:

  • user_* levels are for the user, visible in the console and the log-file
  • internal_* levels are for developers, visible in the log-file
  • verbose_* levels are for debugging, only visible if the context of the output is activated

Usage in C++:

  • orxout() << "message" << endl;
  • orxout(level) << "message" << endl;
  • orxout(level, context) << "message" << endl;

Usage in Lua:

  • orxout("message")
  • orxout(orxonox.level.levelname, "message")
  • orxout(orxonox.level.levelname, "context", "message")

Usage in Tcl (and in the in-game-console):

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