diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index db612631167..2b78eb4e8f0 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -709,7 +709,7 @@ function(qt_internal_create_tracepoints name tracepoints_file) set(header_filename "${provider_name}_tracepoints_p.h") set(header_path "${CMAKE_CURRENT_BINARY_DIR}/${header_filename}") - if(QT_FEATURE_lttng OR QT_FEATURE_etw) + if(QT_FEATURE_lttng OR QT_FEATURE_etw OR QT_FEATURE_ctf) set(source_path "${CMAKE_CURRENT_BINARY_DIR}/${provider_name}_tracepoints.cpp") qt_configure_file(OUTPUT "${source_path}" CONTENT "#define TRACEPOINT_CREATE_PROBES @@ -723,6 +723,8 @@ function(qt_internal_create_tracepoints name tracepoints_file) target_link_libraries(${name} PRIVATE LTTng::UST) elseif(QT_FEATURE_etw) set(tracegen_arg "etw") + elseif(QT_FEATURE_ctf) + set(tracegen_arg "ctf") endif() if(NOT "${QT_HOST_PATH}" STREQUAL "") @@ -754,7 +756,7 @@ function(qt_internal_generate_tracepoints name provider) set(header_filename "${provider_name}_tracepoints_p.h") set(header_path "${CMAKE_CURRENT_BINARY_DIR}/${header_filename}") - if(QT_FEATURE_lttng OR QT_FEATURE_etw) + if(QT_FEATURE_lttng OR QT_FEATURE_etw OR QT_FEATURE_ctf) set(absolute_file_paths "") foreach(file IN LISTS arg_SOURCES) @@ -791,6 +793,8 @@ function(qt_internal_generate_tracepoints name provider) target_link_libraries(${name} PRIVATE LTTng::UST) elseif(QT_FEATURE_etw) set(tracegen_arg "etw") + elseif(QT_FEATURE_ctf) + set(tracegen_arg "ctf") endif() if(NOT "${QT_HOST_PATH}" STREQUAL "") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87232fc6643..52371c84bf9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,9 @@ endif() qt_install_3rdparty_library_wrap_config_extra_file(BundledZLIB) add_subdirectory(corelib) +if (QT_FEATURE_ctf AND QT_FEATURE_library) + add_subdirectory(corelib/tracing) +endif() # Needs to be after corelib, because some of them reference Core. add_subdirectory(3rdparty) diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 8742a646f1d..3053cfaf94c 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -1318,6 +1318,13 @@ qt_internal_extend_target(Core CONDITION WASM kernel/qeventdispatcher_wasm.cpp kernel/qeventdispatcher_wasm_p.h ) +qt_internal_extend_target(Core CONDITION QT_FEATURE_ctf AND QT_FEATURE_library + SOURCES + tracing/qctf_p.h tracing/qctf.cpp + PLUGIN_TYPES + tracing +) + set_source_files_properties( thread/qmutex_mac.cpp thread/qmutex_unix.cpp diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 14ea626def0..8be7d61c5c4 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -925,6 +925,12 @@ qt_feature("etw" PRIVATE ENABLE INPUT_trace STREQUAL 'etw' OR ( INPUT_trace STREQUAL 'yes' AND WIN32 ) DISABLE INPUT_trace STREQUAL 'lttng' OR INPUT_trace STREQUAL 'no' ) +qt_feature("ctf" PRIVATE + LABEL "CTF" + AUTODETECT OFF + ENABLE INPUT_trace STREQUAL 'ctf' + DISABLE INPUT_trace STREQUAL 'etw' OR INPUT_trace STREQUAL 'no' OR INPUT_trace STREQUAL 'lttng' +) qt_feature("forkfd_pidfd" PRIVATE LABEL "CLONE_PIDFD support in forkfd" CONDITION LINUX @@ -962,7 +968,7 @@ qt_configure_add_summary_entry(ARGS "mimetype-database") qt_configure_add_summary_entry(ARGS "cpp-winrt") qt_configure_add_summary_entry( TYPE "firstAvailableFeature" - ARGS "etw lttng" + ARGS "etw lttng ctf" MESSAGE "Tracing backend" ) qt_configure_add_summary_section(NAME "Logging backends") diff --git a/src/corelib/global/qtrace_p.h b/src/corelib/global/qtrace_p.h index f548afd91c1..4b96916c97f 100644 --- a/src/corelib/global/qtrace_p.h +++ b/src/corelib/global/qtrace_p.h @@ -57,7 +57,7 @@ * qcoreapplication_qrect(const QRect &rect) * * The provider file is then parsed by src/tools/tracegen, which can be - * switched to output either ETW or LTTNG tracepoint definitions. The provider + * switched to output either ETW, CTF or LTTNG tracepoint definitions. The provider * name is deduced to be basename(provider_file). * * To use the above (inside qtcore), you need to include diff --git a/src/corelib/qt_cmdline.cmake b/src/corelib/qt_cmdline.cmake index c82d77a60d4..fecc43cce6d 100644 --- a/src/corelib/qt_cmdline.cmake +++ b/src/corelib/qt_cmdline.cmake @@ -15,4 +15,4 @@ qt_commandline_option(posix-ipc TYPE boolean NAME ipc_posix) qt_commandline_option(pps TYPE boolean NAME qqnx_pps) qt_commandline_option(slog2 TYPE boolean) qt_commandline_option(syslog TYPE boolean) -qt_commandline_option(trace TYPE optionalString VALUES etw lttng no yes) +qt_commandline_option(trace TYPE optionalString VALUES etw lttng ctf no yes) diff --git a/src/corelib/tracing/CMakeLists.txt b/src/corelib/tracing/CMakeLists.txt new file mode 100644 index 00000000000..c8723609783 --- /dev/null +++ b/src/corelib/tracing/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(make_includable input_file output_file) + get_filename_component(infile "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}" ABSOLUTE) + set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${output_file}) + file(READ ${infile} content) + set(content "R\"(${content})\"") + file(WRITE ${outfile} "${content}") +endfunction(make_includable) + +make_includable(metadata_template.txt metadata_template.h) + +qt_internal_add_plugin(QCtfTracePlugin + SHARED + CLASS_NAME QCtfTracePlugin + PLUGIN_TYPE tracing + SOURCES + qctflib_p.h qctflib.cpp metadata_template.txt qctfplugin.cpp qctfplugin_p.h + LIBRARIES + Qt6::Core +) + diff --git a/src/corelib/tracing/metadata_template.txt b/src/corelib/tracing/metadata_template.txt new file mode 100644 index 00000000000..5f27e79da50 --- /dev/null +++ b/src/corelib/tracing/metadata_template.txt @@ -0,0 +1,77 @@ +/* CTF 1.8 */ + +typealias integer { size = 8; align = 8; signed = false; } := uint8_t; +typealias integer { size = 16; align = 8; signed = false; } := uint16_t; +typealias integer { size = 32; align = 8; signed = false; } := uint32_t; +typealias integer { size = 64; align = 8; signed = false; } := uint64_t; +typealias integer { size = 8; align = 8; signed = true; } := int8_t; +typealias integer { size = 16; align = 8; signed = true; } := int16_t; +typealias integer { size = 32; align = 8; signed = true; } := int32_t; +typealias integer { size = 64; align = 8; signed = true; } := int64_t; +typealias integer { size = 32; align = 8; signed = true; base = 16; } := intptr32_t; +typealias integer { size = 64; align = 8; signed = true; base = 16; } := intptr64_t; +typealias floating_point { exp_dig = 8; mant_dig = 24; align = 8; byte_order = native; } := float; +typealias floating_point { exp_dig = 11; mant_dig = 53; align = 8; byte_order = native; } := double; + +typealias enum : integer { size = 8; } { + false, + true +} := Boolean; + +trace { + major = 1; + minor = 8; + uuid = "$TRACE_UUID"; + byte_order = $ENDIANNESS; + packet.header := struct { + uint32_t magic; + uint8_t uuid[16]; + uint32_t stream_id; + } align(8); +}; + +env { + domain = "ust"; + tracer_name = "qtctf"; + tracer_major = 1; + tracer_minor = 0; + architecture_bit_width = $ARC_BIT_WIDTH; + trace_name = "$SESSION_NAME"; + trace_creation_datetime = "$CREATION_TIME"; + hostname = "$HOST_NAME"; +}; + +clock { + name = "$CLOCK_NAME"; + uuid = "53836526-5a62-4de0-93c8-3a1970afab23"; + description = "$CLOCK_TYPE"; + freq = $CLOCK_FREQUENCY; + offset = $CLOCK_OFFSET; +}; + +typealias integer { + size = 64; align = 8; signed = false; + map = clock.monotonic.value; +} := uint64_clock_monotonic_t; + +struct packet_context { + uint64_clock_monotonic_t timestamp_begin; + uint64_clock_monotonic_t timestamp_end; + uint64_t content_size; + uint64_t packet_size; + uint64_t packet_seq_num; + uint64_t events_discarded; + uint32_t thread_id; + string thread_name; +} align(8); + +struct event_header { + uint32_t id; + uint64_clock_monotonic_t timestamp; +} align(8); + +stream { + id = 0; + event.header := struct event_header; + packet.context := struct packet_context; +}; diff --git a/src/corelib/tracing/qctf.cpp b/src/corelib/tracing/qctf.cpp new file mode 100644 index 00000000000..22bb587acd4 --- /dev/null +++ b/src/corelib/tracing/qctf.cpp @@ -0,0 +1,119 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#define BUILD_LIBRARY + +#include +#include +#include +#include + +#include "qctflib_p.h" + +QT_BEGIN_NAMESPACE + +static bool s_initialized = false; +static bool s_triedLoading = false; +static bool s_prevent_recursion = false; +static QCtfLib* s_plugin = nullptr; + +#if defined(Q_OS_ANDROID) +static QString findPlugin(const QString &plugin) +{ + QString pluginPath = QString::fromUtf8(qgetenv("QT_PLUGIN_PATH")); + QDir dir(pluginPath); + const QStringList files = dir.entryList(QDir::Files); + for (const QString &file : files) { + if (file.contains(plugin)) + return QFileInfo(pluginPath + QLatin1Char('/') + file).absoluteFilePath(); + } + return {}; +} +#endif + +static bool loadPlugin(bool &retry) +{ + retry = false; +#ifdef Q_OS_WIN +#ifdef QT_DEBUG + QPluginLoader loader(QStringLiteral("tracing/QCtfTracePlugind.dll")); +#else + QPluginLoader loader(QStringLiteral("tracing/QCtfTracePlugin.dll")); +#endif +#elif defined(Q_OS_ANDROID) + + QString plugin = findPlugin(QStringLiteral("QCtfTracePlugin")); + if (plugin.isEmpty()) { + retry = true; + return false; + } + QPluginLoader loader(plugin); +#else + QPluginLoader loader(QStringLiteral("tracing/libQCtfTracePlugin.so")); +#endif + + if (!loader.isLoaded()) { + if (!loader.load()) + return false; + } + s_plugin = qobject_cast(loader.instance()); + if (!s_plugin) + return false; + QObject *obj = loader.instance(); + if (obj) { + QObject::connect(obj, &QObject::destroyed, []() { + s_plugin = nullptr; + }); + } + return true; +} + +static bool initialize() +{ + if (s_prevent_recursion) + return false; + if (s_initialized || s_triedLoading) + return s_initialized; + s_prevent_recursion = true; + bool retry = false; + if (!loadPlugin(retry)) { + if (!retry) { + s_triedLoading = true; + s_initialized = false; + } + } else { + bool enabled = s_plugin->sessionEnabled(); + if (!enabled) { + s_triedLoading = true; + s_initialized = false; + } else { + s_initialized = true; + } + } + s_prevent_recursion = false; + return s_initialized; +} + +bool _tracepoint_enabled(const QCtfTracePointEvent &point) +{ + if (!initialize()) + return false; + return s_plugin ? s_plugin->tracepointEnabled(point) : false; +} + +void _do_tracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) +{ + if (!initialize()) + return; + if (s_plugin) + s_plugin->doTracepoint(point, arr); +} + +QCtfTracePointPrivate *_initialize_tracepoint(const QCtfTracePointEvent &point) +{ + if (!initialize()) + return nullptr; + return s_plugin ? s_plugin->initializeTracepoint(point) : nullptr; +} + +QT_END_NAMESPACE diff --git a/src/corelib/tracing/qctf_p.h b/src/corelib/tracing/qctf_p.h new file mode 100644 index 00000000000..10cec632f1e --- /dev/null +++ b/src/corelib/tracing/qctf_p.h @@ -0,0 +1,225 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef Q_CTF_H +#define Q_CTF_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include + +QT_REQUIRE_CONFIG(library); + +QT_BEGIN_NAMESPACE + +struct QCtfTraceMetadata; +struct Q_CORE_EXPORT QCtfTracePointProvider +{ + const QString provider; + QCtfTraceMetadata *metadata; + QCtfTracePointProvider(const QString &provider) + : provider(provider), metadata(nullptr) + { + + } +}; + +struct Q_CORE_EXPORT QCtfTraceMetadata +{ + const QString name; + const QString metadata; + QCtfTraceMetadata(QCtfTracePointProvider &provider, const QString &name, const QString &metadata) + : name(name), metadata(metadata) + { + next = provider.metadata; + provider.metadata = this; + } + QCtfTraceMetadata *next = nullptr; +}; + +struct QCtfTracePointPrivate; +struct Q_CORE_EXPORT QCtfTracePointEvent +{ + const QCtfTracePointProvider &provider; + const QString eventName; + const QString metadata; + const int size; + const bool variableSize; + + QCtfTracePointEvent(const QCtfTracePointProvider &provider, const QString &name, const QString &metadata, int size, bool variableSize) + : provider(provider), eventName(name), metadata(metadata), size(size), variableSize(variableSize) + { + } + QCtfTracePointPrivate *d = nullptr; +}; + + + +Q_CORE_EXPORT bool _tracepoint_enabled(const QCtfTracePointEvent &point); +Q_CORE_EXPORT void _do_tracepoint(const QCtfTracePointEvent &point, const QByteArray &arr); +Q_CORE_EXPORT QCtfTracePointPrivate *_initialize_tracepoint(const QCtfTracePointEvent &point); + +#ifndef BUILD_LIBRARY +#include +#include +#include +namespace trace { +inline void toByteArray(QByteArray &) +{ +} + +inline void toByteArray(QByteArray &arr, const QString &value) +{ + arr.append(value.toUtf8()); + arr.append((char)0); +} + +inline void toByteArray(QByteArray &arr, const QUrl &value) +{ + arr.append(value.toString().toUtf8()); + arr.append((char)0); +} + +inline void toByteArray(QByteArray &arr, const QByteArray &data) +{ + arr.append(data); +} + +template +inline void toByteArray(QByteArray &arr, T value) +{ + arr.append((char *)&value, sizeof(value)); +} + +template +inline void toByteArray(QByteArray &arr, T value, Ts... args) +{ + toByteArray(arr, value); + toByteArray(arr, args...); +} + +inline QByteArray toByteArray() +{ + return {}; +} + +template +inline QByteArray toByteArray(Ts... args) +{ + QByteArray data; + toByteArray(data, args...); + return data; +} + +template +inline QByteArray toByteArrayFromArray(const T *values, int arraySize) +{ + QByteArray data; + data.append((char *)values, arraySize * sizeof(T)); + return data; +} + +template +inline QByteArray toByteArrayFromEnum(T value) +{ + IntegerType e = static_cast(value); + QByteArray data; + data.append((char *)&e, sizeof(e)); + return data; +} + +inline QByteArray toByteArrayFromCString(const char *str) +{ + QByteArray data; + if (str && *str != 0) + data.append(str); + data.append((char)0); + return data; +} + +static inline void appendFlags(QByteArray &data, quint8 &count, quint32 value) +{ + count = 0; + quint8 d = 1; + while (value) { + if (value&1) { + data.append(d); + count++; + } + d++; + value >>= 1; + } +} + +template +inline QByteArray toByteArrayFromFlags(QFlags value) +{ + quint32 intValue = static_cast(value.toInt()); + quint8 count; + QByteArray data; + data.append((char)0); + if (intValue == 0) { + data.append((char)0); + data.data()[0] = 1; + } else { + appendFlags(data, count, intValue); + data.data()[0] = count; + } + return data; +} + +} // trace + +#define _DEFINE_EVENT(provider, event, metadata, size, varSize) \ + static QCtfTracePointEvent _ctf_ ## event = QCtfTracePointEvent(_ctf_provider_ ## provider, QStringLiteral(QT_STRINGIFY(event)), QStringLiteral(metadata), size, varSize); +#define _DEFINE_METADATA(provider, name, metadata) \ + static QCtfTraceMetadata _ctf_metadata_ ## name = QCtfTraceMetadata(_ctf_provider_ ## provider, QStringLiteral(QT_STRINGIFY(name)), QStringLiteral(metadata)); +#define _DEFINE_TRACEPOINT_PROVIDER(provider) \ + static QCtfTracePointProvider _ctf_provider_ ## provider = QCtfTracePointProvider(QStringLiteral(QT_STRINGIFY(provider))); + +#define TRACEPOINT_EVENT(provider, event, metadata, size, varSize) \ + _DEFINE_EVENT(provider, event, metadata, size, varSize) + +#define TRACEPOINT_PROVIDER(provider) \ + _DEFINE_TRACEPOINT_PROVIDER(provider) + +#define TRACEPOINT_METADATA(provider, name, metadata) \ + _DEFINE_METADATA(provider, name, metadata) + +#define tracepoint_enabled(provider, event) \ + _tracepoint_enabled(_ctf_ ## event) + +#define do_tracepoint(provider, event, ...) \ +{ \ + auto &tp = _ctf_ ## event; \ + if (!tp.d) \ + tp.d = _initialize_tracepoint(tp); \ + if (tp.d) { \ + QByteArray data(tp.size, 0); \ + if (!tp.metadata.isEmpty()) \ + data = trace::toByteArray(__VA_ARGS__); \ + _do_tracepoint(tp, data); \ + } \ +} + +#define tracepoint(provider, name, ...) \ + do { \ + if (tracepoint_enabled(provider, name)) \ + do_tracepoint(provider, name, __VA_ARGS__); \ + } while (0) + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/tracing/qctflib.cpp b/src/corelib/tracing/qctflib.cpp new file mode 100644 index 00000000000..ea84e7938b6 --- /dev/null +++ b/src/corelib/tracing/qctflib.cpp @@ -0,0 +1,303 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#define BUILD_LIBRARY +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qctflib_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcDebugTrace, "qt.core.ctf"); + +#define PACKET_SIZE (4096) +#define PACKET_HEADER_SIZE (24 + 6 * 8 + 4) + +static const char s_trace_metadata_template[] = +#include "metadata_template.h" +; +static const int s_trace_metadata_size = sizeof(s_trace_metadata_template); + +template +QByteArray &operator << (QByteArray &arr, T val) +{ + arr.append((char *)&val, sizeof(val)); + return arr; +} + +QCtfLibImpl *QCtfLibImpl::s_instance = nullptr; + +QCtfLib *QCtfLibImpl::instance() +{ + if (!s_instance) + s_instance = new QCtfLibImpl(); + return s_instance; +} + +QCtfLibImpl::QCtfLibImpl() +{ + QString location = QString::fromUtf8(qgetenv("QTRACE_LOCATION")); + if (location.isEmpty()) { + qCWarning (lcDebugTrace) << "QTRACE_LOCATION not set"; + return; + } + FILE *file = nullptr; + file = fopen(qPrintable(location + QStringLiteral("/session.json")), "rb"); + if (!file) { + qCWarning (lcDebugTrace) << "unable to open session file: " << (location + QStringLiteral("/session.json")); + m_location = location; + m_session.tracepoints.append(QStringLiteral("all")); + m_session.name = QStringLiteral("default"); + } else { + QByteArray data; + fseek(file, 0, SEEK_END); + long pos = ftell(file); + fseek(file, 0, SEEK_SET); + data.resize(pos); + long size = (long)fread(data.data(), pos, 1, file); + fclose(file); + if (size != 1) + return; + QJsonDocument json(QJsonDocument::fromJson(data)); + + QJsonObject obj = json.object(); + QJsonValue value = *obj.begin(); + if (value.isNull() || !value.isArray()) + return; + m_session.name = obj.begin().key(); + QJsonArray arr = value.toArray(); + for (auto var : arr) + m_session.tracepoints.append(var.toString()); + + m_location = location + QStringLiteral("/ust"); + } + m_session.all = m_session.tracepoints.contains(QStringLiteral("all")); + + auto datetime = QDateTime::currentDateTime(); + QString mhn = QSysInfo::machineHostName(); + QString metadata = QString::fromUtf8(s_trace_metadata_template, s_trace_metadata_size); + metadata.replace(QStringLiteral("$TRACE_UUID"), s_TraceUuid.toString(QUuid::WithoutBraces)); + metadata.replace(QStringLiteral("$ARC_BIT_WIDTH"), QString::number(Q_PROCESSOR_WORDSIZE * 8)); + metadata.replace(QStringLiteral("$SESSION_NAME"), m_session.name); + metadata.replace(QStringLiteral("$CREATION_TIME"), datetime.toString()); + metadata.replace(QStringLiteral("$HOST_NAME"), mhn); + metadata.replace(QStringLiteral("$CLOCK_FREQUENCY"), m_timer.isMonotonic() ? QStringLiteral("1000000000") : QStringLiteral("1000")); + metadata.replace(QStringLiteral("$CLOCK_NAME"), m_timer.isMonotonic() ? QStringLiteral("monotonic") : QStringLiteral("system")); + metadata.replace(QStringLiteral("$CLOCK_TYPE"), m_timer.isMonotonic() ? QStringLiteral("Monotonic clock") : QStringLiteral("System clock")); + metadata.replace(QStringLiteral("$CLOCK_OFFSET"), QString::number(datetime.toMSecsSinceEpoch() * 1000000)); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("be")); +#else + metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("le")); +#endif + writeMetadata(metadata.toUtf8(), true); + + m_timer.start(); +} + +void QCtfLibImpl::writeMetadata(const QByteArray &data, bool overwrite) +{ + FILE *file = nullptr; + file = fopen(qPrintable(m_location + QStringLiteral("/metadata")), overwrite ? "w+b": "ab"); + if (!file) + return; + + if (!overwrite) + fputs("\n", file); + fwrite(data.data(), data.size() - 1, 1, file); + fclose(file); +} + +void QCtfLibImpl::writeCtfPacket(QCtfLibImpl::Channel &ch) +{ + FILE *file = nullptr; + file = fopen(ch.channelName, "ab"); + if (file) { + /* Each packet contains header and context, which are defined in the metadata.txt */ + QByteArray packet; + packet << s_CtfHeaderMagic; + /* Uuid is array of bytes hence implicitely big endian. */ + packet << qToBigEndian(s_TraceUuid.data1); + packet << qToBigEndian(s_TraceUuid.data2); + packet << qToBigEndian(s_TraceUuid.data3); + for (int i = 0; i < 8; i++) + packet << s_TraceUuid.data4[i]; + + packet << quint32(0); + packet << ch.minTimestamp; + packet << ch.maxTimestamp; + packet << quint64(ch.data.size() + PACKET_HEADER_SIZE + ch.threadNameLength) * 8u; + packet << quint64(PACKET_SIZE) * 8u; + packet << ch.seqnumber++; + packet << quint64(0); + packet << ch.threadIndex; + if (ch.threadName.size()) + packet.append(ch.threadName); + packet << (char)0; + + Q_ASSERT(ch.data.size() + PACKET_HEADER_SIZE + ch.threadNameLength <= PACKET_SIZE); + Q_ASSERT(packet.size() == PACKET_HEADER_SIZE + ch.threadNameLength); + fwrite(packet.data(), packet.size(), 1, file); + ch.data.resize(PACKET_SIZE - packet.size(), 0); + fwrite(ch.data.data(), ch.data.size(), 1, file); + } + fclose(file); +} + +QCtfLibImpl::~QCtfLibImpl() +{ + qDeleteAll(m_eventPrivs); +} + +bool QCtfLibImpl::tracepointEnabled(const QCtfTracePointEvent &point) +{ + return m_session.all || m_session.tracepoints.contains(point.provider.provider); +} + +QCtfLibImpl::Channel::~Channel() +{ + if (data.size()) + QCtfLibImpl::writeCtfPacket(*this); +} + +static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId) +{ +/* + generates event structure: +event { + name = provider:tracepoint_name; + id = eventId; + stream_id = 0; + loglevel = 13; + fields := struct { + metadata + }; +}; +*/ + QString ret; + ret = QStringLiteral("event {\n name = \"") + provider + QLatin1Char(':') + name + QStringLiteral("\";\n"); + ret += QStringLiteral(" id = ") + QString::number(eventId) + QStringLiteral(";\n"); + ret += QStringLiteral(" stream_id = 0;\n loglevel = 13;\n fields := struct {\n "); + ret += metadata + QStringLiteral("\n };\n};\n"); + return ret; +} + +QCtfTracePointPrivate *QCtfLibImpl::initializeTracepoint(const QCtfTracePointEvent &point) +{ + QMutexLocker lock(&m_mutex); + QCtfTracePointPrivate *priv = point.d; + if (!point.d) { + if (const auto &it = m_eventPrivs.find(point.eventName); it != m_eventPrivs.end()) { + priv = *it; + } else { + priv = new QCtfTracePointPrivate(); + m_eventPrivs.insert(point.eventName, priv); + priv->id = eventId(); + priv->metadata = toMetadata(point.provider.provider, point.eventName, point.metadata, priv->id); + } + } + return priv; +} + +void QCtfLibImpl::doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) +{ + QCtfTracePointPrivate *priv = point.d; + quint64 timestamp = 0; + QThread *thread = nullptr; + { + QMutexLocker lock(&m_mutex); + if (!priv->metadataWritten) { + priv->metadataWritten = true; + auto providerMetadata = point.provider.metadata; + while (providerMetadata) { + registerMetadata(*providerMetadata); + providerMetadata = providerMetadata->next; + } + if (m_newAdditionalMetadata.size()) { + for (const QString &name : m_newAdditionalMetadata) + writeMetadata(m_additionalMetadata[name]->metadata.toUtf8()); + m_newAdditionalMetadata.clear(); + } + writeMetadata(priv->metadata.toUtf8()); + } + timestamp = m_timer.nsecsElapsed(); + } + if (arr.size() != point.size) { + if (arr.size() < point.size) + return; + if (arr.size() > point.size && !point.variableSize && !point.metadata.isEmpty()) + return; + } + + thread = QThread::currentThread(); + if (thread == nullptr) + return; + + Channel &ch = m_threadData.localData(); + + if (ch.channelName[0] == 0) { + m_threadIndices.insert(thread, m_threadIndices.size()); + sprintf(ch.channelName, "%s/channel_%d", qPrintable(m_location), m_threadIndices[thread]); + FILE *f = nullptr; + f = fopen(ch.channelName, "wb"); + fclose(f); + ch.minTimestamp = ch.maxTimestamp = timestamp; + ch.thread = thread; + ch.threadIndex = m_threadIndices[thread]; + ch.threadName = thread->objectName().toUtf8(); + if (ch.threadName.isEmpty()) { + const QMetaObject *obj = thread->metaObject(); + ch.threadName = QByteArray(obj->className()); + } + ch.threadNameLength = ch.threadName.size() + 1; + } + if (ch.locked) + return; + Q_ASSERT(ch.thread == thread); + ch.locked = true; + + QByteArray event; + event << priv->id << timestamp; + if (!point.metadata.isEmpty()) + event.append(arr); + + if (ch.threadNameLength + ch.data.size() + event.size() + PACKET_HEADER_SIZE >= PACKET_SIZE) { + writeCtfPacket(ch); + ch.data = event; + ch.minTimestamp = ch.maxTimestamp = timestamp; + } else { + ch.data.append(event); + } + + ch.locked = false; + ch.maxTimestamp = timestamp; +} + +bool QCtfLibImpl::sessionEnabled() +{ + return !m_session.name.isEmpty(); +} + +int QCtfLibImpl::eventId() +{ + return m_eventId++; +} + +void QCtfLibImpl::registerMetadata(const QCtfTraceMetadata &metadata) +{ + if (m_additionalMetadata.contains(metadata.name)) + return; + + m_additionalMetadata.insert(metadata.name, &metadata); + m_newAdditionalMetadata.insert(metadata.name); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tracing/qctflib_p.h b/src/corelib/tracing/qctflib_p.h new file mode 100644 index 00000000000..0f2838515c7 --- /dev/null +++ b/src/corelib/tracing/qctflib_p.h @@ -0,0 +1,107 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QT_CTFLIB_H +#define QT_CTFLIB_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include "qctf_p.h" +#include "qctfplugin_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcDebugTrace) + +struct QCtfTracePointPrivate +{ + QString metadata; + quint32 id = 0; + quint32 payloadSize = 0; + bool metadataWritten = false; +}; + +class QCtfLibImpl : public QCtfLib +{ + struct Session + { + QString name; + QStringList tracepoints; + bool all = false; + }; + struct Channel + { + char channelName[512]; + QByteArray data; + quint64 minTimestamp = 0; + quint64 maxTimestamp = 0; + quint64 seqnumber = 0; + QThread *thread = nullptr; + quint32 threadIndex = 0; + QByteArray threadName; + quint32 threadNameLength = 0; + bool locked = false; + Channel() + { + memset(channelName, 0, sizeof(channelName)); + } + + ~Channel(); + }; + +public: + QCtfLibImpl(); + ~QCtfLibImpl(); + + bool tracepointEnabled(const QCtfTracePointEvent &point) override; + void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) override; + bool sessionEnabled() override; + QCtfTracePointPrivate *initializeTracepoint(const QCtfTracePointEvent &point) override; + void registerMetadata(const QCtfTraceMetadata &metadata); + int eventId(); + + static QCtfLib *instance(); +private: + static QCtfLibImpl *s_instance; + QHash m_eventPrivs; + void updateMetadata(const QCtfTracePointEvent &point); + void writeMetadata(const QByteArray &data, bool overwrite = false); + static void writeCtfPacket(Channel &ch); + + static constexpr QUuid s_TraceUuid = QUuid(0x3e589c95, 0xed11, 0xc159, 0x42, 0x02, 0x6a, 0x9b, 0x02, 0x00, 0x12, 0xac); + static constexpr quint32 s_CtfHeaderMagic = 0xC1FC1FC1; + + QMutex m_mutex; + QElapsedTimer m_timer; + QString m_metadata; + QString m_location; + Session m_session; + QHash m_threadIndices; + QThreadStorage m_threadData; + QHash m_additionalMetadata; + QSet m_newAdditionalMetadata; + int m_eventId = 0; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/tracing/qctfplugin.cpp b/src/corelib/tracing/qctfplugin.cpp new file mode 100644 index 00000000000..4e966b32f85 --- /dev/null +++ b/src/corelib/tracing/qctfplugin.cpp @@ -0,0 +1,44 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#define BUILD_LIBRARY +#include +#include "qctfplugin_p.h" +#include "qctflib_p.h" + +QT_BEGIN_NAMESPACE + +class QCtfTracePlugin : public QObject, public QCtfLib +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCtfLib" FILE "trace.json") + Q_INTERFACES(QCtfLib) + +public: + QCtfTracePlugin() + { + + } + ~QCtfTracePlugin() = default; + + bool tracepointEnabled(const QCtfTracePointEvent &point) override + { + return QCtfLibImpl::instance()->tracepointEnabled(point); + } + void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) override + { + QCtfLibImpl::instance()->doTracepoint(point, arr); + } + bool sessionEnabled() override + { + return QCtfLibImpl::instance()->sessionEnabled(); + } + QCtfTracePointPrivate *initializeTracepoint(const QCtfTracePointEvent &point) override + { + return QCtfLibImpl::instance()->initializeTracepoint(point); + } +}; + +#include "qctfplugin.moc" + +QT_END_NAMESPACE diff --git a/src/corelib/tracing/qctfplugin_p.h b/src/corelib/tracing/qctfplugin_p.h new file mode 100644 index 00000000000..d09e0404e4d --- /dev/null +++ b/src/corelib/tracing/qctfplugin_p.h @@ -0,0 +1,38 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef Q_CTFPLUGIN_P_H +#define Q_CTFPLUGIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include "qctf_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QCtfLib +{ +public: + virtual ~QCtfLib() = default; + virtual bool tracepointEnabled(const QCtfTracePointEvent &point) = 0; + virtual void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) = 0; + virtual bool sessionEnabled() = 0; + virtual QCtfTracePointPrivate *initializeTracepoint(const QCtfTracePointEvent &point) = 0; +}; + +Q_DECLARE_INTERFACE(QCtfLib, "org.qt-project.Qt.QCtfLib"); + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/tracing/trace.json b/src/corelib/tracing/trace.json new file mode 100644 index 00000000000..1b991122d47 --- /dev/null +++ b/src/corelib/tracing/trace.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "CTF" ] +} diff --git a/src/tools/tracegen/CMakeLists.txt b/src/tools/tracegen/CMakeLists.txt index 7e00f2aef77..5acf781bf9f 100644 --- a/src/tools/tracegen/CMakeLists.txt +++ b/src/tools/tracegen/CMakeLists.txt @@ -15,6 +15,7 @@ qt_internal_add_tool(${target_name} SOURCES etw.cpp etw.h helpers.cpp helpers.h + ctf.cpp ctf.h lttng.cpp lttng.h panic.cpp panic.h provider.cpp provider.h diff --git a/src/tools/tracegen/ctf.cpp b/src/tools/tracegen/ctf.cpp new file mode 100644 index 00000000000..f7f2b399a0b --- /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 +#include +#include +#include + + +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 \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 + * 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); +} diff --git a/src/tools/tracegen/ctf.h b/src/tools/tracegen/ctf.h new file mode 100644 index 00000000000..54f5e398c2e --- /dev/null +++ b/src/tools/tracegen/ctf.h @@ -0,0 +1,12 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CTF_H +#define CTF_H + +struct Provider; +class QFile; + +void writeCtf(QFile &device, const Provider &p); + +#endif // CTF_H diff --git a/src/tools/tracegen/etw.cpp b/src/tools/tracegen/etw.cpp index c0e13ffb757..415a0a8dcb4 100644 --- a/src/tools/tracegen/etw.cpp +++ b/src/tools/tracegen/etw.cpp @@ -160,11 +160,11 @@ static void writeEpilogue(QTextStream &stream, const QString &fileName) << "#include \n"; } -static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, +static void writeWrapper(QTextStream &stream, const Provider &provider, const Tracepoint &tracepoint, const QString &providerName) { const QString argList = formatFunctionSignature(tracepoint.args); - const QString paramList = formatParameterList(tracepoint.args, tracepoint.fields, ETW); + const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, ETW); const QString &name = tracepoint.name; const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper(); const QString provar = providerVar(providerName); @@ -259,7 +259,7 @@ static void writeTracepoints(QTextStream &stream, const Provider &provider) writeFlagConverter(stream, flag); for (const Tracepoint &t : provider.tracepoints) - writeWrapper(stream, t, provider.name); + writeWrapper(stream, provider, t, provider.name); stream << "} // namespace QtPrivate\n" << "QT_END_NAMESPACE\n" diff --git a/src/tools/tracegen/helpers.cpp b/src/tools/tracegen/helpers.cpp index 6f0c7cb0f2f..ea43d711542 100644 --- a/src/tools/tracegen/helpers.cpp +++ b/src/tools/tracegen/helpers.cpp @@ -49,7 +49,7 @@ QString formatFunctionSignature(const QList &args) }); } -QString formatParameterList(const QList &args, const QList &fields, ParamType type) +QString formatParameterList(const Provider &provider, const QList &args, const QList &fields, ParamType type) { if (type == LTTNG) { QString ret; @@ -65,12 +65,40 @@ QString formatParameterList(const QList &args, const QList return ret; } - if (type == LTTNG) { + auto findEnumeration = [](const QList &enums, const QString &name) { + for (const auto &e : enums) { + if (e.name == name) + return e; + } + return TraceEnum(); + }; + + if (type == CTF) { QString ret; - for (const Tracepoint::Argument &arg : args) - ret += ", "_L1 + arg.name; - + for (int i = 0; i < args.size(); i++) { + const Tracepoint::Argument &arg = args[i]; + const Tracepoint::Field &field = fields[i]; + if (arg.arrayLen > 1) { + ret += ", trace::toByteArrayFromArray("_L1 + arg.name + ", "_L1 + QString::number(arg.arrayLen) + ") "_L1; + } else if (field.backendType.backendType == Tracepoint::Field::EnumeratedType) { + const TraceEnum &e = findEnumeration(provider.enumerations, arg.type); + QString integerType; + if (e.valueSize == 8) + integerType = QStringLiteral("quint8"); + else if (e.valueSize == 16) + integerType = QStringLiteral("quint16"); + else + integerType = QStringLiteral("quint32"); + ret += ", trace::toByteArrayFromEnum<"_L1 + integerType + ">("_L1 + arg.name + ")"_L1; + } else if (field.backendType.backendType == Tracepoint::Field::FlagType) { + ret += ", trace::toByteArrayFromFlags("_L1 + arg.name + ")"_L1; + } else if (field.backendType.backendType == Tracepoint::Field::String) { + ret += ", trace::toByteArrayFromCString("_L1 + arg.name + ")"_L1; + } else { + ret += ", "_L1 + arg.name; + } + } return ret; } diff --git a/src/tools/tracegen/helpers.h b/src/tools/tracegen/helpers.h index 45a0383ff96..1dcfc26519f 100644 --- a/src/tools/tracegen/helpers.h +++ b/src/tools/tracegen/helpers.h @@ -11,12 +11,13 @@ enum ParamType { LTTNG, - ETW + ETW, + CTF }; QString typeToName(const QString &name); QString includeGuard(const QString &filename); QString formatFunctionSignature(const QList &args); -QString formatParameterList(const QList &args, const QList &fields, ParamType type); +QString formatParameterList(const Provider &provider, const QList &args, const QList &fields, ParamType type); #endif // HELPERS_H diff --git a/src/tools/tracegen/lttng.cpp b/src/tools/tracegen/lttng.cpp index f9e8ce5c2f7..effe927e4ff 100644 --- a/src/tools/tracegen/lttng.cpp +++ b/src/tools/tracegen/lttng.cpp @@ -124,7 +124,7 @@ static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, const Provider &provider) { const QString argList = formatFunctionSignature(tracepoint.args); - const QString paramList = formatParameterList(tracepoint.args, tracepoint.fields, LTTNG); + const QString paramList = formatParameterList(provider, tracepoint.args, tracepoint.fields, LTTNG); const QString &name = tracepoint.name; const QString includeGuard = QStringLiteral("TP_%1_%2").arg(provider.name).arg(name).toUpper(); diff --git a/src/tools/tracegen/tracegen.cpp b/src/tools/tracegen/tracegen.cpp index 6a5f3286a9a..46b354c520f 100644 --- a/src/tools/tracegen/tracegen.cpp +++ b/src/tools/tracegen/tracegen.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "provider.h" +#include "ctf.h" #include "lttng.h" #include "etw.h" #include "panic.h" @@ -12,12 +13,13 @@ enum class Target { LTTNG, - ETW + ETW, + CTF, }; static inline void usage(int status) { - printf("Usage: tracegen \n"); + printf("Usage: tracegen \n"); exit(status); } @@ -34,6 +36,8 @@ static void parseArgs(int argc, char *argv[], Target *target, QString *inFile, Q *target = Target::LTTNG; } else if (qstrcmp(targetString, "etw") == 0) { *target = Target::ETW; + } else if (qstrcmp(targetString, "ctf") == 0) { + *target = Target::CTF; } else { fprintf(stderr, "Invalid target: %s\n", targetString); usage(EXIT_FAILURE); @@ -61,6 +65,9 @@ int main(int argc, char *argv[]) } switch (target) { + case Target::CTF: + writeCtf(out, p); + break; case Target::LTTNG: writeLttng(out, p); break;