#include "stdafx.h" #include "DebugHelpers.h" #include "Common.h" #include #ifdef WIN32 #include #endif bool IsDebuggerAttached() { bool isDebuggerPresent = false; // Note: on linux change edit the value in debgger to indicate precense #ifdef WIN32 if (IsDebuggerPresent()) return true; #endif return isDebuggerPresent; } void WaitForDebugger() { while (!IsDebuggerAttached()) QThread::msleep(500); } #if defined(_DEBUG) || defined(_TRACE) #ifdef WIN32 bool g_assert_active = IsDebuggerPresent(); #else bool g_assert_active = true; #endif void Assert(bool test) { if(!test && g_assert_active) _CrtDbgBreak(); } void CTracer::operator()(const QString &sLine) const { qDebug() << sLine; } void CTracer::operator()(const wchar_t *sLine, ...) const { const size_t bufferSize = 10241; wchar_t bufferline[bufferSize]; va_list argptr; va_start(argptr, sLine); #ifndef WIN32 if (vswprintf_l(bufferline, bufferSize, sLine, argptr) == -1) #else if (vswprintf(bufferline, bufferSize, sLine, argptr) == -1) #endif bufferline[bufferSize - 1] = L'\0'; va_end(argptr); QString sBufferLine = QString::fromWCharArray(bufferline); qDebug() << sBufferLine; } void CTracer::operator()(const char *sLine, ...) const { const size_t bufferSize = 10241; char bufferline[bufferSize]; va_list argptr; va_start(argptr, sLine); if (vsprintf(bufferline, sLine, argptr) == -1) bufferline[bufferSize - 1] = L'\0'; va_end(argptr); QString sBufferLine = bufferline; qDebug() << sBufferLine; } //////////////////////////////////////////////////////////////////////////////////////////// // Tracers //////////////////////////////////////////////////////////////////////////////////////////// CMemTracer memTracer; void CMemTracer::DumpTrace() { QMutexLocker Locker(&m_Locker); for(std::map::iterator I = m_MemoryTrace.begin(); I != m_MemoryTrace.end(); I++) { std::map::iterator J = m_MemoryTrace2.find(I->first); if(J != m_MemoryTrace2.end()) TRACE(L"MEMORY TRACE: Object '%S' has %d (%d) instances.",I->first.c_str(),I->second, J->second); else TRACE(L"MEMORY TRACE: Object '%S' has %d instances.",I->first.c_str(),I->second); } } void CMemTracer::TraceAlloc(std::string Name) { QMutexLocker Locker(&m_Locker); m_MemoryTrace[Name] += 1; } void CMemTracer::TraceFree(std::string Name) { QMutexLocker Locker(&m_Locker); m_MemoryTrace[Name] -= 1; //if(m_MemoryTrace2[Name] > 0) if(m_MemoryTrace2.find(Name) != m_MemoryTrace2.end()) m_MemoryTrace2[Name] -= 1; } void CMemTracer::TracePre(std::string Name) { QMutexLocker Locker(&m_Locker); m_MemoryTrace2[Name] += 1; } ///////////////////////////////////////////////////////////////////////////////////// CCpuTracer cpuTracer; void CCpuTracer::DumpTrace() { QMutexLocker Locker(&m_Locker); for(std::map::iterator I = m_CpuUsageTrace.begin(); I != m_CpuUsageTrace.end(); I++) TRACE(L"CPU TRACE: Prozedure '%S' needed %f seconds.",I->first.c_str(),(double)I->second.Total/1000000.0); } void CCpuTracer::ResetTrace() { QMutexLocker Locker(&m_Locker); m_CpuUsageTrace.clear(); } void CCpuTracer::TraceStart(std::string Name) { QMutexLocker Locker(&m_Locker); m_CpuUsageTrace[Name].Counting = GetCurCycle(); } void CCpuTracer::TraceStop(std::string Name) { QMutexLocker Locker(&m_Locker); m_CpuUsageTrace[Name].Total += GetCurCycle() - m_CpuUsageTrace[Name].Counting; } ///////////////////////////////////////////////////////////////////////////////////// CLockTracer lockTracer; void CLockTracer::DumpTrace() { QMutexLocker Locker(&m_Locker); for(std::map::iterator I = m_LockTrace.begin(); I != m_LockTrace.end(); I++) TRACE(L"LOCK TRACE: Lock '%S' has %d Locks for %f seconds.",I->first.c_str(),I->second.LockCount, (double)(GetCurCycle()/1000 - I->second.LockTime) / 1000); } void CLockTracer::TraceLock(std::string Name, int op) { QMutexLocker Locker(&m_Locker); SLocks &Locks = m_LockTrace[Name]; if(Locks.LockCount == 0) Locks.LockTime = GetCurCycle()/1000; Locks.LockCount += op; if(Locks.LockCount == 0) Locks.LockTime = 0; } #endif ///////////////////////////////////////////////////////////////////////////////////////////// // #ifdef WIN32 #include typedef BOOL (__stdcall *tMDWD)( IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL ); static tMDWD s_pMDWD; static HMODULE s_hDbgHelpMod; static MINIDUMP_TYPE s_dumpTyp = MiniDumpNormal; // MiniDumpWithDataSegs or MiniDumpWithFullMemory static wchar_t s_szMiniDumpName[64]; static wchar_t s_szMiniDumpPath[MAX_PATH]; static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS* pEx) { #ifdef _M_IX86 if (pEx->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { // be sure that we have enought space... static char MyStack[1024*128]; // it assumes that DS and SS are the same!!! (this is the case for Win32) // change the stack only if the selectors are the same (this is the case for Win32) //__asm push offset MyStack[1024*128]; //__asm pop esp; __asm mov eax,offset MyStack[1024*128]; __asm mov esp,eax; } #endif bool bSuccess = false; wchar_t szMiniDumpFileName[128]; wsprintf(szMiniDumpFileName, L"%s %s.dmp", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy hh-mm-ss,zzz").replace(QRegularExpression("[:*?<>|\"\\/]"), "_").toStdWString().c_str()); /*wchar_t szMiniDumpPath[MAX_PATH] = { 0 }; HANDLE hFile = CreateFile(szMiniDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) GetCurrentDirectory(MAX_PATH, szMiniDumpPath); else { GetTempPath(MAX_PATH, szMiniDumpPath); wchar_t szMiniDumpFilePath[MAX_PATH] = { 0 }; wsprintf(szMiniDumpFilePath, L"%s\\%s.dmp", szMiniDumpPath, szMiniDumpFileName); hFile = CreateFile(szMiniDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); }*/ wchar_t szMiniDumpPath[MAX_PATH]; wcscpy(szMiniDumpPath, s_szMiniDumpPath); wcscat(szMiniDumpPath, L"\\"); wcscat(szMiniDumpPath, szMiniDumpFileName); HANDLE hFile = CreateFile(szMiniDumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION stMDEI; stMDEI.ThreadId = GetCurrentThreadId(); stMDEI.ExceptionPointers = pEx; stMDEI.ClientPointers = TRUE; // try to create an miniDump: if (s_pMDWD(GetCurrentProcess(), GetCurrentProcessId(), hFile, s_dumpTyp, &stMDEI, NULL, NULL)) { bSuccess = true; } CloseHandle(hFile); } wchar_t szMiniDumpMessage[256]; if (!bSuccess) wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump creation failed.", s_szMiniDumpName); else wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump saved to \"%s\".\r\nPlease report the crash and attach the file \"%s\".", s_szMiniDumpName, szMiniDumpPath, szMiniDumpFileName); MessageBox(NULL, szMiniDumpMessage, s_szMiniDumpName, MB_OK | MB_ICONERROR); // or return one of the following: // - EXCEPTION_CONTINUE_SEARCH // - EXCEPTION_CONTINUE_EXECUTION // - EXCEPTION_EXECUTE_HANDLER return EXCEPTION_CONTINUE_SEARCH; // this will trigger the "normal" OS error-dialog } void InitMiniDumpWriter(const wchar_t* Name, const wchar_t* Path) { if (s_hDbgHelpMod != NULL) return; ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName)); wcscpy(s_szMiniDumpName, Name); ASSERT(wcslen(Path) < ARRSIZE(s_szMiniDumpPath)); wcscpy(s_szMiniDumpPath, Path); // Initialize the member, so we do not load the dll after the exception has occured // which might be not possible anymore... s_hDbgHelpMod = LoadLibrary(L"dbghelp.dll"); if (s_hDbgHelpMod != NULL) s_pMDWD = (tMDWD) GetProcAddress(s_hDbgHelpMod, "MiniDumpWriteDump"); // Register Unhandled Exception-Filter: SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter); // Additional call "PreventSetUnhandledExceptionFilter"... // See also: "SetUnhandledExceptionFilter" and VC8 (and later) // http://blog.kalmbachnet.de/?postid=75 } quint64 GetCurCycle() { quint64 freq, now; QueryPerformanceFrequency((LARGE_INTEGER*)&freq); QueryPerformanceCounter((LARGE_INTEGER*)&now); quint64 dwNow = ((now * 1000000) / freq) & 0xffffffff; return dwNow; // returns time since system start in us } #elif !defined(__APPLE__) #include #include #include #include #include static wchar_t s_szMiniDumpName[64]; /* This structure mirrors the one found in /usr/include/asm/ucontext.h */ typedef struct _sig_ucontext { unsigned long uc_flags; struct ucontext *uc_link; stack_t uc_stack; struct sigcontext uc_mcontext; sigset_t uc_sigmask; } sig_ucontext_t; void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) { void * array[50]; void * caller_address; char ** messages; int size, i; sig_ucontext_t * uc; uc = (sig_ucontext_t *)ucontext; /* Get the address at the time the signal was raised */ #if defined(__i386__) // gcc specific caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific #elif defined(__x86_64__) // gcc specific caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific #else #error Unsupported architecture. // TO-DO: Add support for other arch. #endif fprintf(stderr, "signal %d (%s), address is %p from %p\n", sig_num, strsignal(sig_num), info->si_addr, (void *)caller_address); size = backtrace(array, 50); /* overwrite sigaction with caller's address */ array[1] = caller_address; messages = backtrace_symbols(array, size); char szMiniDumpFileName[128]; sprintf(szMiniDumpFileName, "%S_%s.log", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy_hh-mm-ss,zzz").replace(QRegularExpression("[:*?<>|\"\\/]"), "_").toStdString().c_str()); FILE* file = fopen(szMiniDumpFileName, "wb"); /* skip first stack frame (points here) */ for (i = 1; i < size && messages != NULL; ++i) { fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]); fprintf(file, "[bt]: (%d) %s\n", i, messages[i]); } fclose(file); free(messages); exit(EXIT_FAILURE); } void InstallSigAction(int sig) { struct sigaction sigact; sigact.sa_sigaction = crit_err_hdlr; sigact.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(sig, &sigact, (struct sigaction *)NULL) != 0) { fprintf(stderr, "error setting signal handler for %d (%s)\n", sig, strsignal(sig)); exit(EXIT_FAILURE); } } void InitMiniDumpWriter(const wchar_t* Name, const wchar_t* Path) { ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName)); wcscpy(s_szMiniDumpName, Name); InstallSigAction(SIGABRT); InstallSigAction(SIGSEGV); } quint64 GetCurCycle() { return GetCurTick()*1000; // ToDo } #else void InitMiniDumpWriter(const wchar_t* Name, const wchar_t* Path) { } #endif