// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QT_CTFSERVER_H #define QT_CTFSERVER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // // #include #include #include #include #include #include #include #include #include #include #include typedef struct ZSTD_CCtx_s ZSTD_CCtx; QT_BEGIN_NAMESPACE class QCtfServer; struct TracePacket { static constexpr quint32 PacketMagicNumber = 0x100924da; static constexpr quint32 PacketSize = 4096 + 9; QByteArray stream_name; QByteArray stream_data; quint32 flags = 0; TracePacket() = default; TracePacket(const TracePacket &t) { stream_name = t.stream_name; stream_data = t.stream_data; flags = t.flags; } TracePacket &operator = (const TracePacket &t) { stream_name = t.stream_name; stream_data = t.stream_data; flags = t.flags; return *this; } TracePacket(TracePacket &&t) { stream_name = std::move(t.stream_name); stream_data = std::move(t.stream_data); flags = t.flags; } TracePacket &operator = (TracePacket &&t) { stream_name = std::move(t.stream_name); stream_data = std::move(t.stream_data); flags = t.flags; return *this; } }; auto constexpr operator""_MB(quint64 s) -> quint64 { return s * 1024ul * 1024ul; } struct TraceRequest { quint32 clientId; quint32 clientVersion; quint32 flags; quint32 bufferSize; QString sessionName; QString sessionTracepoints; static constexpr quint32 MaxBufferSize = 1024_MB; bool isValid() const { if (clientId != 0 && clientVersion != 0 && !sessionName.isEmpty() && !sessionTracepoints.isEmpty() && bufferSize < MaxBufferSize) return true; return false; } }; struct TraceResponse { quint32 serverId; quint32 serverVersion; QString serverName; }; class QCtfServer : public QThread { Q_OBJECT public: enum ServerStatus { Uninitialized, Idle, Connected, Error, }; enum ServerFlags { CompressionMask = 255, DontBufferOnIdle = 256, // not set -> the server is buffering even without client connection // set -> the server is buffering only when client is connected }; enum RequestIds { RequestClientId = 0, RequestClientVersion, RequestSessionName, RequestSessionTracepoints, RequestFlags, RequestBufferSize, RequestCompressionScheme, }; struct ServerCallback { virtual void handleSessionChange() = 0; virtual void handleStatusChange(ServerStatus status) = 0; }; QCtfServer(QObject *parent = nullptr); ~QCtfServer(); void setCallback(ServerCallback *cb); void setHost(const QString &address); void setPort(int port); void run() override; void startServer(); void stopServer(); void bufferData(const QString &stream, const QByteArray &data, quint32 flags); QString sessionName() const; QString sessionTracepoints() const; bool bufferOnIdle() const; ServerStatus status() const; private: void initWrite(); void bytesWritten(qint64 size); bool waitSocket(); void readCbor(QCborStreamReader &cbor); void handleString(QCborStreamReader &cbor); void handleFixedWidth(QCborStreamReader &cbor); bool recognizedCompressionScheme() const; void setStatusAndNotify(ServerStatus status); void writePacket(TracePacket &packet, QCborStreamWriter &cbor); QMutex m_mutex; QWaitCondition m_bufferHasData; QList m_packets; QString m_address; QTcpServer *m_server = nullptr; QTcpSocket *m_socket = nullptr; QEventLoop *m_eventLoop = nullptr; QList m_keySet; TraceRequest m_req; ServerCallback *m_cb = nullptr; ServerStatus m_status = Uninitialized; qint64 m_waitWriteSize = 0; qint64 m_writtenSize = 0; int m_port; int m_compression = 0; int m_maxPackets = DefaultMaxPackets; QAtomicInt m_stopping; bool m_bufferOnIdle = true; QString m_currentKey; QString m_requestedCompressionScheme; #if QT_CONFIG(zstd) ZSTD_CCtx *m_zstdCCtx = nullptr; #endif static constexpr quint32 ServerId = 1; static constexpr quint32 DefaultMaxPackets = 256; // 1 MB }; QT_END_NAMESPACE #endif