7#include <qjsondocument.h>
9#include <qjsonobject.h>
13#include <qmetaobject.h>
15#include <qplatformdefs.h>
18#if QT_CONFIG(cxx17_filesystem)
24using namespace Qt::StringLiterals;
32#include "metadata_template.h"
36static inline QString
allLiteral() {
return QStringLiteral(
"all"); }
43 static_assert(
std::is_arithmetic_v<T>);
44 arr.append(
reinterpret_cast<
char *>(&val),
sizeof(val));
48static FILE *
openFile(
const QString &filename,
const QString &mode)
51 return _wfopen(qUtf16Printable(filename), qUtf16Printable(mode));
74 m_sessionChanged =
true;
80 case QCtfServer::Error: {
81 m_serverClosed =
true;
90 const QString mhn = QSysInfo::machineHostName();
91 QString metadata = QString::fromUtf8(traceMetadataTemplate, traceMetadataSize);
92 metadata.replace(QStringLiteral(
"$TRACE_UUID"), s_TraceUuid.toString(QUuid::WithoutBraces));
93 metadata.replace(QStringLiteral(
"$ARC_BIT_WIDTH"), QString::number(Q_PROCESSOR_WORDSIZE * 8));
94 metadata.replace(QStringLiteral(
"$SESSION_NAME"), m_session.name);
95 metadata.replace(QStringLiteral(
"$CREATION_TIME"), m_datetime.toString(Qt::ISODate));
96 metadata.replace(QStringLiteral(
"$HOST_NAME"), mhn);
97 metadata.replace(QStringLiteral(
"$CLOCK_FREQUENCY"), QStringLiteral(
"1000000000"));
98 metadata.replace(QStringLiteral(
"$CLOCK_NAME"), QStringLiteral(
"monotonic"));
99 metadata.replace(QStringLiteral(
"$CLOCK_TYPE"), QStringLiteral(
"Monotonic clock"));
100 metadata.replace(QStringLiteral(
"$CLOCK_OFFSET"), QString::number(m_datetime.toMSecsSinceEpoch() * 1000000));
101 metadata.replace(QStringLiteral(
"$ENDIANNESS"), QSysInfo::ByteOrder == QSysInfo::BigEndian ? u"be"_s : u"le"_s);
102 writeMetadata(metadata,
true);
107 QString location = qEnvironmentVariable(
"QTRACE_LOCATION");
108 if (location.isEmpty()) {
109 qCInfo(lcDebugTrace) <<
"QTRACE_LOCATION not set";
113 if (location.startsWith(u"tcp")) {
115 m_server.reset(
new QCtfServer());
116 m_server->setCallback(
this);
117 m_server->setHost(url.host());
118 m_server->setPort(url.port());
119 m_server->startServer();
121 m_session.tracepoints.append(allLiteral());
122 m_session.name = defaultLiteral();
124#if !QT_CONFIG(cxx17_filesystem)
125 qCWarning(lcDebugTrace) <<
"Unable to use filesystem";
129 if (QT_ACCESS(
qPrintable(location), W_OK) != 0) {
130 qCWarning(lcDebugTrace) <<
"Unable to write to location";
133 const QString filename = location + u"/session.json";
134 FILE *file = openFile(
qPrintable(filename),
"rb"_L1);
136 qCWarning(lcDebugTrace) <<
"unable to open session file: "
137 << filename <<
", " << qt_error_string();
138 m_location = location;
139 m_session.tracepoints.append(allLiteral());
140 m_session.name = defaultLiteral();
143 if (QT_FSTAT(QT_FILENO(file), &stat) != 0) {
144 qCWarning(lcDebugTrace) <<
"Unable to stat session file, " << qt_error_string();
147 qsizetype filesize = qMin(stat.st_size, std::numeric_limits<qsizetype>::max());
149 qsizetype size =
static_cast<qsizetype>(fread(data.data(), 1, filesize, file));
151 if (size != filesize)
154 QJsonObject obj = json.object();
156 if (!obj.isEmpty()) {
157 const auto it = obj.begin();
158 if (it.value().isArray()) {
159 m_session.name = it.key();
160 for (
auto var : it.value().toArray())
161 m_session.tracepoints.append(var.toString());
166 qCWarning(lcDebugTrace) <<
"Session file is not valid";
167 m_session.tracepoints.append(allLiteral());
168 m_session.name = defaultLiteral();
170 m_location = location + u"/ust";
171#if QT_CONFIG(cxx17_filesystem)
172 std::filesystem::create_directory(qPrintable(m_location), qPrintable(location));
178 m_session.all = m_session.tracepoints.contains(allLiteral());
180 m_datetime = QDateTime::currentDateTime().toUTC();
188#if QT_CONFIG(cxx17_filesystem)
189 const std::filesystem::path location{m_location.toStdU16String()};
190 for (
auto const& dirEntry : std::filesystem::directory_iterator{location})
192 const auto path = dirEntry.path();
193#if __cplusplus > 201703L
194 if (dirEntry.is_regular_file()
195 && path.filename().wstring().starts_with(std::wstring_view(L"channel_"))
196 && !path.has_extension()) {
198 const auto strview = std::wstring_view(L"channel_");
199 const auto sub = path.filename().wstring().substr(0, strview.length());
200 if (dirEntry.is_regular_file() && sub.compare(strview) == 0
201 && !path.has_extension()) {
203 if (!std::filesystem::remove(path)) {
204 qCInfo(lcDebugTrace) <<
"Unable to clear output location.";
212void QCtfLibImpl::writeMetadata(
const QString &metadata,
bool overwrite)
215 auto mt = metadata.toUtf8();
216 mt.resize(mt.size() - 1);
217 m_server->bufferData(QStringLiteral(
"metadata"), mt, overwrite);
219 FILE *file =
nullptr;
220 file = openFile(
qPrintable(m_location +
"/metadata"_L1), overwrite ?
"w+b"_L1:
"ab"_L1);
229 fwrite(data.data(), data.size() - 1, 1, file);
236 FILE *file =
nullptr;
238 file = openFile(ch.channelName,
"ab"_L1);
239 if (file || m_streaming) {
242 packet << s_CtfHeaderMagic;
243 packet.append(QByteArrayView(s_TraceUuid.toBytes()));
245 packet << quint32(0);
246 packet << ch.minTimestamp;
247 packet << ch.maxTimestamp;
248 packet << quint64(ch.data.size() + packetHeaderSize + ch.threadNameLength) * 8u;
249 packet << quint64(packetSize) * 8u;
250 packet << ch.seqnumber++;
251 packet << quint64(0);
252 packet << ch.threadIndex;
253 if (ch.threadName.size())
254 packet.append(ch.threadName);
257 Q_ASSERT(ch.data.size() + packetHeaderSize + ch.threadNameLength <= packetSize);
258 Q_ASSERT(packet.size() == qsizetype(packetHeaderSize + ch.threadNameLength));
260 ch.data.resize(packetSize - packet.size(), 0);
262 m_server->bufferData(QString::fromLatin1(ch.channelName), packet,
false);
264 fwrite(packet.data(), packet.size(), 1, file);
265 ch.data.resize(packetSize - packet.size(), 0);
266 fwrite(ch.data.data(), ch.data.size(), 1, file);
274 impl->writeCtfPacket(*
this);
275 impl->removeChannel(
this);
280 if (!m_server.isNull())
281 m_server->stopServer();
282 qDeleteAll(m_eventPrivs);
288 m_channels.removeOne(ch);
293 if (m_sessionChanged) {
296 m_session.name = m_server->sessionName();
297 m_session.tracepoints = m_server->sessionTracepoints().split(
';');
298 m_session.all = m_session.tracepoints.contains(allLiteral());
299 m_sessionChanged =
false;
300 for (
const auto &meta : m_additionalMetadata)
301 writeMetadata(meta->metadata);
302 for (
auto *priv : m_eventPrivs)
303 writeMetadata(priv->metadata);
304 quint64 timestamp = m_timer.nsecsElapsed();
305 for (
auto *ch : m_channels) {
308 ch->minTimestamp = ch->maxTimestamp = timestamp;
311 if (m_streaming && (m_serverClosed || (!m_server->bufferOnIdle() && m_server->status() == QCtfServer::Idle)))
313 return m_session.all || m_session.tracepoints.contains(point.provider.provider);
316static QString
toMetadata(
const QString &provider,
const QString &name,
const QString &metadata, quint32 eventId)
319
320
321
322
323
324
325
326
327
328
329
330 return QStringView(u"event {\n name = \"") + provider + QLatin1Char(
':') + name + u"\";\n"
331 + u" id = " + QString::number(eventId) + u";\n"
332 + u" stream_id = 0;\n loglevel = 13;\n fields := struct {\n "
333 + metadata + u"\n };\n};\n";
341 if (
const auto &it = m_eventPrivs.find(point.eventName); it != m_eventPrivs.end()) {
345 m_eventPrivs.insert(point.eventName, priv);
347 priv->metadata = toMetadata(point.provider.provider, point.eventName, point.metadata, priv->id);
356 quint64 timestamp = 0;
357 QThread *thread =
nullptr;
358 if (m_streaming && m_serverClosed)
364 auto providerMetadata = point.provider.metadata;
365 while (providerMetadata) {
366 registerMetadata(*providerMetadata);
367 providerMetadata = providerMetadata->next;
369 if (m_newAdditionalMetadata.size()) {
370 for (
const QString &name : m_newAdditionalMetadata)
371 writeMetadata(m_additionalMetadata[name]->metadata);
372 m_newAdditionalMetadata.clear();
374 writeMetadata(priv->metadata);
376 timestamp = m_timer.nsecsElapsed();
378 if (arr.size() != point.size) {
379 if (arr.size() < point.size)
381 if (arr.size() > point.size && !point.variableSize && !point.metadata.isEmpty())
385 thread = QThread::currentThread();
386 if (thread ==
nullptr)
389 Channel &ch = m_threadData.localData();
391 if (ch.channelName[0] == 0) {
393 m_channels.append(&ch);
394 m_threadIndices.insert(thread, m_threadIndices.size());
395 sprintf(ch.channelName,
"%s/channel_%d",
qPrintable(m_location), m_threadIndices[thread]);
396 ch.minTimestamp = ch.maxTimestamp = timestamp;
398 ch.threadIndex = m_threadIndices[thread];
399 ch.threadName = thread->objectName().toUtf8();
400 if (ch.threadName.isEmpty()) {
401 const QMetaObject *obj = thread->metaObject();
404 ch.threadNameLength = ch.threadName.size() + 1;
408 Q_ASSERT(ch.thread == thread);
412 event << priv->id << timestamp;
413 if (!point.metadata.isEmpty())
416 if (ch.threadNameLength + ch.data.size() + event.size() + packetHeaderSize >= packetSize) {
419 ch.minTimestamp = ch.maxTimestamp = timestamp;
421 ch.data.append(event);
425 ch.maxTimestamp = timestamp;
430 return !m_session.name.isEmpty();
440 if (m_additionalMetadata.contains(metadata.name))
443 m_additionalMetadata.insert(metadata.name, &metadata);
444 m_newAdditionalMetadata.insert(metadata.name);
void handleStatusChange(QCtfServer::ServerStatus status) override
bool sessionEnabled() override
void registerMetadata(const QCtfTraceMetadata &metadata)
bool tracepointEnabled(const QCtfTracePointEvent &point) override
QCtfTracePointPrivate * initializeTracepoint(const QCtfTracePointEvent &point) override
void handleSessionChange() override
void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) override
\inmodule QtCore\reentrant
Combined button and popup list for selecting options.
const QString & asString(const QString &s)
static const size_t traceMetadataSize
static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId)
static FILE * openFile(const QString &filename, const QString &mode)
static const size_t packetSize
static QString defaultLiteral()
static QString allLiteral()
static const char traceMetadataTemplate[]
static const size_t packetHeaderSize
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qPrintable(string)