Attempt to get better logging from pure virtual call crashes

This commit is contained in:
Brad Davis 2016-06-08 14:56:18 -07:00
parent 4e418f328f
commit ff8d4883b1
4 changed files with 70 additions and 6 deletions

View file

@ -10,15 +10,20 @@
// //
#ifdef HAS_BUGSPLAT
#include "CrashReporter.h" #include "CrashReporter.h"
#ifdef _WIN32
#include <WinSock2.h>
#include <new.h> #include <new.h>
#include <Windows.h> #include <Windows.h>
#include <DbgHelp.h>
#include <csignal> #include <csignal>
#include <QDebug> #include <QDebug>
#include "Application.h"
#pragma comment(lib, "Dbghelp.lib")
// SetUnhandledExceptionFilter can be overridden by the CRT at the point that an error occurs. More information // SetUnhandledExceptionFilter can be overridden by the CRT at the point that an error occurs. More information
// can be found here: http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li // can be found here: http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li
@ -77,13 +82,43 @@ BOOL redirectLibraryFunctionToFunction(char* library, char* function, void* fn)
return bRet; return bRet;
} }
void printStackTrace(ULONG framesToSkip = 1) {
QString result;
unsigned int i;
void * stack[100];
unsigned short frames;
SYMBOL_INFO * symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(framesToSkip, 100, stack, NULL);
symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
qWarning() << QString("%1: %2 - 0x%0X").arg(QString::number(frames - i - 1), QString(symbol->Name), QString::number(symbol->Address, 16));
}
free(symbol);
// Try to force the log to sync to the filesystem
auto app = qApp;
if (app && app->getLogger()) {
app->getLogger()->sync();
}
}
void handleSignal(int signal) { void handleSignal(int signal) {
// Throw so BugSplat can handle // Throw so BugSplat can handle
throw(signal); throw(signal);
} }
void handlePureVirtualCall() { void __cdecl handlePureVirtualCall() {
qWarning() << "Pure virtual function call detected";
printStackTrace(2);
// Throw so BugSplat can handle // Throw so BugSplat can handle
throw("ERROR: Pure virtual call"); throw("ERROR: Pure virtual call");
} }
@ -107,6 +142,8 @@ _purecall_handler __cdecl noop_set_purecall_handler(_purecall_handler pNew) {
return nullptr; return nullptr;
} }
#ifdef HAS_BUGSPLAT
static const DWORD BUG_SPLAT_FLAGS = MDSF_PREVENTHIJACKING | MDSF_USEGUARDMEMORY; static const DWORD BUG_SPLAT_FLAGS = MDSF_PREVENTHIJACKING | MDSF_USEGUARDMEMORY;
CrashReporter::CrashReporter(QString bugSplatDatabase, QString bugSplatApplicationName, QString version) CrashReporter::CrashReporter(QString bugSplatDatabase, QString bugSplatApplicationName, QString version)
@ -133,3 +170,4 @@ CrashReporter::CrashReporter(QString bugSplatDatabase, QString bugSplatApplicati
} }
} }
#endif #endif
#endif

View file

@ -115,3 +115,7 @@ QString FileLogger::getLogData() {
} }
return result; return result;
} }
void FileLogger::sync() {
_persistThreadInstance->waitIdle();
}

View file

@ -28,6 +28,7 @@ public:
virtual void addMessage(const QString&) override; virtual void addMessage(const QString&) override;
virtual QString getLogData() override; virtual QString getLogData() override;
virtual void locateLog() override; virtual void locateLog() override;
void sync();
signals: signals:
void rollingLogFile(QString newFilename); void rollingLogFile(QString newFilename);

View file

@ -12,9 +12,10 @@
#include <stdint.h> #include <stdint.h>
#include <QQueue> #include <QtCore/QElapsedTimer>
#include <QMutex> #include <QtCore/QMutex>
#include <QWaitCondition> #include <QtCore/QQueue>
#include <QtCore/QWaitCondition>
#include "GenericThread.h" #include "GenericThread.h"
#include "NumericalConstants.h" #include "NumericalConstants.h"
@ -35,6 +36,25 @@ public:
_hasItems.wakeAll(); _hasItems.wakeAll();
} }
void waitIdle(uint32_t maxWaitMs = UINT32_MAX) {
QElapsedTimer timer;
timer.start();
// FIXME this will work as long as the thread doing the wait
// is the only thread which can add work to the queue.
// It would be better if instead we had a queue empty condition to wait on
// that would ensure that we get woken as soon as we're idle the very
// first time the queue was empty.
while (timer.elapsed() < maxWaitMs) {
lock();
if (!_items.size()) {
unlock();
return;
}
unlock();
}
}
protected: protected:
virtual void queueItemInternal(const T& t) { virtual void queueItemInternal(const T& t) {
_items.push_back(t); _items.push_back(t);
@ -44,6 +64,7 @@ protected:
return MSECS_PER_SECOND; return MSECS_PER_SECOND;
} }
virtual bool process() { virtual bool process() {
lock(); lock();
if (!_items.size()) { if (!_items.size()) {