diff options
Diffstat (limited to 'src/plugins/tracing/qctflib.cpp')
-rw-r--r-- | src/plugins/tracing/qctflib.cpp | 297 |
1 files changed, 207 insertions, 90 deletions
diff --git a/src/plugins/tracing/qctflib.cpp b/src/plugins/tracing/qctflib.cpp index 367ee3c3c8..fe3946d27c 100644 --- a/src/plugins/tracing/qctflib.cpp +++ b/src/plugins/tracing/qctflib.cpp @@ -14,7 +14,10 @@ #include <qendian.h> #include <qplatformdefs.h> #include "qctflib_p.h" + +#if QT_CONFIG(cxx17_filesystem) #include <filesystem> +#endif QT_BEGIN_NAMESPACE @@ -30,6 +33,10 @@ static const char traceMetadataTemplate[] = ; static const size_t traceMetadataSize = sizeof(traceMetadataTemplate); +static inline QString allLiteral() { return QStringLiteral("all"); } +static inline QString defaultLiteral() { return QStringLiteral("default"); } + + template <typename T> static QByteArray &operator<<(QByteArray &arr, T val) { @@ -38,6 +45,15 @@ static QByteArray &operator<<(QByteArray &arr, T val) return arr; } +static FILE *openFile(const QString &filename, const QString &mode) +{ +#ifdef Q_OS_WINDOWS + return _wfopen(qUtf16Printable(filename), qUtf16Printable(mode)); +#else + return fopen(qPrintable(filename), qPrintable(mode)); +#endif +} + QCtfLibImpl *QCtfLibImpl::s_instance = nullptr; QCtfLib *QCtfLibImpl::instance() @@ -53,6 +69,39 @@ void QCtfLibImpl::cleanup() s_instance = nullptr; } +void QCtfLibImpl::handleSessionChange() +{ + m_sessionChanged = true; +} + +void QCtfLibImpl::handleStatusChange(QCtfServer::ServerStatus status) +{ + switch (status) { + case QCtfServer::Error: { + m_serverClosed = true; + } break; + default: + break; + } +} + +void QCtfLibImpl::buildMetadata() +{ + const QString mhn = QSysInfo::machineHostName(); + QString metadata = QString::fromUtf8(traceMetadataTemplate, traceMetadataSize); + 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"), m_datetime.toString(Qt::ISODate)); + metadata.replace(QStringLiteral("$HOST_NAME"), mhn); + metadata.replace(QStringLiteral("$CLOCK_FREQUENCY"), QStringLiteral("1000000000")); + metadata.replace(QStringLiteral("$CLOCK_NAME"), QStringLiteral("monotonic")); + metadata.replace(QStringLiteral("$CLOCK_TYPE"), QStringLiteral("Monotonic clock")); + metadata.replace(QStringLiteral("$CLOCK_OFFSET"), QString::number(m_datetime.toMSecsSinceEpoch() * 1000000)); + metadata.replace(QStringLiteral("$ENDIANNESS"), QSysInfo::ByteOrder == QSysInfo::BigEndian ? u"be"_s : u"le"_s); + writeMetadata(metadata, true); +} + QCtfLibImpl::QCtfLibImpl() { QString location = qEnvironmentVariable("QTRACE_LOCATION"); @@ -61,97 +110,133 @@ QCtfLibImpl::QCtfLibImpl() return; } - // Check if the location is writable - if (QT_ACCESS(qPrintable(location), W_OK) != 0) { - qCWarning(lcDebugTrace) << "Unable to write to location"; - return; - } - - const QString filename = location + QStringLiteral("/session.json"); - FILE *file = fopen(qPrintable(filename), "rb"); - if (!file) { - qCWarning(lcDebugTrace) << "unable to open session file: " - << filename << ", " << qt_error_string(); - m_location = location; - m_session.tracepoints.append(QStringLiteral("all")); - m_session.name = QStringLiteral("default"); + if (location.startsWith(u"tcp")) { + QUrl url(location); + m_server.reset(new QCtfServer()); + m_server->setCallback(this); + m_server->setHost(url.host()); + m_server->setPort(url.port()); + m_server->startServer(); + m_streaming = true; + m_session.tracepoints.append(allLiteral()); + m_session.name = defaultLiteral(); } else { - QT_STATBUF stat; - if (QT_FSTAT(QT_FILENO(file), &stat) != 0) { - qCWarning(lcDebugTrace) << "Unable to stat session file, " << qt_error_string(); +#if !QT_CONFIG(cxx17_filesystem) + qCWarning(lcDebugTrace) << "Unable to use filesystem"; + return; +#endif + // Check if the location is writable + if (QT_ACCESS(qPrintable(location), W_OK) != 0) { + qCWarning(lcDebugTrace) << "Unable to write to location"; return; } - qsizetype filesize = qMin(stat.st_size, std::numeric_limits<qsizetype>::max()); - QByteArray data(filesize, Qt::Uninitialized); - qsizetype size = static_cast<qsizetype>(fread(data.data(), 1, filesize, file)); - fclose(file); - if (size != filesize) - return; - - QJsonDocument json(QJsonDocument::fromJson(data)); - QJsonObject obj = json.object(); - bool valid = false; - if (!obj.isEmpty()) { - const auto it = obj.begin(); - if (it.value().isArray()) { - m_session.name = it.key(); - for (auto var : it.value().toArray()) - m_session.tracepoints.append(var.toString()); - valid = true; + const QString filename = location + u"/session.json"; + FILE *file = openFile(qPrintable(filename), "rb"_L1); + if (!file) { + qCWarning(lcDebugTrace) << "unable to open session file: " + << filename << ", " << qt_error_string(); + m_location = location; + m_session.tracepoints.append(allLiteral()); + m_session.name = defaultLiteral(); + } else { + QT_STATBUF stat; + if (QT_FSTAT(QT_FILENO(file), &stat) != 0) { + qCWarning(lcDebugTrace) << "Unable to stat session file, " << qt_error_string(); + return; } + qsizetype filesize = qMin(stat.st_size, std::numeric_limits<qsizetype>::max()); + QByteArray data(filesize, Qt::Uninitialized); + qsizetype size = static_cast<qsizetype>(fread(data.data(), 1, filesize, file)); + fclose(file); + if (size != filesize) + return; + QJsonDocument json(QJsonDocument::fromJson(data)); + QJsonObject obj = json.object(); + bool valid = false; + if (!obj.isEmpty()) { + const auto it = obj.begin(); + if (it.value().isArray()) { + m_session.name = it.key(); + for (auto var : it.value().toArray()) + m_session.tracepoints.append(var.toString()); + valid = true; + } + } + if (!valid) { + qCWarning(lcDebugTrace) << "Session file is not valid"; + m_session.tracepoints.append(allLiteral()); + m_session.name = defaultLiteral(); + } + m_location = location + u"/ust"; +#if QT_CONFIG(cxx17_filesystem) + std::filesystem::create_directory(qPrintable(m_location), qPrintable(location)); +#endif } - if (!valid) { - qCWarning(lcDebugTrace) << "Session file is not valid"; - m_session.tracepoints.append(QStringLiteral("all")); - m_session.name = QStringLiteral("default"); - } - m_location = location + QStringLiteral("/ust"); - std::filesystem::create_directory(qPrintable(m_location), qPrintable(location)); + clearLocation(); } - m_session.all = m_session.tracepoints.contains(QStringLiteral("all")); - auto datetime = QDateTime::currentDateTime().toUTC(); - const QString mhn = QSysInfo::machineHostName(); - QString metadata = QString::fromUtf8(traceMetadataTemplate, traceMetadataSize); - 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(Qt::ISODate)); - metadata.replace(QStringLiteral("$HOST_NAME"), mhn); - metadata.replace(QStringLiteral("$CLOCK_FREQUENCY"), QStringLiteral("1000000000")); - metadata.replace(QStringLiteral("$CLOCK_NAME"), QStringLiteral("monotonic")); - metadata.replace(QStringLiteral("$CLOCK_TYPE"), QStringLiteral("Monotonic clock")); - metadata.replace(QStringLiteral("$CLOCK_OFFSET"), QString::number(datetime.toMSecsSinceEpoch() * 1000000)); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("be")); + m_session.all = m_session.tracepoints.contains(allLiteral()); + // Get datetime to when the timer was started to store the offset to epoch time for the traces + m_datetime = QDateTime::currentDateTime().toUTC(); + m_timer.start(); + if (!m_streaming) + buildMetadata(); +} + +void QCtfLibImpl::clearLocation() +{ +#if QT_CONFIG(cxx17_filesystem) + const std::filesystem::path location{qUtf16Printable(m_location)}; + for (auto const& dirEntry : std::filesystem::directory_iterator{location}) + { + const auto path = dirEntry.path(); +#if __cplusplus > 201703L + if (dirEntry.is_regular_file() + && path.filename().wstring().starts_with(std::wstring_view(L"channel_")) + && !path.has_extension()) { #else - metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("le")); + const auto strview = std::wstring_view(L"channel_"); + const auto sub = path.filename().wstring().substr(0, strview.length()); + if (dirEntry.is_regular_file() && sub.compare(strview) == 0 + && !path.has_extension()) { +#endif + if (!std::filesystem::remove(path)) { + qCInfo(lcDebugTrace) << "Unable to clear output location."; + break; + } + } + } #endif - writeMetadata(metadata, true); - m_timer.start(); } void QCtfLibImpl::writeMetadata(const QString &metadata, bool overwrite) { - FILE *file = nullptr; - file = fopen(qPrintable(m_location + "/metadata"_L1), overwrite ? "w+b": "ab"); - if (!file) - return; + if (m_streaming) { + auto mt = metadata.toUtf8(); + mt.resize(mt.size() - 1); + m_server->bufferData(QStringLiteral("metadata"), mt, overwrite); + } else { + FILE *file = nullptr; + file = openFile(qPrintable(m_location + "/metadata"_L1), overwrite ? "w+b"_L1: "ab"_L1); + if (!file) + return; - if (!overwrite) - fputs("\n", file); + if (!overwrite) + fputs("\n", file); - // data contains zero at the end, hence size - 1. - const QByteArray data = metadata.toUtf8(); - fwrite(data.data(), data.size() - 1, 1, file); - fclose(file); + // data contains zero at the end, hence size - 1. + const QByteArray data = metadata.toUtf8(); + 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) { + if (!m_streaming) + file = openFile(ch.channelName, "ab"_L1); + if (file || m_streaming) { /* Each packet contains header and context, which are defined in the metadata.txt */ QByteArray packet; packet << s_CtfHeaderMagic; @@ -171,27 +256,61 @@ void QCtfLibImpl::writeCtfPacket(QCtfLibImpl::Channel &ch) Q_ASSERT(ch.data.size() + packetHeaderSize + ch.threadNameLength <= packetSize); Q_ASSERT(packet.size() == qsizetype(packetHeaderSize + ch.threadNameLength)); - fwrite(packet.data(), packet.size(), 1, file); - ch.data.resize(packetSize - packet.size(), 0); - fwrite(ch.data.data(), ch.data.size(), 1, file); - fclose(file); + if (m_streaming) { + ch.data.resize(packetSize - packet.size(), 0); + packet += ch.data; + m_server->bufferData(QString::fromLatin1(ch.channelName), packet, false); + } else { + fwrite(packet.data(), packet.size(), 1, file); + ch.data.resize(packetSize - packet.size(), 0); + fwrite(ch.data.data(), ch.data.size(), 1, file); + fclose(file); + } } } +QCtfLibImpl::Channel::~Channel() +{ + impl->writeCtfPacket(*this); + impl->removeChannel(this); +} + QCtfLibImpl::~QCtfLibImpl() { + if (!m_server.isNull()) + m_server->stopServer(); qDeleteAll(m_eventPrivs); } -bool QCtfLibImpl::tracepointEnabled(const QCtfTracePointEvent &point) +void QCtfLibImpl::removeChannel(Channel *ch) { - return m_session.all || m_session.tracepoints.contains(point.provider.provider); + const QMutexLocker lock(&m_mutex); + m_channels.removeOne(ch); } -QCtfLibImpl::Channel::~Channel() +bool QCtfLibImpl::tracepointEnabled(const QCtfTracePointEvent &point) { - if (data.size()) - QCtfLibImpl::writeCtfPacket(*this); + if (m_sessionChanged) { + const QMutexLocker lock(&m_mutex); + buildMetadata(); + m_session.name = m_server->sessionName(); + m_session.tracepoints = m_server->sessionTracepoints().split(';'); + m_session.all = m_session.tracepoints.contains(allLiteral()); + m_sessionChanged = false; + for (const auto &meta : m_additionalMetadata) + writeMetadata(meta->metadata); + for (auto *priv : m_eventPrivs) + writeMetadata(priv->metadata); + quint64 timestamp = m_timer.nsecsElapsed(); + for (auto *ch : m_channels) { + writeCtfPacket(*ch); + ch->data.clear(); + ch->minTimestamp = ch->maxTimestamp = timestamp; + } + } + if (m_streaming && (m_serverClosed || (!m_server->bufferOnIdle() && m_server->status() == QCtfServer::Idle))) + return false; + return m_session.all || m_session.tracepoints.contains(point.provider.provider); } static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId) @@ -208,12 +327,10 @@ event { }; }; */ - 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; + return QStringView(u"event {\n name = \"") + provider + QLatin1Char(':') + name + u"\";\n" + + u" id = " + QString::number(eventId) + u";\n" + + u" stream_id = 0;\n loglevel = 13;\n fields := struct {\n " + + metadata + u"\n };\n};\n"; } QCtfTracePointPrivate *QCtfLibImpl::initializeTracepoint(const QCtfTracePointEvent &point) @@ -238,6 +355,8 @@ void QCtfLibImpl::doTracepoint(const QCtfTracePointEvent &point, const QByteArra QCtfTracePointPrivate *priv = point.d; quint64 timestamp = 0; QThread *thread = nullptr; + if (m_streaming && m_serverClosed) + return; { QMutexLocker lock(&m_mutex); if (!priv->metadataWritten) { @@ -270,12 +389,10 @@ void QCtfLibImpl::doTracepoint(const QCtfTracePointEvent &point, const QByteArra Channel &ch = m_threadData.localData(); if (ch.channelName[0] == 0) { + ch.impl = this; + m_channels.append(&ch); 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"); - if (f) - fclose(f); ch.minTimestamp = ch.maxTimestamp = timestamp; ch.thread = thread; ch.threadIndex = m_threadIndices[thread]; |