summaryrefslogtreecommitdiffstats
path: root/src/plugins/tracing/qctflib.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/tracing/qctflib.cpp')
-rw-r--r--src/plugins/tracing/qctflib.cpp300
1 files changed, 212 insertions, 88 deletions
diff --git a/src/plugins/tracing/qctflib.cpp b/src/plugins/tracing/qctflib.cpp
index cbbefa1511..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,14 +33,27 @@ 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>
-QByteArray &operator<<(QByteArray &arr, T val)
+static QByteArray &operator<<(QByteArray &arr, T val)
{
static_assert(std::is_arithmetic_v<T>);
- arr.append((char *)&val, sizeof(val));
+ arr.append(reinterpret_cast<char *>(&val), sizeof(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()
@@ -49,102 +65,178 @@ QCtfLib *QCtfLibImpl::instance()
void QCtfLibImpl::cleanup()
{
- if (s_instance)
- delete s_instance;
+ delete s_instance;
s_instance = nullptr;
}
-QCtfLibImpl::QCtfLibImpl()
+void QCtfLibImpl::handleSessionChange()
{
- QString location = QString::fromUtf8(qgetenv("QTRACE_LOCATION"));
- if (location.isEmpty()) {
- qCInfo (lcDebugTrace) << "QTRACE_LOCATION not set";
- return;
- }
-
- // Check if the location is writable
- FILE *file = nullptr;
- file = fopen(qPrintable(location + "/metadata"_L1), "w+b");
- if (!file) {
- qCWarning (lcDebugTrace) << "Unable to write to location";
- return;
- }
- fclose(file);
-
- const QString filename = location + QStringLiteral("/session.json");
- file = fopen(qPrintable(filename), "rb");
- if (!file) {
- qCWarning (lcDebugTrace) << "unable to open session file: " << filename;
- m_location = location;
- m_session.tracepoints.append(QStringLiteral("all"));
- m_session.name = QStringLiteral("default");
- } else {
- fseek(file, 0, SEEK_END);
- qsizetype pos = ftell(file);
- fseek(file, 0, SEEK_SET);
- QByteArray data(pos, Qt::Uninitialized);
- qsizetype size = (qsizetype)fread(data.data(), 1, pos, file);
- fclose(file);
- if (size != pos)
- 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();
- const QJsonArray arr = value.toArray();
- for (auto var : arr)
- m_session.tracepoints.append(var.toString());
+ m_sessionChanged = true;
+}
- m_location = location + QStringLiteral("/ust");
- std::filesystem::create_directory(qPrintable(m_location), qPrintable(location));
+void QCtfLibImpl::handleStatusChange(QCtfServer::ServerStatus status)
+{
+ switch (status) {
+ case QCtfServer::Error: {
+ m_serverClosed = true;
+ } break;
+ default:
+ break;
}
- m_session.all = m_session.tracepoints.contains(QStringLiteral("all"));
+}
- auto datetime = QDateTime::currentDateTime().toUTC();
+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"), datetime.toString(Qt::ISODate));
+ 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(datetime.toMSecsSinceEpoch() * 1000000));
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("be"));
-#else
- metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("le"));
-#endif
+ 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");
+ if (location.isEmpty()) {
+ qCInfo(lcDebugTrace) << "QTRACE_LOCATION not set";
+ return;
+ }
+
+ 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 {
+#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;
+ }
+ 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
+ }
+ clearLocation();
+ }
+
+ 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
+ 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
}
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;
@@ -164,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)
@@ -201,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)
@@ -231,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) {
@@ -263,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];