// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "etw.h" #include "provider.h" #include "helpers.h" #include "qtheaders.h" #include #include #include #include using namespace Qt::StringLiterals; static inline QString providerVar(const QString &providerName) { return providerName + "_provider"_L1; } static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field) { const QString &name = field.name; switch (field.backendType) { case Tracepoint::Field::QtString: stream << "TraceLoggingCountedWideString(reinterpret_cast(" << name << ".utf16()), static_cast(" << name << ".size()), \"" << name << "\")"; return; case Tracepoint::Field::QtByteArray: stream << "TraceLoggingBinary(" << name << ".constData(), " << name << ".size(), \"" << name << "\")"; return; case Tracepoint::Field::QtUrl: stream << "TraceLoggingValue(" << name << ".toEncoded().constData(), \"" << name << "\")"; return; case Tracepoint::Field::QtRect: stream << "TraceLoggingValue(" << name << ".x(), \"x\"), " << "TraceLoggingValue(" << name << ".y(), \"y\"), " << "TraceLoggingValue(" << name << ".width(), \"width\"), " << "TraceLoggingValue(" << name << ".height(), \"height\")"; return; case Tracepoint::Field::Pointer: stream << "TraceLoggingPointer(" << name << ", \"" << name << "\")"; return; case Tracepoint::Field::Unknown: // Write down the previously stringified data (like we do for QString). // The string is already created in writeWrapper(). // Variable name is name##Str. stream << "TraceLoggingCountedWideString(reinterpret_cast(" << name << "Str.utf16()), static_cast(" << name << "Str.size()), \"" << name << "\")"; return; default: break; } stream << "TraceLoggingValue(" << name << ", \"" << name << "\")"; } static QString createGuid(const QUuid &uuid) { QString guid; QTextStream stream(&guid); Qt::hex(stream); stream << "(" << "0x" << uuid.data1 << ", " << "0x" << uuid.data2 << ", " << "0x" << uuid.data3 << ", " << "0x" << uuid.data4[0] << ", " << "0x" << uuid.data4[1] << ", " << "0x" << uuid.data4[2] << ", " << "0x" << uuid.data4[3] << ", " << "0x" << uuid.data4[4] << ", " << "0x" << uuid.data4[5] << ", " << "0x" << uuid.data4[6] << ", " << "0x" << uuid.data4[7] << ")"; return guid; } static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider) { QUuid uuid = QUuid::createUuidV5(QUuid(), provider.name.toLocal8Bit()); const QString providerV = providerVar(provider.name); const QString guard = includeGuard(fileName); const QString guid = createGuid(uuid); const QString guidString = uuid.toString(); stream << "#ifndef " << guard << "\n" << "#define " << guard << "\n" << "\n" << "#include \n" << "#include \n" << "\n"; /* TraceLogging API macros cannot deal with UTF8 * source files, so we work around it like this */ stream << "#undef _TlgPragmaUtf8Begin\n" "#undef _TlgPragmaUtf8End\n" "#define _TlgPragmaUtf8Begin\n" "#define _TlgPragmaUtf8End\n"; stream << "\n"; stream << qtHeaders(); stream << "\n"; if (!provider.prefixText.isEmpty()) stream << provider.prefixText.join(u'\n') << "\n\n"; stream << "#ifdef TRACEPOINT_DEFINE\n" << "/* " << guidString << " */\n" << "TRACELOGGING_DEFINE_PROVIDER(" << providerV << ", \"" << provider.name <<"\", " << guid << ");\n\n"; stream << "static inline void registerProvider()\n" << "{\n" << " TraceLoggingRegister(" << providerV << ");\n" << "}\n\n"; stream << "static inline void unregisterProvider()\n" << "{\n" << " TraceLoggingUnregister(" << providerV << ");\n" << "}\n"; stream << "Q_CONSTRUCTOR_FUNCTION(registerProvider)\n" << "Q_DESTRUCTOR_FUNCTION(unregisterProvider)\n\n"; stream << "#else\n" << "TRACELOGGING_DECLARE_PROVIDER(" << providerV << ");\n" << "#endif // TRACEPOINT_DEFINE\n\n"; } static void writeEpilogue(QTextStream &stream, const QString &fileName) { stream << "\n#endif // " << includeGuard(fileName) << "\n" << "#include \n"; } static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, const QString &providerName) { const QString argList = formatFunctionSignature(tracepoint.args); const QString paramList = formatParameterList(tracepoint.args, ETW); const QString &name = tracepoint.name; const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper(); const QString provider = providerVar(providerName); stream << "\n"; stream << "inline void trace_" << name << "(" << argList << ")\n" << "{\n"; // Convert all unknown types to QString's using QDebug. // Note the naming convention: it's field.name##Str for (const Tracepoint::Field &field : tracepoint.fields) { if (field.backendType == Tracepoint::Field::Unknown) { stream << " const QString " << field.name << "Str = QDebug::toString(" << field.name << ");\n"; } } stream << " TraceLoggingWrite(" << provider << ", \"" << name << "\""; for (const Tracepoint::Field &field : tracepoint.fields) { stream << ",\n"; stream << " "; writeEtwMacro(stream, field); } stream << ");\n" << "}\n\n"; stream << "inline void do_trace_" << name << "(" << argList << ")\n" << "{\n" << " trace_" << name << "(" << paramList << ");\n" << "}\n"; stream << "inline bool trace_" << name << "_enabled()\n" << "{\n" << " return TraceLoggingProviderEnabled(" << provider << ", 0, 0);\n" << "}\n"; } static void writeTracepoints(QTextStream &stream, const Provider &provider) { if (provider.tracepoints.isEmpty()) return; const QString includeGuard = QStringLiteral("TP_%1_PROVIDER").arg(provider.name).toUpper(); stream << "#if !defined(" << includeGuard << ") && !defined(TRACEPOINT_DEFINE)\n" << "#define " << includeGuard << "\n" << "QT_BEGIN_NAMESPACE\n" << "namespace QtPrivate {\n"; for (const Tracepoint &t : provider.tracepoints) writeWrapper(stream, t, provider.name); stream << "} // namespace QtPrivate\n" << "QT_END_NAMESPACE\n" << "#endif // " << includeGuard << "\n\n"; } void writeEtw(QFile &file, const Provider &provider) { QTextStream stream(&file); const QString fileName = QFileInfo(file.fileName()).fileName(); writePrologue(stream, fileName, provider); writeTracepoints(stream, provider); writeEpilogue(stream, fileName); }