21#include <QCoreApplication>
29#include "tracy/Tracy.hpp"
30#include "tracy/TracyC.h"
33using namespace Qt::StringLiterals;
37using namespace Qt::StringLiterals;
41 QgsEventTracing::EventType type;
50static bool sIsTracing =
false;
59bool QgsEventTracing::startTracing()
65 sTraceEventsMutex()->lock();
66 sTracingTimer()->start();
67 sTraceEvents()->clear();
68 sTraceEvents()->reserve( 1000 );
69 sTraceEventsMutex()->unlock();
73bool QgsEventTracing::stopTracing()
79 sTracingTimer()->invalidate();
83bool QgsEventTracing::isTracingEnabled()
88static char _eventTypeToChar( QgsEventTracing::EventType type )
92 case QgsEventTracing::Begin:
94 case QgsEventTracing::End:
96 case QgsEventTracing::Instant:
98 case QgsEventTracing::AsyncBegin:
100 case QgsEventTracing::AsyncEnd:
106bool QgsEventTracing::writeTrace(
const QString &fileName )
112 if ( !f.open( QIODevice::WriteOnly ) )
115 f.write(
"{\n\"traceEvents\": [\n" );
118 for (
const auto &item : *sTraceEvents() )
124 const char t = _eventTypeToChar( item.type );
125 QString msg = u
" {\"cat\": \"%1\", \"pid\": 1, \"tid\": %2, \"ts\": %3, \"ph\": \"%4\", \"name\": \"%5\""_s.arg( item.category ).arg( item.threadId ).arg( item.timestamp ).arg( t ).arg( item.name );
128 if ( item.type == Instant )
129 msg +=
", \"s\": \"g\""_L1;
132 if ( item.type == AsyncBegin || item.type == AsyncEnd )
133 msg += u
", \"id\": \"%1\""_s.arg( item.id );
137 f.write( msg.toUtf8() );
140 f.write(
"\n]\n}\n" );
152 bool operator==(
const NameAndId &other )
const {
return name == other.name &&
id == other.id; }
154 friend size_t qHash(
const NameAndId &key,
size_t seed = 0 ) {
return qHash( key.name, seed ) ^
qHash( key.id, seed ); }
159 TracyCZoneCtx zoneCtx;
160 std::optional<qsizetype> asyncDummyId;
163struct TracyZoneDummyThread
165 QByteArray nameHandle;
170void QgsEventTracing::addEvent( QgsEventTracing::EventType type,
const QString &category,
const QString &name,
const QString &
id )
173 static QMutex zonesRegistryLock;
174 static QHash<NameAndId, TracyZoneState> zonesByNameAndId;
179 static QList<TracyZoneDummyThread> asyncDummyThreads;
180 QString zoneNameStr = u
"[%1] %2"_s.arg( category, name );
181 QByteArray zoneName = ( zoneNameStr ).toLocal8Bit();
182 NameAndId nameAndId { zoneName,
id };
184 std::optional<qsizetype> usingDummyThread = std::nullopt;
189 QMutexLocker<QMutex> lock( &zonesRegistryLock );
190 for ( qsizetype i = 0; i < asyncDummyThreads.size(); i++ )
192 TracyZoneDummyThread &dummyThread = asyncDummyThreads[i];
193 if ( !dummyThread.occupied )
196 TracyCFiberEnter( dummyThread.nameHandle.data() );
197 usingDummyThread = i;
198 dummyThread.occupied =
true;
202 if ( !usingDummyThread )
205 asyncDummyThreads.append( {
206 u
"Async %1"_s.arg( asyncDummyThreads.size(), 3, 10,
'0' ).toLocal8Bit(),
209 TracyCFiberEnter( asyncDummyThreads.last().nameHandle.data() );
210 usingDummyThread = asyncDummyThreads.size() - 1;
216 QMutexLocker<QMutex> lock( &zonesRegistryLock );
217 if ( zonesByNameAndId.contains( nameAndId ) )
219 QgsDebugError( u
"Tried to re-begin zone! (name: '%1', id: '%1')"_s.arg( zoneNameStr,
id ) );
223 uint64_t srcloc = ___tracy_alloc_srcloc_name( 0,
"", 0,
"", 0, zoneName.constData(), zoneName.size(), 0 );
224 TracyCZoneCtx zone = ___tracy_emit_zone_begin_alloc( srcloc,
true );
227 QByteArray extraText =
id.toLocal8Bit();
228 TracyCZoneText( zone, extraText.data(), extraText.size() );
230 zonesByNameAndId[nameAndId] = { zone, usingDummyThread };
236 QMutexLocker<QMutex> lock( &zonesRegistryLock );
237 auto zoneIt = zonesByNameAndId.constFind( nameAndId );
238 if ( zoneIt == zonesByNameAndId.end() )
240 QgsDebugError( u
"Tried to end unstarted zone! "_s + zoneNameStr );
244 if ( zoneIt->asyncDummyId )
246 TracyZoneDummyThread &dummyThread = asyncDummyThreads[*zoneIt->asyncDummyId];
247 TracyCFiberEnter( dummyThread.nameHandle.data() );
248 dummyThread.occupied =
false;
249 usingDummyThread =
true;
252 ___tracy_emit_zone_end( zoneIt->zoneCtx );
253 zonesByNameAndId.erase( zoneIt );
257 TracyMessageC( zoneName.constData(), zoneName.size(), 0x4444EE );
260 if ( usingDummyThread )
266 QgsEventTracing::addEventToQgisTrace( type, category, name,
id );
269void QgsEventTracing::addEventToQgisTrace( QgsEventTracing::EventType type,
const QString &category,
const QString &name,
const QString &
id )
274 sTraceEventsMutex()->lock();
277 item.timestamp = sTracingTimer()->nsecsElapsed() / 1000;
278 if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
281 item.threadId =
static_cast<uint
>(
reinterpret_cast<quint64
>( QThread::currentThreadId() ) );
282 item.category = category;
285 sTraceEvents()->append( item );
286 sTraceEventsMutex()->unlock();
289void QgsEventTracing::setFloatVariable(
const char *name,
double value,
bool continuous )
293 TracyPlotConfig( name, tracy::PlotFormatType::Number,
true,
false, 0 );
294 TracyPlot( name, value );
298 Q_UNUSED( continuous );
302void QgsEventTracing::setIntVariable(
const char *name, int64_t value,
bool continuous )
306 TracyPlotConfig( name, tracy::PlotFormatType::Number,
true,
false, 0 );
307 TracyPlot( name, value );
311 Q_UNUSED( continuous );
315size_t QgsScopedEvent::sNextId = 0;
uint qHash(const QVariant &variant)
Hash for QVariant.
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)
#define QgsDebugError(str)