Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7455 was 7455, checked in by landauf, 14 years ago

added special implementation of abort() and _assert() for mingw which contain a break point.
also added displacement to the callstack

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