Shared/Misc: Improve Windows crash report

Improve WheatyExceptionReport Windows crash report to write a cleaner and more readable crash report, in particular:
- Fix missing variable Type in some cases
- Fix missing variable Name in some cases
- Properly format each member to avoid confusion when reading the log
- Add support for double pointers, e.g. Type**
- Add initial support for arrays, logging the number of elements and the first 10 elements for now. UDT members of array elements are not supported yet
- Skip static members, SymTagVTable and other not needed types
- Skip SymTagUDT with no children
- Fix possible stack overflows when logging strings/char*/char[] values.
- Fix "punting on symbol" error happening when trying to dereference bad pointers.
- Fix <user defined> type being logged instead of the correct type when reaching WER_MAX_NESTING_LEVEL or with pointer types with invalid addresses.
This commit is contained in:
jackpoz
2014-06-15 18:14:43 +02:00
parent 6098a5e43a
commit 4688676f3e
2 changed files with 369 additions and 148 deletions

View File

@@ -60,6 +60,7 @@ HANDLE WheatyExceptionReport::m_hReportFile;
HANDLE WheatyExceptionReport::m_hDumpFile;
HANDLE WheatyExceptionReport::m_hProcess;
SymbolPairs WheatyExceptionReport::symbols;
std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails;
// Declare global instance of class
WheatyExceptionReport g_WheatyExceptionReport;
@@ -767,18 +768,21 @@ ULONG /*SymbolSize*/,
PVOID UserContext)
{
char szBuffer[1024 * 64];
char szBuffer[WER_LARGE_BUFFER_SIZE];
memset(szBuffer, 0, sizeof(szBuffer));
__try
{
ClearSymbols();
if (FormatSymbolValue(pSymInfo, (STACKFRAME64*)UserContext,
szBuffer, sizeof(szBuffer)))
_tprintf(_T("\t%s\r\n"), szBuffer);
_tprintf(_T("%s"), szBuffer);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
_tprintf(_T("punting on symbol %s\r\n"), pSymInfo->Name);
_tprintf(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
if (szBuffer[0] != '\0')
_tprintf(_T("%s"), szBuffer);
}
return TRUE;
@@ -797,12 +801,6 @@ unsigned /*cbBuffer*/)
{
char * pszCurrBuffer = pszBuffer;
// Indicate if the variable is a local or parameter
if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
pszCurrBuffer += sprintf(pszCurrBuffer, "Parameter ");
else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
pszCurrBuffer += sprintf(pszCurrBuffer, "Local ");
// If it's a function, don't do anything.
if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
return false;
@@ -824,19 +822,25 @@ unsigned /*cbBuffer*/)
// return false;
}
else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
{
return false; // Don't try to report register variable
}
else
{
pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
}
pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
// Indicate if the variable is a local or parameter
if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
symbolDetails.top().Prefix = "Parameter ";
else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
symbolDetails.top().Prefix = "Local ";
// Determine if the variable is a user defined type (UDT). IF so, bHandled
// will return true.
bool bHandled;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex,
0, pVariable, bHandled, pSym->Name, "");
0, pVariable, bHandled, pSym->Name, "", false, true);
if (!bHandled)
{
@@ -844,15 +848,19 @@ unsigned /*cbBuffer*/)
// variable. Based on the size, we're assuming it's a char, WORD, or
// DWORD.
BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
if (symbolDetails.top().Type.empty())
symbolDetails.top().Type = rgBaseType[basicType];
// Emit the variable name
pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", pSym->Name);
if (pSym->Name[0] != '\0')
symbolDetails.top().Name = pSym->Name;
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size,
(PVOID)pVariable);
char buffer[50];
FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
symbolDetails.top().Value = buffer;
}
pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
return true;
}
@@ -868,13 +876,15 @@ DWORD dwTypeIndex,
unsigned nestingLevel,
DWORD_PTR offset,
bool & bHandled,
char* Name,
char* suffix)
const char* Name,
char* suffix,
bool newSymbol,
bool logChildren)
{
bHandled = false;
if (!StoreSymbol(dwTypeIndex, offset))
return pszCurrBuffer;
if (newSymbol)
pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
DWORD typeTag;
if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag))
@@ -890,19 +900,39 @@ char* suffix)
if (wcscmp(pwszTypeName, L"std::basic_string<char,std::char_traits<char>,std::allocator<char> >") == 0)
{
LocalFree(pwszTypeName);
pszCurrBuffer += sprintf(pszCurrBuffer, " %s", "std::string");
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, btStdString, 0, (PVOID)offset);
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
symbolDetails.top().Type = "std::string";
char buffer[50];
FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer));
symbolDetails.top().Value = buffer;
if (Name != NULL && Name[0] != '\0')
symbolDetails.top().Name = Name;
bHandled = true;
return pszCurrBuffer;
}
pszCurrBuffer += sprintf(pszCurrBuffer, " %ls", pwszTypeName);
char buffer[200];
wcstombs(buffer, pwszTypeName, sizeof(buffer));
buffer[199] = '\0';
if (Name != NULL && Name[0] != '\0')
{
symbolDetails.top().Type = buffer;
symbolDetails.top().Name = Name;
}
else if (buffer[0] != '\0')
symbolDetails.top().Name = buffer;
LocalFree(pwszTypeName);
}
else if (Name != NULL && Name[0] != '\0')
symbolDetails.top().Name = Name;
if (strlen(suffix) > 0)
pszCurrBuffer += sprintf(pszCurrBuffer, "%s", suffix);
if (!StoreSymbol(dwTypeIndex, offset))
{
// Skip printing address and base class if it has been printed already
if (typeTag == SymTagBaseClass)
bHandled = true;
return pszCurrBuffer;
}
DWORD innerTypeID;
switch (typeTag)
@@ -910,11 +940,9 @@ char* suffix)
case SymTagPointerType:
if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
{
#define MAX_NESTING_LEVEL 5
if (nestingLevel >= MAX_NESTING_LEVEL)
break;
if (Name != NULL && Name[0] != '\0')
symbolDetails.top().Name = Name;
pszCurrBuffer += sprintf(pszCurrBuffer, " %s", Name);
BOOL isReference;
SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference);
@@ -922,44 +950,56 @@ char* suffix)
memset(addressStr, 0, sizeof(addressStr));
if (isReference)
addressStr[0] = '&';
symbolDetails.top().Suffix += "&";
else
addressStr[0] = '*';
symbolDetails.top().Suffix += "*";
DWORD_PTR address = *(PDWORD_PTR)offset;
if (address == NULL)
// Try to dereference the pointer in a try/except block since it might be invalid
DWORD_PTR address = DereferenceUnsafePointer(offset);
char buffer[50];
FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
symbolDetails.top().Value = buffer;
if (nestingLevel >= WER_MAX_NESTING_LEVEL)
logChildren = false;
// no need to log any children since the address is invalid anyway
if (address == NULL || address == DWORD_PTR(-1))
logChildren = false;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
address, bHandled, Name, addressStr, false, logChildren);
if (!bHandled)
{
pwszTypeName;
if (SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMNAME,
&pwszTypeName))
BasicType basicType = GetBasicType(dwTypeIndex, modBase);
if (symbolDetails.top().Type.empty())
symbolDetails.top().Type = rgBaseType[basicType];
if (address == NULL)
symbolDetails.top().Value = "NULL";
else if (address == DWORD_PTR(-1))
symbolDetails.top().Value = "<Unable to read memory>";
else
{
pszCurrBuffer += sprintf(pszCurrBuffer, " %ls", pwszTypeName);
LocalFree(pwszTypeName);
}
pszCurrBuffer += sprintf(pszCurrBuffer, "%s = NULL\r\n", addressStr);
bHandled = true;
return pszCurrBuffer;
}
else
{
FormatOutputValue(&addressStr[1], btVoid, sizeof(PVOID), (PVOID)offset);
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
address, bHandled, "", addressStr);
if (!bHandled)
{
BasicType basicType = GetBasicType(dwTypeIndex, modBase);
pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
// Get the size of the child member
ULONG64 length;
SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, length, (PVOID)address);
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
bHandled = true;
return pszCurrBuffer;
char buffer[50];
FormatOutputValue(buffer, basicType, length, (PVOID)address, sizeof(buffer));
symbolDetails.top().Value = buffer;
}
bHandled = true;
return pszCurrBuffer;
}
else if (address == NULL)
symbolDetails.top().Value = "NULL";
else if (address == DWORD_PTR(-1))
{
symbolDetails.top().Value = "<Unable to read memory>";
bHandled = true;
return pszCurrBuffer;
}
}
break;
@@ -970,15 +1010,82 @@ char* suffix)
if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag))
break;
if (innerTypeTag == SymTagPointerType)
switch (innerTypeTag)
{
pszCurrBuffer += sprintf(pszCurrBuffer, " %s", Name);
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
offset, bHandled, "", "");
case SymTagUDT:
if (nestingLevel >= WER_MAX_NESTING_LEVEL)
logChildren = false;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
case SymTagPointerType:
if (Name != NULL && Name[0] != '\0')
symbolDetails.top().Name = Name;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
case SymTagArrayType:
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
default:
break;
}
}
break;
case SymTagArrayType:
if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
{
symbolDetails.top().HasChildren = true;
BasicType basicType = btNoType;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
offset, bHandled, Name, "", false, false);
// Set Value back to an empty string since the Array object itself has no value, only its elements have
symbolDetails.top().Value = "";
DWORD elementsCount;
if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount))
symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]";
else
symbolDetails.top().Suffix += "[<unknown count>]";
if (!bHandled)
{
basicType = GetBasicType(dwTypeIndex, modBase);
if (symbolDetails.top().Type.empty())
symbolDetails.top().Type = rgBaseType[basicType];
bHandled = true;
}
// Get the size of the child member
ULONG64 length;
SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
char buffer[50];
switch (basicType)
{
case btChar:
case btStdString:
FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer));
symbolDetails.top().Value = buffer;
break;
default:
for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++)
{
pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
symbolDetails.top().Suffix += "[" + std::to_string(index) + "]";
FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
symbolDetails.top().Value = buffer;
pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
}
break;
}
return pszCurrBuffer;
}
break;
case SymTagBaseType:
break;
case SymTagEnum:
@@ -1013,26 +1120,37 @@ char* suffix)
return pszCurrBuffer;
}
// Append a line feed
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
// Iterate through each of the children
for (unsigned i = 0; i < dwChildrenCount; i++)
{
DWORD symTag;
SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag);
if (symTag == SymTagFunction || symTag == SymTagTypedef)
if (symTag == SymTagFunction ||
symTag == SymTagEnum ||
symTag == SymTagTypedef ||
symTag == SymTagVTable)
continue;
// Add appropriate indentation level (since this routine is recursive)
for (unsigned j = 0; j <= nestingLevel+1; j++)
pszCurrBuffer += sprintf(pszCurrBuffer, "\t");
// Ignore static fields
DWORD dataKind;
SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind);
if (dataKind == DataIsStaticLocal ||
dataKind == DataIsGlobal ||
dataKind == DataIsStaticMember)
continue;
symbolDetails.top().HasChildren = true;
if (!logChildren)
{
bHandled = false;
return pszCurrBuffer;
}
// Recurse for each of the child types
bool bHandled2;
BasicType basicType = GetBasicType(children.ChildId[i], modBase);
pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
// Get the offset of the child member, relative to its parent
DWORD dwMemberOffset;
@@ -1044,11 +1162,14 @@ char* suffix)
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase,
children.ChildId[i], nestingLevel+1,
dwFinalOffset, bHandled2, ""/*Name */, "");
dwFinalOffset, bHandled2, ""/*Name */, "", true, true);
// If the child wasn't a UDT, format it appropriately
if (!bHandled2)
{
if (symbolDetails.top().Type.empty())
symbolDetails.top().Type = rgBaseType[basicType];
// Get the real "TypeId" of the child. We need this for the
// SymGetTypeInfo(TI_GET_TYPEID) call below.
DWORD typeId;
@@ -1059,75 +1180,75 @@ char* suffix)
ULONG64 length;
SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType,
length, (PVOID)dwFinalOffset);
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
char buffer[50];
FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer));
symbolDetails.top().Value = buffer;
}
pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
}
bHandled = true;
return pszCurrBuffer;
}
char * WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
void WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
BasicType basicType,
DWORD64 length,
PVOID pAddress)
PVOID pAddress,
size_t bufferSize)
{
__try
{
switch (basicType)
{
case btChar:
pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%s\"", pAddress);
{
if (strlen((char*)pAddress) > bufferSize - 6)
pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", bufferSize - 6, (char*)pAddress);
else
pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", (char*)pAddress);
break;
}
case btStdString:
pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%s\"", static_cast<std::string*>(pAddress)->c_str());
{
std::string* value = static_cast<std::string*>(pAddress);
if (value->length() > bufferSize - 6)
pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", bufferSize - 6, value->c_str());
else
pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", value->c_str());
break;
}
default:
// Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
if (length == 1)
pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PBYTE)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PBYTE)pAddress);
else if (length == 2)
pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PWORD)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PWORD)pAddress);
else if (length == 4)
{
if (basicType == btFloat)
{
pszCurrBuffer += sprintf(pszCurrBuffer, " = %f", *(PFLOAT)pAddress);
}
else if (basicType == btChar)
{
if (!IsBadStringPtr(*(PSTR*)pAddress, 32))
{
pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%.31s\"",
*(PSTR*)pAddress);
}
else
pszCurrBuffer += sprintf(pszCurrBuffer, " = %X",
*(PDWORD)pAddress);
}
pszCurrBuffer += sprintf(pszCurrBuffer, "%f", *(PFLOAT)pAddress);
else
pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PDWORD)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PDWORD)pAddress);
}
else if (length == 8)
{
if (basicType == btFloat)
{
pszCurrBuffer += sprintf(pszCurrBuffer, " = %lf",
pszCurrBuffer += sprintf(pszCurrBuffer, "%lf",
*(double *)pAddress);
}
else
pszCurrBuffer += sprintf(pszCurrBuffer, " = %I64X",
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X",
*(DWORD64*)pAddress);
}
else
{
#if _WIN64
pszCurrBuffer += sprintf(pszCurrBuffer, " = %I64X", (DWORD64*)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X", (DWORD64*)pAddress);
#else
pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", (PDWORD)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", (PDWORD)pAddress);
#endif
}
break;
@@ -1136,13 +1257,11 @@ PVOID pAddress)
__except (EXCEPTION_EXECUTE_HANDLER)
{
#if _WIN64
pszCurrBuffer += sprintf(pszCurrBuffer, " <Unable to read memory> = %I64X", (DWORD64*)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X <Unable to read memory>", (DWORD64*)pAddress);
#else
pszCurrBuffer += sprintf(pszCurrBuffer, " <Unable to read memory> = %X", (PDWORD)pAddress);
pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X <Unable to read memory>", (PDWORD)pAddress);
#endif
}
return pszCurrBuffer;
}
BasicType
@@ -1170,13 +1289,25 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
return btNoType;
}
DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer(DWORD_PTR address)
{
__try
{
return *(PDWORD_PTR)address;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return DWORD_PTR(-1);
}
}
//============================================================================
// Helper function that writes to the report file, and allows the user to use
// printf style formating
//============================================================================
int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
{
TCHAR szBuff[1024 * 64];
TCHAR szBuff[WER_LARGE_BUFFER_SIZE];
int retValue;
DWORD cbWritten;
va_list argptr;
@@ -1198,6 +1329,41 @@ bool WheatyExceptionReport::StoreSymbol(DWORD type, DWORD_PTR offset)
void WheatyExceptionReport::ClearSymbols()
{
symbols.clear();
while (!symbolDetails.empty())
symbolDetails.pop();
}
char* WheatyExceptionReport::PushSymbolDetail(char* pszCurrBuffer)
{
// Log current symbol and then add another to the stack to keep the hierarchy format
pszCurrBuffer = PrintSymbolDetail(pszCurrBuffer);
symbolDetails.emplace();
return pszCurrBuffer;
}
char* WheatyExceptionReport::PopSymbolDetail(char* pszCurrBuffer)
{
pszCurrBuffer = PrintSymbolDetail(pszCurrBuffer);
symbolDetails.pop();
return pszCurrBuffer;
}
char* WheatyExceptionReport::PrintSymbolDetail(char* pszCurrBuffer)
{
if (symbolDetails.empty())
return pszCurrBuffer;
// Don't log anything if has been logged already or if it's empty
if (symbolDetails.top().Logged || symbolDetails.top().empty())
return pszCurrBuffer;
// Add appropriate indentation level (since this routine is recursive)
for (size_t i = 0; i < symbolDetails.size(); i++)
pszCurrBuffer += sprintf(pszCurrBuffer, "\t");
pszCurrBuffer += sprintf(pszCurrBuffer, "%s\r\n", symbolDetails.top().ToString().c_str());
return pszCurrBuffer;
}
#endif // _WIN32

View File

@@ -5,12 +5,13 @@
#include <dbghelp.h>
#include <set>
#if _MSC_VER < 1400
# define countof(array) (sizeof(array) / sizeof(array[0]))
#else
# include <stdlib.h>
# define countof _countof
#endif // _MSC_VER < 1400
#include <stdlib.h>
#include <stack>
#define countof _countof
#define WER_MAX_ARRAY_ELEMENTS_COUNT 10
#define WER_MAX_NESTING_LEVEL 5
#define WER_LARGE_BUFFER_SIZE 1024 * 128
enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
{
@@ -37,40 +38,54 @@ enum BasicType // Stolen from CVCON
btStdString = 101
};
enum DataKind // Stolen from CVCONST.H in the DIA 2.0 SDK
{
DataIsUnknown,
DataIsLocal,
DataIsStaticLocal,
DataIsParam,
DataIsObjectPtr,
DataIsFileStatic,
DataIsGlobal,
DataIsMember,
DataIsStaticMember,
DataIsConstant
};
const char* const rgBaseType[] =
{
" <user defined> ", // btNoType = 0,
" void ", // btVoid = 1,
" char* ", // btChar = 2,
" wchar_t* ", // btWChar = 3,
" signed char ",
" unsigned char ",
" int ", // btInt = 6,
" unsigned int ", // btUInt = 7,
" float ", // btFloat = 8,
" <BCD> ", // btBCD = 9,
" bool ", // btBool = 10,
" short ",
" unsigned short ",
" long ", // btLong = 13,
" unsigned long ", // btULong = 14,
" __int8 ",
" __int16 ",
" __int32 ",
" __int64 ",
" __int128 ",
" unsigned __int8 ",
" unsigned __int16 ",
" unsigned __int32 ",
" unsigned __int64 ",
" unsigned __int128 ",
" <currency> ", // btCurrency = 25,
" <date> ", // btDate = 26,
" VARIANT ", // btVariant = 27,
" <complex> ", // btComplex = 28,
" <bit> ", // btBit = 29,
" BSTR ", // btBSTR = 30,
" HRESULT " // btHresult = 31
"<user defined>", // btNoType = 0,
"void", // btVoid = 1,
"char",//char* // btChar = 2,
"wchar_t*", // btWChar = 3,
"signed char",
"unsigned char",
"int", // btInt = 6,
"unsigned int", // btUInt = 7,
"float", // btFloat = 8,
"<BCD>", // btBCD = 9,
"bool", // btBool = 10,
"short",
"unsigned short",
"long", // btLong = 13,
"unsigned long", // btULong = 14,
"int8",
"int16",
"int32",
"int64",
"int128",
"uint8",
"uint16",
"uint32",
"uint64",
"uint128",
"<currency>", // btCurrency = 25,
"<date>", // btDate = 26,
"VARIANT", // btVariant = 27,
"<complex>", // btComplex = 28,
"<bit>", // btBit = 29,
"BSTR", // btBSTR = 30,
"HRESULT" // btHresult = 31
};
struct SymbolPair
@@ -92,6 +107,39 @@ struct SymbolPair
};
typedef std::set<SymbolPair> SymbolPairs;
struct SymbolDetail
{
SymbolDetail() : Prefix(), Type(), Suffix(), Name(), Value(), Logged(false), HasChildren(false) {}
std::string ToString()
{
Logged = true;
std::string formatted = Prefix + Type + Suffix;
if (!Name.empty())
{
if (!formatted.empty())
formatted += " ";
formatted += Name;
}
if (!Value.empty())
formatted += " = " + Value;
return formatted;
}
bool empty() const
{
return Value.empty() && !HasChildren;
}
std::string Prefix;
std::string Type;
std::string Suffix;
std::string Name;
std::string Value;
bool Logged;
bool HasChildren;
};
class WheatyExceptionReport
{
public:
@@ -122,11 +170,12 @@ class WheatyExceptionReport
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *, char * pszBuffer, unsigned cbBuffer);
static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, char*, char*);
static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, const char*, char*, bool, bool);
static char * FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress);
static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize);
static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase);
static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address);
static int __cdecl _tprintf(const TCHAR * format, ...);
@@ -141,6 +190,12 @@ class WheatyExceptionReport
static HANDLE m_hDumpFile;
static HANDLE m_hProcess;
static SymbolPairs symbols;
static std::stack<SymbolDetail> symbolDetails;
static char* PushSymbolDetail(char* pszCurrBuffer);
static char* PopSymbolDetail(char* pszCurrBuffer);
static char* PrintSymbolDetail(char* pszCurrBuffer);
};
extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class