Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qctflib.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#define BUILD_LIBRARY
4#include <qstring.h>
5#include <qthread.h>
6#include <stdio.h>
7#include <qjsondocument.h>
8#include <qjsonarray.h>
9#include <qjsonobject.h>
10#include <qfileinfo.h>
11#include <qrect.h>
12#include <qsize.h>
13#include <qmetaobject.h>
14#include <qendian.h>
15#include <qplatformdefs.h>
16#include "qctflib_p.h"
17#include <filesystem>
18
20
21using namespace Qt::StringLiterals;
22
23Q_LOGGING_CATEGORY(lcDebugTrace, "qt.core.ctf", QtWarningMsg)
24
25static const size_t packetHeaderSize = 24 + 6 * 8 + 4;
26static const size_t packetSize = 4096;
27
28static const char traceMetadataTemplate[] =
29#include "metadata_template.h"
30;
31static const size_t traceMetadataSize = sizeof(traceMetadataTemplate);
32
33template <typename T>
35{
36 static_assert(std::is_arithmetic_v<T>);
37 arr.append(reinterpret_cast<char *>(&val), sizeof(val));
38 return arr;
39}
40
41QCtfLibImpl *QCtfLibImpl::s_instance = nullptr;
42
44{
45 if (!s_instance)
46 s_instance = new QCtfLibImpl();
47 return s_instance;
48}
49
51{
52 delete s_instance;
53 s_instance = nullptr;
54}
55
57{
58 QString location = qEnvironmentVariable("QTRACE_LOCATION");
59 if (location.isEmpty()) {
60 qCInfo(lcDebugTrace) << "QTRACE_LOCATION not set";
61 return;
62 }
63
64 // Check if the location is writable
65 if (QT_ACCESS(qPrintable(location), W_OK) != 0) {
66 qCWarning(lcDebugTrace) << "Unable to write to location";
67 return;
68 }
69
70 const QString filename = location + QStringLiteral("/session.json");
71 FILE *file = fopen(qPrintable(filename), "rb");
72 if (!file) {
73 qCWarning(lcDebugTrace) << "unable to open session file: "
74 << filename << ", " << qt_error_string();
75 m_location = location;
76 m_session.tracepoints.append(QStringLiteral("all"));
77 m_session.name = QStringLiteral("default");
78 } else {
79 QT_STATBUF stat;
80 if (QT_FSTAT(QT_FILENO(file), &stat) != 0) {
81 qCWarning(lcDebugTrace) << "Unable to stat session file, " << qt_error_string();
82 return;
83 }
84 qsizetype filesize = qMin(stat.st_size, std::numeric_limits<qsizetype>::max());
86 qsizetype size = static_cast<qsizetype>(fread(data.data(), 1, filesize, file));
87 fclose(file);
88 if (size != filesize)
89 return;
90
92 QJsonObject obj = json.object();
93 bool valid = false;
94 if (!obj.isEmpty()) {
95 const auto it = obj.begin();
96 if (it.value().isArray()) {
97 m_session.name = it.key();
98 for (auto var : it.value().toArray())
99 m_session.tracepoints.append(var.toString());
100 valid = true;
101 }
102 }
103 if (!valid) {
104 qCWarning(lcDebugTrace) << "Session file is not valid";
105 m_session.tracepoints.append(QStringLiteral("all"));
106 m_session.name = QStringLiteral("default");
107 }
108 m_location = location + QStringLiteral("/ust");
109 std::filesystem::create_directory(qPrintable(m_location), qPrintable(location));
110 }
111 m_session.all = m_session.tracepoints.contains(QStringLiteral("all"));
112
113 auto datetime = QDateTime::currentDateTime().toUTC();
116 metadata.replace(QStringLiteral("$TRACE_UUID"), s_TraceUuid.toString(QUuid::WithoutBraces));
117 metadata.replace(QStringLiteral("$ARC_BIT_WIDTH"), QString::number(Q_PROCESSOR_WORDSIZE * 8));
118 metadata.replace(QStringLiteral("$SESSION_NAME"), m_session.name);
119 metadata.replace(QStringLiteral("$CREATION_TIME"), datetime.toString(Qt::ISODate));
120 metadata.replace(QStringLiteral("$HOST_NAME"), mhn);
121 metadata.replace(QStringLiteral("$CLOCK_FREQUENCY"), QStringLiteral("1000000000"));
122 metadata.replace(QStringLiteral("$CLOCK_NAME"), QStringLiteral("monotonic"));
123 metadata.replace(QStringLiteral("$CLOCK_TYPE"), QStringLiteral("Monotonic clock"));
124 metadata.replace(QStringLiteral("$CLOCK_OFFSET"), QString::number(datetime.toMSecsSinceEpoch() * 1000000));
125#if Q_BYTE_ORDER == Q_BIG_ENDIAN
126 metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("be"));
127#else
128 metadata.replace(QStringLiteral("$ENDIANNESS"), QStringLiteral("le"));
129#endif
130 writeMetadata(metadata, true);
131 m_timer.start();
132}
133
134void QCtfLibImpl::writeMetadata(const QString &metadata, bool overwrite)
135{
136 FILE *file = nullptr;
137 file = fopen(qPrintable(m_location + "/metadata"_L1), overwrite ? "w+b": "ab");
138 if (!file)
139 return;
140
141 if (!overwrite)
142 fputs("\n", file);
143
144 // data contains zero at the end, hence size - 1.
145 const QByteArray data = metadata.toUtf8();
146 fwrite(data.data(), data.size() - 1, 1, file);
147 fclose(file);
148}
149
150void QCtfLibImpl::writeCtfPacket(QCtfLibImpl::Channel &ch)
151{
152 FILE *file = nullptr;
153 file = fopen(ch.channelName, "ab");
154 if (file) {
155 /* Each packet contains header and context, which are defined in the metadata.txt */
156 QByteArray packet;
157 packet << s_CtfHeaderMagic;
158 packet.append(QByteArrayView(s_TraceUuid.toBytes()));
159
160 packet << quint32(0);
161 packet << ch.minTimestamp;
162 packet << ch.maxTimestamp;
163 packet << quint64(ch.data.size() + packetHeaderSize + ch.threadNameLength) * 8u;
164 packet << quint64(packetSize) * 8u;
165 packet << ch.seqnumber++;
166 packet << quint64(0);
167 packet << ch.threadIndex;
168 if (ch.threadName.size())
169 packet.append(ch.threadName);
170 packet << (char)0;
171
172 Q_ASSERT(ch.data.size() + packetHeaderSize + ch.threadNameLength <= packetSize);
173 Q_ASSERT(packet.size() == qsizetype(packetHeaderSize + ch.threadNameLength));
174 fwrite(packet.data(), packet.size(), 1, file);
175 ch.data.resize(packetSize - packet.size(), 0);
176 fwrite(ch.data.data(), ch.data.size(), 1, file);
177 fclose(file);
178 }
179}
180
182{
183 qDeleteAll(m_eventPrivs);
184}
185
187{
188 return m_session.all || m_session.tracepoints.contains(point.provider.provider);
189}
190
191QCtfLibImpl::Channel::~Channel()
192{
193 if (data.size())
194 QCtfLibImpl::writeCtfPacket(*this);
195}
196
197static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId)
198{
199/*
200 generates event structure:
201event {
202 name = provider:tracepoint_name;
203 id = eventId;
204 stream_id = 0;
205 loglevel = 13;
206 fields := struct {
207 metadata
208 };
209};
210*/
211 QString ret;
212 ret = QStringLiteral("event {\n name = \"") + provider + QLatin1Char(':') + name + QStringLiteral("\";\n");
213 ret += QStringLiteral(" id = ") + QString::number(eventId) + QStringLiteral(";\n");
214 ret += QStringLiteral(" stream_id = 0;\n loglevel = 13;\n fields := struct {\n ");
215 ret += metadata + QStringLiteral("\n };\n};\n");
216 return ret;
217}
218
220{
221 QMutexLocker lock(&m_mutex);
223 if (!point.d) {
224 if (const auto &it = m_eventPrivs.find(point.eventName); it != m_eventPrivs.end()) {
225 priv = *it;
226 } else {
228 m_eventPrivs.insert(point.eventName, priv);
229 priv->id = eventId();
230 priv->metadata = toMetadata(point.provider.provider, point.eventName, point.metadata, priv->id);
231 }
232 }
233 return priv;
234}
235
237{
239 quint64 timestamp = 0;
240 QThread *thread = nullptr;
241 {
242 QMutexLocker lock(&m_mutex);
243 if (!priv->metadataWritten) {
244 priv->metadataWritten = true;
245 auto providerMetadata = point.provider.metadata;
246 while (providerMetadata) {
247 registerMetadata(*providerMetadata);
248 providerMetadata = providerMetadata->next;
249 }
250 if (m_newAdditionalMetadata.size()) {
251 for (const QString &name : m_newAdditionalMetadata)
252 writeMetadata(m_additionalMetadata[name]->metadata);
253 m_newAdditionalMetadata.clear();
254 }
255 writeMetadata(priv->metadata);
256 }
257 timestamp = m_timer.nsecsElapsed();
258 }
259 if (arr.size() != point.size) {
260 if (arr.size() < point.size)
261 return;
262 if (arr.size() > point.size && !point.variableSize && !point.metadata.isEmpty())
263 return;
264 }
265
267 if (thread == nullptr)
268 return;
269
270 Channel &ch = m_threadData.localData();
271
272 if (ch.channelName[0] == 0) {
273 m_threadIndices.insert(thread, m_threadIndices.size());
274 sprintf(ch.channelName, "%s/channel_%d", qPrintable(m_location), m_threadIndices[thread]);
275 FILE *f = nullptr;
276 f = fopen(ch.channelName, "wb");
277 if (f)
278 fclose(f);
279 ch.minTimestamp = ch.maxTimestamp = timestamp;
280 ch.thread = thread;
281 ch.threadIndex = m_threadIndices[thread];
282 ch.threadName = thread->objectName().toUtf8();
283 if (ch.threadName.isEmpty()) {
284 const QMetaObject *obj = thread->metaObject();
285 ch.threadName = QByteArray(obj->className());
286 }
287 ch.threadNameLength = ch.threadName.size() + 1;
288 }
289 if (ch.locked)
290 return;
291 Q_ASSERT(ch.thread == thread);
292 ch.locked = true;
293
295 event << priv->id << timestamp;
296 if (!point.metadata.isEmpty())
297 event.append(arr);
298
299 if (ch.threadNameLength + ch.data.size() + event.size() + packetHeaderSize >= packetSize) {
300 writeCtfPacket(ch);
301 ch.data = event;
302 ch.minTimestamp = ch.maxTimestamp = timestamp;
303 } else {
304 ch.data.append(event);
305 }
306
307 ch.locked = false;
308 ch.maxTimestamp = timestamp;
309}
310
312{
313 return !m_session.name.isEmpty();
314}
315
317{
318 return m_eventId++;
319}
320
322{
323 if (m_additionalMetadata.contains(metadata.name))
324 return;
325
326 m_additionalMetadata.insert(metadata.name, &metadata);
327 m_newAdditionalMetadata.insert(metadata.name);
328}
329
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
int eventId()
Definition qctflib.cpp:316
static QCtfLib * instance()
Definition qctflib.cpp:43
static void cleanup()
Definition qctflib.cpp:50
bool sessionEnabled() override
Definition qctflib.cpp:311
void registerMetadata(const QCtfTraceMetadata &metadata)
Definition qctflib.cpp:321
bool tracepointEnabled(const QCtfTracePointEvent &point) override
Definition qctflib.cpp:186
QCtfTracePointPrivate * initializeTracepoint(const QCtfTracePointEvent &point) override
Definition qctflib.cpp:219
void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) override
Definition qctflib.cpp:236
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
QDateTime toUTC() const
Returns a copy of this datetime converted to UTC.
void start() noexcept
Starts this timer.
qint64 nsecsElapsed() const noexcept
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:925
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
\inmodule QtCore\reentrant
QJsonObject object() const
Returns the QJsonObject contained in the document.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
\inmodule QtCore
Definition qmutex.h:317
QString objectName
the name of this object
Definition qobject.h:94
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
qsizetype size() const
Definition qset.h:50
iterator begin()
Definition qset.h:136
void clear()
Definition qset.h:61
iterator insert(const T &value)
Definition qset.h:155
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QByteArray toUtf8() const &
Definition qstring.h:563
static QString machineHostName()
Definition qsysinfo.cpp:926
T & localData()
Returns a reference to the data that was set by the calling thread.
static QThread * currentThread()
Definition qthread.cpp:966
@ WithoutBraces
Definition quuid.h:54
QString toString(StringFormat mode=WithBraces) const
Definition quuid.cpp:602
Id128Bytes toBytes(QSysInfo::Endian order=QSysInfo::BigEndian) const noexcept
Definition quuid.h:226
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ ISODate
constexpr Initialization Uninitialized
static const char traceMetadataTemplate[]
Definition qctflib.cpp:28
static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId)
Definition qctflib.cpp:197
static const size_t packetSize
Definition qctflib.cpp:26
static const size_t traceMetadataSize
Definition qctflib.cpp:31
static const size_t packetHeaderSize
Definition qctflib.cpp:25
static QByteArray & operator<<(QByteArray &arr, T val)
Definition qctflib.cpp:34
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
@ QtWarningMsg
Definition qlogging.h:31
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
return ret
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLint location
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
struct _cl_event * event
GLhandleARB obj
[2]
GLuint GLfloat * val
#define Q_PROCESSOR_WORDSIZE
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1391
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
unsigned int quint32
Definition qtypes.h:45
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
QFile file
[0]
QReadWriteLock lock
[0]
const QString name
Definition qctf_p.h:40
const bool variableSize
Definition qctf_p.h:58
const int size
Definition qctf_p.h:57
const QCtfTracePointProvider & provider
Definition qctf_p.h:54
QCtfTracePointPrivate * d
Definition qctf_p.h:64
const QString eventName
Definition qctf_p.h:55
const QString metadata
Definition qctf_p.h:56
QCtfTraceMetadata * metadata
Definition qctf_p.h:30
const QString provider
Definition qctf_p.h:29
\inmodule QtCore \reentrant
Definition qchar.h:17
\inmodule QtCore