QTestLib/Windows: Try to obtain a stack trace on crash.

Add a helper class for resolving debug symbols by dynamically
loading dbghelp.dll and try to obtain a symbol at the exception
location and a stack trace by using CaptureStackBackTrace().

The output looks like:

A crash occurred in d:\dev\projects\crashingtest_5d\debug\tst_crashingtesttest.exe.

Exception address: 0x0000000052E2853A
Exception code   : 0xc0000005
Nearby symbol    : QString::length

Stack:
 #  1: windowsFaultHandler() - 0x00007FFE080CACD0
...
 #  8: QString::length() - 0x0000000052E28530

[ChangeLog][QtTest] A stack trace will be output on standard error
if a test crashes.

Task-number: QTBUG-47370
Change-Id: I7217e02ec7dc0c96132fe84d1a175d0bed9c5aaf
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
This commit is contained in:
Friedemann Kleint 2015-07-22 12:17:25 +02:00 committed by Simon Hausmann
parent eff4a29489
commit aa42a1a67e

View File

@ -2617,13 +2617,137 @@ FatalSignalHandler::~FatalSignalHandler()
} // namespace
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
class DebugSymbolResolver
{
Q_DISABLE_COPY(DebugSymbolResolver)
public:
struct Symbol {
Symbol() : name(Q_NULLPTR), address(0) {}
const char *name; // Must be freed by caller.
DWORD64 address;
};
explicit DebugSymbolResolver(HANDLE process);
~DebugSymbolResolver() { cleanup(); }
bool isValid() const { return m_symFromAddr; }
Symbol resolveSymbol(DWORD64 address) const;
private:
// typedefs from DbgHelp.h/.dll
struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
ULONG SizeOfStruct;
ULONG TypeIndex; // Type Index of symbol
ULONG64 Reserved[2];
ULONG Index;
ULONG Size;
ULONG64 ModBase; // Base Address of module comtaining this symbol
ULONG Flags;
ULONG64 Value; // Value of symbol, ValuePresent should be 1
ULONG64 Address; // Address of symbol including base address of module
ULONG Register; // register holding value or pointer to value
ULONG Scope; // scope of the symbol
ULONG Tag; // pdb classification
ULONG NameLen; // Actual length of name
ULONG MaxNameLen;
CHAR Name[1]; // Name of symbol
};
typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
void cleanup();
const HANDLE m_process;
HMODULE m_dbgHelpLib;
SymFromAddrType m_symFromAddr;
};
void DebugSymbolResolver::cleanup()
{
if (m_dbgHelpLib)
FreeLibrary(m_dbgHelpLib);
m_dbgHelpLib = 0;
m_symFromAddr = Q_NULLPTR;
}
DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
: m_process(process), m_dbgHelpLib(0), m_symFromAddr(Q_NULLPTR)
{
bool success = false;
m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
if (m_dbgHelpLib) {
SymInitializeType symInitialize = (SymInitializeType)(GetProcAddress(m_dbgHelpLib, "SymInitialize"));
m_symFromAddr = (SymFromAddrType)(GetProcAddress(m_dbgHelpLib, "SymFromAddr"));
success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
}
if (!success)
cleanup();
}
DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
{
// reserve additional buffer where SymFromAddr() will store the name
struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
enum { symbolNameLength = 255 };
char name[symbolNameLength + 1];
};
Symbol result;
if (!isValid())
return result;
NamedSymbolInfo symbolBuffer;
memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
return result;
result.name = qstrdup(symbolBuffer.Name);
result.address = symbolBuffer.Address;
return result;
}
static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
{
enum { maxStackFrames = 100 };
char appName[MAX_PATH];
if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
appName[0] = 0;
fprintf(stderr, "A crash occurred in %s (exception code 0x%lx).",
appName, exInfo->ExceptionRecord->ExceptionCode);
const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
fprintf(stderr, "A crash occurred in %s.\n\n"
"Exception address: 0x%p\n"
"Exception code : 0x%lx\n",
appName, exceptionAddress, exInfo->ExceptionRecord->ExceptionCode);
DebugSymbolResolver resolver(GetCurrentProcess());
if (resolver.isValid()) {
DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
if (exceptionSymbol.name) {
fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
delete [] exceptionSymbol.name;
}
void *stack[maxStackFrames];
fputs("\nStack:\n", stderr);
const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
for (unsigned f = 0; f < frameCount; ++f) {
DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
if (symbol.name) {
fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
delete [] symbol.name;
} else {
fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
}
}
}
fputc('\n', stderr);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif // Q_OS_WIN) && !Q_OS_WINCE && !Q_OS_WINRT