Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
18#if QT_CONFIG(cxx17_filesystem)
19#include <filesystem>
20#endif
21
23
24using namespace Qt::StringLiterals;
25
26Q_LOGGING_CATEGORY(lcDebugTrace, "qt.core.ctf", QtWarningMsg)
27
28static const size_t packetHeaderSize = 24 + 6 * 8 + 4;
29static const size_t packetSize = 4096;
30
31static const char traceMetadataTemplate[] =
32#include "metadata_template.h"
33;
34static const size_t traceMetadataSize = sizeof(traceMetadataTemplate);
35
36static inline QString allLiteral() { return QStringLiteral("all"); }
37static inline QString defaultLiteral() { return QStringLiteral("default"); }
38
39
40template <typename T>
42{
43 static_assert(std::is_arithmetic_v<T>);
44 arr.append(reinterpret_cast<char *>(&val), sizeof(val));
45 return arr;
46}
47
48static FILE *openFile(const QString &filename, const QString &mode)
49{
50#ifdef Q_OS_WINDOWS
51 return _wfopen(qUtf16Printable(filename), qUtf16Printable(mode));
52#else
53 return fopen(qPrintable(filename), qPrintable(mode));
54#endif
55}
56
57QCtfLibImpl *QCtfLibImpl::s_instance = nullptr;
58
60{
61 if (!s_instance)
62 s_instance = new QCtfLibImpl();
63 return s_instance;
64}
65
67{
68 delete s_instance;
69 s_instance = nullptr;
70}
71
73{
74 m_sessionChanged = true;
75}
76
78{
79 switch (status) {
80 case QCtfServer::Error: {
81 m_serverClosed = true;
82 } break;
83 default:
84 break;
85 }
86}
87
88void QCtfLibImpl::buildMetadata()
89{
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);
103}
104
106{
107 QString location = qEnvironmentVariable("QTRACE_LOCATION");
108 if (location.isEmpty()) {
109 qCInfo(lcDebugTrace) << "QTRACE_LOCATION not set";
110 return;
111 }
112
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();
120 m_streaming = true;
121 m_session.tracepoints.append(allLiteral());
122 m_session.name = defaultLiteral();
123 } else {
124#if !QT_CONFIG(cxx17_filesystem)
125 qCWarning(lcDebugTrace) << "Unable to use filesystem";
126 return;
127#endif
128 // Check if the location is writable
129 if (QT_ACCESS(qPrintable(location), W_OK) != 0) {
130 qCWarning(lcDebugTrace) << "Unable to write to location";
131 return;
132 }
133 const QString filename = location + u"/session.json";
134 FILE *file = openFile(qPrintable(filename), "rb"_L1);
135 if (!file) {
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();
141 } else {
142 QT_STATBUF stat;
143 if (QT_FSTAT(QT_FILENO(file), &stat) != 0) {
144 qCWarning(lcDebugTrace) << "Unable to stat session file, " << qt_error_string();
145 return;
146 }
147 qsizetype filesize = qMin(stat.st_size, std::numeric_limits<qsizetype>::max());
149 qsizetype size = static_cast<qsizetype>(fread(data.data(), 1, filesize, file));
150 fclose(file);
151 if (size != filesize)
152 return;
154 QJsonObject obj = json.object();
155 bool valid = false;
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());
162 valid = true;
163 }
164 }
165 if (!valid) {
166 qCWarning(lcDebugTrace) << "Session file is not valid";
167 m_session.tracepoints.append(allLiteral());
168 m_session.name = defaultLiteral();
169 }
170 m_location = location + u"/ust";
171#if QT_CONFIG(cxx17_filesystem)
172 std::filesystem::create_directory(qPrintable(m_location), qPrintable(location));
173#endif
174 }
175 clearLocation();
176 }
177
178 m_session.all = m_session.tracepoints.contains(allLiteral());
179 // Get datetime to when the timer was started to store the offset to epoch time for the traces
180 m_datetime = QDateTime::currentDateTime().toUTC();
181 m_timer.start();
182 if (!m_streaming)
183 buildMetadata();
184}
185
186void QCtfLibImpl::clearLocation()
187{
188#if QT_CONFIG(cxx17_filesystem)
189 const std::filesystem::path location{qUtf16Printable(m_location)};
190 for (auto const& dirEntry : std::filesystem::directory_iterator{location})
191 {
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()) {
197#else
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()) {
202#endif
203 if (!std::filesystem::remove(path)) {
204 qCInfo(lcDebugTrace) << "Unable to clear output location.";
205 break;
206 }
207 }
208 }
209#endif
210}
211
212void QCtfLibImpl::writeMetadata(const QString &metadata, bool overwrite)
213{
214 if (m_streaming) {
215 auto mt = metadata.toUtf8();
216 mt.resize(mt.size() - 1);
217 m_server->bufferData(QStringLiteral("metadata"), mt, overwrite);
218 } else {
219 FILE *file = nullptr;
220 file = openFile(qPrintable(m_location + "/metadata"_L1), overwrite ? "w+b"_L1: "ab"_L1);
221 if (!file)
222 return;
223
224 if (!overwrite)
225 fputs("\n", file);
226
227 // data contains zero at the end, hence size - 1.
228 const QByteArray data = metadata.toUtf8();
229 fwrite(data.data(), data.size() - 1, 1, file);
230 fclose(file);
231 }
232}
233
234void QCtfLibImpl::writeCtfPacket(QCtfLibImpl::Channel &ch)
235{
236 FILE *file = nullptr;
237 if (!m_streaming)
238 file = openFile(ch.channelName, "ab"_L1);
239 if (file || m_streaming) {
240 /* Each packet contains header and context, which are defined in the metadata.txt */
241 QByteArray packet;
242 packet << s_CtfHeaderMagic;
243 packet.append(QByteArrayView(s_TraceUuid.toBytes()));
244
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);
255 packet << (char)0;
256
257 Q_ASSERT(ch.data.size() + packetHeaderSize + ch.threadNameLength <= packetSize);
258 Q_ASSERT(packet.size() == qsizetype(packetHeaderSize + ch.threadNameLength));
259 if (m_streaming) {
260 ch.data.resize(packetSize - packet.size(), 0);
261 packet += ch.data;
262 m_server->bufferData(QString::fromLatin1(ch.channelName), packet, false);
263 } else {
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);
267 fclose(file);
268 }
269 }
270}
271
272QCtfLibImpl::Channel::~Channel()
273{
274 impl->writeCtfPacket(*this);
275 impl->removeChannel(this);
276}
277
279{
280 if (!m_server.isNull())
281 m_server->stopServer();
282 qDeleteAll(m_eventPrivs);
283}
284
285void QCtfLibImpl::removeChannel(Channel *ch)
286{
287 const QMutexLocker lock(&m_mutex);
288 m_channels.removeOne(ch);
289}
290
292{
293 if (m_sessionChanged) {
294 const QMutexLocker lock(&m_mutex);
295 buildMetadata();
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) {
306 writeCtfPacket(*ch);
307 ch->data.clear();
308 ch->minTimestamp = ch->maxTimestamp = timestamp;
309 }
310 }
311 if (m_streaming && (m_serverClosed || (!m_server->bufferOnIdle() && m_server->status() == QCtfServer::Idle)))
312 return false;
313 return m_session.all || m_session.tracepoints.contains(point.provider.provider);
314}
315
316static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId)
317{
318/*
319 generates event structure:
320event {
321 name = provider:tracepoint_name;
322 id = eventId;
323 stream_id = 0;
324 loglevel = 13;
325 fields := struct {
326 metadata
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";
334}
335
337{
338 QMutexLocker lock(&m_mutex);
340 if (!point.d) {
341 if (const auto &it = m_eventPrivs.find(point.eventName); it != m_eventPrivs.end()) {
342 priv = *it;
343 } else {
345 m_eventPrivs.insert(point.eventName, priv);
346 priv->id = eventId();
347 priv->metadata = toMetadata(point.provider.provider, point.eventName, point.metadata, priv->id);
348 }
349 }
350 return priv;
351}
352
354{
356 quint64 timestamp = 0;
357 QThread *thread = nullptr;
358 if (m_streaming && m_serverClosed)
359 return;
360 {
361 QMutexLocker lock(&m_mutex);
362 if (!priv->metadataWritten) {
363 priv->metadataWritten = true;
364 auto providerMetadata = point.provider.metadata;
365 while (providerMetadata) {
366 registerMetadata(*providerMetadata);
367 providerMetadata = providerMetadata->next;
368 }
369 if (m_newAdditionalMetadata.size()) {
370 for (const QString &name : m_newAdditionalMetadata)
371 writeMetadata(m_additionalMetadata[name]->metadata);
372 m_newAdditionalMetadata.clear();
373 }
374 writeMetadata(priv->metadata);
375 }
376 timestamp = m_timer.nsecsElapsed();
377 }
378 if (arr.size() != point.size) {
379 if (arr.size() < point.size)
380 return;
381 if (arr.size() > point.size && !point.variableSize && !point.metadata.isEmpty())
382 return;
383 }
384
386 if (thread == nullptr)
387 return;
388
389 Channel &ch = m_threadData.localData();
390
391 if (ch.channelName[0] == 0) {
392 ch.impl = this;
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;
397 ch.thread = thread;
398 ch.threadIndex = m_threadIndices[thread];
399 ch.threadName = thread->objectName().toUtf8();
400 if (ch.threadName.isEmpty()) {
401 const QMetaObject *obj = thread->metaObject();
402 ch.threadName = QByteArray(obj->className());
403 }
404 ch.threadNameLength = ch.threadName.size() + 1;
405 }
406 if (ch.locked)
407 return;
408 Q_ASSERT(ch.thread == thread);
409 ch.locked = true;
410
412 event << priv->id << timestamp;
413 if (!point.metadata.isEmpty())
414 event.append(arr);
415
416 if (ch.threadNameLength + ch.data.size() + event.size() + packetHeaderSize >= packetSize) {
417 writeCtfPacket(ch);
418 ch.data = event;
419 ch.minTimestamp = ch.maxTimestamp = timestamp;
420 } else {
421 ch.data.append(event);
422 }
423
424 ch.locked = false;
425 ch.maxTimestamp = timestamp;
426}
427
429{
430 return !m_session.name.isEmpty();
431}
432
434{
435 return m_eventId++;
436}
437
439{
440 if (m_additionalMetadata.contains(metadata.name))
441 return;
442
443 m_additionalMetadata.insert(metadata.name, &metadata);
444 m_newAdditionalMetadata.insert(metadata.name);
445}
446
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void handleStatusChange(QCtfServer::ServerStatus status) override
Definition qctflib.cpp:77
int eventId()
Definition qctflib.cpp:433
static QCtfLib * instance()
Definition qctflib.cpp:59
static void cleanup()
Definition qctflib.cpp:66
bool sessionEnabled() override
Definition qctflib.cpp:428
void registerMetadata(const QCtfTraceMetadata &metadata)
Definition qctflib.cpp:438
bool tracepointEnabled(const QCtfTracePointEvent &point) override
Definition qctflib.cpp:291
QCtfTracePointPrivate * initializeTracepoint(const QCtfTracePointEvent &point) override
Definition qctflib.cpp:336
friend struct Channel
Definition qctflib_p.h:120
void handleSessionChange() override
Definition qctflib.cpp:72
void doTracepoint(const QCtfTracePointEvent &point, const QByteArray &arr) override
Definition qctflib.cpp:353
void setHost(const QString &address)
QString sessionTracepoints() const
bool bufferOnIdle() const
void setCallback(ServerCallback *cb)
void startServer()
void stopServer()
void bufferData(const QString &stream, const QByteArray &data, quint32 flags)
void setPort(int port)
ServerStatus status() const
QString sessionName() const
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
qint64 toMSecsSinceEpoch() const
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
qint64 nsecsElapsed() const noexcept
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\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
bool removeOne(const AT &t)
Definition qlist.h:598
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmutex.h:313
QString objectName
the name of this object
Definition qobject.h:107
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
bool isNull() const noexcept
Returns true if this object refers to \nullptr.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
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
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
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:8084
QByteArray toUtf8() const &
Definition qstring.h:634
static QString machineHostName()
Definition qsysinfo.cpp:929
@ BigEndian
Definition qsysinfo.h:29
@ ByteOrder
Definition qsysinfo.h:34
T & localData()
Returns a reference to the data that was set by the calling thread.
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qurl.h:94
QString host(ComponentFormattingOptions=FullyDecoded) const
Returns the host of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2340
int port(int defaultPort=-1) const
Definition qurl.cpp:2383
@ WithoutBraces
Definition quuid.h:54
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:31
static QString toMetadata(const QString &provider, const QString &name, const QString &metadata, quint32 eventId)
Definition qctflib.cpp:316
static const size_t packetSize
Definition qctflib.cpp:29
static FILE * openFile(const QString &filename, const QString &mode)
Definition qctflib.cpp:48
static const size_t traceMetadataSize
Definition qctflib.cpp:34
static QString defaultLiteral()
Definition qctflib.cpp:37
static QString allLiteral()
Definition qctflib.cpp:36
static const size_t packetHeaderSize
Definition qctflib.cpp:28
static QByteArray & operator<<(QByteArray &arr, T val)
Definition qctflib.cpp:41
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter * sub
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
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,...)
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLint location
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
struct _cl_event * event
GLhandleARB obj
[2]
GLuint GLfloat * val
GLsizei const GLchar *const * path
#define Q_PROCESSOR_WORDSIZE
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
#define qUtf16Printable(string)
Definition qstring.h:1543
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
unsigned int quint32
Definition qtypes.h:50
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
QReadWriteLock lock
[0]
const QString name
Definition qctf_p.h:38
const bool variableSize
Definition qctf_p.h:56
const int size
Definition qctf_p.h:55
const QCtfTracePointProvider & provider
Definition qctf_p.h:52
QCtfTracePointPrivate * d
Definition qctf_p.h:62
const QString eventName
Definition qctf_p.h:53
const QString metadata
Definition qctf_p.h:54
QCtfTraceMetadata * metadata
Definition qctf_p.h:28
const QString provider
Definition qctf_p.h:27
\inmodule QtCore \reentrant
Definition qchar.h:18
\inmodule QtCore