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
hitrace.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "hitrace.h"
5#include "provider.h"
6#include "helpers.h"
7#include "qtheaders.h"
8
9#include <qfile.h>
10#include <qfileinfo.h>
11#include <qstringlist.h>
12#include <qtextstream.h>
13
14using namespace Qt::StringLiterals;
15
16namespace {
17
18constexpr QLatin1StringView traceLevel = "HITRACE_LEVEL_COMMERCIAL"_L1;
19constexpr QLatin1StringView rangeSeparator = " + "_L1;
20constexpr char flagSeparator = '+';
21
22QString sliceName(const Provider &provider, const QString &name)
23{
24 return provider.name + u':' + name;
25}
26
27QString valueBudget(const Provider &provider, const QString &tracepointName)
28{
29 return "qtTraceValueBudget_"_L1 + provider.name + u'_' + tracepointName;
30}
31
32QString sliceConstant(const Provider &provider, const QString &tracepointName)
33{
34 return "qtTraceSlice_"_L1 + provider.name + u'_' + tracepointName;
35}
36
37QString renderFunction(const Provider &provider, const QString &tracepointName)
38{
39 return "qtTraceRender_"_L1 + provider.name + u'_' + tracepointName;
40}
41
42QString sanitized(const QString &valueExpression, const QString &budget)
43{
44 return "qtHiTraceArgument("_L1 + valueExpression + ", "_L1 + budget + ").constData()"_L1;
45}
46
47QString enumConverter(const QString &type)
48{
49 return "qtTraceEnum_"_L1 + typeToTypeName(type);
50}
51
52QString flagsConverter(const QString &type)
53{
54 return "qtTraceFlags_"_L1 + typeToTypeName(type);
55}
56
57struct CustomArgs
58{
59 QString format;
60 QStringList arguments;
61 QStringList unusedNames;
62 int scalarWidth = 0;
63 int stringCount = 0;
64};
65
66constexpr int numberWidth = 20;
67constexpr int booleanWidth = 5;
68constexpr int floatWidth = 14;
69constexpr int pointerWidth = 18;
70
71int renderedWidth(QLatin1StringView specifier)
72{
73 if (specifier == "%s"_L1)
74 return booleanWidth;
75 if (specifier == "%g"_L1)
76 return floatWidth;
77 if (specifier == "%p"_L1)
78 return pointerWidth;
79 return numberWidth;
80}
81
82enum class ValueKind { Scalar, Budgeted };
83
84void appendScalar(CustomArgs &args, QStringList &pairs, const QString &name,
85 QLatin1StringView specifier, const QString &expression,
86 ValueKind kind = ValueKind::Scalar)
87{
88 pairs << name + u'=' + specifier;
89 args.arguments << expression;
90 if (kind == ValueKind::Budgeted)
91 ++args.stringCount;
92 else
93 args.scalarWidth += renderedWidth(specifier);
94}
95
96enum class MemberKind { Integral, Fractional };
97
98void appendMembers(CustomArgs &args, QStringList &pairs, const QString &name, MemberKind kind,
99 std::initializer_list<QLatin1StringView> members)
100{
101 for (QLatin1StringView member : members) {
102 const QString accessor = name + u'.' + member + "()"_L1;
103 const QString pairName = name + u'_' + member;
104 if (kind == MemberKind::Integral) {
105 appendScalar(args, pairs, pairName, "%lld"_L1,
106 "static_cast<long long>("_L1 + accessor + u')');
107 } else {
108 appendScalar(args, pairs, pairName, "%g"_L1, accessor);
109 }
110 }
111}
112
113CustomArgs customArgsFor(const Provider &provider, const Tracepoint &tracepoint)
114{
115 CustomArgs args;
116 QStringList pairs;
117 const QString budget = valueBudget(provider, tracepoint.name);
118
119 for (int i = 0; i < tracepoint.args.size(); ++i) {
120 const Tracepoint::Argument &arg = tracepoint.args[i];
121 const Tracepoint::Field &field = tracepoint.fields[i];
122 const QString &name = arg.name;
123 const qsizetype before = pairs.size();
124
125 if (arg.arrayLen == 0) {
126 switch (field.backendType) {
127 case Tracepoint::Field::Integer:
128 if (arg.type.contains("unsigned"_L1)) {
129 appendScalar(args, pairs, name, "%llu"_L1,
130 "static_cast<unsigned long long>("_L1 + name + u')');
131 } else {
132 appendScalar(args, pairs, name, "%lld"_L1,
133 "static_cast<long long>("_L1 + name + u')');
134 }
135 break;
136 case Tracepoint::Field::IntegerHex:
137 appendScalar(args, pairs, name, "%#llx"_L1,
138 "static_cast<unsigned long long>("_L1 + name + u')');
139 break;
140 case Tracepoint::Field::EnumeratedType:
141 appendScalar(args, pairs, name, "%s"_L1,
142 sanitized(enumConverter(arg.type) + u'(' + name + ").constData()"_L1,
143 budget),
144 ValueKind::Budgeted);
145 break;
146 case Tracepoint::Field::FlagType:
147 appendScalar(args, pairs, name, "%s"_L1,
148 sanitized(flagsConverter(arg.type) + u'(' + name + ").constData()"_L1,
149 budget),
150 ValueKind::Budgeted);
151 break;
152 case Tracepoint::Field::Boolean:
153 appendScalar(args, pairs, name, "%s"_L1,
154 u'(' + name + " ? \"true\" : \"false\")"_L1);
155 break;
156 case Tracepoint::Field::Float:
157 appendScalar(args, pairs, name, "%g"_L1, "static_cast<double>("_L1 + name + u')');
158 break;
159 case Tracepoint::Field::Pointer:
160 appendScalar(args, pairs, name, "%p"_L1,
161 "static_cast<const void *>("_L1 + name + u')');
162 break;
163 case Tracepoint::Field::String:
164 case Tracepoint::Field::QtString:
165 appendScalar(args, pairs, name, "%s"_L1, sanitized(name, budget),
166 ValueKind::Budgeted);
167 break;
168 case Tracepoint::Field::QtUrl:
169 appendScalar(args, pairs, name, "%s"_L1, sanitized(name + ".toString()"_L1, budget),
170 ValueKind::Budgeted);
171 break;
172 case Tracepoint::Field::QtRect:
173 appendMembers(args, pairs, name, MemberKind::Integral,
174 { "x"_L1, "y"_L1, "width"_L1, "height"_L1 });
175 break;
176 case Tracepoint::Field::QtRectF:
177 appendMembers(args, pairs, name, MemberKind::Fractional,
178 { "x"_L1, "y"_L1, "width"_L1, "height"_L1 });
179 break;
180 case Tracepoint::Field::QtSize:
181 appendMembers(args, pairs, name, MemberKind::Integral, { "width"_L1, "height"_L1 });
182 break;
183 case Tracepoint::Field::QtSizeF:
184 appendMembers(args, pairs, name, MemberKind::Fractional,
185 { "width"_L1, "height"_L1 });
186 break;
187 case Tracepoint::Field::QtByteArray:
188 appendScalar(args, pairs, name, "%s"_L1,
189 sanitized(name + ".toHex().constData()"_L1, budget),
190 ValueKind::Budgeted);
191 break;
192 case Tracepoint::Field::Sequence:
193 case Tracepoint::Field::Unknown:
194 break;
195 }
196 }
197
198 if (pairs.size() == before) {
199 args.unusedNames << name;
200 printf("Warning: '%s' argument '%s' has no HiTrace representation "
201 "and is left out of the trace.\n",
202 qPrintable(tracepoint.name), qPrintable(name));
203 }
204 }
205
206 args.format = pairs.join(u',');
207 return args;
208}
209
210void writeEnumConverter(QTextStream &stream, const TraceEnum &enumeration)
211{
212 stream << "inline QByteArray " << enumConverter(enumeration.name) << "(" << enumeration.name
213 << " value)\n"
214 << "{\n"
215 << " const long long raw = static_cast<long long>(value);\n";
216
217 for (const TraceEnum::EnumValue &v : enumeration.values) {
218 if (v.range != 0) {
219 stream << " if (raw >= " << v.value << " && raw <= " << v.range << ")\n"
220 << " return QByteArrayLiteral(\"" << v.name << rangeSeparator
221 << "\") + QByteArray::number(raw - " << v.value << ");\n";
222 }
223 }
224
225 stream << " switch (raw) {\n";
226 QList<int> handled;
227 for (const TraceEnum::EnumValue &v : enumeration.values) {
228 if (v.range == 0 && !handled.contains(v.value)) {
229 stream << " case " << v.value << ": return QByteArrayLiteral(\""
230 << aggregateListValues(v.value, enumeration.values) << "\");\n";
231 handled.append(v.value);
232 }
233 }
234 stream << " default: break;\n"
235 << " }\n"
236 << " return QByteArray::number(raw);\n"
237 << "}\n";
238}
239
240void writeFlagsConverter(QTextStream &stream, const TraceFlags &flag)
241{
242 stream << "inline QByteArray " << flagsConverter(flag.name) << "(" << flag.name << " value)\n"
243 << "{\n"
244 << " const long long raw = static_cast<long long>(value);\n";
245
246 for (const TraceFlags::FlagValue &v : flag.values) {
247 if (v.value == 0) {
248 stream << " if (raw == 0)\n"
249 << " return QByteArrayLiteral(\""
250 << aggregateListValues(v.value, flag.values) << "\");\n";
251 break;
252 }
253 }
254
255 stream << " QByteArray names;\n";
256
257 QList<int> handled;
258 for (const TraceFlags::FlagValue &v : flag.values) {
259 if (v.value != 0 && !handled.contains(v.value)) {
260 handled.append(v.value);
261 stream << " if (raw & " << (1LL << (v.value - 1)) << ") {\n"
262 << " if (!names.isEmpty())\n"
263 << " names += '" << flagSeparator << "';\n"
264 << " names += \"" << v.name << "\";\n"
265 << " }\n";
266 }
267 }
268 stream << " return names.isEmpty() ? QByteArray::number(raw) : names;\n"
269 << "}\n";
270}
271
272void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider)
273{
274 writeCommonPrologue(stream);
275 const QString guard = includeGuard(fileName);
276
277 stream << "\n"
278 << "#include <hitrace/trace.h>\n"
279 << "#include <private/qhitrace_p.h>\n\n";
280
281 stream << "#if !defined(" << guard << ")\n";
282 stream << qtHeaders();
283 stream << "\n";
284 if (!provider.prefixText.isEmpty())
285 stream << provider.prefixText.join(u'\n') << "\n\n";
286 stream << "#endif\n\n";
287
288 stream << "#if !defined(" << guard << ")\n";
289 stream << "#define " << guard << "\n\n";
290
291 const QString namespaceGuard = guard + QStringLiteral("_USE_NAMESPACE");
292 stream << "#if !defined(" << namespaceGuard << ")\n"
293 << "#define " << namespaceGuard << "\n"
294 << "QT_USE_NAMESPACE\n"
295 << "#endif // " << namespaceGuard << "\n\n";
296
297 if (provider.enumerations.isEmpty() && provider.flags.isEmpty())
298 return;
299
300 stream << "QT_BEGIN_NAMESPACE\n"
301 << "namespace QtPrivate {\n";
302
303 for (const TraceEnum &enumeration : provider.enumerations)
304 writeEnumConverter(stream, enumeration);
305 for (const TraceFlags &flag : provider.flags)
306 writeFlagsConverter(stream, flag);
307
308 stream << "} // namespace QtPrivate\n"
309 << "QT_END_NAMESPACE\n\n";
310}
311
312void writeRenderDefinition(QTextStream &stream, const Tracepoint &tracepoint,
313 const CustomArgs &customArgs, const Provider &provider)
314{
315 if (customArgs.format.isEmpty())
316 return;
317
318 const QString &name = tracepoint.name;
319 const QString guardName = QStringLiteral("TPDEF_%1_%2").arg(provider.name, name).toUpper();
320
321 stream << "\n"
322 << "#if defined(TRACEPOINT_DEFINE) && !defined(" << guardName << ")\n"
323 << "#define " << guardName << "\n"
324 << "QT_BEGIN_NAMESPACE\n"
325 << "namespace QtPrivate {\n"
326 << "const char *" << renderFunction(provider, name) << "("
327 << formatFunctionSignature(tracepoint.args) << ")\n"
328 << "{\n";
329 for (const QString &unusedName : customArgs.unusedNames)
330 stream << " Q_UNUSED(" << unusedName << ");\n";
331 stream << " return qtHiTraceFormatArguments(sizeof(" << sliceConstant(provider, name)
332 << ") - 1, \"" << customArgs.format << "\", " << customArgs.arguments.join(", "_L1)
333 << ");\n"
334 << "}\n"
335 << "} // namespace QtPrivate\n"
336 << "QT_END_NAMESPACE\n"
337 << "#endif // " << guardName << "\n";
338}
339
340void writeEpilogue(QTextStream &stream, const QString &fileName)
341{
342 stream << "\n";
343 stream << "#endif // " << includeGuard(fileName) << "\n";
344}
345
346void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, const CustomArgs &customArgs,
347 const Provider &provider)
348{
349 const QString argList = formatFunctionSignature(tracepoint.args);
350 const QString paramList =
351 formatParameterList(provider, tracepoint.args, tracepoint.fields, HITRACE);
352 const QString &name = tracepoint.name;
353 const QString guardName = QStringLiteral("TP_%1_%2").arg(provider.name, name).toUpper();
354 const QString slice = sliceName(provider, name);
355 const QString sliceIdentifier = sliceConstant(provider, name);
356 const bool hasCustomArgs = !customArgs.format.isEmpty();
357
358 stream << "\n"
359 << "#ifndef " << guardName << "\n"
360 << "#define " << guardName << "\n"
361 << "QT_BEGIN_NAMESPACE\n"
362 << "namespace QtPrivate {\n";
363
364 stream << "inline constexpr char " << sliceIdentifier << "[] = \"" << slice << "\";\n";
365 if (customArgs.stringCount > 0) {
366 stream << "inline constexpr int " << valueBudget(provider, name)
367 << " = qtHiTraceValueBudget(sizeof(" << sliceIdentifier << ") - 1, "
368 << customArgs.format.size() + customArgs.scalarWidth << ", "
369 << customArgs.stringCount << ");\n";
370 }
371
372 const QString render = renderFunction(provider, name);
373
374 if (hasCustomArgs)
375 stream << "const char *" << render << "(" << argList << ");\n";
376
377 stream << "inline void do_trace_" << name << "(" << argList << ")\n"
378 << "{\n";
379
380 if (!hasCustomArgs) {
381 for (const QString &unusedName : customArgs.unusedNames)
382 stream << " Q_UNUSED(" << unusedName << ");\n";
383 }
384
385 const QString startTrace =
386 "OH_HiTrace_StartTraceEx("_L1 + traceLevel + ", "_L1 + sliceIdentifier + ", "_L1;
387
388 if (hasCustomArgs) {
389 stream << " " << startTrace << "qtHiTraceIsEnabled() ? " << render << "("
390 << paramList << ") : \"\");\n";
391 } else {
392 stream << " " << startTrace << "\"\");\n";
393 }
394 stream << " OH_HiTrace_FinishTraceEx(" << traceLevel << ");\n";
395
396 stream << "}\n";
397
398 stream << "inline void trace_" << name << "(" << argList << ") { do_trace_" << name << "("
399 << paramList << "); }\n";
400
401 stream << "inline bool trace_" << name << "_enabled()\n"
402 << "{\n"
403 << " return qtHiTraceIsEnabled();\n"
404 << "}\n";
405
406 stream << "} // namespace QtPrivate\n"
407 << "QT_END_NAMESPACE\n"
408 << "#endif // " << guardName << "\n";
409}
410
411} // namespace
412
413void writeHiTrace(QFile &file, const Provider &provider)
414{
415 QTextStream stream(&file);
416
417 const QString fileName = QFileInfo(file.fileName()).fileName();
418
419 QList<CustomArgs> customArgs;
420 for (const Tracepoint &t : provider.tracepoints)
421 customArgs << customArgsFor(provider, t);
422
423 writePrologue(stream, fileName, provider);
424 for (qsizetype i = 0; i < provider.tracepoints.size(); ++i)
425 writeWrapper(stream, provider.tracepoints[i], customArgs[i], provider);
426 writeEpilogue(stream, fileName);
427
428 for (qsizetype i = 0; i < provider.tracepoints.size(); ++i)
429 writeRenderDefinition(stream, provider.tracepoints[i], customArgs[i], provider);
430
431 stream << "#include <private/qtrace_p.h>\n";
432}
void writeHiTrace(QFile &file, const Provider &provider)
Definition hitrace.cpp:413
const QString & asString(const QString &s)
Definition qstring.h:1713
#define qPrintable(string)
Definition qstring.h:1718
#define QStringLiteral(str)
Definition qstring.h:1865