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
|
} // namespace
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
#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)
|
static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
|
||||||
{
|
{
|
||||||
|
enum { maxStackFrames = 100 };
|
||||||
char appName[MAX_PATH];
|
char appName[MAX_PATH];
|
||||||
if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
|
if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
|
||||||
appName[0] = 0;
|
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;
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
}
|
}
|
||||||
#endif // Q_OS_WIN) && !Q_OS_WINCE && !Q_OS_WINRT
|
#endif // Q_OS_WIN) && !Q_OS_WINCE && !Q_OS_WINRT
|
||||||
|
Loading…
x
Reference in New Issue
Block a user