Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/tutorial/src/libraries/util/SignalHandler.cc @ 11468

Last change on this file since 11468 was 7801, checked in by dafrick, 14 years ago

Merging presentation2 branch back to trunk.

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