Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/libs/src/libraries/util/SignalHandler.cc @ 9873

Last change on this file since 9873 was 9550, checked in by landauf, 12 years ago

merged testing branch back to trunk. unbelievable it took me 13 months to finish this chore…

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