TEST(RecorderTest, CallStackRecorderTest) { TRACER_TRACE(&C::Foo) foo; auto fc = tracer::RecordCallStack(foo); RunFoo(); for (auto itr : fc.GetCallStack(0).Entries()) ;//std::cout << itr.File() << " " << itr.Line() << " " << itr.FuncName() << std::endl; EXPECT_EQ(true, fc.GetCallStack(0).IsCalledBy("RunFoo")); void(*f)(); f = [] () { RunFoo(); }; f(); EXPECT_EQ(true, fc.GetCallStack(1).IsCalledBy(f)); }
// トラッキングの初期化 void InitTracking() { UINT i; CALLSTACK_DATA *s; // ハッシュリスト初期化 hashlist = (TRACKING_LIST **)OSMemoryAlloc(sizeof(TRACKING_LIST *) * TRACKING_NUM_ARRAY); for (i = 0; i < TRACKING_NUM_ARRAY; i++) { hashlist[i] = NULL; } obj_id = 0; // ロック作成 obj_lock = OSNewLock(); obj_id_lock = OSNewLock(); cs_lock = OSNewLock(); s = GetCallStack(); if (s == NULL) { do_not_get_callstack = true; } else { do_not_get_callstack = false; FreeCallStack(s); } }
void CGameAISystem::Error(const char* functionName) const { string logMessage; logMessage.Format( "[%s]: An entity entered or left a module while the modules were being updated.\n" "This is bad and we'd appreciate it if you'd help us by informing Jonas, Mario and Marcio.\n", functionName); gEnv->pLog->LogError("-------------------------- Error in GameAISystem ---------------------------------"); gEnv->pLog->LogError(logMessage.c_str()); #if defined(GAME_AI_ASSERTS_ENABLED) if (ICVar* sys_no_crash_dialog = gEnv->pConsole->GetCVar("sys_no_crash_dialog")) { const bool dialogsAreAllowed = sys_no_crash_dialog->GetIVal() == 0; if (dialogsAreAllowed) { InformContentCreatorOfError(logMessage); } } #else gEnv->pLog->LogError("Generating callstack... please wait..."); string callstack; GetCallStack(callstack); gEnv->pLog->LogError("Callstack:\n%s", callstack.c_str()); //CrashOnPurpose(); #endif }
int StackTrace::GetCallStack(Address* callStack, int maxDepth, int entriesToSkip) { PCONTEXT pContext(0); HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll"); void (WINAPI *pRtlCaptureContext)(PCONTEXT); *(void**)&pRtlCaptureContext = GetProcAddress(hKernel32Dll, "RtlCaptureContext"); CONTEXT context; if (pRtlCaptureContext) { memset(&context, 0, sizeof(context)); context.ContextFlags = CONTEXT_FULL; pRtlCaptureContext(&context); pContext = &context; } // +1 -> skip over "us" return GetCallStack(pContext, callStack, maxDepth, entriesToSkip + 1); }
// 新しいオブジェクトを追跡リストに入れる void TrackNewObj(UINT64 addr, char *name, UINT size) { TRACKING_OBJECT *o; UINT new_id; // 引数チェック if (addr == 0 || name == NULL) { return; } if (IsMemCheck() == false) { // リリースモードでは追跡しない return; } if (disable_tracking) { return; } // 新しい ID の生成 OSLock(obj_id_lock); { new_id = ++obj_id; } OSUnlock(obj_id_lock); o = OSMemoryAlloc(sizeof(TRACKING_OBJECT)); o->Id = new_id; o->Address = addr; o->Name = name; o->Size = size; o->CreatedDate = LocalTime64(); o->CallStack = WalkDownCallStack(GetCallStack(), 2); o->FileName[0] = 0; o->LineNumber = 0; LockTrackingList(); { InsertTrackingList(o); } UnlockTrackingList(); }
// Put a new object into the tracking list void TrackNewObj(UINT64 addr, char *name, UINT size) { TRACKING_OBJECT *o; UINT new_id; // Validate arguments if (addr == 0 || name == NULL) { return; } if (IsMemCheck() == false) { // Don't track in the release mode return; } if (disable_tracking) { return; } // Generate a new ID OSLock(obj_id_lock); { new_id = ++obj_id; } OSUnlock(obj_id_lock); o = OSMemoryAlloc(sizeof(TRACKING_OBJECT)); o->Id = new_id; o->Address = addr; o->Name = name; o->Size = size; o->CreatedDate = LocalTime64(); o->CallStack = WalkDownCallStack(GetCallStack(), 2); o->FileName[0] = 0; o->LineNumber = 0; LockTrackingList(); { InsertTrackingList(o); } UnlockTrackingList(); }
void CGameAISystem::InformContentCreatorOfError(string logMessage) const { #if defined(GAME_AI_ASSERTS_ENABLED) if (g_gameAISystemAssertsAllowed) { CursorShowerWithStack cursorShowerWithStack; cursorShowerWithStack.StoreCurrentAndShow(); string dialogMessage = logMessage + "\n" "Do you want to help?\n" "Yes\t- Help out and generate callstack (takes around 30 seconds)\n" "No\t- Not this time\n" "Cancel\t- Ignore similar errors for the rest of this session"; const char* dialogCaption = "Error in GameAISystem"; int messageBoxResult = CryMessageBox(dialogMessage.c_str(), dialogCaption, MB_ICONERROR | MB_YESNOCANCEL); switch (messageBoxResult) { case IDYES: { string callstack; GetCallStack(callstack); gEnv->pLog->LogError("Callstack:\n%s", callstack.c_str()); messageBoxResult = CryMessageBox( "The callstack has been generated.\n" "\n" "Do you want me to prepare an email for you, describing the issue?", dialogCaption, MB_ICONINFORMATION | MB_YESNO); if (messageBoxResult == IDYES) { string_replace(callstack, "\n", "%%0A"); string command = string().Format( "mailto:[email protected];[email protected];[email protected]" "?subject=Error in GameAISystem" "&body=" "[Auto-generated e-mail]%%0A" "%%0A" "Hi,%%0A" "%%0A" "This is an auto-generated email.%%0A" "An error occurred in the GameAISystem: An entity entered or left a module while the modules were being updated.%%0A" "%%0A" "Here is the callstack:%%0A" + callstack); ShellExecute(0, "open", command.c_str(), "", "", SW_NORMAL); CryMessageBox("Thanks! The email is being created for you.\nHave a nice day! :)", dialogCaption, MB_ICONINFORMATION | MB_OK); } else { CryMessageBox("Ok! The callstack has been written to the log file anyway.\nHave a nice day! :)", dialogCaption, MB_ICONINFORMATION | MB_OK); } break; } case IDNO: break; case IDCANCEL: g_gameAISystemAssertsAllowed = false; break; } cursorShowerWithStack.RevertToPrevious(); } #endif }
static BOOL saveMiniDump(EXCEPTION_POINTERS* pExceptionInfos) { BOOL result = FALSE; #ifdef _M_IX86 if (pExceptionInfos->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { static char TempStack[1024 * 128]; __asm mov eax,offset TempStack[1024 * 128]; __asm mov esp,eax; } #endif CrashInfo crashInfo = GetCrashInfo(pExceptionInfos->ExceptionRecord); vector<CallStackInfo> arrCallStackInfo = GetCallStack(pExceptionInfos->ContextRecord); CString dumpPath = FileMgr::GetAppPath() + _T("\\MyEcho.dmp"); Json::Value jDump, jCrash, jCallStack; CHAR szObject[MAX_PATH]; ::GetModuleFileNameA(NULL, szObject, MAX_PATH); jCrash["Object"] = szObject; jCrash["ErrorCode"] = crashInfo.ErrorCode; jCrash["Address"] = crashInfo.Address; jCrash["Flags"] = crashInfo.Flags; for (vector<CallStackInfo>::iterator i = arrCallStackInfo.begin(); i != arrCallStackInfo.end(); ++i) { CallStackInfo callstackinfo = (*i); jCallStack[callstackinfo.MethodName] = std::string("[") + callstackinfo.ModuleName + std::string("] [File:") + callstackinfo.FileName + std::string(" @Line ") + callstackinfo.LineNumber + std::string("]"); //cout << callstackinfo.MethodName << "() : [" << callstackinfo.ModuleName << "] (File: " << callstackinfo.FileName << " @Line " << callstackinfo.LineNumber << ")" << endl; } jDump["crash"] = jCrash; jDump["callstack"] = jCallStack; // 程序崩溃时,将写入程序目录下的MyDump.dmp文件 HANDLE hFile = ::CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile != INVALID_HANDLE_VALUE) { /* MINIDUMP_EXCEPTION_INFORMATION exptInfo; exptInfo.ThreadId = ::GetCurrentThreadId(); exptInfo.ExceptionPointers = pExceptionInfos; result = ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &exptInfo, NULL, NULL); if (!result) { TCHAR temp[1024] = {0}; _stprintf(temp, _T("GetLastError() 2 = %d"), GetLastError()); OutputDebugString(temp); } */ DWORD dwWritten = 0; WriteFile(hFile, jDump.toStyledString().c_str(), jDump.toStyledString().length(), &dwWritten, NULL); ::CloseHandle(hFile); } else { TCHAR temp[1024] = {0}; _stprintf(temp, _T("GetLastError() 1 = %d"), GetLastError()); OutputDebugString(temp); } return result; }
LONG WINAPI UnhandledExceptionHandler(_EXCEPTION_POINTERS *ExceptionInfo){ List<LoadedModule*> LoadedModules; EnumerateLoadedModules(GetCurrentProcess(), (PENUMLOADED_MODULES_CALLBACK)AddLoadedModule, &LoadedModules); StringList HeaderList; if(ExceptionInfo->ContextRecord->ContextFlags & CONTEXT_INTEGER){ PCONTEXT reg = ExceptionInfo->ContextRecord; HeaderList.push_back(String("Register-EAX: %1").arg(reg->Eax, 16, true, 8)); HeaderList.push_back(String("Register-ECX: %1").arg(reg->Ecx, 16, true, 8)); HeaderList.push_back(String("Register-EDX: %1").arg(reg->Edx, 16, true, 8)); HeaderList.push_back(String("Register-EBX: %1").arg(reg->Ebx, 16, true, 8)); HeaderList.push_back(String("Register-EDI: %1").arg(reg->Edi, 16, true, 8)); HeaderList.push_back(String("Register-ESI: %1").arg(reg->Esi, 16, true, 8)); } if(ExceptionInfo->ContextRecord->ContextFlags & CONTEXT_CONTROL){ PCONTEXT reg = ExceptionInfo->ContextRecord; HeaderList.push_back(String("Register-EBP: %1").arg(reg->Edi, 16, true, 8)); HeaderList.push_back(String("Register-ESP: %1").arg(reg->Esi, 16, true, 8)); } HeaderList.push_back(String("Exception-Address: %1").arg((unsigned int)ExceptionInfo->ExceptionRecord->ExceptionAddress, 16, true, 8)); HeaderList.push_back(String("Exception-Flags: %1").arg((unsigned int)ExceptionInfo->ExceptionRecord->ExceptionFlags, 16, true, 8)); HeaderList.push_back(String("Exception-Code: %1").arg((unsigned int)ExceptionInfo->ExceptionRecord->ExceptionCode, 16, true, 8)); CGameOBJ* self = CObjMGR::GetSelf(); const char* name = (self)?self->GetName():"No Character"; HeaderList.push_back(String("Character: %1").arg(name)); String module = "Unknown"; unsigned int codeAddress = (unsigned int)((char*)ExceptionInfo->ExceptionRecord->ExceptionAddress); module = GetModuleName(codeAddress, &LoadedModules); HeaderList.push_back(String("Module: %1").arg(module)); unsigned char* codePtr = (unsigned char*)ExceptionInfo->ExceptionRecord->ExceptionAddress; //if(IsBadReadPtr(codePtr - 20, 40) || !IsBadCodePtr(FARPROC(codePtr))){ String code = String("Code-Data: Version: %1 ").arg(gVersion); if(CTERRAIN::Instance()) { code += String("Zone: %1 ").arg(CTERRAIN::Instance()->m_nZoneNO); } if(gRecvPacket) { code += String("RecvPacket: %1").arg(gRecvPacket->Command(), 16); } /*}else{ for(int i = -20; i < 20; ++i) code += String::Create("%02X", codePtr[i]); }*/ HeaderList.push_back(code); OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); HeaderList.push_back(String("Windows: %1.%2").arg((unsigned int)osvi.dwMajorVersion).arg((unsigned int)osvi.dwMinorVersion)); GetCallStack(ExceptionInfo, &LoadedModules, HeaderList); unsigned int bufLen = 100; for(StringList::Iterator itr = HeaderList.begin(); itr != HeaderList.end(); ++itr) bufLen += strlen(*itr) + 3; char* theHeaders = new char[bufLen]; theHeaders[0] = 0; for(StringList::Iterator itr = HeaderList.begin(); itr != HeaderList.end(); ++itr){ strcat_s(theHeaders, bufLen, *itr); strcat_s(theHeaders, bufLen, ";\r\n"); } if(MessageBoxA(NULL, "Would you like to submit this error to the titanROSE team?", "An error has occured.", MB_ICONERROR | MB_YESNO) == IDYES){ HINTERNET theInternet = InternetOpenA("titanROSE.Client", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); HINTERNET theConnection = InternetConnectA(theInternet, "crash.titanrose.com", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL); HINTERNET theRequest = HttpOpenRequestA(theConnection, "GET", NULL, NULL, "trose.exe", NULL, INTERNET_FLAG_PRAGMA_NOCACHE |INTERNET_FLAG_RELOAD, NULL); HttpAddRequestHeadersA(theRequest, theHeaders, -1, HTTP_ADDREQ_FLAG_ADD); BOOL theResult = HttpSendRequestA(theRequest, NULL, 0, NULL, 0); if(theResult == TRUE){ MessageBoxA(NULL, "Your error report was successfully uploaded, thank you.", "Error Report Submitted", MB_OK | MB_ICONASTERISK); }else{ strcat_s(theHeaders, bufLen, "\r\nThere was an error submitting your error report, please post this message box on the forums."); MessageBoxA(NULL, theHeaders, "Error submitting Error Report", MB_OK); } } LoadedModules.delete_values(); delete [] theHeaders; return EXCEPTION_EXECUTE_HANDLER; }