Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtestcrashhandler_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// Copyright (C) 2024 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <QtTest/private/qtestcrashhandler_p.h>
6
7#include <QtCore/qbytearray.h>
8#include <QtCore/qstring.h>
9
10#include <QtTest/qtestcase.h>
11#include <QtTest/private/qtestlog_p.h>
12
13#include <stdio.h>
14#include <stdlib.h>
15
16#if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
17# include <crtdbg.h>
18#endif
19#include <qt_windows.h>
20
21QT_BEGIN_NAMESPACE
22
23using namespace Qt::StringLiterals;
24
25namespace QTest {
26namespace CrashHandler {
28{
29 return IsDebuggerPresent();
30}
31
33{
34}
35
38{
39
40 bool ok = false;
41 const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
42 if (ok && disableStackDump)
43 return;
44
45 // ### Implement finding a debugger on Windows
46}
47
49{
50 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
51 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
52 const char *const name = QTest::currentTestFunction();
53
54 // Windows doesn't have the concept of async-safety, so fprintf() is
55 // probably as good as WriteFile() and WriteConsole().
56 fprintf(stderr, "\n %s function time: %dms, total time: %dms\n",
57 name ? name : "[Non-test]", msecsFunctionTime, msecsTotalTime);
58}
59
60void generateStackTrace(quintptr ip)
61{
62 if (debugger == None || alreadyDebugging())
63 return;
64
65 // ### Implement starting a debugger on Windows
66 Q_UNUSED(ip);
67}
68
70{
71 // Windows does have C signals, but doesn't use them for the purposes we're
72 // talking about here
73}
74
75namespace {
76// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
77class DebugSymbolResolver
78{
79 Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
80public:
81 struct Symbol
82 {
83 Symbol() : name(nullptr), address(0) {}
84
85 const char *name; // Must be freed by caller.
86 DWORD64 address;
87 };
88
89 explicit DebugSymbolResolver(HANDLE process);
90 ~DebugSymbolResolver() { cleanup(); }
91
92 bool isValid() const { return m_symFromAddr; }
93
94 Symbol resolveSymbol(DWORD64 address) const;
95
96private:
97 // typedefs from DbgHelp.h/.dll
98 struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
99 ULONG SizeOfStruct;
100 ULONG TypeIndex; // Type Index of symbol
101 ULONG64 Reserved[2];
102 ULONG Index;
103 ULONG Size;
104 ULONG64 ModBase; // Base Address of module comtaining this symbol
105 ULONG Flags;
106 ULONG64 Value; // Value of symbol, ValuePresent should be 1
107 ULONG64 Address; // Address of symbol including base address of module
108 ULONG Register; // register holding value or pointer to value
109 ULONG Scope; // scope of the symbol
110 ULONG Tag; // pdb classification
111 ULONG NameLen; // Actual length of name
112 ULONG MaxNameLen;
113 CHAR Name[1]; // Name of symbol
114 };
115
116 typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
117 typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
118
119 void cleanup();
120
121 const HANDLE m_process;
122 HMODULE m_dbgHelpLib;
123 SymFromAddrType m_symFromAddr;
124};
125
126void DebugSymbolResolver::cleanup()
127{
128 if (m_dbgHelpLib)
129 FreeLibrary(m_dbgHelpLib);
130 m_dbgHelpLib = 0;
131 m_symFromAddr = nullptr;
132}
133
134DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
135 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
136{
137 bool success = false;
138 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
139 if (m_dbgHelpLib) {
140 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
141 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
142 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
143 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
144 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
145 }
146 if (!success)
147 cleanup();
148}
149
150DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
151{
152 // reserve additional buffer where SymFromAddr() will store the name
153 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
154 enum { symbolNameLength = 255 };
155
156 char name[symbolNameLength + 1];
157 };
158
159 Symbol result;
160 if (!isValid())
161 return result;
162 NamedSymbolInfo symbolBuffer;
163 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
164 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
165 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
166 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
167 return result;
168 result.name = qstrdup(symbolBuffer.Name);
169 result.address = symbolBuffer.Address;
170 return result;
171}
172} // unnamed namespace
173
174static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo);
175
177{
178# if !defined(Q_CC_MINGW)
180# endif
183}
184
186{
187 enum { maxStackFrames = 100 };
188 char appName[MAX_PATH];
190 appName[0] = 0;
194 fprintf(stderr, "A crash occurred in %s.\n", appName);
195 if (const char *name = QTest::currentTestFunction())
196 fprintf(stderr, "While testing %s\n", name);
197 fprintf(stderr, "Function time: %dms Total time: %dms\n\n"
198 "Exception address: 0x%p\n"
199 "Exception code : 0x%lx\n",
202
204 if (resolver.isValid()) {
206 if (exceptionSymbol.name) {
207 fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
208 delete [] exceptionSymbol.name;
209 }
211 fputs("\nStack:\n", stderr);
213 for (unsigned f = 0; f < frameCount; ++f) {
215 if (symbol.name) {
216 fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
217 delete [] symbol.name;
218 } else {
219 fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
220 }
221 }
222 }
223
224 fputc('\n', stderr);
225
227}
228} // namespace CrashHandler
229} // namespace QTest
230
231QT_END_NAMESPACE
static DebuggerProgram debugger
void generateStackTrace(quintptr ip)