//////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #ifdef __EXCEPTION_TRACER__ #include "otpch.h" #include "exception.h" #include "otsystem.h" #include #include #include #ifdef WIN32 #include #include #endif #include "tools.h" #include "configmanager.h" extern ConfigManager g_config; typedef std::map FunctionMap; FunctionMap functionMap; uint32_t max_off, min_off; bool maploaded = false; OTSYS_THREAD_LOCKVAR maploadlock; #ifdef WIN32 void printPointer(std::ostream* output,uint32_t p); EXCEPTION_DISPOSITION __cdecl _SEHHandler(struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext); #endif #ifndef COMPILER_STRING #ifdef __GNUC__ #define COMPILER_STRING "gcc " __VERSION__ #else #define COMPILER_STRING "" #endif #endif #define COMPILATION_DATE __DATE__ " " __TIME__ ExceptionHandler::ExceptionHandler() { installed = false; } ExceptionHandler::~ExceptionHandler() { if(installed) RemoveHandler(); } bool ExceptionHandler::InstallHandler() { #ifdef WIN32 OTSYS_THREAD_LOCK_CLASS lockObj(maploadlock); if(!maploaded) LoadMap(); if(installed) return false; /* mov eax,fs:[0] mov [prevSEH],eax mov [chain].prev,eax mov [chain].SEHfunction,_SEHHandler lea eax,[chain] mov fs:[0],eax */ #ifdef __GNUC__ SEHChain *prevSEH; __asm__ ("movl %%fs:0,%%eax;movl %%eax,%0;":"=r"(prevSEH)::"%eax"); chain.prev = prevSEH; chain.SEHfunction = (void*)&_SEHHandler; __asm__("movl %0,%%eax;movl %%eax,%%fs:0;": : "g" (&chain):"%eax"); #endif #endif installed = true; return true; } bool ExceptionHandler::RemoveHandler() { if(!installed) return false; #ifdef WIN32 #ifdef __GNUC__ __asm__ ("movl %0,%%eax;movl %%eax,%%fs:0;"::"r"(chain.prev):"%eax" ); #endif #endif installed = false; return true; } char* getFunctionName(unsigned long addr, unsigned long& start) { FunctionMap::iterator functions; if(addr >= min_off && addr <= max_off) { for(functions = functionMap.begin(); functions != functionMap.end(); ++functions) { if(functions->first > addr && functions != functionMap.begin()) { functions--; start = functions->first; return functions->second; } } } return NULL; } #ifdef WIN32 EXCEPTION_DISPOSITION __cdecl _SEHHandler(struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext) { uint32_t *esp; uint32_t *next_ret; uint32_t stack_val; uint32_t *stacklimit; uint32_t *stackstart; uint32_t nparameters = 0; uint32_t file,foundRetAddress = 0; _MEMORY_BASIC_INFORMATION mbi; std::ostream *outdriver; std::cout << ">> CRASH: Writing report file..." << std::endl; std::ofstream output(getFilePath(FILE_TYPE_LOG, "server/exceptions.log").c_str(), std::ios_base::app); if(output.fail()) { outdriver = &std::cout; file = false; } else { file = true; outdriver = &output; } time_t rawtime; time(&rawtime); *outdriver << "*****************************************************" << std::endl; *outdriver << "Error report - " << std::ctime(&rawtime) << std::endl; *outdriver << "Compiler info - " << COMPILER_STRING << std::endl; *outdriver << "Compilation Date - " << COMPILATION_DATE << std::endl << std::endl; //system and process info //- global memory information MEMORYSTATUSEX mstate; mstate.dwLength = sizeof(mstate); if(GlobalMemoryStatusEx(&mstate)) { *outdriver << "Memory load: " << mstate.dwMemoryLoad << std::endl << "Total phys: " << mstate.ullTotalPhys/1024 << " K available phys: " << mstate.ullAvailPhys/1024 << " K" << std::endl; } else *outdriver << "Memory load: Error" << std::endl; //- process info FILETIME FTcreation, FTexit, FTkernel, FTuser; SYSTEMTIME systemtime; GetProcessTimes(GetCurrentProcess(), &FTcreation, &FTexit, &FTkernel, &FTuser); // creation time FileTimeToSystemTime(&FTcreation, &systemtime); *outdriver << "Start time: " << systemtime.wDay << "-" << systemtime.wMonth << "-" << systemtime.wYear << " " << systemtime.wHour << ":" << systemtime.wMinute << ":" << systemtime.wSecond << std::endl; // kernel time uint32_t miliseconds; miliseconds = FTkernel.dwHighDateTime * 429497 + FTkernel.dwLowDateTime/10000; *outdriver << "Kernel time: " << miliseconds/3600000; miliseconds = miliseconds - (miliseconds/3600000)*3600000; *outdriver << ":" << miliseconds/60000; miliseconds = miliseconds - (miliseconds/60000)*60000; *outdriver << ":" << miliseconds/1000; miliseconds = miliseconds - (miliseconds/1000)*1000; *outdriver << "." << miliseconds << std::endl; // user time miliseconds = FTuser.dwHighDateTime * 429497 + FTuser.dwLowDateTime/10000; *outdriver << "User time: " << miliseconds/3600000; miliseconds = miliseconds - (miliseconds/3600000)*3600000; *outdriver << ":" << miliseconds/60000; miliseconds = miliseconds - (miliseconds/60000)*60000; *outdriver << ":" << miliseconds/1000; miliseconds = miliseconds - (miliseconds/1000)*1000; *outdriver << "." << miliseconds << std::endl; // n threads PROCESSENTRY32 uProcess; HANDLE lSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); BOOL r; if(lSnapShot != 0) { uProcess.dwSize = sizeof(uProcess); r = Process32First(lSnapShot, &uProcess); while(r) { if(uProcess.th32ProcessID == GetCurrentProcessId()) { *outdriver << "Threads: " << uProcess.cntThreads << std::endl; break; } r = Process32Next(lSnapShot, &uProcess); } CloseHandle(lSnapShot); } *outdriver << std::endl; //exception header type and eip outdriver->flags(std::ios::hex | std::ios::showbase); *outdriver << "Exception: " << (uint32_t)ExceptionRecord->ExceptionCode << " at eip = " << (uint32_t)ExceptionRecord->ExceptionAddress; FunctionMap::iterator functions; unsigned long functionAddr; char* functionName = getFunctionName((unsigned long)ExceptionRecord->ExceptionAddress, functionAddr); if(functionName) *outdriver << "(" << functionName << " - " << functionAddr << ")"; //registers *outdriver << std::endl; *outdriver << "eax = ";printPointer(outdriver,ContextRecord->Eax);*outdriver << std::endl; *outdriver << "ebx = ";printPointer(outdriver,ContextRecord->Ebx);*outdriver << std::endl; *outdriver << "ecx = ";printPointer(outdriver,ContextRecord->Ecx);*outdriver << std::endl; *outdriver << "edx = ";printPointer(outdriver,ContextRecord->Edx);*outdriver << std::endl; *outdriver << "esi = ";printPointer(outdriver,ContextRecord->Esi);*outdriver << std::endl; *outdriver << "edi = ";printPointer(outdriver,ContextRecord->Edi);*outdriver << std::endl; *outdriver << "ebp = ";printPointer(outdriver,ContextRecord->Ebp);*outdriver << std::endl; *outdriver << "esp = ";printPointer(outdriver,ContextRecord->Esp);*outdriver << std::endl; *outdriver << "efl = " << ContextRecord->EFlags << std::endl; //stack dump esp = (uint32_t *)(ContextRecord->Esp); VirtualQuery(esp, &mbi, sizeof(mbi)); stacklimit = (uint32_t*)((uint32_t)(mbi.BaseAddress) + mbi.RegionSize); *outdriver << std::endl; *outdriver << "---Stack Trace---" << std::endl; *outdriver << "From: " << (uint32_t)esp << " to: " << (uint32_t)stacklimit << std::endl; stackstart = esp; next_ret = (uint32_t*)(ContextRecord->Ebp); uint32_t frame_param_counter; frame_param_counter = 0; while(esp < stacklimit) { stack_val = *esp; if(foundRetAddress) nparameters++; if(esp - stackstart < 20 || nparameters < 10 || std::abs(esp - next_ret) < 10 || frame_param_counter < 8) { *outdriver << (uint32_t)esp << " | "; printPointer(outdriver,stack_val); if(esp == next_ret) *outdriver << " \\\\\\\\\\\\ stack frame //////"; else if(esp - next_ret == 1) *outdriver << " <-- ret" ; else if(esp - next_ret == 2) { next_ret = (uint32_t*)*(esp - 2); frame_param_counter = 0; } frame_param_counter++; *outdriver<< std::endl; } if(stack_val >= min_off && stack_val <= max_off) { foundRetAddress++; // unsigned long functionAddr; char* functionName = getFunctionName(stack_val, functionAddr); output << (unsigned long)esp << " " << functionName << "(" << functionAddr << ")" << std::endl; } esp++; } *outdriver << "*****************************************************" << std::endl; if(file) ((std::ofstream*)outdriver)->close(); if(g_config.getBool(ConfigManager::TRACER_BOX)) { std::stringstream ss; ss << "If you want developers review this crash log, please open a tracker ticket for the software at OtLand.net and attach the " << getFilePath(FILE_TYPE_LOG, "server/exceptions.log") << " file."; MessageBoxA(NULL, ss.str().c_str(), "Error", MB_OK | MB_ICONERROR); } std::cout << "> Crash report generated, killing server." << std::endl; exit(1); return ExceptionContinueSearch; } void printPointer(std::ostream* output,uint32_t p) { *output << p; if(IsBadReadPtr((void*)p, 4) == 0) *output << " -> " << *(uint32_t*)p; } #endif bool ExceptionHandler::LoadMap() { #ifdef __GNUC__ if(maploaded) return false; functionMap.clear(); installed = false; //load map file if exists char line[1024]; FILE* input = fopen("forgottenserver.map", "r"); min_off = 0xFFFFFF; max_off = 0; int32_t n = 0; if(!input) { MessageBoxA(NULL, "Failed loading symbols, forgottenserver.map file not found.", "Error", MB_OK | MB_ICONERROR); std::cout << "Failed loading symbols, forgottenserver.map file not found. " << std::endl; exit(1); return false; } //read until found .text 0x00401000 while(fgets(line, 1024, input)) { if(memcmp(line,".text",5) == 0) break; } if(feof(input)) return false; char tofind[] = "0x"; char lib[] = ".a("; while(fgets(line, 1024, input)) { char* pos = strstr(line, lib); if(pos) break; //not load libs pos = strstr(line, tofind); if(pos) { //read hex offset char hexnumber[12]; strncpy(hexnumber, pos, 10); hexnumber[10] = 0; char* pEnd; uint32_t offset = strtol(hexnumber, &pEnd, 0); if(offset) { //read function name char* pos2 = pos + 12; while(*pos2 != 0) { if(*pos2 != ' ') break; pos2++; } if(*pos2 == 0 || (*pos2 == '0' && *(pos2+1) == 'x')) continue; char* name = new char[strlen(pos2)+1]; strcpy(name, pos2); name[strlen(pos2) - 1] = 0; functionMap[offset] = name; if(offset > max_off) max_off = offset; if(offset < min_off) min_off = offset; n++; } } } //close file fclose(input); maploaded = true; #endif return true; } void ExceptionHandler::dumpStack() { #ifndef __GNUC__ return; #endif uint32_t *esp; uint32_t *next_ret; uint32_t stack_val; uint32_t *stacklimit; uint32_t *stackstart; uint32_t nparameters = 0; uint32_t foundRetAddress = 0; _MEMORY_BASIC_INFORMATION mbi; std::cout << ">> CRASH: Writing report file..." << std::endl; std::ofstream output(getFilePath(FILE_TYPE_LOG, "server/exceptions.log").c_str(), std::ios_base::app); output.flags(std::ios::hex | std::ios::showbase); time_t rawtime; time(&rawtime); output << "*****************************************************" << std::endl; output << "Stack dump - " << std::ctime(&rawtime) << std::endl; output << "Compiler info - " << COMPILER_STRING << std::endl; output << "Compilation Date - " << COMPILATION_DATE << std::endl << std::endl; #ifdef __GNUC__ __asm__ ("movl %%esp, %0;":"=r"(esp)::); #else // #endif VirtualQuery(esp, &mbi, sizeof(mbi)); stacklimit = (uint32_t*)((uint32_t)(mbi.BaseAddress) + mbi.RegionSize); output << "---Stack Trace---" << std::endl; output << "From: " << (uint32_t)esp << " to: " << (uint32_t)stacklimit << std::endl; stackstart = esp; #ifdef __GNUC__ __asm__ ("movl %%ebp, %0;":"=r"(next_ret)::); #else // #endif uint32_t frame_param_counter; frame_param_counter = 0; while(esp < stacklimit) { stack_val = *esp; if(foundRetAddress) nparameters++; if(esp - stackstart < 20 || nparameters < 10 || std::abs(esp - next_ret) < 10 || frame_param_counter < 8) { output << (uint32_t)esp << " | "; printPointer(&output, stack_val); if(esp == next_ret) output << " \\\\\\\\\\\\ stack frame //////"; else if(esp - next_ret == 1) output << " <-- ret" ; else if(esp - next_ret == 2) { next_ret = (uint32_t*)*(esp - 2); frame_param_counter = 0; } frame_param_counter++; output << std::endl; } if(stack_val >= min_off && stack_val <= max_off) { foundRetAddress++; // unsigned long functionAddr; char* functionName = getFunctionName(stack_val, functionAddr); output << (unsigned long)esp << " " << functionName << "(" << functionAddr << ")" << std::endl; } esp++; } output << "*****************************************************" << std::endl; output.close(); std::cout << "> Crash report generated, killing server." << std::endl; } #endif