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:
parent
eff4a29489
commit
aa42a1a67e
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user