tracy/client/TracyProfiler.cpp

221 lines
5.6 KiB
C++
Raw Normal View History

2017-09-14 17:25:16 +00:00
#ifdef _MSC_VER
# include <winsock2.h>
#else
# include <sys/time.h>
#endif
2017-09-10 15:43:56 +00:00
#include <assert.h>
2017-09-14 17:25:16 +00:00
#include <limits>
2017-09-21 22:36:36 +00:00
#include <memory>
2017-09-19 00:19:20 +00:00
#include <string.h>
2017-09-10 15:43:56 +00:00
#include "../common/TracyProtocol.hpp"
2017-09-11 20:51:47 +00:00
#include "../common/TracySocket.hpp"
#include "../common/TracySystem.hpp"
#include "concurrentqueue.h"
2017-09-10 15:43:56 +00:00
#include "TracyProfiler.hpp"
#ifdef _DEBUG
# define DISABLE_LZ4
#endif
2017-09-10 15:43:56 +00:00
namespace tracy
{
static moodycamel::ConcurrentQueue<QueueItem> s_queue;
static moodycamel::ProducerToken& GetToken()
{
static thread_local moodycamel::ProducerToken token( s_queue );
return token;
}
extern const char* PointerCheckA;
const char* PointerCheckB = "tracy";
#ifndef TRACY_DISABLE
Profiler s_profiler;
#endif
2017-09-10 15:43:56 +00:00
static Profiler* s_instance = nullptr;
Profiler::Profiler()
2017-09-10 18:14:16 +00:00
: m_timeBegin( GetTime() )
, m_shutdown( false )
2017-09-10 18:08:42 +00:00
, m_id( 0 )
, m_stream( LZ4_createStream() )
, m_buffer( new char[TargetFrameSize*3] )
, m_bufferOffset( 0 )
2017-09-10 15:43:56 +00:00
{
// This check verifies that the string literals from different compilation units are placed at the
// same address by the linker.
// MSVC: make sure "Enable String Pooling" is active (/GF).
// gcc: make sure -fmerge-constants is enabled (it is automatically enabled only in optimized builds).
// clang: seems to be enabled by default.
assert( PointerCheckA == PointerCheckB );
2017-09-10 15:43:56 +00:00
assert( !s_instance );
s_instance = this;
m_thread = std::thread( [this] { Worker(); } );
2017-09-10 15:46:20 +00:00
SetThreadName( m_thread, "Tracy Profiler" );
2017-09-10 15:43:56 +00:00
}
Profiler::~Profiler()
{
m_shutdown.store( true, std::memory_order_relaxed );
m_thread.join();
delete[] m_buffer;
LZ4_freeStream( m_stream );
2017-09-10 15:43:56 +00:00
assert( s_instance );
s_instance = nullptr;
}
2017-09-10 18:08:42 +00:00
uint64_t Profiler::GetNewId()
{
return s_instance->m_id.fetch_add( 1, std::memory_order_relaxed );
}
uint64_t Profiler::ZoneBegin( QueueZoneBegin&& data )
2017-09-10 18:09:14 +00:00
{
auto id = GetNewId();
QueueItem item;
item.hdr.type = QueueType::ZoneBegin;
item.hdr.id = id;
2017-09-10 18:09:14 +00:00
item.zoneBegin = std::move( data );
s_queue.enqueue( GetToken(), std::move( item ) );
return id;
2017-09-10 18:09:14 +00:00
}
void Profiler::ZoneEnd( uint64_t id, QueueZoneEnd&& data )
2017-09-10 18:09:14 +00:00
{
QueueItem item;
item.hdr.type = QueueType::ZoneEnd;
item.hdr.id = id;
2017-09-10 18:09:14 +00:00
item.zoneEnd = std::move( data );
s_queue.enqueue( GetToken(), std::move( item ) );
}
2017-09-15 22:30:27 +00:00
void Profiler::FrameMark()
{
QueueItem item;
item.hdr.type = QueueType::FrameMark;
item.hdr.id = (uint64_t)GetTime();
s_queue.enqueue( GetToken(), std::move( item ) );
2017-09-10 18:09:14 +00:00
}
bool Profiler::ShouldExit()
{
return s_instance->m_shutdown.load( std::memory_order_relaxed );
}
2017-09-10 15:43:56 +00:00
void Profiler::Worker()
{
2017-09-11 22:49:38 +00:00
enum { BulkSize = TargetFrameSize / QueueItemSize };
2017-09-14 17:25:16 +00:00
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
moodycamel::ConsumerToken token( s_queue );
2017-09-11 20:51:47 +00:00
ListenSocket listen;
listen.Listen( "8086", 8 );
2017-09-10 15:43:56 +00:00
for(;;)
{
2017-09-11 20:51:47 +00:00
for(;;)
{
2017-09-11 20:51:47 +00:00
if( m_shutdown.load( std::memory_order_relaxed ) ) return;
m_sock = listen.Accept();
if( m_sock ) break;
}
2017-09-11 20:51:47 +00:00
m_sock->Send( &m_timeBegin, sizeof( m_timeBegin ) );
#ifdef DISABLE_LZ4
// notify client that lz4 compression is disabled (too slow in debug builds)
char val = 0;
m_sock->Send( &val, 1 );
#else
char val = 1;
m_sock->Send( &val, 1 );
#endif
2017-09-11 20:51:47 +00:00
LZ4_resetStream( m_stream );
2017-09-11 20:51:47 +00:00
for(;;)
{
2017-09-11 20:51:47 +00:00
if( m_shutdown.load( std::memory_order_relaxed ) ) return;
QueueItem item[BulkSize];
const auto sz = s_queue.try_dequeue_bulk( token, item, BulkSize );
2017-09-11 20:51:47 +00:00
if( sz > 0 )
{
auto buf = m_buffer + m_bufferOffset;
auto ptr = buf;
2017-09-11 23:14:04 +00:00
for( int i=0; i<sz; i++ )
{
const auto dsz = QueueDataSize[item[i].hdr.idx];
2017-09-11 23:14:04 +00:00
memcpy( ptr, item+i, dsz );
ptr += dsz;
}
if( !SendData( buf, ptr - buf ) ) break;
m_bufferOffset += ptr - buf;
if( m_bufferOffset > TargetFrameSize * 2 ) m_bufferOffset = 0;
2017-09-11 20:51:47 +00:00
}
else
{
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
}
2017-09-14 17:25:16 +00:00
while( m_sock->HasData() )
{
uint64_t ptr;
if( !m_sock->Read( &ptr, sizeof( ptr ), &tv, ShouldExit ) ) break;
SendString( ptr );
}
}
2017-09-10 15:43:56 +00:00
}
}
bool Profiler::SendData( const char* data, size_t len )
{
#ifdef DISABLE_LZ4
if( m_sock->Send( data, len ) == -1 ) return false;
#else
char lz4[LZ4Size + sizeof( lz4sz_t )];
const lz4sz_t lz4sz = LZ4_compress_fast_continue( m_stream, data, lz4 + sizeof( lz4sz_t ), len, LZ4Size, 1 );
memcpy( lz4, &lz4sz, sizeof( lz4sz ) );
if( m_sock->Send( lz4, lz4sz + sizeof( lz4sz_t ) ) == -1 ) return false;
#endif
return true;
}
bool Profiler::SendString( uint64_t str )
{
auto ptr = (const char*)str;
QueueHeader hdr;
hdr.type = QueueType::StringData;
hdr.id = str;
auto buf = m_buffer + m_bufferOffset;
memcpy( buf, &hdr, sizeof( hdr ) );
auto len = strlen( ptr );
assert( len < TargetFrameSize - sizeof( hdr ) - sizeof( uint16_t ) );
assert( len <= std::numeric_limits<uint16_t>::max() );
uint16_t l16 = len;
memcpy( buf + sizeof( hdr ), &l16, sizeof( l16 ) );
memcpy( buf + sizeof( hdr ) + sizeof( l16 ), ptr, l16 );
m_bufferOffset += sizeof( hdr ) + sizeof( l16 ) + l16;
if( m_bufferOffset > TargetFrameSize * 2 ) m_bufferOffset = 0;
return SendData( buf, sizeof( hdr ) + sizeof( l16 ) + l16 );
}
2017-09-10 15:43:56 +00:00
}