Changeset 7449 for code/trunk/src/libraries
- Timestamp:
- Sep 13, 2010, 11:12:21 PM (14 years ago)
- Location:
- code/trunk/src/libraries/util
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
code/trunk/src/libraries/util/CMakeLists.txt
r7284 r7449 59 59 ${UTIL_SRC_FILES} 60 60 ) 61 62 IF (DBGHELP_FOUND) 63 TARGET_LINK_LIBRARIES(util ${DBGHELP_LIBRARY}) 64 ENDIF() -
code/trunk/src/libraries/util/SignalHandler.cc
r7163 r7449 44 44 } 45 45 46 #if def ORXONOX_PLATFORM_LINUX46 #if defined(ORXONOX_PLATFORM_LINUX) 47 47 48 48 #include <wait.h> … … 124 124 if( SignalHandler::singletonPtr_s == 0 ) 125 125 { 126 COUT(0) << " received signal " << sigName.c_str() << std::endl << "can't write backtrace because SignalHandleralready destroyed" << std::endl;126 COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl; 127 127 exit(EXIT_FAILURE); 128 128 } … … 134 134 135 135 136 COUT(0) << " received signal " << sigName.c_str() << std::endl << "try to write backtrace to file orxonox_crash.log" << std::endl;136 COUT(0) << "Received signal " << sigName.c_str() << std::endl << "Try to write backtrace to file orxonox_crash.log" << std::endl; 137 137 138 138 int sigPipe[2]; … … 328 328 } 329 329 330 #endif /* ORXONOX_PLATFORM_LINUX */ 330 #elif defined(ORXONOX_PLATFORM_WINDOWS) && defined(DBGHELP_FOUND) 331 332 #include <iostream> 333 #include <iomanip> 334 #include <fstream> 335 #include <dbghelp.h> 336 337 namespace orxonox 338 { 339 /// Constructor: Initializes the values, but doesn't register the exception handler. 340 SignalHandler::SignalHandler() 341 { 342 this->prevExceptionFilter_ = NULL; 343 } 344 345 /// Destructor: Removes the exception handler. 346 SignalHandler::~SignalHandler() 347 { 348 if (this->prevExceptionFilter_ != NULL) 349 { 350 // Remove the unhandled exception filter function 351 SetUnhandledExceptionFilter(this->prevExceptionFilter_); 352 this->prevExceptionFilter_ = NULL; 353 } 354 } 355 356 /// Registers an exception handler and initializes the filename of the crash log. 357 void SignalHandler::doCatch(const std::string&, const std::string& filename) 358 { 359 this->filename_ = filename; 360 361 // don't register twice 362 assert(this->prevExceptionFilter_ == NULL); 363 364 if (this->prevExceptionFilter_ == NULL) 365 { 366 // Install the unhandled exception filter function 367 this->prevExceptionFilter_ = SetUnhandledExceptionFilter(&SignalHandler::exceptionFilter); 368 } 369 } 370 371 /// Exception handler: Will be called by Windows if an unhandled exceptions occurs. 372 /* static */ LONG WINAPI SignalHandler::exceptionFilter(PEXCEPTION_POINTERS pExceptionInfo) 373 { 374 // avoid loops 375 static bool bExecuting = false; 376 377 if (!bExecuting) 378 { 379 bExecuting = true; 380 381 382 // if the signalhandler has already been destroyed then don't do anything 383 if (SignalHandler::singletonPtr_s == 0) 384 { 385 COUT(1) << "Catched an unhandled exception" << std::endl << "Can't write backtrace because SignalHandler is already destroyed" << std::endl; 386 exit(EXIT_FAILURE); 387 } 388 389 COUT(1) << "Catched an unhandled exception" << std::endl << "Try to write backtrace to orxonox_crash.log..." << std::endl; 390 391 // write the crash log 392 std::ofstream crashlog(SignalHandler::getInstance().filename_.c_str()); 393 394 time_t now = time(NULL); 395 396 crashlog << "=======================================================" << std::endl; 397 crashlog << "= Time: " << std::string(ctime(&now)); 398 crashlog << "=======================================================" << std::endl; 399 crashlog << std::endl; 400 401 const std::string& error = SignalHandler::getExceptionType(pExceptionInfo); 402 403 crashlog << error << std::endl; 404 crashlog << std::endl; 405 406 const std::string& callstack = SignalHandler::getStackTrace(pExceptionInfo); 407 408 crashlog << "Call stack:" << std::endl; 409 crashlog << callstack << std::endl; 410 411 crashlog.close(); 412 413 // print the same information also to the console 414 COUT(1) << std::endl; 415 COUT(1) << error << std::endl; 416 COUT(1) << std::endl; 417 COUT(1) << "Call stack:" << std::endl; 418 COUT(1) << callstack << std::endl; 419 420 bExecuting = false; 421 } 422 else 423 { 424 COUT(1) << "An error occurred while writing the backtrace" << std::endl; 425 } 426 427 if (SignalHandler::getInstance().prevExceptionFilter_) 428 return SignalHandler::getInstance().prevExceptionFilter_(pExceptionInfo); 429 else 430 return EXCEPTION_CONTINUE_SEARCH; 431 } 432 433 /// Returns the stack trace for either the current function, or, if @a pExceptionInfo is not NULL, for the given exception context. 434 /* static */ std::string SignalHandler::getStackTrace(PEXCEPTION_POINTERS pExceptionInfo) 435 { 436 // Initialise the symbol table to get function names: 437 SymInitialize(GetCurrentProcess(), 0, true); 438 439 // Store the current stack frame here: 440 STACKFRAME frame; 441 memset(&frame, 0, sizeof(STACKFRAME)); 442 443 // Get processor information for the current thread: 444 CONTEXT context; 445 memset(&context, 0, sizeof(CONTEXT)); 446 447 if (pExceptionInfo) 448 { 449 // get the context of the exception 450 context = *pExceptionInfo->ContextRecord; 451 } 452 else 453 { 454 context.ContextFlags = CONTEXT_FULL; 455 456 // Load the RTLCapture context function: 457 HINSTANCE kernel32 = LoadLibrary("Kernel32.dll"); 458 typedef void (*RtlCaptureContextFunc) (CONTEXT* ContextRecord); 459 RtlCaptureContextFunc rtlCaptureContext = (RtlCaptureContextFunc) GetProcAddress(kernel32, "RtlCaptureContext"); 460 461 // Capture the thread context 462 rtlCaptureContext(&context); 463 } 464 465 DWORD type; 466 467 // set the flags and initialize the stackframe struct 468 #ifdef _M_IX86 469 type = IMAGE_FILE_MACHINE_I386; 470 471 frame.AddrPC.Offset = context.Eip; // Current location in program 472 frame.AddrPC.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 473 frame.AddrStack.Offset = context.Esp; // Stack pointers current value 474 frame.AddrStack.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 475 frame.AddrFrame.Offset = context.Ebp; // Value of register used to access local function variables. 476 frame.AddrFrame.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 477 #elif _M_X64 478 type = IMAGE_FILE_MACHINE_AMD64; 479 480 frame.AddrPC.Offset = context.Rip; // Current location in program 481 frame.AddrPC.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 482 frame.AddrStack.Offset = context.Rsp; // Stack pointers current value 483 frame.AddrStack.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 484 frame.AddrFrame.Offset = context.Rsp; // Value of register used to access local function variables. 485 frame.AddrFrame.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 486 #elif _M_IA64 487 type = IMAGE_FILE_MACHINE_IA64; 488 489 frame.AddrPC.Offset = context.StIIP; // Current location in program 490 frame.AddrPC.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 491 frame.AddrStack.Offset = context.IntSp; // Stack pointers current value 492 frame.AddrStack.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 493 frame.AddrFrame.Offset = context.IntSp; // Value of register used to access local function variables. 494 frame.AddrFrame.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing 495 frame.AddrBStore.Offset = context.RsBSP; // 496 frame.AddrBStore.Mode = AddrModeFlat; // 497 #else 498 return 499 #endif 500 501 std::string output; 502 503 // Keep getting stack frames from windows till there are no more left: 504 for (int i = 0; 505 StackWalk 506 ( 507 type , // MachineType 508 GetCurrentProcess() , // Process to get stack trace for 509 GetCurrentThread() , // Thread to get stack trace for 510 &frame , // Where to store next stack frame 511 &context , // Pointer to processor context record 512 0 , // Routine to read process memory: use the default ReadProcessMemory 513 &SymFunctionTableAccess , // Routine to access the modules symbol table 514 &SymGetModuleBase , // Routine to access the modules base address 515 0 // Something to do with 16-bit code. Not needed. 516 ); 517 ++i 518 ) 519 { 520 //------------------------------------------------------------------ 521 // Declare an image help symbol structure to hold symbol info and 522 // name up to 256 chars This struct is of variable lenght though so 523 // it must be declared as a raw byte buffer. 524 //------------------------------------------------------------------ 525 static char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255]; 526 memset(symbolBuffer, 0, sizeof(IMAGEHLP_SYMBOL) + 255); 527 528 // Cast it to a symbol struct: 529 IMAGEHLP_SYMBOL * symbol = (IMAGEHLP_SYMBOL*) symbolBuffer; 530 531 // Need to set the first two fields of this symbol before obtaining name info: 532 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL) + 255; 533 symbol->MaxNameLength = 254; 534 535 // The displacement from the beginning of the symbol is stored here: pretty useless 536 unsigned displacement = 0; 537 538 output += multi_cast<std::string>(i) + ": "; 539 540 // Get the symbol information from the address of the instruction pointer register: 541 if 542 ( 543 SymGetSymFromAddr 544 ( 545 GetCurrentProcess() , // Process to get symbol information for 546 frame.AddrPC.Offset , // Address to get symbol for: instruction pointer register 547 (DWORD*) &displacement , // Displacement from the beginning of the symbol: what is this for ? 548 symbol // Where to save the symbol 549 ) 550 ) 551 { 552 // Add the name of the function to the function list: 553 output += SignalHandler::pointerToString(frame.AddrPC.Offset) + " " + symbol->Name; 554 } 555 else 556 { 557 // Print an unknown location: 558 output += SignalHandler::pointerToString(frame.AddrPC.Offset); 559 } 560 561 // output += " (" + SignalHandler::pointerToString(displacement) + ")"; 562 output += "\n"; 563 } 564 565 // Cleanup the symbol table: 566 SymCleanup(GetCurrentProcess()); 567 568 return output; 569 } 570 571 /// Returns a description of the given exception. 572 // Based on code from Dr. Mingw by José Fonseca 573 /* static */ std::string SignalHandler::getExceptionType(PEXCEPTION_POINTERS pExceptionInfo) 574 { 575 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; 576 TCHAR szModule[MAX_PATH]; 577 HMODULE hModule; 578 579 std::string output = (GetModuleFileName(NULL, szModule, MAX_PATH) ? SignalHandler::getModuleName(szModule) : "Application"); 580 output += " caused "; 581 582 switch(pExceptionRecord->ExceptionCode) 583 { 584 case EXCEPTION_ACCESS_VIOLATION: output += "an Access Violation"; break; 585 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: output += "an Array Bound Exceeded"; break; 586 case EXCEPTION_BREAKPOINT: output += "a Breakpoint"; break; 587 case EXCEPTION_DATATYPE_MISALIGNMENT: output += "a Datatype Misalignment"; break; 588 case EXCEPTION_FLT_DENORMAL_OPERAND: output += "a Float Denormal Operand"; break; 589 case EXCEPTION_FLT_DIVIDE_BY_ZERO: output += "a Float Divide By Zero"; break; 590 case EXCEPTION_FLT_INEXACT_RESULT: output += "a Float Inexact Result"; break; 591 case EXCEPTION_FLT_INVALID_OPERATION: output += "a Float Invalid Operation"; break; 592 case EXCEPTION_FLT_OVERFLOW: output += "a Float Overflow"; break; 593 case EXCEPTION_FLT_STACK_CHECK: output += "a Float Stack Check"; break; 594 case EXCEPTION_FLT_UNDERFLOW: output += "a Float Underflow"; break; 595 case EXCEPTION_GUARD_PAGE: output += "a Guard Page"; break; 596 case EXCEPTION_ILLEGAL_INSTRUCTION: output += "an Illegal Instruction"; break; 597 case EXCEPTION_IN_PAGE_ERROR: output += "an In Page Error"; break; 598 case EXCEPTION_INT_DIVIDE_BY_ZERO: output += "an Integer Divide By Zero"; break; 599 case EXCEPTION_INT_OVERFLOW: output += "an Integer Overflow"; break; 600 case EXCEPTION_INVALID_DISPOSITION: output += "an Invalid Disposition"; break; 601 case EXCEPTION_INVALID_HANDLE: output += "an Invalid Handle"; break; 602 case EXCEPTION_NONCONTINUABLE_EXCEPTION: output += "a Noncontinuable Exception"; break; 603 case EXCEPTION_PRIV_INSTRUCTION: output += "a Privileged Instruction"; break; 604 case EXCEPTION_SINGLE_STEP: output += "a Single Step"; break; 605 case EXCEPTION_STACK_OVERFLOW: output += "a Stack Overflow"; break; 606 case DBG_CONTROL_C: output += "a Control+C"; break; 607 case DBG_CONTROL_BREAK: output += "a Control+Break"; break; 608 case DBG_TERMINATE_THREAD: output += "a Terminate Thread"; break; 609 case DBG_TERMINATE_PROCESS: output += "a Terminate Process"; break; 610 case RPC_S_UNKNOWN_IF: output += "an Unknown Interface"; break; 611 case RPC_S_SERVER_UNAVAILABLE: output += "a Server Unavailable"; break; 612 default: output += "an Unknown Exception (" + SignalHandler::pointerToString(pExceptionRecord->ExceptionCode) + ")"; break; 613 } 614 615 // Now print information about where the fault occured 616 output += " at location " + SignalHandler::pointerToString(pExceptionRecord->ExceptionAddress); 617 if ((hModule = (HMODULE) SignalHandler::getModuleBase((DWORD) pExceptionRecord->ExceptionAddress)) && GetModuleFileName(hModule, szModule, MAX_PATH)) 618 { 619 output += " in module "; 620 output += SignalHandler::getModuleName(szModule); 621 } 622 623 // If the exception was an access violation, print out some additional information, to the error log and the debugger. 624 if(pExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && pExceptionRecord->NumberParameters >= 2) 625 { 626 output += " "; 627 output += pExceptionRecord->ExceptionInformation[0] ? "writing to" : "reading from"; 628 output += " location "; 629 output += SignalHandler::pointerToString(pExceptionRecord->ExceptionInformation[1]); 630 } 631 632 return output; 633 } 634 635 /// Strips the directories from the path to the module and returns the module's name only. 636 /* static */ std::string SignalHandler::getModuleName(const std::string& path) 637 { 638 return path.substr(path.find_last_of('\\') + 1); 639 } 640 641 /// Retrieves the base address of the module that contains the specified address. 642 // Code from Dr. Mingw by José Fonseca 643 /* static */ DWORD SignalHandler::getModuleBase(DWORD dwAddress) 644 { 645 MEMORY_BASIC_INFORMATION Buffer; 646 647 return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer)) ? (DWORD) Buffer.AllocationBase : 0; 648 } 649 650 /// Converts a value to string, formatted as pointer. 651 template <typename T> 652 /* static */ std::string SignalHandler::pointerToString(T pointer) 653 { 654 std::ostringstream oss; 655 656 oss << std::setw(8) << std::setfill('0') << std::hex << pointer; 657 658 return std::string("0x") + oss.str(); 659 } 660 661 /// Converts a pointer to string. 662 template <typename T> 663 /* static */ std::string SignalHandler::pointerToString(T* pointer) 664 { 665 std::ostringstream oss; 666 667 oss << pointer; 668 669 return oss.str(); 670 } 671 } 672 673 #endif -
code/trunk/src/libraries/util/SignalHandler.h
r7401 r7449 39 39 40 40 #include <cassert> 41 #include <list>42 41 #include <string> 43 42 #include "Singleton.h" 43 #include "SpecialConfig.h" 44 44 45 45 namespace orxonox … … 48 48 } 49 49 50 #ifdef ORXONOX_PLATFORM_LINUX 50 #if defined(ORXONOX_PLATFORM_LINUX) 51 52 #include <list> 51 53 #include <signal.h> 52 54 … … 97 99 } 98 100 99 #else /* ORXONOX_PLATFORM_LINUX */ 101 #elif defined(ORXONOX_PLATFORM_WINDOWS) && defined(DBGHELP_FOUND) 102 103 #include <windows.h> 100 104 101 105 namespace orxonox 102 106 { 103 /// The SignalHandler is used to catch signals like SIGSEGV and write a backtrace to the logfile. Not implemented on Windows.107 /// The SignalHandler is used to catch unhandled exceptions like access violations and write a backtrace to the logfile. 104 108 class _UtilExport SignalHandler : public Singleton<SignalHandler> 105 109 { 106 110 friend class Singleton<SignalHandler>; 107 111 public: 108 SignalHandler() { } 109 ~SignalHandler() { } 112 SignalHandler(); 113 ~SignalHandler(); 114 115 void doCatch( const std::string & appName, const std::string & filename ); 116 117 private: 118 static LONG WINAPI exceptionFilter(PEXCEPTION_POINTERS pExceptionInfo); 119 120 static std::string getStackTrace(PEXCEPTION_POINTERS pExceptionInfo = NULL); 121 static std::string getExceptionType(PEXCEPTION_POINTERS pExceptionInfo); 122 static std::string getModuleName(const std::string& path); 123 static DWORD getModuleBase(DWORD dwAddress); 124 125 template <typename T> 126 static std::string pointerToString(T pointer); 127 template <typename T> 128 static std::string pointerToString(T* pointer); 129 130 static SignalHandler* singletonPtr_s; 131 132 std::string filename_; 133 LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter_; 134 }; 135 } 136 137 #else 138 139 namespace orxonox 140 { 141 /// The SignalHandler is used to catch signals like SIGSEGV and write a backtrace to the logfile. Not implemented on systems except Linux and Windows. 142 class _UtilExport SignalHandler : public Singleton<SignalHandler> 143 { 144 friend class Singleton<SignalHandler>; 145 public: 110 146 void doCatch( const std::string & appName, const std::string & filename ) {} 111 void dontCatch() {}112 void registerCallback( SignalCallback cb, void * someData ) {}113 147 114 148 private: … … 117 151 } 118 152 119 #endif /* ORXONOX_PLATFORM_LINUX */153 #endif 120 154 121 155 #endif /* _SignalHandler_H__ */
Note: See TracChangeset
for help on using the changeset viewer.