// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "ctf.h" #include "provider.h" #include "helpers.h" #include "panic.h" #include "qtheaders.h" #include #include #include #include static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider) { writeCommonPrologue(stream); const QString guard = includeGuard(fileName); // include prefix text or qt headers only once stream << "#if !defined(" << guard << ")\n"; stream << qtHeaders(); stream << "\n"; if (!provider.prefixText.isEmpty()) stream << provider.prefixText.join(u'\n') << "\n\n"; stream << "#endif\n\n"; /* the first guard is the usual one, the second is required * by LTTNG to force the re-evaluation of TRACEPOINT_* macros */ stream << "#if !defined(" << guard << ") || defined(TRACEPOINT_HEADER_MULTI_READ)\n"; stream << "#define " << guard << "\n\n" << "#undef TRACEPOINT_INCLUDE\n" << "#define TRACEPOINT_INCLUDE \"" << fileName << "\"\n\n"; stream << "#include \n\n"; const QString namespaceGuard = guard + QStringLiteral("_USE_NAMESPACE"); stream << "#if !defined(" << namespaceGuard << ")\n" << "#define " << namespaceGuard << "\n" << "QT_USE_NAMESPACE\n" << "#endif // " << namespaceGuard << "\n\n"; stream << "TRACEPOINT_PROVIDER(" << provider.name << ");\n\n"; } static void writeEpilogue(QTextStream &stream, const QString &fileName) { stream << "\n"; stream << "#endif // " << includeGuard(fileName) << "\n" << "#include \n"; } static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, const Provider &provider) { const QString argList = formatFunctionSignature(tracepoint.args); const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, CTF); const QString &name = tracepoint.name; const QString includeGuard = QStringLiteral("TP_%1_%2").arg(provider.name).arg(name).toUpper(); /* prevents the redefinion of the inline wrapper functions */ stream << "\n" << "#ifndef " << includeGuard << "\n" << "#define " << includeGuard << "\n" << "QT_BEGIN_NAMESPACE\n" << "namespace QtPrivate {\n"; stream << "inline void trace_" << name << "(" << argList << ")\n" << "{\n" << " tracepoint(" << provider.name << ", " << name << paramList << ");\n" << "}\n"; stream << "inline void do_trace_" << name << "(" << argList << ")\n" << "{\n" << " do_tracepoint(" << provider.name << ", " << name << paramList << ");\n" << "}\n"; stream << "inline bool trace_" << name << "_enabled()\n" << "{\n" << " return tracepoint_enabled(" << provider.name << ", " << name << ");\n" << "}\n"; stream << "} // namespace QtPrivate\n" << "QT_END_NAMESPACE\n" << "#endif // " << includeGuard << "\n\n"; } static void writeMetadataGenerators(QTextStream &stream) { stream << R"CPP( template inline QString integerToMetadata(const QString &name) { QString ret; if (!std::is_signed().value) ret += QLatin1Char('u'); if (sizeof(T) == 8) ret += QStringLiteral("int64_t "); else if (sizeof(T) == 4) ret += QStringLiteral("int32_t "); else if (sizeof(T) == 2) ret += QStringLiteral("int16_t "); else if (sizeof(T) == 1) ret += QStringLiteral("int8_t "); ret += name + QLatin1Char(';'); return ret; } template inline QString integerArrayToMetadata(const QString &size, const QString &name) { QString ret; if (!std::is_signed().value) ret += QLatin1Char('u'); if (sizeof(T) == 8) ret += QStringLiteral("int64_t "); else if (sizeof(T) == 4) ret += QStringLiteral("int32_t "); else if (sizeof(T) == 2) ret += QStringLiteral("int16_t "); else if (sizeof(T) == 1) ret += QStringLiteral("int8_t "); ret += name + QLatin1Char('[') + size + QStringLiteral("];"); return ret; } template inline QString floatToMetadata(const QString &name) { QString ret; if (sizeof(T) == 8) ret += QStringLiteral("double "); else if (sizeof(T) == 4) ret += QStringLiteral("float "); ret += name + QLatin1Char(';'); return ret; } template inline QString floatArrayToMetadata(const QString &size, const QString &name) { QString ret; if (sizeof(T) == 8) ret += QStringLiteral("double "); else if (sizeof(T) == 4) ret += QStringLiteral("float "); ret += name + QLatin1Char('[') + size + QLatin1Char(']'); return ret + QLatin1Char(';'); } inline QString pointerToMetadata(const QString &name) { QString ret; if (QT_POINTER_SIZE == 8) ret += QStringLiteral("intptr64_t "); else if (QT_POINTER_SIZE == 4) ret += QStringLiteral("intptr32_t "); ret += name + QLatin1Char(';'); return ret; } )CPP"; } static void writeTracepoint(QTextStream &stream, const Tracepoint &tracepoint, const QString &providerName) { stream << "TRACEPOINT_EVENT(\n" << " " << providerName << ",\n" << " " << tracepoint.name << ",\n"; const auto checkUnknownArgs = [](const Tracepoint &tracepoint) { for (auto &field : tracepoint.fields) { if (field.backendType == Tracepoint::Field::Unknown) return true; } return false; }; const auto formatType = [](const QString &type) { QString ret = type; if (type.endsWith(QLatin1Char('*')) || type.endsWith(QLatin1Char('&'))) ret = type.left(type.length() - 1).simplified(); if (ret.startsWith(QStringLiteral("const"))) ret = ret.right(ret.length() - 6).simplified(); return typeToTypeName(ret); }; QString eventSize; bool variableSize = false; const bool emptyMetadata = checkUnknownArgs(tracepoint) || tracepoint.args.size() == 0; if (!emptyMetadata) { for (int i = 0; i < tracepoint.args.size(); i++) { auto &arg = tracepoint.args[i]; auto &field = tracepoint.fields[i]; if (i > 0) { stream << " + QStringLiteral(\"\\n\\\n \") + "; eventSize += QStringLiteral(" + "); } const bool array = field.arrayLen > 0; switch (field.backendType) { case Tracepoint::Field::Boolean: { stream << "QStringLiteral(\"Boolean " << arg.name << ";\")"; eventSize += QStringLiteral("sizeof(bool)"); } break; case Tracepoint::Field::Integer: { if (array) { stream << "integerArrayToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\"" << field.arrayLen << "\"), QStringLiteral(\"" << arg.name << "\"))"; } else { stream << "integerToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\"" << arg.name << "\"))"; } eventSize += QStringLiteral("sizeof(") + formatType(arg.type) + QStringLiteral(")"); if (array) eventSize += QStringLiteral(" * ") + QString::number(field.arrayLen); } break; case Tracepoint::Field::Pointer: case Tracepoint::Field::IntegerHex: { stream << "pointerToMetadata(QStringLiteral(\"" << formatType(arg.type) << "_" << arg.name << "\"))"; eventSize += QStringLiteral("QT_POINTER_SIZE"); } break; case Tracepoint::Field::Float: { if (array) { stream << "floatArrayToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\"" << field.arrayLen << "\"), QStringLiteral(\"" << arg.name << "\"))"; } else { stream << "floatToMetadata<" << formatType(arg.type) << ">(QStringLiteral(\"" << arg.name << "\"))"; } eventSize += QStringLiteral("sizeof(") + formatType(arg.type) + QStringLiteral(")"); if (array) eventSize += QStringLiteral(" * ") + QString::number(field.arrayLen); } break; case Tracepoint::Field::QtUrl: case Tracepoint::Field::QtString: case Tracepoint::Field::String: { stream << "QStringLiteral(\"string " << arg.name << ";\")"; eventSize += QStringLiteral("1"); variableSize = true; } break; case Tracepoint::Field::QtRect: { stream << "QStringLiteral(\"int32_t QRect_" << arg.name << "_x;\\n\\\n \")"; stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_y;\\n\\\n \")"; stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_width;\\n\\\n \")"; stream << " + QStringLiteral(\"int32_t QRect_" << arg.name << "_height;\\n\\\n \")"; eventSize += QStringLiteral("16"); } break; case Tracepoint::Field::QtSize: { stream << "QStringLiteral(\"int32_t QSize_" << arg.name << "_width;\\n\\\n \")"; stream << " + QStringLiteral(\"int32_t QSize_" << arg.name << "_height;\\n\\\n \")"; eventSize += QStringLiteral("8"); } break; case Tracepoint::Field::QtRectF: { stream << "QStringLiteral(\"float QRectF_" << arg.name << "_x;\\n\\\n \")"; stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_y;\\n\\\n \")"; stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_width;\\n\\\n \")"; stream << " + QStringLiteral(\"float QRectF_" << arg.name << "_height;\\n\\\n \")"; eventSize += QStringLiteral("16"); } break; case Tracepoint::Field::QtSizeF: { stream << "QStringLiteral(\"float QSizeF_" << arg.name << "_width;\\n\\\n \")"; stream << " + QStringLiteral(\"float QSizeF_" << arg.name << "_height;\\n\\\n \")"; eventSize += QStringLiteral("8"); } break; case Tracepoint::Field::Unknown: break; case Tracepoint::Field::EnumeratedType: { stream << "QStringLiteral(\"" << typeToTypeName(arg.type) << " " << arg.name << ";\")"; eventSize += QString::number(field.enumValueSize / 8); variableSize = true; } break; case Tracepoint::Field::FlagType: { stream << "QStringLiteral(\"uint8_t " << arg.name << "_length;\\n\\\n "; stream << typeToTypeName(arg.type) << " " << arg.name << "[" << arg.name << "_length];\")"; eventSize += QStringLiteral("2"); variableSize = true; } break; case Tracepoint::Field::QtByteArray: case Tracepoint::Field::Sequence: panic("Unhandled type '%s %s", qPrintable(arg.type), qPrintable(arg.name)); break; } } } if (emptyMetadata) stream << "{},\n"; else stream << ",\n"; if (eventSize.length()) stream << eventSize << ", \n"; else stream << "0, \n"; stream << (variableSize ? "true" : "false") << "\n"; stream << ")\n\n"; } static void writeTracepoints(QTextStream &stream, const Provider &provider) { for (const Tracepoint &t : provider.tracepoints) { writeTracepoint(stream, t, provider.name); writeWrapper(stream, t, provider); } } static void writeEnums(QTextStream &stream, const Provider &provider) { for (const auto &e : provider.enumerations) { QString name = e.name; name.replace(QStringLiteral("::"), QStringLiteral("_")); stream << "TRACEPOINT_METADATA(" << provider.name << ", " << name << ", \n"; stream << "QStringLiteral(\"typealias enum : integer { size = " << e.valueSize << "; } {\\n\\\n"; const auto values = e.values; QList handledValues; for (const auto &v : values) { if (handledValues.contains(v.value)) continue; if (v.range) { stream << v.name << " = " << v.value << " ... " << v.range << ", \\n\\\n"; } else { const QString names = aggregateListValues(v.value, values); stream << names << " = " << v.value << ", \\n\\\n"; handledValues.append(v.value); } } stream << "} := " << name << ";\\n\\n\"));\n\n"; } stream << "\n"; } static void writeFlags(QTextStream &stream, const Provider &provider) { for (const auto &e : provider.flags) { QString name = e.name; name.replace(QStringLiteral("::"), QStringLiteral("_")); stream << "TRACEPOINT_METADATA(" << provider.name << ", " << name << ", \n"; stream << "QStringLiteral(\"typealias enum : integer { size = 8; } {\\n\\\n"; const auto values = e.values; QList handledValues; for (const auto &v : values) { if (handledValues.contains(v.value)) continue; const QString names = aggregateListValues(v.value, values); stream << names << " = " << v.value << ", \\n\\\n"; handledValues.append(v.value); } stream << "} := " << name << ";\\n\\n\"));\n\n"; } stream << "\n"; } void writeCtf(QFile &file, const Provider &provider) { QTextStream stream(&file); const QString fileName = QFileInfo(file.fileName()).fileName(); writePrologue(stream, fileName, provider); writeMetadataGenerators(stream); writeEnums(stream, provider); writeFlags(stream, provider); writeTracepoints(stream, provider); writeEpilogue(stream, fileName); }