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
etw.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "etw.h"
5#include "provider.h"
6#include "helpers.h"
7#include "qtheaders.h"
8
9#include <qfile.h>
10#include <qfileinfo.h>
11#include <qtextstream.h>
12
13// This is a bootstrapped tool, so we can't rely on QCryptographicHash for the
14// faster SHA1 implementations from OpenSSL.
15#include "../../3rdparty/sha1/sha1.cpp"
16
17using namespace Qt::StringLiterals;
18
19static inline QString providerVar(const QString &providerName)
20{
21 return providerName + "_provider"_L1;
22}
23
24static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field)
25{
26 const QString &name = field.name;
27
28 if (field.arrayLen > 0) {
29 for (int i = 0; i < field.arrayLen; i++) {
30 stream << "TraceLoggingValue(" << name << "[" << i << "], \"" << name << "[" << i << "]\")";
31 if (i + 1 < field.arrayLen)
32 stream << ",\n ";
33 }
34 return;
35 }
36
37 switch (field.backendType) {
38 case Tracepoint::Field::QtString:
39 stream << "TraceLoggingCountedWideString(reinterpret_cast<LPCWSTR>("
40 << name << ".utf16()), static_cast<ULONG>(" << name << ".size()), \""
41 << name << "\")";
42 return;
43 case Tracepoint::Field::QtByteArray:
44 stream << "TraceLoggingBinary(" << name << ".constData(), "
45 << name << ".size(), \"" << name << "\")";
46 return;
47 case Tracepoint::Field::QtUrl:
48 stream << "TraceLoggingValue(" << name << ".toEncoded().constData(), \"" << name << "\")";
49 return;
50 case Tracepoint::Field::QtRect:
51 case Tracepoint::Field::QtRectF:
52 stream << "TraceLoggingValue(" << name << ".x(), \"x\"), "
53 << "TraceLoggingValue(" << name << ".y(), \"y\"), "
54 << "TraceLoggingValue(" << name << ".width(), \"width\"), "
55 << "TraceLoggingValue(" << name << ".height(), \"height\")";
56 return;
57 case Tracepoint::Field::QtSize:
58 case Tracepoint::Field::QtSizeF:
59 stream << "TraceLoggingValue(" << name << ".width(), \"width\"), "
60 << "TraceLoggingValue(" << name << ".height(), \"height\")";
61 return;
62 case Tracepoint::Field::Pointer:
63 stream << "TraceLoggingPointer(" << name << ", \"" << name << "\")";
64 return;
65 case Tracepoint::Field::Unknown:
66 // Write down the previously stringified data (like we do for QString).
67 // The string is already created in writeWrapper().
68 // Variable name is name##Str.
69 stream << "TraceLoggingCountedWideString(reinterpret_cast<LPCWSTR>(" << name
70 << "Str.utf16()), static_cast<ULONG>(" << name << "Str.size()), \"" << name << "\")";
71 return;
72 case Tracepoint::Field::EnumeratedType:
73 case Tracepoint::Field::FlagType:
74 stream << "TraceLoggingString(trace_convert_" << typeToTypeName(field.paramType) << "(" << name << ").toUtf8().constData(), \"" << name << "\")";
75 return;
76 default:
77 break;
78 }
79
80 stream << "TraceLoggingValue(" << name << ", \"" << name << "\")";
81}
82
83static QString createGuid(QByteArrayView name)
84{
85 quint8 uuid[20] = {}; // SHA1 produces 160 bits of data
86
87 Sha1State state;
88 sha1InitState(&state);
89#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
90 // namespace {00000000-0000-0000-0000-000000000000} for compatibility
91 sha1Update(&state, uuid, 16);
92#endif
93 sha1Update(&state, reinterpret_cast<const uchar *>(name.data()), name.size());
94 sha1FinalizeState(&state);
95
96 sha1ToHash(&state, uuid);
97
98 // set UUID OSF DCE version 5 (SHA1)
99 uuid[6] = (uuid[6] & 0xF) | 0x50; // version 5
100 uuid[8] = (uuid[8] & 0x3F) | 0x80; // OSF DCE variant
101
102 QString guid;
103
104 QTextStream stream(&guid);
105
106 Qt::hex(stream);
107 Qt::showbase(stream);
108
109 stream << "("
110 << qFromBigEndian<quint32>(uuid + 0) << ", "
111 << qFromBigEndian<quint16>(uuid + 4) << ", "
112 << qFromBigEndian<quint16>(uuid + 6) << ", "
113 << uuid[8] << ", "
114 << uuid[9] << ", "
115 << uuid[10] << ", "
116 << uuid[11] << ", "
117 << uuid[12] << ", "
118 << uuid[13] << ", "
119 << uuid[14] << ", "
120 << uuid[15]
121 << ")";
122
123 return guid;
124}
125
126static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
127{
128 writeCommonPrologue(stream);
129
130 const QString providerV = providerVar(provider.name);
131 const QString guard = includeGuard(fileName);
132 const QString guid = createGuid(provider.name.toLocal8Bit());
133
134 stream << "#ifndef " << guard << "\n"
135 << "#define " << guard << "\n"
136 << "\n"
137 << "#include <qt_windows.h>\n"
138 << "#include <TraceLoggingProvider.h>\n"
139 << "\n";
140
141 /* TraceLogging API macros cannot deal with UTF8
142 * source files, so we work around it like this
143 */
144 stream << "#undef _TlgPragmaUtf8Begin\n"
145 "#undef _TlgPragmaUtf8End\n"
146 "#define _TlgPragmaUtf8Begin\n"
147 "#define _TlgPragmaUtf8End\n";
148
149 stream << "\n";
150 stream << qtHeaders();
151 stream << "\n";
152
153 if (!provider.prefixText.isEmpty())
154 stream << provider.prefixText.join(u'\n') << "\n\n";
155
156 stream << "#ifdef TRACEPOINT_DEFINE\n"
157 << "TRACELOGGING_DEFINE_PROVIDER(" << providerV << ", \""
158 << provider.name <<"\", " << guid << ");\n\n";
159
160 stream << "static inline void registerProvider()\n"
161 << "{\n"
162 << " TraceLoggingRegister(" << providerV << ");\n"
163 << "}\n\n";
164
165 stream << "static inline void unregisterProvider()\n"
166 << "{\n"
167 << " TraceLoggingUnregister(" << providerV << ");\n"
168 << "}\n";
169
170 stream << "Q_CONSTRUCTOR_FUNCTION(registerProvider)\n"
171 << "Q_DESTRUCTOR_FUNCTION(unregisterProvider)\n\n";
172
173 stream << "#else\n"
174 << "TRACELOGGING_DECLARE_PROVIDER(" << providerV << ");\n"
175 << "#endif // TRACEPOINT_DEFINE\n\n";
176}
177
178static void writeEpilogue(QTextStream &stream, const QString &fileName)
179{
180 stream << "\n#endif // " << includeGuard(fileName) << "\n"
181 << "#include <private/qtrace_p.h>\n";
182}
183
184static void writeWrapper(QTextStream &stream, const Provider &provider, const Tracepoint &tracepoint,
185 const QString &providerName)
186{
187 const QString argList = formatFunctionSignature(tracepoint.args);
188 const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, ETW);
189 const QString &name = tracepoint.name;
190 const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper();
191 const QString provar = providerVar(providerName);
192
193 stream << "\n";
194
195 stream << "inline void trace_" << name << "(" << argList << ")\n"
196 << "{\n";
197
198 // Convert all unknown types to QString's using QDebug.
199 // Note the naming convention: it's field.name##Str
200 for (const Tracepoint::Field &field : tracepoint.fields) {
201 if (field.backendType == Tracepoint::Field::Unknown) {
202 stream << " const QString " << field.name << "Str = QDebug::toString(" << field.name
203 << ");\n";
204 }
205 }
206 stream << " TraceLoggingWrite(" << provar << ", \"" << name << "\"";
207
208 for (const Tracepoint::Field &field : tracepoint.fields) {
209 stream << ",\n";
210 stream << " ";
211 writeEtwMacro(stream, field);
212 }
213
214 stream << ");\n"
215 << "}\n\n";
216
217 stream << "inline void do_trace_" << name << "(" << argList << ")\n"
218 << "{\n"
219 << " trace_" << name << "(" << paramList << ");\n"
220 << "}\n";
221
222 stream << "inline bool trace_" << name << "_enabled()\n"
223 << "{\n"
224 << " return TraceLoggingProviderEnabled(" << provar << ", 0, 0);\n"
225 << "}\n";
226}
227
228static void writeEnumConverter(QTextStream &stream, const TraceEnum &enumeration)
229{
230 stream << "inline QString trace_convert_" << typeToTypeName(enumeration.name) << "(" << enumeration.name << " val)\n";
231 stream << "{\n";
232 for (const auto &v : enumeration.values) {
233 if (v.range != 0) {
234 stream << " if (val >= " << v.value << " && val <= " << v.range << ")\n"
235 << " return QStringLiteral(\"" << v.name << " + \") + QString::number((int)val - " << v.value << ");\n";
236 }
237 }
238 stream << "\n QString ret;\n switch (val) {\n";
239
240 QList<int> handledValues;
241 for (const auto &v : enumeration.values) {
242 if (v.range == 0 && !handledValues.contains(v.value)) {
243 stream << " case " << v.value << ": ret = QStringLiteral(\""
244 << aggregateListValues(v.value, enumeration.values) << "\"); break;\n";
245 handledValues.append(v.value);
246 }
247 }
248
249 stream << " }\n return ret;\n}\n";
250}
251
252static void writeFlagConverter(QTextStream &stream, const TraceFlags &flag)
253{
254 stream << "inline QString trace_convert_" << typeToTypeName(flag.name) << "(" << flag.name << " val)\n";
255 stream << "{\n QString ret;\n";
256 for (const auto &v : flag.values) {
257 if (v.value == 0) {
258 stream << " if (val == 0)\n return QStringLiteral(\""
259 << aggregateListValues(v.value, flag.values) << "\");\n";
260 break;
261 }
262 }
263 QList<int> handledValues;
264 for (const auto &v : flag.values) {
265 if (v.value != 0 && !handledValues.contains(v.value)) {
266 stream << " if (val & " << (1 << (v.value - 1))
267 << ") { if (ret.length()) ret += QLatin1Char(\'|\'); ret += QStringLiteral(\"" << v.name << "\"); }\n";
268 handledValues.append(v.value);
269 }
270 }
271 stream << " return ret;\n}\n";
272}
273
274static void writeTracepoints(QTextStream &stream, const Provider &provider)
275{
276 if (provider.tracepoints.isEmpty())
277 return;
278
279 const QString includeGuard = QStringLiteral("TP_%1_PROVIDER").arg(provider.name).toUpper();
280
281 stream << "#if !defined(" << includeGuard << ") && !defined(TRACEPOINT_DEFINE)\n"
282 << "#define " << includeGuard << "\n"
283 << "QT_BEGIN_NAMESPACE\n"
284 << "namespace QtPrivate {\n";
285
286 for (const auto &enumeration : provider.enumerations)
287 writeEnumConverter(stream, enumeration);
288
289 for (const auto &flag : provider.flags)
290 writeFlagConverter(stream, flag);
291
292 for (const Tracepoint &t : provider.tracepoints)
293 writeWrapper(stream, provider, t, provider.name);
294
295 stream << "} // namespace QtPrivate\n"
296 << "QT_END_NAMESPACE\n"
297 << "#endif // " << includeGuard << "\n\n";
298}
299
300void writeEtw(QFile &file, const Provider &provider)
301{
302 QTextStream stream(&file);
303
304 const QString fileName = QFileInfo(file.fileName()).fileName();
305
306 writePrologue(stream, fileName, provider);
307 writeTracepoints(stream, provider);
308 writeEpilogue(stream, fileName);
309}
QT_FORWARD_DECLARE_CLASS(QTextStream)
static QString providerVar(const QString &providerName)
Definition etw.cpp:19
static void writeEpilogue(QTextStream &stream, const QString &fileName)
Definition etw.cpp:178
void writeEtw(QFile &file, const Provider &provider)
Definition etw.cpp:300
static void writeEnumConverter(QTextStream &stream, const TraceEnum &enumeration)
Definition etw.cpp:228
static void writeFlagConverter(QTextStream &stream, const TraceFlags &flag)
Definition etw.cpp:252
static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field)
Definition etw.cpp:24
static void writeWrapper(QTextStream &stream, const Provider &provider, const Tracepoint &tracepoint, const QString &providerName)
Definition etw.cpp:184
static QString createGuid(QByteArrayView name)
Definition etw.cpp:83
static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
Definition etw.cpp:126
static void writeTracepoints(QTextStream &stream, const Provider &provider)
Definition etw.cpp:274