Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/util/SignalHandler.cc @ 11014

Last change on this file since 11014 was 10916, checked in by landauf, 10 years ago

use actual types instead of 'auto'. only exception is for complicated template types, e.g. when iterating over a map

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