Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/portals2/src/libraries/util/SignalHandler.cc @ 8600

Last change on this file since 8600 was 8351, checked in by rgrieder, 14 years ago

Merged kicklib2 branch back to trunk (includes former branches ois_update, mac_osx and kicklib).

Notes for updating

Linux:
You don't need an extra package for CEGUILua and Tolua, it's already shipped with CEGUI.
However you do need to make sure that the OgreRenderer is installed too with CEGUI 0.7 (may be a separate package).
Also, Orxonox now recognises if you install the CgProgramManager (a separate package available on newer Ubuntu on Debian systems).

Windows:
Download the new dependency packages versioned 6.0 and use these. If you have problems with that or if you don't like the in game console problem mentioned below, you can download the new 4.3 version of the packages (only available for Visual Studio 2005/2008).

Key new features:

  • *Support for Mac OS X*
  • Visual Studio 2010 support
  • Bullet library update to 2.77
  • OIS library update to 1.3
  • Support for CEGUI 0.7 —> Support for Arch Linux and even SuSE
  • Improved install target
  • Compiles now with GCC 4.6
  • Ogre Cg Shader plugin activated for Linux if available
  • And of course lots of bug fixes

There are also some regressions:

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