Sandboxie/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp

408 lines
11 KiB
C++

#include "stdafx.h"
#include "DebugHelpers.h"
#include "Common.h"
#include <QDebug>
#ifdef WIN32
#include <windows.h>
#endif
bool IsDebuggerAttached()
{
bool isDebuggerPresent = false; // Note: on linux change edit the value in debgger to indicate presence
#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<std::string,int>::iterator I = m_MemoryTrace.begin(); I != m_MemoryTrace.end(); I++)
{
std::map<std::string,int>::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<std::string,SCycles>::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<std::string,SLocks>::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 <dbghelp.h>
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 enough 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;
if (pEx->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C || pEx->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C)
return EXCEPTION_CONTINUE_SEARCH;
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 occurred
// 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
// Register Vectored Exception Handler
//AddVectoredExceptionHandler(0, MyCrashHandlerExceptionFilter);
}
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 <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
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