Commit aca4386a authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Ignore EXCEPTION_ACCESS_VIOLATION in the dumpers for speedup.

Do not trigger the whole debugger mechanism when Dumpers
crash on encountering uninitizialized variables. Temporarily
ignore the exceptions while in dumper call.
parent 18cba96a
......@@ -39,6 +39,16 @@
#include <stdio.h>
#include <string.h>
// Test uninitialized variables allocing memory
bool optTestUninitialized = false;
template <class T>
inline T* testAddress(T* in)
{
return optTestUninitialized ?
(reinterpret_cast<T*>(new char[sizeof(T)]))
: in;
}
/* Test program for Dumper development/porting.
* Takes the type as first argument. */
......@@ -85,9 +95,10 @@ static int dumpQString()
{
QString test = QLatin1String("hallo");
prepareInBuffer("QString", "local.qstring", "local.qstring", "");
qDumpObjectData440(2, 42, &test, 1, 0, 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
QString uninitialized;
return 0;
}
......@@ -95,7 +106,7 @@ static int dumpQStringList()
{
QStringList test = QStringList() << QLatin1String("item1") << QLatin1String("item2");
prepareInBuffer("QList", "local.qstringlist", "local.qstringlist", "QString");
qDumpObjectData440(2, 42, &test, 1, sizeof(QString), 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -105,7 +116,7 @@ static int dumpQIntList()
{
QList<int> test = QList<int>() << 1 << 2;
prepareInBuffer("QList", "local.qintlist", "local.qintlist", "int");
qDumpObjectData440(2, 42, &test, 1, sizeof(int), 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -115,7 +126,7 @@ static int dumpQIntVector()
{
QVector<int> test = QVector<int>() << 1 << 2;
prepareInBuffer("QVector", "local.qintvector", "local.qintvector", "int");
qDumpObjectData440(2, 42, &test, 1, sizeof(int), 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -127,7 +138,7 @@ static int dumpStdString()
{
std::string test = "hallo";
prepareInBuffer("std::string", "local.string", "local.string", "");
qDumpObjectData440(2, 42, &test, 1, 0, 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -137,20 +148,19 @@ static int dumpStdWString()
{
std::wstring test = L"hallo";
prepareInBuffer("std::wstring", "local.wstring", "local.wstring", "");
qDumpObjectData440(2, 42, &test, 1, 0, 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
}
static int dumpStdStringList()
{
std::list<std::string> test;
test.push_back("item1");
test.push_back("item2");
prepareInBuffer("std::list", "local.stringlist", "local.stringlist", "std::string");
qDumpObjectData440(2, 42, &test, 1, sizeof(std::string), sizeof(std::list<std::string>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::string), sizeof(std::list<std::string>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -162,7 +172,7 @@ static int dumpStdStringQList()
test.push_back("item1");
test.push_back("item2");
prepareInBuffer("QList", "local.stringqlist", "local.stringqlist", "std::string");
qDumpObjectData440(2, 42, &test, 1, sizeof(std::string), 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::string), 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -174,7 +184,7 @@ static int dumpStdIntList()
test.push_back(1);
test.push_back(2);
prepareInBuffer("std::list", "local.intlist", "local.intlist", "int");
qDumpObjectData440(2, 42, &test, 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -186,7 +196,7 @@ static int dumpStdIntVector()
test.push_back(1);
test.push_back(2);
prepareInBuffer("std::vector", "local.intvector", "local.intvector", "int");
qDumpObjectData440(2, 42, &test, 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -198,7 +208,7 @@ static int dumpStdStringVector()
test.push_back("item1");
test.push_back("item2");
prepareInBuffer("std::vector", "local.stringvector", "local.stringvector", "std::string");
qDumpObjectData440(2, 42, &test, 1, sizeof(std::string), sizeof(std::list<int>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::string), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -210,7 +220,7 @@ static int dumpStdIntSet()
test.insert(1);
test.insert(2);
prepareInBuffer("std::set", "local.intset", "local.intset", "int");
qDumpObjectData440(2, 42, &test, 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -222,7 +232,7 @@ static int dumpStdStringSet()
test.insert("item1");
test.insert("item2");
prepareInBuffer("std::set", "local.stringset", "local.stringset", "std::string");
qDumpObjectData440(2, 42, &test, 1, sizeof(std::string), sizeof(std::list<int>::allocator_type), 0, 0);
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::string), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -233,7 +243,7 @@ static int dumpQObject()
// Requires the childOffset to be know, but that is not critical
QTimer t;
prepareInBuffer("QObject", "local.qobject", "local.qobject", "");
qDumpObjectData440(2, 42, &t, 1, 0, 0, 0, 0);
qDumpObjectData440(2, 42, testAddress(&t), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
......@@ -250,6 +260,11 @@ int main(int argc, char *argv[])
return 0;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "-u")) {
optTestUninitialized = true;
printf("\nTesting uninitialized...\n");
continue;
}
printf("\nTesting %s\n", arg);
if (!qstrcmp(arg, "QString"))
dumpQString();
......
......@@ -41,7 +41,8 @@ HEADERS += \
$$PWD/cdboptions.h \
$$PWD/cdboptionspage.h \
$$PWD/cdbdumperhelper.h \
$$PWD/cdbsymbolpathlisteditor.h
$$PWD/cdbsymbolpathlisteditor.h \
$$PWD/cdbexceptionutils.h
SOURCES += \
$$PWD/cdbdebugengine.cpp \
......@@ -56,7 +57,8 @@ SOURCES += \
$$PWD/cdboptions.cpp \
$$PWD/cdboptionspage.cpp \
$$PWD/cdbdumperhelper.cpp \
$$PWD/cdbsymbolpathlisteditor.cpp
$$PWD/cdbsymbolpathlisteditor.cpp \
$$PWD/cdbexceptionutils.cpp
FORMS += $$PWD/cdboptionspagewidget.ui
......
......@@ -29,13 +29,9 @@
#include "cdbdebugeventcallback.h"
#include "cdbdebugengine.h"
#include "cdbexceptionutils.h"
#include "cdbdebugengine_p.h"
#include "debuggermanager.h"
#include "breakhandler.h"
#include "cdbstacktracecontext.h"
enum { cppExceptionCode = 0xe06d7363, startupCompleteTrap = 0x406d1388,
rpcServerUnavailableExceptionCode = 0x6ba };
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
......@@ -236,123 +232,6 @@ STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2 Bp)
return S_OK;
}
// Simple exception formatting
void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
{
str.setIntegerBase(16);
str << "\nException at 0x" << e->ExceptionAddress
<< ", code: 0x" << e->ExceptionCode << ": ";
switch (e->ExceptionCode) {
case cppExceptionCode:
str << "C++ exception";
break;
case startupCompleteTrap:
str << "Startup complete";
break;
case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write" : "read")
<< " access violation at: 0x" << e->ExceptionInformation[1];
}
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
str << "arrary bounds exceeded";
break;
case EXCEPTION_BREAKPOINT:
str << "breakpoint";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
str << "datatype misalignment";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
str << "floating point exception";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
str << "division by zero";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
str << " floating-point operation cannot be represented exactly as a decimal fraction";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
str << "invalid floating-point operation";
break;
case EXCEPTION_FLT_OVERFLOW:
str << "floating-point overflow";
break;
case EXCEPTION_FLT_STACK_CHECK:
str << "floating-point operation stack over/underflow";
break;
case EXCEPTION_FLT_UNDERFLOW:
str << "floating-point UNDERFLOW";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
str << "invalid instruction";
break;
case EXCEPTION_IN_PAGE_ERROR:
str << "page in error";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
str << "integer division by zero";
break;
case EXCEPTION_INT_OVERFLOW:
str << "integer overflow";
break;
case EXCEPTION_INVALID_DISPOSITION:
str << "invalid disposition to exception dispatcher";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
str << "attempt to continue execution after noncontinuable exception";
break;
case EXCEPTION_PRIV_INSTRUCTION:
str << "privileged instruction";
break;
case EXCEPTION_SINGLE_STEP:
str << "single step";
break;
case EXCEPTION_STACK_OVERFLOW:
str << "stack_overflow";
break;
}
str << ", flags=0x" << e->ExceptionFlags;
if (e->ExceptionFlags == EXCEPTION_NONCONTINUABLE) {
str << " (execution cannot be continued)";
}
str << "\n\n";
str.setIntegerBase(10);
}
// Format exception with stacktrace in case of C++ exception
void formatException(const EXCEPTION_RECORD64 *e,
const QSharedPointer<CdbDumperHelper> &dumper,
QTextStream &str)
{
formatException(e, str);
if (e->ExceptionCode == cppExceptionCode) {
QString errorMessage;
ULONG currentThreadId = 0;
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(&currentThreadId);
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
str << "at:\n";
stc->format(str);
str <<'\n';
delete stc;
}
}
}
static bool isFatalException(LONG code)
{
switch (code) {
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
case startupCompleteTrap: // Mysterious exception at start of application
case rpcServerUnavailableExceptionCode:
return false;
default:
break;
}
return true;
}
STDMETHODIMP CdbDebugEventCallback::Exception(
THIS_
......@@ -512,6 +391,7 @@ STDMETHODIMP CdbExceptionLoggerEventCallback::Exception(
__in ULONG /* FirstChance */
)
{
m_exceptionCodes.push_back(Exception->ExceptionCode);
m_exceptionMessages.push_back(QString());
{
QTextStream str(&m_exceptionMessages.back());
......
......@@ -257,10 +257,12 @@ public:
int exceptionCount() const { return m_exceptionMessages.size(); }
QStringList exceptionMessages() const { return m_exceptionMessages; }
QList<ULONG> exceptionCodes() const { return m_exceptionCodes; }
private:
const QString m_logPrefix;
IDebuggerManagerAccessForEngines *m_access;
QList<ULONG> m_exceptionCodes;
QStringList m_exceptionMessages;
};
......
......@@ -34,6 +34,7 @@
#include "cdbdebugeventcallback.h"
#include "cdbsymbolgroupcontext.h"
#include "watchhandler.h"
#include "cdbexceptionutils.h"
#include "shared/sharedlibraryinjector.h"
......@@ -440,7 +441,7 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
QString callCmd;
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
const char *outData;
if (!callDumper(callCmd, QByteArray(), &outData, errorMessage)) {
if (!callDumper(callCmd, QByteArray(), &outData, false, errorMessage)) {
return false;
}
if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) {
......@@ -469,7 +470,8 @@ bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &b
return true;
}
bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr, QString *errorMessage)
bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
bool ignoreAccessViolation, QString *errorMessage)
{
*outDataPtr = 0;
CdbExceptionLoggerEventCallback exLogger(m_messagePrefix, m_access);
......@@ -483,7 +485,7 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
return false;
// Set up call and a temporary breakpoint after it.
// Try to skip debuggee crash exceptions and dumper exceptions
// by using 'gh' (go handled)
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
for (int i = 0; i < 10; i++) {
const int oldExceptionCount = exLogger.exceptionCount();
// Go. If an exception occurs in loop 2, let the dumper handle it.
......@@ -499,6 +501,13 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
// no new exceptions? -> break
if (oldExceptionCount == newExceptionCount)
break;
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
// else occurred.
if (ignoreAccessViolation) {
const QList<ULONG> newExceptionCodes = exLogger.exceptionCodes().mid(oldExceptionCount);
if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
break;
}
}
if (exLogger.exceptionCount()) {
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
......@@ -612,7 +621,13 @@ CdbDumperHelper::DumpExecuteResult
if (loadDebug)
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
const char *outputData;
if (!callDumper(callCmd, inBuffer, &outputData, errorMessage))
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
ExceptionBlocker eb(m_cif->debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException);
if (!eb) {
*errorMessage = eb.errorString();
return DumpExecuteCallFailed;
}
if (!callDumper(callCmd, inBuffer, &outputData, true, errorMessage))
return DumpExecuteCallFailed;
QtDumperResult dumpResult;
if (!QtDumperHelper::parseValue(outputData, &dumpResult)) {
......
......@@ -106,7 +106,8 @@ private:
bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage);
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, QString *errorMessage);
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
bool ignoreAccessViolation, QString *errorMessage);
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed };
DumpExecuteResult executeDump(const WatchData &wd,
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#include "cdbexceptionutils.h"
#include "cdbdebugengine_p.h"
#include "cdbdumperhelper.h"
#include "cdbstacktracecontext.h"
#include <QtCore/QString>
#include <QtCore/QTextStream>
#include <QtCore/QDebug>
enum { debugExc = 0 };
// Special exception codes.
enum { cppExceptionCode = 0xe06d7363, startupCompleteTrap = 0x406d1388,
rpcServerUnavailableExceptionCode = 0x6ba };
namespace Debugger {
namespace Internal {
static inline void formatDebugFilterExecFlags(ULONG f, const char *title, QTextStream &str)
{
str.setIntegerBase(16);
str << ' ' << title << "=0x" << f << " (";
str.setIntegerBase(10);
switch (f) {
case DEBUG_FILTER_BREAK:
str << "DEBUG_FILTER_BREAK";
break;
case DEBUG_FILTER_SECOND_CHANCE_BREAK:
str << "DEBUG_FILTER_SECOND_CHANCE_BREAK";
break;
case DEBUG_FILTER_OUTPUT:
str << "DEBUG_FILTER_OUTPUT";
break;
case DEBUG_FILTER_IGNORE:
str << "DEBUG_FILTER_IGNORE";
break;
}
str << ')';
}
static inline void formatDebugFilterContFlags(ULONG f, const char *title, QTextStream &str)
{
str.setIntegerBase(16);
str << ' ' << title << "=0x" << f << " (";
str.setIntegerBase(10);
switch (f) {
case DEBUG_FILTER_GO_HANDLED:
str << "DEBUG_FILTER_GO_HANDLED";
break;
case DEBUG_FILTER_GO_NOT_HANDLED:
str << "DEBUG_FILTER_GO_NOT_HANDLED";
}
str << ')';
}
ExceptionBlocker::ExceptionBlocker(CIDebugControl *ctrl, ULONG code, Mode m) :
m_ctrl(ctrl),
m_code(code),
m_state(StateError)
{
// Retrieve current state
memset(&m_oldParameters, 0, sizeof(DEBUG_EXCEPTION_FILTER_PARAMETERS));
if (getExceptionParameters(ctrl, code, &m_oldParameters, &m_errorString)) {
// Are we in a nested instantiation?
const ULONG desiredExOption = m == IgnoreException ? DEBUG_FILTER_IGNORE : DEBUG_FILTER_OUTPUT;
const bool isAlreadyBlocked = m_oldParameters.ExecutionOption == desiredExOption
&& m_oldParameters.ContinueOption == DEBUG_FILTER_GO_NOT_HANDLED;
if (isAlreadyBlocked) {
m_state = StateNested;
} else {
// Nope, block it now.
DEBUG_EXCEPTION_FILTER_PARAMETERS blockedState = m_oldParameters;
blockedState.ExecutionOption = desiredExOption;
blockedState.CommandSize = DEBUG_FILTER_GO_NOT_HANDLED;
const bool ok = setExceptionParameters(m_ctrl, blockedState, &m_errorString);
m_state = ok ? StateOk : StateError;
} // not blocked
} else {
m_state = StateError;
}
if (debugExc)
qDebug() << "ExceptionBlocker: state=" << m_state << format(m_oldParameters) << m_errorString;
}
ExceptionBlocker::~ExceptionBlocker()
{
if (m_state == StateOk) {
// Restore
if (debugExc)
qDebug() << "~ExceptionBlocker: unblocking " << m_oldParameters.ExceptionCode;
if (!setExceptionParameters(m_ctrl, m_oldParameters, &m_errorString))
qWarning("Unable to restore exception state for %lu: %s\n", m_oldParameters.ExceptionCode, qPrintable(m_errorString));
}
}
bool ExceptionBlocker::getExceptionParameters(CIDebugControl *ctrl, ULONG exCode, DEBUG_EXCEPTION_FILTER_PARAMETERS *result, QString *errorMessage)
{
const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result);
if (FAILED(ihr)) {
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
return false;
}
return true;
}
bool ExceptionBlocker::setExceptionParameters(CIDebugControl *ctrl, const DEBUG_EXCEPTION_FILTER_PARAMETERS &p, QString *errorMessage)
{
const HRESULT ihr = ctrl->SetExceptionFilterParameters(1, const_cast<DEBUG_EXCEPTION_FILTER_PARAMETERS*>(&p));
if (FAILED(ihr)) {
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
return false;
}
return true;
}
QString ExceptionBlocker::format(const DEBUG_EXCEPTION_FILTER_PARAMETERS &p)
{
QString rc;
QTextStream str(&rc);
str << "Code=" << p.ExceptionCode;
formatDebugFilterExecFlags(p.ExecutionOption, "ExecutionOption", str);
formatDebugFilterContFlags(p.ContinueOption, "ContinueOption", str);
str << " TextSize=" << p.TextSize << " CommandSizes=" << p.CommandSize << ','
<< p.SecondCommandSize;
return rc;
}
// ------------------ further exception utilities
// Simple exception formatting
void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
{
str.setIntegerBase(16);
str << "\nException at 0x" << e->ExceptionAddress
<< ", code: 0x" << e->ExceptionCode << ": ";
switch (e->ExceptionCode) {
case cppExceptionCode:
str << "C++ exception";
break;
case startupCompleteTrap:
str << "Startup complete";
break;
case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write" : "read")
<< " access violation at: 0x" << e->ExceptionInformation[1];
}
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
str << "arrary bounds exceeded";
break;
case EXCEPTION_BREAKPOINT:
str << "breakpoint";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT: