mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-25 07:24:21 +01:00
Say what crashed and where in Windows crash reports
A Windows crash report held the header and the log and nothing else: no exception, no address, no stack (issue #1179, two reports that could not be traced). It now also has: - the exception, with its name ("access violation", "stack overflow"...) - where it happened, as module+offset, and for an access violation the address that was read or written - a backtrace, one module+offset per frame, walked with the unwind tables every x64 image carries (RtlLookupFunctionEntry and RtlVirtualUnwind): no dbghelp, no symbols, no allocation. addr2line on the same build turns each offset into a function and line. The dump is written by a reporter thread started at install(), not on the crashing thread, which may have no stack left or hold the loader lock. The crashing thread waits for it for at most ten seconds. Two kinds of crash wrote no report at all before and now do: - abort() (std::terminate(), a failed assert): no SEH exception, so SIGABRT is handled as well. - qFatal(): Qt ends it with TerminateProcess() on Windows, so the message handler now asks for the dump once the message is logged. Elsewhere reportFatal() does nothing; SIGABRT follows there. Tested with a cross-built RelWithDebInfo build under Wine 11, crashing on purpose from inside the event loop: a null pointer, a call through a null pointer, a stack overflow, abort() and qFatal(). Each report resolved to the line that crashed. Linux reports are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,11 +53,15 @@ class tst_CrashHandler : public QObject
|
||||
void formatsIntMax();
|
||||
void truncatesRatherThanOverflowing();
|
||||
void writesNothingWhenThereIsNoRoom();
|
||||
void formatsHex();
|
||||
void formatsHexAddressesAndCodes();
|
||||
void truncatesHexRatherThanOverflowing();
|
||||
|
||||
private:
|
||||
/// Formats into a buffer poisoned with a sentinel, and fails if
|
||||
/// anything past the returned length was touched.
|
||||
static QByteArray format(int value, int size = 32);
|
||||
static QByteArray formatHex(unsigned long long value, int size = 32);
|
||||
};
|
||||
|
||||
QByteArray tst_CrashHandler::format(int value, int size)
|
||||
@@ -79,6 +83,24 @@ QByteArray tst_CrashHandler::format(int value, int size)
|
||||
return QByteArray(buffer, len);
|
||||
}
|
||||
|
||||
QByteArray tst_CrashHandler::formatHex(unsigned long long value, int size)
|
||||
{
|
||||
char buffer[64];
|
||||
memset(buffer, '\xAB', sizeof(buffer));
|
||||
|
||||
const int len = CrashHandler::formatHex(buffer, size, value);
|
||||
|
||||
for (int i = qMax(len, 0) ; i < static_cast<int>(sizeof(buffer)) ; ++i) {
|
||||
if (buffer[i] != '\xAB') {
|
||||
return QByteArray("WROTE PAST END at ") + QByteArray::number(i);
|
||||
}
|
||||
}
|
||||
if (len < 0 || len > size) {
|
||||
return QByteArray("BAD LENGTH ") + QByteArray::number(len);
|
||||
}
|
||||
return QByteArray(buffer, len);
|
||||
}
|
||||
|
||||
void tst_CrashHandler::formatsZero()
|
||||
{
|
||||
QCOMPARE(format(0), QByteArray("0"));
|
||||
@@ -147,5 +169,32 @@ void tst_CrashHandler::writesNothingWhenThereIsNoRoom()
|
||||
QCOMPARE(format(-5, 0), QByteArray());
|
||||
}
|
||||
|
||||
void tst_CrashHandler::formatsHex()
|
||||
{
|
||||
QCOMPARE(formatHex(0), QByteArray("0x0"));
|
||||
QCOMPARE(formatHex(9), QByteArray("0x9"));
|
||||
QCOMPARE(formatHex(10), QByteArray("0xa"));
|
||||
QCOMPARE(formatHex(0x10), QByteArray("0x10"));
|
||||
}
|
||||
|
||||
/**
|
||||
The values the Windows dump writes: an exception code, a module
|
||||
offset, a full 64-bit address and the largest value there is.
|
||||
*/
|
||||
void tst_CrashHandler::formatsHexAddressesAndCodes()
|
||||
{
|
||||
QCOMPARE(formatHex(0xC0000005u), QByteArray("0xc0000005"));
|
||||
QCOMPARE(formatHex(0x1a2b3cu), QByteArray("0x1a2b3c"));
|
||||
QCOMPARE(formatHex(0x00007ff6a1b2c3d4ull), QByteArray("0x7ff6a1b2c3d4"));
|
||||
QCOMPARE(formatHex(~0ull), QByteArray("0xffffffffffffffff"));
|
||||
}
|
||||
|
||||
void tst_CrashHandler::truncatesHexRatherThanOverflowing()
|
||||
{
|
||||
QCOMPARE(formatHex(0xabcdef, 5), QByteArray("0xabc"));
|
||||
QCOMPARE(formatHex(0xabcdef, 1), QByteArray("0"));
|
||||
QCOMPARE(formatHex(0xabcdef, 0), QByteArray());
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_CrashHandler)
|
||||
#include "tst_crashhandler.moc"
|
||||
|
||||
Reference in New Issue
Block a user