The open source OpenXR runtime
1#ifndef __TRACYPROFILER_HPP__
2#define __TRACYPROFILER_HPP__
3
4#include <assert.h>
5#include <atomic>
6#include <stdint.h>
7#include <string.h>
8#include <time.h>
9
10#include "tracy_concurrentqueue.h"
11#include "tracy_SPSCQueue.h"
12#include "TracyCallstack.hpp"
13#include "TracySysTime.hpp"
14#include "TracyFastVector.hpp"
15#include "../common/TracyQueue.hpp"
16#include "../common/TracyAlign.hpp"
17#include "../common/TracyAlloc.hpp"
18#include "../common/TracyMutex.hpp"
19#include "../common/TracyProtocol.hpp"
20
21#if defined _WIN32
22# include <intrin.h>
23#endif
24#ifdef __APPLE__
25# include <TargetConditionals.h>
26# include <mach/mach_time.h>
27#endif
28
29#if ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) )
30# define TRACY_HW_TIMER
31#endif
32
33#ifdef __linux__
34# include <signal.h>
35#endif
36
37#if defined TRACY_TIMER_FALLBACK || !defined TRACY_HW_TIMER
38# include <chrono>
39#endif
40
41#ifndef TracyConcat
42# define TracyConcat(x,y) TracyConcatIndirect(x,y)
43#endif
44#ifndef TracyConcatIndirect
45# define TracyConcatIndirect(x,y) x##y
46#endif
47
48namespace tracy
49{
50#if defined(TRACY_DELAYED_INIT) && defined(TRACY_MANUAL_LIFETIME)
51TRACY_API void StartupProfiler();
52TRACY_API void ShutdownProfiler();
53#endif
54
55class GpuCtx;
56class Profiler;
57class Socket;
58class UdpBroadcast;
59
60struct GpuCtxWrapper
61{
62 GpuCtx* ptr;
63};
64
65TRACY_API moodycamel::ConcurrentQueue<QueueItem>::ExplicitProducer* GetToken();
66TRACY_API Profiler& GetProfiler();
67TRACY_API std::atomic<uint32_t>& GetLockCounter();
68TRACY_API std::atomic<uint8_t>& GetGpuCtxCounter();
69TRACY_API GpuCtxWrapper& GetGpuCtx();
70TRACY_API uint32_t GetThreadHandle();
71TRACY_API bool ProfilerAvailable();
72TRACY_API bool ProfilerAllocatorAvailable();
73TRACY_API int64_t GetFrequencyQpc();
74
75#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
76TRACY_API bool HardwareSupportsInvariantTSC(); // check, if we need fallback scenario
77#else
78# if defined TRACY_HW_TIMER
79tracy_force_inline bool HardwareSupportsInvariantTSC()
80{
81 return true; // this is checked at startup
82}
83# else
84tracy_force_inline bool HardwareSupportsInvariantTSC()
85{
86 return false;
87}
88# endif
89#endif
90
91
92struct SourceLocationData
93{
94 const char* name;
95 const char* function;
96 const char* file;
97 uint32_t line;
98 uint32_t color;
99};
100
101#ifdef TRACY_ON_DEMAND
102struct LuaZoneState
103{
104 uint32_t counter;
105 bool active;
106};
107#endif
108
109
110#define TracyLfqPrepare( _type ) \
111 moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \
112 auto __token = GetToken(); \
113 auto& __tail = __token->get_tail_index(); \
114 auto item = __token->enqueue_begin( __magic ); \
115 MemWrite( &item->hdr.type, _type );
116
117#define TracyLfqCommit \
118 __tail.store( __magic + 1, std::memory_order_release );
119
120#define TracyLfqPrepareC( _type ) \
121 tracy::moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \
122 auto __token = tracy::GetToken(); \
123 auto& __tail = __token->get_tail_index(); \
124 auto item = __token->enqueue_begin( __magic ); \
125 tracy::MemWrite( &item->hdr.type, _type );
126
127#define TracyLfqCommitC \
128 __tail.store( __magic + 1, std::memory_order_release );
129
130
131#ifdef TRACY_FIBERS
132# define TracyQueuePrepare( _type ) \
133 auto item = Profiler::QueueSerial(); \
134 MemWrite( &item->hdr.type, _type );
135# define TracyQueueCommit( _name ) \
136 MemWrite( &item->_name.thread, GetThreadHandle() ); \
137 Profiler::QueueSerialFinish();
138# define TracyQueuePrepareC( _type ) \
139 auto item = tracy::Profiler::QueueSerial(); \
140 tracy::MemWrite( &item->hdr.type, _type );
141# define TracyQueueCommitC( _name ) \
142 tracy::MemWrite( &item->_name.thread, tracy::GetThreadHandle() ); \
143 tracy::Profiler::QueueSerialFinish();
144#else
145# define TracyQueuePrepare( _type ) TracyLfqPrepare( _type )
146# define TracyQueueCommit( _name ) TracyLfqCommit
147# define TracyQueuePrepareC( _type ) TracyLfqPrepareC( _type )
148# define TracyQueueCommitC( _name ) TracyLfqCommitC
149#endif
150
151
152typedef void(*ParameterCallback)( void* data, uint32_t idx, int32_t val );
153typedef char*(*SourceContentsCallback)( void* data, const char* filename, size_t& size );
154
155class Profiler
156{
157 struct FrameImageQueueItem
158 {
159 void* image;
160 uint32_t frame;
161 uint16_t w;
162 uint16_t h;
163 bool flip;
164 };
165
166 enum class SymbolQueueItemType
167 {
168 CallstackFrame,
169 SymbolQuery,
170 ExternalName,
171 KernelCode,
172 SourceCode
173 };
174
175 struct SymbolQueueItem
176 {
177 SymbolQueueItemType type;
178 uint64_t ptr;
179 uint64_t extra;
180 uint32_t id;
181
182 SymbolQueueItem(SymbolQueueItemType type, uint64_t ptr)
183 : SymbolQueueItem(type, ptr, 0) {}
184 SymbolQueueItem(SymbolQueueItemType type, uint64_t ptr, uint64_t extra)
185 : SymbolQueueItem(type, ptr, extra, 0) {}
186 SymbolQueueItem(SymbolQueueItemType type, uint64_t ptr, uint64_t extra, uint32_t id)
187 : type{type}, ptr{ptr}, extra{extra}, id{id} {}
188 };
189
190public:
191 Profiler();
192 ~Profiler();
193
194 void SpawnWorkerThreads();
195
196 static tracy_force_inline int64_t GetTime()
197 {
198#ifdef TRACY_HW_TIMER
199# if defined TARGET_OS_IOS && TARGET_OS_IOS == 1
200 if( HardwareSupportsInvariantTSC() ) return mach_absolute_time();
201# elif defined _WIN32
202# ifdef TRACY_TIMER_QPC
203 return GetTimeQpc();
204# else
205 if( HardwareSupportsInvariantTSC() ) return int64_t( __rdtsc() );
206# endif
207# elif defined __i386 || defined _M_IX86
208 if( HardwareSupportsInvariantTSC() )
209 {
210 uint32_t eax, edx;
211 asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) );
212 return ( uint64_t( edx ) << 32 ) + uint64_t( eax );
213 }
214# elif defined __x86_64__ || defined _M_X64
215 if( HardwareSupportsInvariantTSC() )
216 {
217 uint64_t rax, rdx;
218 asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) );
219 return (int64_t)(( rdx << 32 ) + rax);
220 }
221# else
222# error "TRACY_HW_TIMER detection logic needs fixing"
223# endif
224#endif
225
226#if !defined TRACY_HW_TIMER || defined TRACY_TIMER_FALLBACK
227# if defined __linux__ && defined CLOCK_MONOTONIC_RAW
228 struct timespec ts;
229 clock_gettime( CLOCK_MONOTONIC_RAW, &ts );
230 return int64_t( ts.tv_sec ) * 1000000000ll + int64_t( ts.tv_nsec );
231# else
232 return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
233# endif
234#endif
235
236#if !defined TRACY_TIMER_FALLBACK
237 return 0; // unreachable branch
238#endif
239 }
240
241 tracy_force_inline uint32_t GetNextZoneId()
242 {
243 return m_zoneId.fetch_add( 1, std::memory_order_relaxed );
244 }
245
246 static tracy_force_inline QueueItem* QueueSerial()
247 {
248 auto& p = GetProfiler();
249 p.m_serialLock.lock();
250 return p.m_serialQueue.prepare_next();
251 }
252
253 static tracy_force_inline QueueItem* QueueSerialCallstack( void* ptr )
254 {
255 auto& p = GetProfiler();
256 p.m_serialLock.lock();
257 p.SendCallstackSerial( ptr );
258 return p.m_serialQueue.prepare_next();
259 }
260
261 static tracy_force_inline void QueueSerialFinish()
262 {
263 auto& p = GetProfiler();
264 p.m_serialQueue.commit_next();
265 p.m_serialLock.unlock();
266 }
267
268 static tracy_force_inline void SendFrameMark( const char* name )
269 {
270 if( !name ) GetProfiler().m_frameCount.fetch_add( 1, std::memory_order_relaxed );
271#ifdef TRACY_ON_DEMAND
272 if( !GetProfiler().IsConnected() ) return;
273#endif
274 auto item = QueueSerial();
275 MemWrite( &item->hdr.type, QueueType::FrameMarkMsg );
276 MemWrite( &item->frameMark.time, GetTime() );
277 MemWrite( &item->frameMark.name, uint64_t( name ) );
278 QueueSerialFinish();
279 }
280
281 static tracy_force_inline void SendFrameMark( const char* name, QueueType type )
282 {
283 assert( type == QueueType::FrameMarkMsgStart || type == QueueType::FrameMarkMsgEnd );
284#ifdef TRACY_ON_DEMAND
285 if( !GetProfiler().IsConnected() ) return;
286#endif
287 auto item = QueueSerial();
288 MemWrite( &item->hdr.type, type );
289 MemWrite( &item->frameMark.time, GetTime() );
290 MemWrite( &item->frameMark.name, uint64_t( name ) );
291 QueueSerialFinish();
292 }
293
294 static tracy_force_inline void SendFrameImage( const void* image, uint16_t w, uint16_t h, uint8_t offset, bool flip )
295 {
296#ifndef TRACY_NO_FRAME_IMAGE
297 auto& profiler = GetProfiler();
298 assert( profiler.m_frameCount.load( std::memory_order_relaxed ) < std::numeric_limits<uint32_t>::max() );
299# ifdef TRACY_ON_DEMAND
300 if( !profiler.IsConnected() ) return;
301# endif
302 const auto sz = size_t( w ) * size_t( h ) * 4;
303 auto ptr = (char*)tracy_malloc( sz );
304 memcpy( ptr, image, sz );
305
306 profiler.m_fiLock.lock();
307 auto fi = profiler.m_fiQueue.prepare_next();
308 fi->image = ptr;
309 fi->frame = uint32_t( profiler.m_frameCount.load( std::memory_order_relaxed ) - offset );
310 fi->w = w;
311 fi->h = h;
312 fi->flip = flip;
313 profiler.m_fiQueue.commit_next();
314 profiler.m_fiLock.unlock();
315#endif
316 }
317
318 static tracy_force_inline void PlotData( const char* name, int64_t val )
319 {
320#ifdef TRACY_ON_DEMAND
321 if( !GetProfiler().IsConnected() ) return;
322#endif
323 TracyLfqPrepare( QueueType::PlotDataInt );
324 MemWrite( &item->plotDataInt.name, (uint64_t)name );
325 MemWrite( &item->plotDataInt.time, GetTime() );
326 MemWrite( &item->plotDataInt.val, val );
327 TracyLfqCommit;
328 }
329
330 static tracy_force_inline void PlotData( const char* name, float val )
331 {
332#ifdef TRACY_ON_DEMAND
333 if( !GetProfiler().IsConnected() ) return;
334#endif
335 TracyLfqPrepare( QueueType::PlotDataFloat );
336 MemWrite( &item->plotDataFloat.name, (uint64_t)name );
337 MemWrite( &item->plotDataFloat.time, GetTime() );
338 MemWrite( &item->plotDataFloat.val, val );
339 TracyLfqCommit;
340 }
341
342 static tracy_force_inline void PlotData( const char* name, double val )
343 {
344#ifdef TRACY_ON_DEMAND
345 if( !GetProfiler().IsConnected() ) return;
346#endif
347 TracyLfqPrepare( QueueType::PlotDataDouble );
348 MemWrite( &item->plotDataDouble.name, (uint64_t)name );
349 MemWrite( &item->plotDataDouble.time, GetTime() );
350 MemWrite( &item->plotDataDouble.val, val );
351 TracyLfqCommit;
352 }
353
354 static tracy_force_inline void ConfigurePlot( const char* name, PlotFormatType type, bool step, bool fill, uint32_t color )
355 {
356 TracyLfqPrepare( QueueType::PlotConfig );
357 MemWrite( &item->plotConfig.name, (uint64_t)name );
358 MemWrite( &item->plotConfig.type, (uint8_t)type );
359 MemWrite( &item->plotConfig.step, (uint8_t)step );
360 MemWrite( &item->plotConfig.fill, (uint8_t)fill );
361 MemWrite( &item->plotConfig.color, color );
362
363#ifdef TRACY_ON_DEMAND
364 GetProfiler().DeferItem( *item );
365#endif
366
367 TracyLfqCommit;
368 }
369
370 static tracy_force_inline void Message( const char* txt, size_t size, int callstack )
371 {
372 assert( size < std::numeric_limits<uint16_t>::max() );
373#ifdef TRACY_ON_DEMAND
374 if( !GetProfiler().IsConnected() ) return;
375#endif
376 if( callstack != 0 )
377 {
378 tracy::GetProfiler().SendCallstack( callstack );
379 }
380
381 auto ptr = (char*)tracy_malloc( size );
382 memcpy( ptr, txt, size );
383
384 TracyQueuePrepare( callstack == 0 ? QueueType::Message : QueueType::MessageCallstack );
385 MemWrite( &item->messageFat.time, GetTime() );
386 MemWrite( &item->messageFat.text, (uint64_t)ptr );
387 MemWrite( &item->messageFat.size, (uint16_t)size );
388 TracyQueueCommit( messageFatThread );
389 }
390
391 static tracy_force_inline void Message( const char* txt, int callstack )
392 {
393#ifdef TRACY_ON_DEMAND
394 if( !GetProfiler().IsConnected() ) return;
395#endif
396 if( callstack != 0 )
397 {
398 tracy::GetProfiler().SendCallstack( callstack );
399 }
400
401 TracyQueuePrepare( callstack == 0 ? QueueType::MessageLiteral : QueueType::MessageLiteralCallstack );
402 MemWrite( &item->messageLiteral.time, GetTime() );
403 MemWrite( &item->messageLiteral.text, (uint64_t)txt );
404 TracyQueueCommit( messageLiteralThread );
405 }
406
407 static tracy_force_inline void MessageColor( const char* txt, size_t size, uint32_t color, int callstack )
408 {
409 assert( size < std::numeric_limits<uint16_t>::max() );
410#ifdef TRACY_ON_DEMAND
411 if( !GetProfiler().IsConnected() ) return;
412#endif
413 if( callstack != 0 )
414 {
415 tracy::GetProfiler().SendCallstack( callstack );
416 }
417
418 auto ptr = (char*)tracy_malloc( size );
419 memcpy( ptr, txt, size );
420
421 TracyQueuePrepare( callstack == 0 ? QueueType::MessageColor : QueueType::MessageColorCallstack );
422 MemWrite( &item->messageColorFat.time, GetTime() );
423 MemWrite( &item->messageColorFat.text, (uint64_t)ptr );
424 MemWrite( &item->messageColorFat.b, uint8_t( ( color ) & 0xFF ) );
425 MemWrite( &item->messageColorFat.g, uint8_t( ( color >> 8 ) & 0xFF ) );
426 MemWrite( &item->messageColorFat.r, uint8_t( ( color >> 16 ) & 0xFF ) );
427 MemWrite( &item->messageColorFat.size, (uint16_t)size );
428 TracyQueueCommit( messageColorFatThread );
429 }
430
431 static tracy_force_inline void MessageColor( const char* txt, uint32_t color, int callstack )
432 {
433#ifdef TRACY_ON_DEMAND
434 if( !GetProfiler().IsConnected() ) return;
435#endif
436 if( callstack != 0 )
437 {
438 tracy::GetProfiler().SendCallstack( callstack );
439 }
440
441 TracyQueuePrepare( callstack == 0 ? QueueType::MessageLiteralColor : QueueType::MessageLiteralColorCallstack );
442 MemWrite( &item->messageColorLiteral.time, GetTime() );
443 MemWrite( &item->messageColorLiteral.text, (uint64_t)txt );
444 MemWrite( &item->messageColorLiteral.b, uint8_t( ( color ) & 0xFF ) );
445 MemWrite( &item->messageColorLiteral.g, uint8_t( ( color >> 8 ) & 0xFF ) );
446 MemWrite( &item->messageColorLiteral.r, uint8_t( ( color >> 16 ) & 0xFF ) );
447 TracyQueueCommit( messageColorLiteralThread );
448 }
449
450 static tracy_force_inline void MessageAppInfo( const char* txt, size_t size )
451 {
452 assert( size < std::numeric_limits<uint16_t>::max() );
453 auto ptr = (char*)tracy_malloc( size );
454 memcpy( ptr, txt, size );
455 TracyLfqPrepare( QueueType::MessageAppInfo );
456 MemWrite( &item->messageFat.time, GetTime() );
457 MemWrite( &item->messageFat.text, (uint64_t)ptr );
458 MemWrite( &item->messageFat.size, (uint16_t)size );
459
460#ifdef TRACY_ON_DEMAND
461 GetProfiler().DeferItem( *item );
462#endif
463
464 TracyLfqCommit;
465 }
466
467 static tracy_force_inline void MemAlloc( const void* ptr, size_t size, bool secure )
468 {
469 if( secure && !ProfilerAvailable() ) return;
470#ifdef TRACY_ON_DEMAND
471 if( !GetProfiler().IsConnected() ) return;
472#endif
473 const auto thread = GetThreadHandle();
474
475 GetProfiler().m_serialLock.lock();
476 SendMemAlloc( QueueType::MemAlloc, thread, ptr, size );
477 GetProfiler().m_serialLock.unlock();
478 }
479
480 static tracy_force_inline void MemFree( const void* ptr, bool secure )
481 {
482 if( secure && !ProfilerAvailable() ) return;
483#ifdef TRACY_ON_DEMAND
484 if( !GetProfiler().IsConnected() ) return;
485#endif
486 const auto thread = GetThreadHandle();
487
488 GetProfiler().m_serialLock.lock();
489 SendMemFree( QueueType::MemFree, thread, ptr );
490 GetProfiler().m_serialLock.unlock();
491 }
492
493 static tracy_force_inline void MemAllocCallstack( const void* ptr, size_t size, int depth, bool secure )
494 {
495 if( secure && !ProfilerAvailable() ) return;
496#ifdef TRACY_HAS_CALLSTACK
497 auto& profiler = GetProfiler();
498# ifdef TRACY_ON_DEMAND
499 if( !profiler.IsConnected() ) return;
500# endif
501 const auto thread = GetThreadHandle();
502
503 auto callstack = Callstack( depth );
504
505 profiler.m_serialLock.lock();
506 SendCallstackSerial( callstack );
507 SendMemAlloc( QueueType::MemAllocCallstack, thread, ptr, size );
508 profiler.m_serialLock.unlock();
509#else
510 static_cast<void>(depth); // unused
511 MemAlloc( ptr, size, secure );
512#endif
513 }
514
515 static tracy_force_inline void MemFreeCallstack( const void* ptr, int depth, bool secure )
516 {
517 if( secure && !ProfilerAvailable() ) return;
518 if( !ProfilerAllocatorAvailable() )
519 {
520 MemFree( ptr, secure );
521 return;
522 }
523#ifdef TRACY_HAS_CALLSTACK
524 auto& profiler = GetProfiler();
525# ifdef TRACY_ON_DEMAND
526 if( !profiler.IsConnected() ) return;
527# endif
528 const auto thread = GetThreadHandle();
529
530 auto callstack = Callstack( depth );
531
532 profiler.m_serialLock.lock();
533 SendCallstackSerial( callstack );
534 SendMemFree( QueueType::MemFreeCallstack, thread, ptr );
535 profiler.m_serialLock.unlock();
536#else
537 static_cast<void>(depth); // unused
538 MemFree( ptr, secure );
539#endif
540 }
541
542 static tracy_force_inline void MemAllocNamed( const void* ptr, size_t size, bool secure, const char* name )
543 {
544 if( secure && !ProfilerAvailable() ) return;
545#ifdef TRACY_ON_DEMAND
546 if( !GetProfiler().IsConnected() ) return;
547#endif
548 const auto thread = GetThreadHandle();
549
550 GetProfiler().m_serialLock.lock();
551 SendMemName( name );
552 SendMemAlloc( QueueType::MemAllocNamed, thread, ptr, size );
553 GetProfiler().m_serialLock.unlock();
554 }
555
556 static tracy_force_inline void MemFreeNamed( const void* ptr, bool secure, const char* name )
557 {
558 if( secure && !ProfilerAvailable() ) return;
559#ifdef TRACY_ON_DEMAND
560 if( !GetProfiler().IsConnected() ) return;
561#endif
562 const auto thread = GetThreadHandle();
563
564 GetProfiler().m_serialLock.lock();
565 SendMemName( name );
566 SendMemFree( QueueType::MemFreeNamed, thread, ptr );
567 GetProfiler().m_serialLock.unlock();
568 }
569
570 static tracy_force_inline void MemAllocCallstackNamed( const void* ptr, size_t size, int depth, bool secure, const char* name )
571 {
572 if( secure && !ProfilerAvailable() ) return;
573#ifdef TRACY_HAS_CALLSTACK
574 auto& profiler = GetProfiler();
575# ifdef TRACY_ON_DEMAND
576 if( !profiler.IsConnected() ) return;
577# endif
578 const auto thread = GetThreadHandle();
579
580 auto callstack = Callstack( depth );
581
582 profiler.m_serialLock.lock();
583 SendCallstackSerial( callstack );
584 SendMemName( name );
585 SendMemAlloc( QueueType::MemAllocCallstackNamed, thread, ptr, size );
586 profiler.m_serialLock.unlock();
587#else
588 static_cast<void>(depth); // unused
589 static_cast<void>(name); // unused
590 MemAlloc( ptr, size, secure );
591#endif
592 }
593
594 static tracy_force_inline void MemFreeCallstackNamed( const void* ptr, int depth, bool secure, const char* name )
595 {
596 if( secure && !ProfilerAvailable() ) return;
597#ifdef TRACY_HAS_CALLSTACK
598 auto& profiler = GetProfiler();
599# ifdef TRACY_ON_DEMAND
600 if( !profiler.IsConnected() ) return;
601# endif
602 const auto thread = GetThreadHandle();
603
604 auto callstack = Callstack( depth );
605
606 profiler.m_serialLock.lock();
607 SendCallstackSerial( callstack );
608 SendMemName( name );
609 SendMemFree( QueueType::MemFreeCallstackNamed, thread, ptr );
610 profiler.m_serialLock.unlock();
611#else
612 static_cast<void>(depth); // unused
613 static_cast<void>(name); // unused
614 MemFree( ptr, secure );
615#endif
616 }
617
618 static tracy_force_inline void SendCallstack( int depth )
619 {
620#ifdef TRACY_HAS_CALLSTACK
621 auto ptr = Callstack( depth );
622 TracyQueuePrepare( QueueType::Callstack );
623 MemWrite( &item->callstackFat.ptr, (uint64_t)ptr );
624 TracyQueueCommit( callstackFatThread );
625#else
626 static_cast<void>(depth); // unused
627#endif
628 }
629
630 static tracy_force_inline void ParameterRegister( ParameterCallback cb, void* data )
631 {
632 auto& profiler = GetProfiler();
633 profiler.m_paramCallback = cb;
634 profiler.m_paramCallbackData = data;
635 }
636
637 static tracy_force_inline void ParameterSetup( uint32_t idx, const char* name, bool isBool, int32_t val )
638 {
639 TracyLfqPrepare( QueueType::ParamSetup );
640 tracy::MemWrite( &item->paramSetup.idx, idx );
641 tracy::MemWrite( &item->paramSetup.name, (uint64_t)name );
642 tracy::MemWrite( &item->paramSetup.isBool, (uint8_t)isBool );
643 tracy::MemWrite( &item->paramSetup.val, val );
644
645#ifdef TRACY_ON_DEMAND
646 GetProfiler().DeferItem( *item );
647#endif
648
649 TracyLfqCommit;
650 }
651
652 static tracy_force_inline void SourceCallbackRegister( SourceContentsCallback cb, void* data )
653 {
654 auto& profiler = GetProfiler();
655 profiler.m_sourceCallback = cb;
656 profiler.m_sourceCallbackData = data;
657 }
658
659#ifdef TRACY_FIBERS
660 static tracy_force_inline void EnterFiber( const char* fiber )
661 {
662 TracyQueuePrepare( QueueType::FiberEnter );
663 MemWrite( &item->fiberEnter.time, GetTime() );
664 MemWrite( &item->fiberEnter.fiber, (uint64_t)fiber );
665 TracyQueueCommit( fiberEnter );
666 }
667
668 static tracy_force_inline void LeaveFiber()
669 {
670 TracyQueuePrepare( QueueType::FiberLeave );
671 MemWrite( &item->fiberLeave.time, GetTime() );
672 TracyQueueCommit( fiberLeave );
673 }
674#endif
675
676 void SendCallstack( int depth, const char* skipBefore );
677 static void CutCallstack( void* callstack, const char* skipBefore );
678
679 static bool ShouldExit();
680
681 tracy_force_inline bool IsConnected() const
682 {
683 return m_isConnected.load( std::memory_order_acquire );
684 }
685
686#ifdef TRACY_ON_DEMAND
687 tracy_force_inline uint64_t ConnectionId() const
688 {
689 return m_connectionId.load( std::memory_order_acquire );
690 }
691
692 tracy_force_inline void DeferItem( const QueueItem& item )
693 {
694 m_deferredLock.lock();
695 auto dst = m_deferredQueue.push_next();
696 memcpy( dst, &item, sizeof( item ) );
697 m_deferredLock.unlock();
698 }
699#endif
700
701 void RequestShutdown() { m_shutdown.store( true, std::memory_order_relaxed ); m_shutdownManual.store( true, std::memory_order_relaxed ); }
702 bool HasShutdownFinished() const { return m_shutdownFinished.load( std::memory_order_relaxed ); }
703
704 void SendString( uint64_t str, const char* ptr, QueueType type ) { SendString( str, ptr, strlen( ptr ), type ); }
705 void SendString( uint64_t str, const char* ptr, size_t len, QueueType type );
706 void SendSingleString( const char* ptr ) { SendSingleString( ptr, strlen( ptr ) ); }
707 void SendSingleString( const char* ptr, size_t len );
708 void SendSecondString( const char* ptr ) { SendSecondString( ptr, strlen( ptr ) ); }
709 void SendSecondString( const char* ptr, size_t len );
710
711
712 // Allocated source location data layout:
713 // 2b payload size
714 // 4b color
715 // 4b source line
716 // fsz function name
717 // 1b null terminator
718 // ssz source file name
719 // 1b null terminator
720 // nsz zone name (optional)
721
722 static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function )
723 {
724 return AllocSourceLocation( line, source, function, nullptr, 0 );
725 }
726
727 static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, const char* name, size_t nameSz )
728 {
729 return AllocSourceLocation( line, source, strlen(source), function, strlen(function), name, nameSz );
730 }
731
732 static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz )
733 {
734 return AllocSourceLocation( line, source, sourceSz, function, functionSz, nullptr, 0 );
735 }
736
737 static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz )
738 {
739 const auto sz32 = uint32_t( 2 + 4 + 4 + functionSz + 1 + sourceSz + 1 + nameSz );
740 assert( sz32 <= std::numeric_limits<uint16_t>::max() );
741 const auto sz = uint16_t( sz32 );
742 auto ptr = (char*)tracy_malloc( sz );
743 memcpy( ptr, &sz, 2 );
744 memset( ptr + 2, 0, 4 );
745 memcpy( ptr + 6, &line, 4 );
746 memcpy( ptr + 10, function, functionSz );
747 ptr[10 + functionSz] = '\0';
748 memcpy( ptr + 10 + functionSz + 1, source, sourceSz );
749 ptr[10 + functionSz + 1 + sourceSz] = '\0';
750 if( nameSz != 0 )
751 {
752 memcpy( ptr + 10 + functionSz + 1 + sourceSz + 1, name, nameSz );
753 }
754 return uint64_t( ptr );
755 }
756
757private:
758 enum class DequeueStatus { DataDequeued, ConnectionLost, QueueEmpty };
759 enum class ThreadCtxStatus { Same, Changed, ConnectionLost };
760
761 static void LaunchWorker( void* ptr ) { ((Profiler*)ptr)->Worker(); }
762 void Worker();
763
764#ifndef TRACY_NO_FRAME_IMAGE
765 static void LaunchCompressWorker( void* ptr ) { ((Profiler*)ptr)->CompressWorker(); }
766 void CompressWorker();
767#endif
768
769#ifdef TRACY_HAS_CALLSTACK
770 static void LaunchSymbolWorker( void* ptr ) { ((Profiler*)ptr)->SymbolWorker(); }
771 void SymbolWorker();
772 void HandleSymbolQueueItem( const SymbolQueueItem& si );
773#endif
774
775 void ClearQueues( tracy::moodycamel::ConsumerToken& token );
776 void ClearSerial();
777 DequeueStatus Dequeue( tracy::moodycamel::ConsumerToken& token );
778 DequeueStatus DequeueContextSwitches( tracy::moodycamel::ConsumerToken& token, int64_t& timeStop );
779 DequeueStatus DequeueSerial();
780 ThreadCtxStatus ThreadCtxCheck( uint32_t threadId );
781 bool CommitData();
782
783 tracy_force_inline bool AppendData( const void* data, size_t len )
784 {
785 const auto ret = NeedDataSize( len );
786 AppendDataUnsafe( data, len );
787 return ret;
788 }
789
790 tracy_force_inline bool NeedDataSize( size_t len )
791 {
792 assert( len <= TargetFrameSize );
793 bool ret = true;
794 if( m_bufferOffset - m_bufferStart + (int)len > TargetFrameSize )
795 {
796 ret = CommitData();
797 }
798 return ret;
799 }
800
801 tracy_force_inline void AppendDataUnsafe( const void* data, size_t len )
802 {
803 memcpy( m_buffer + m_bufferOffset, data, len );
804 m_bufferOffset += int( len );
805 }
806
807 bool SendData( const char* data, size_t len );
808 void SendLongString( uint64_t ptr, const char* str, size_t len, QueueType type );
809 void SendSourceLocation( uint64_t ptr );
810 void SendSourceLocationPayload( uint64_t ptr );
811 void SendCallstackPayload( uint64_t ptr );
812 void SendCallstackPayload64( uint64_t ptr );
813 void SendCallstackAlloc( uint64_t ptr );
814
815 void QueueCallstackFrame( uint64_t ptr );
816 void QueueSymbolQuery( uint64_t symbol );
817 void QueueExternalName( uint64_t ptr );
818 void QueueKernelCode( uint64_t symbol, uint32_t size );
819 void QueueSourceCodeQuery( uint32_t id );
820
821 bool HandleServerQuery();
822 void HandleDisconnect();
823 void HandleParameter( uint64_t payload );
824 void HandleSymbolCodeQuery( uint64_t symbol, uint32_t size );
825 void HandleSourceCodeQuery( char* data, char* image, uint32_t id );
826
827 void AckServerQuery();
828 void AckSymbolCodeNotAvailable();
829
830 void CalibrateTimer();
831 void CalibrateDelay();
832 void ReportTopology();
833
834 static tracy_force_inline void SendCallstackSerial( void* ptr )
835 {
836#ifdef TRACY_HAS_CALLSTACK
837 auto item = GetProfiler().m_serialQueue.prepare_next();
838 MemWrite( &item->hdr.type, QueueType::CallstackSerial );
839 MemWrite( &item->callstackFat.ptr, (uint64_t)ptr );
840 GetProfiler().m_serialQueue.commit_next();
841#else
842 static_cast<void>(ptr); // unused
843#endif
844 }
845
846 static tracy_force_inline void SendMemAlloc( QueueType type, const uint32_t thread, const void* ptr, size_t size )
847 {
848 assert( type == QueueType::MemAlloc || type == QueueType::MemAllocCallstack || type == QueueType::MemAllocNamed || type == QueueType::MemAllocCallstackNamed );
849
850 auto item = GetProfiler().m_serialQueue.prepare_next();
851 MemWrite( &item->hdr.type, type );
852 MemWrite( &item->memAlloc.time, GetTime() );
853 MemWrite( &item->memAlloc.thread, thread );
854 MemWrite( &item->memAlloc.ptr, (uint64_t)ptr );
855 if( compile_time_condition<sizeof( size ) == 4>::value )
856 {
857 memcpy( &item->memAlloc.size, &size, 4 );
858 memset( &item->memAlloc.size + 4, 0, 2 );
859 }
860 else
861 {
862 assert( sizeof( size ) == 8 );
863 memcpy( &item->memAlloc.size, &size, 4 );
864 memcpy( ((char*)&item->memAlloc.size)+4, ((char*)&size)+4, 2 );
865 }
866 GetProfiler().m_serialQueue.commit_next();
867 }
868
869 static tracy_force_inline void SendMemFree( QueueType type, const uint32_t thread, const void* ptr )
870 {
871 assert( type == QueueType::MemFree || type == QueueType::MemFreeCallstack || type == QueueType::MemFreeNamed || type == QueueType::MemFreeCallstackNamed );
872
873 auto item = GetProfiler().m_serialQueue.prepare_next();
874 MemWrite( &item->hdr.type, type );
875 MemWrite( &item->memFree.time, GetTime() );
876 MemWrite( &item->memFree.thread, thread );
877 MemWrite( &item->memFree.ptr, (uint64_t)ptr );
878 GetProfiler().m_serialQueue.commit_next();
879 }
880
881 static tracy_force_inline void SendMemName( const char* name )
882 {
883 assert( name );
884 auto item = GetProfiler().m_serialQueue.prepare_next();
885 MemWrite( &item->hdr.type, QueueType::MemNamePayload );
886 MemWrite( &item->memName.name, (uint64_t)name );
887 GetProfiler().m_serialQueue.commit_next();
888 }
889
890#if defined _WIN32 && defined TRACY_TIMER_QPC
891 static int64_t GetTimeQpc();
892#endif
893
894 double m_timerMul;
895 uint64_t m_resolution;
896 uint64_t m_delay;
897 std::atomic<int64_t> m_timeBegin;
898 uint32_t m_mainThread;
899 uint64_t m_epoch, m_exectime;
900 std::atomic<bool> m_shutdown;
901 std::atomic<bool> m_shutdownManual;
902 std::atomic<bool> m_shutdownFinished;
903 Socket* m_sock;
904 UdpBroadcast* m_broadcast;
905 bool m_noExit;
906 uint32_t m_userPort;
907 std::atomic<uint32_t> m_zoneId;
908 int64_t m_samplingPeriod;
909
910 uint32_t m_threadCtx;
911 int64_t m_refTimeThread;
912 int64_t m_refTimeSerial;
913 int64_t m_refTimeCtx;
914 int64_t m_refTimeGpu;
915
916 void* m_stream; // LZ4_stream_t*
917 char* m_buffer;
918 int m_bufferOffset;
919 int m_bufferStart;
920
921 char* m_lz4Buf;
922
923 FastVector<QueueItem> m_serialQueue, m_serialDequeue;
924 TracyMutex m_serialLock;
925
926#ifndef TRACY_NO_FRAME_IMAGE
927 FastVector<FrameImageQueueItem> m_fiQueue, m_fiDequeue;
928 TracyMutex m_fiLock;
929#endif
930
931 SPSCQueue<SymbolQueueItem> m_symbolQueue;
932
933 std::atomic<uint64_t> m_frameCount;
934 std::atomic<bool> m_isConnected;
935#ifdef TRACY_ON_DEMAND
936 std::atomic<uint64_t> m_connectionId;
937
938 TracyMutex m_deferredLock;
939 FastVector<QueueItem> m_deferredQueue;
940#endif
941
942#ifdef TRACY_HAS_SYSTIME
943 void ProcessSysTime();
944
945 SysTime m_sysTime;
946 uint64_t m_sysTimeLast = 0;
947#else
948 void ProcessSysTime() {}
949#endif
950
951 ParameterCallback m_paramCallback;
952 void* m_paramCallbackData;
953 SourceContentsCallback m_sourceCallback;
954 void* m_sourceCallbackData;
955
956 char* m_queryImage;
957 char* m_queryData;
958 char* m_queryDataPtr;
959
960#if defined _WIN32
961 void* m_exceptionHandler;
962#endif
963#ifdef __linux__
964 struct {
965 struct sigaction pwr, ill, fpe, segv, pipe, bus, abrt;
966 } m_prevSignal;
967#endif
968 bool m_crashHandlerInstalled;
969};
970
971}
972
973#endif