diff options
author | Antti Määttä <antti.maatta@qt.io> | 2022-10-24 11:30:56 +0300 |
---|---|---|
committer | Antti Määttä <antti.maatta@qt.io> | 2023-01-13 12:43:46 +0200 |
commit | e3458aac6406e5b624cd54e28b5784fb09f07bc3 (patch) | |
tree | 7d91c7015b03d984a78f6ceb90ae82a89611d887 /src/tools/tracegen/ctf.cpp | |
parent | a37a59eea8b010c45da1722a005946a2450363b3 (diff) | |
download | qtbase-e3458aac6406e5b624cd54e28b5784fb09f07bc3.tar.gz |
Add CTF tracing backend
Implement platform independent tracing backend in Common trace format.
This allows tracing in platforms without own/existing backend and
analysing all platforms with the same tooling. The backend is the basis
for further work in application level profiling area.
The backend is implemented as a plugin that is loaded immediately when
the application starts in order to process all trace events. The backend
avoids using Qt classes so that it doesn't generate trace events
itself. Adds plumbing to configure the new backend.
Modifies the tracegen and tracepointgen tools to support the new
backend.
Task-number: QTBUG-106399
Pick-to: 6.5
Change-Id: I80711be52d4d48e1acbc72edffbdf3f379fce52a
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src/tools/tracegen/ctf.cpp')
-rw-r--r-- | src/tools/tracegen/ctf.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/tools/tracegen/ctf.cpp b/src/tools/tracegen/ctf.cpp new file mode 100644 index 0000000000..f7f2b399a0 --- /dev/null +++ b/src/tools/tracegen/ctf.cpp @@ -0,0 +1,278 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "ctf.h" +#include "provider.h" +#include "helpers.h" +#include "panic.h" +#include "qtheaders.h" + +#include <qfile.h> +#include <qfileinfo.h> +#include <qtextstream.h> +#include <qdebug.h> + + +static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider) +{ + 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 <private/qctf_p.h>\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 <private/qtrace_p.h>\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 + * once LTTNG recursively includes this header file + */ + 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 writeTracepoint(QTextStream &stream, + const Tracepoint &tracepoint, const QString &providerName) +{ + stream << "TRACEPOINT_EVENT(\n" + << " " << providerName << ",\n" + << " " << tracepoint.name << ",\n"; + stream << "\""; + + const auto checkUnknownArgs = [](const Tracepoint &tracepoint) { + for (auto &field : tracepoint.fields) { + if (field.backendType.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 typeToName(ret); + }; + int eventSize = 0; + bool variableSize = false; + if (!checkUnknownArgs(tracepoint)) { + for (int i = 0; i < tracepoint.args.size(); i++) { + auto &arg = tracepoint.args[i]; + auto &field = tracepoint.fields[i]; + if (i > 0) { + stream << " \\n\\\n"; + stream << " "; + } + const bool array = field.arrayLen > 0; + switch (field.backendType.backendType) { + case Tracepoint::Field::Boolean: { + stream << "Boolean " << arg.name << ";"; + eventSize += 8; + } break; + case Tracepoint::Field::Integer: { + if (!field.backendType.isSigned) + stream << "u"; + stream << "int" << field.backendType.bits << "_t "; + if (array) + stream << arg.name << "[" << field.arrayLen << "];"; + else + stream << arg.name << ";"; + eventSize += field.backendType.bits * qMax(1, field.arrayLen); + } break; + case Tracepoint::Field::Pointer: { + if (QT_POINTER_SIZE == 8) + stream << "intptr64_t " << formatType(arg.type) << "_" << arg.name << ";"; + else + stream << "intptr32_t " << formatType(arg.type) << "_" << arg.name << ";"; + eventSize += QT_POINTER_SIZE * 8; + } break; + case Tracepoint::Field::IntegerHex: { + if (field.backendType.bits == 64) + stream << "intptr64_t " << formatType(arg.name) << ";"; + else + stream << "intptr32_t " << formatType(arg.name) << ";"; + eventSize += field.backendType.bits; + } break; + case Tracepoint::Field::Float: { + if (field.backendType.bits == 32) + stream << "float " << arg.name; + else + stream << "double " << arg.name; + if (array) { + stream << "[" << field.arrayLen << "];"; + } else { + stream << ";"; + } + eventSize += field.backendType.bits * qMax(1, field.arrayLen); + } break; + case Tracepoint::Field::String: { + stream << "string " << arg.name << ";"; + eventSize += 8; + variableSize = true; + } break; + case Tracepoint::Field::QtString: { + stream << "string " << arg.name << ";"; + eventSize += 8; + variableSize = true; + } break; + case Tracepoint::Field::QtByteArray: + break; + case Tracepoint::Field::QtUrl: { + stream << "string " << arg.name << ";"; + eventSize += 8; + variableSize = true; + } break; + case Tracepoint::Field::QtRect: { + stream << "int32_t QRect_" << arg.name << "_x;\\n\\\n "; + stream << "int32_t QRect_" << arg.name << "_y;\\n\\\n "; + stream << "int32_t QRect_" << arg.name << "_width;\\n\\\n "; + stream << "int32_t QRect_" << arg.name << "_height;"; + eventSize += 32 * 4; + } break; + case Tracepoint::Field::QtSize: { + stream << "int32_t QSize_" << arg.name << "_width;\\n\\\n "; + stream << "int32_t QSize_" << arg.name << "_height;"; + eventSize += 32 * 2; + } break; + case Tracepoint::Field::Unknown: + break; + case Tracepoint::Field::EnumeratedType: { + QString type = arg.type; + type.replace(QStringLiteral("::"), QStringLiteral("_")); + stream << type << " " << arg.name << ";"; + eventSize += field.backendType.bits; + variableSize = true; + } break; + case Tracepoint::Field::FlagType: { + QString type = arg.type; + type.replace(QStringLiteral("::"), QStringLiteral("_")); + stream << "uint8_t " << arg.name << "_length;\\n\\\n "; + stream << type << " " << arg.name << "[" << arg.name << "_length];"; + eventSize += 16; + } break; + case Tracepoint::Field::Sequence: + panic("Unhandled sequence '%s %s", qPrintable(arg.type), qPrintable(arg.name)); + break; + } + } + } + + stream << "\",\n"; + stream << eventSize / 8 << ", \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 << "\"typealias enum : integer { size = " << e.valueSize << "; } {\\n\\\n"; + for (const auto &v : e.values) { + if (v.range) + stream << v.name << " = " << v.value << " ... " << v.range << ", \\n\\\n"; + else + stream << v.name << " = " << v.value << ", \\n\\\n"; + } + 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 << "\"typealias enum : integer { size = 8; } {\\n\\\n"; + for (const auto &v : e.values) { + stream << v.name << " = " << v.value << ", \\n\\\n"; + } + 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); + writeEnums(stream, provider); + writeFlags(stream, provider); + writeTracepoints(stream, provider); + writeEpilogue(stream, fileName); +} |