Compare commits

...

13 Commits

Author SHA1 Message Date
Bartosz Taudul
0ee42a723a Display sys call names. 2019-10-27 18:34:35 +01:00
Bartosz Taudul
154a3ead5f Transfer code location strings. 2019-10-27 18:34:26 +01:00
Bartosz Taudul
bbdbf2bc78 Initial sys calls drawing. 2019-10-27 18:11:36 +01:00
Bartosz Taudul
1fdb45979c Update sln header. 2019-10-27 18:11:18 +01:00
Bartosz Taudul
359bcee690 Display number of collected system calls. 2019-10-27 17:41:15 +01:00
Bartosz Taudul
8a6f4258e9 Add sys call counter. 2019-10-27 17:39:56 +01:00
Bartosz Taudul
102c835b4c Process sys calls. 2019-10-27 17:39:22 +01:00
Bartosz Taudul
68cffc8525 Capture sys calls on windows.
Only active if TRACY_SYSCALLS is defined, due to the volume of generated
data.
2019-10-27 17:23:30 +01:00
Bartosz Taudul
14af14999e Allow access to profiler's worker thread tid. 2019-10-27 17:20:17 +01:00
Bartosz Taudul
0610c73be3 Allow access to PID. 2019-10-27 16:50:35 +01:00
Bartosz Taudul
17165102fc Track which threads are running on which cores. 2019-10-27 16:40:22 +01:00
Bartosz Taudul
975207a2ad Load symbol information for kernel modules. 2019-10-27 16:37:47 +01:00
Bartosz Taudul
394ad35eb2 Store current process id in a static variable. 2019-10-27 14:01:51 +01:00
12 changed files with 471 additions and 20 deletions

View File

@@ -9,6 +9,7 @@
# define NOMINMAX
# endif
# include <windows.h>
# include <winternl.h>
# ifdef _MSC_VER
# pragma warning( push )
# pragma warning( disable : 4091 )
@@ -37,6 +38,29 @@ int cb_num;
CallstackEntry cb_data[MaxCbTrace];
extern "C" { t_RtlWalkFrameChain RtlWalkFrameChain = 0; }
extern "C" typedef NTSTATUS (WINAPI *t_ZwQuerySystemInformation)( int, PVOID, ULONG, PULONG );
extern "C" {
typedef struct _SYSTEM_MODULE_ENTRY
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} SYSTEM_MODULE_ENTRY;
typedef struct _SYSTEM_MODULE_INFORMATION
{
ULONG Count;
SYSTEM_MODULE_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION;
}
#if defined __MINGW32__ && API_VERSION_NUMBER < 12
extern "C" {
@@ -51,28 +75,55 @@ BOOL IMAGEAPI SymGetLineFromInlineContext(HANDLE hProcess, DWORD64 qwAddr, ULONG
};
#endif
static HANDLE currentProcess;
void InitCallstack()
{
currentProcess = GetCurrentProcess();
#ifdef UNICODE
RtlWalkFrameChain = (t_RtlWalkFrameChain)GetProcAddress( GetModuleHandle( L"ntdll.dll" ), "RtlWalkFrameChain" );
t_ZwQuerySystemInformation ZwQuerySystemInformation = (t_ZwQuerySystemInformation)GetProcAddress( GetModuleHandle( L"ntdll.dll" ), "ZwQuerySystemInformation" );
#else
RtlWalkFrameChain = (t_RtlWalkFrameChain)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "RtlWalkFrameChain" );
t_ZwQuerySystemInformation ZwQuerySystemInformation = (t_ZwQuerySystemInformation)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "ZwQuerySystemInformation" );
#endif
SymInitialize( GetCurrentProcess(), nullptr, true );
SymInitialize( currentProcess, nullptr, true );
SymSetOptions( SYMOPT_LOAD_LINES );
if( ZwQuerySystemInformation )
{
char sysroot[1024];
const auto rootlen = ExpandEnvironmentStringsA( "%SystemRoot%", sysroot, 1024 ) - 1;
ULONG size = 0;
ZwQuerySystemInformation( 11 /*SystemModuleInformation*/, 0, size, &size );
auto modules = (SYSTEM_MODULE_INFORMATION*)tracy_malloc( size );
ZwQuerySystemInformation( 11 /*SystemModuleInformation*/, modules, size, &size );
for( ULONG i=0; i<modules->Count; i++ )
{
char buf[1024];
const char* name = (char*)modules->Module[i].FullPathName;
if( strncmp( "\\SystemRoot", name, 11 ) == 0 )
{
memcpy( buf, sysroot, rootlen );
strcpy( buf + rootlen, name+11 );
name = buf;
}
auto res = SymLoadModuleEx( currentProcess, nullptr, name, nullptr, (DWORD64)modules->Module[i].ImageBase, modules->Module[i].ImageSize, nullptr, 0 );
}
tracy_free( modules );
}
}
const char* DecodeCallstackPtrFast( uint64_t ptr )
{
static char ret[1024];
const auto proc = GetCurrentProcess();
char buf[sizeof( SYMBOL_INFO ) + 1024];
auto si = (SYMBOL_INFO*)buf;
si->SizeOfStruct = sizeof( SYMBOL_INFO );
si->MaxNameLen = 1024;
if( SymFromAddr( proc, ptr, nullptr, si ) == 0 )
if( SymFromAddr( currentProcess, ptr, nullptr, si ) == 0 )
{
*ret = '\0';
}
@@ -87,13 +138,12 @@ const char* DecodeCallstackPtrFast( uint64_t ptr )
CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
{
int write;
const auto proc = GetCurrentProcess();
#ifndef __CYGWIN__
const auto inlineNum = std::min<DWORD>( MaxCbTrace - 1, SymAddrIncludeInlineTrace( proc, ptr ) );
const auto inlineNum = std::min<DWORD>( MaxCbTrace - 1, SymAddrIncludeInlineTrace( currentProcess, ptr ) );
DWORD ctx = 0;
DWORD idx;
BOOL doInline = FALSE;
if( inlineNum != 0 ) doInline = SymQueryInlineTrace( proc, ptr, 0, ptr, ptr, &ctx, &idx );
if( inlineNum != 0 ) doInline = SymQueryInlineTrace( currentProcess, ptr, 0, ptr, ptr, &ctx, &idx );
if( doInline )
{
write = inlineNum;
@@ -111,7 +161,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
si->SizeOfStruct = sizeof( SYMBOL_INFO );
si->MaxNameLen = 1024;
if( SymFromAddr( proc, ptr, nullptr, si ) == 0 )
if( SymFromAddr( currentProcess, ptr, nullptr, si ) == 0 )
{
memcpy( si->Name, "[unknown]", 10 );
si->NameLen = 9;
@@ -129,7 +179,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
cb_data[write].name = name;
const char* filename;
if (SymGetLineFromAddr64(proc, ptr, &displacement, &line) == 0)
if( SymGetLineFromAddr64( currentProcess, ptr, &displacement, &line ) == 0 )
{
filename = "[unknown]";
cb_data[write].line = 0;
@@ -155,7 +205,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
{
auto& cb = cb_data[i];
if( SymFromInlineContext( proc, ptr, ctx, nullptr, si ) == 0 )
if( SymFromInlineContext( currentProcess, ptr, ctx, nullptr, si ) == 0 )
{
memcpy( si->Name, "[unknown]", 10 );
si->NameLen = 9;
@@ -167,7 +217,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
cb.name = name;
const char* filename;
if( SymGetLineFromInlineContext( proc, ptr, ctx, 0, &displacement, &line ) == 0 )
if( SymGetLineFromInlineContext( currentProcess, ptr, ctx, 0, &displacement, &line ) == 0 )
{
filename = "[unknown]";
cb.line = 0;

View File

@@ -1017,6 +1017,7 @@ Profiler::Profiler()
, m_broadcast( nullptr )
, m_noExit( false )
, m_zoneId( 1 )
, m_pid( GetPid() )
, m_stream( LZ4_createStream() )
, m_buffer( (char*)tracy_malloc( TargetFrameSize*3 ) )
, m_bufferOffset( 0 )
@@ -1172,8 +1173,6 @@ void Profiler::Worker()
const auto hostinfo = GetHostInfo();
const auto hisz = std::min<size_t>( strlen( hostinfo ), WelcomeMessageHostInfoSize - 1 );
const uint64_t pid = GetPid();
#ifdef TRACY_ON_DEMAND
uint8_t onDemand = 1;
#else
@@ -1193,7 +1192,7 @@ void Profiler::Worker()
MemWrite( &welcome.delay, m_delay );
MemWrite( &welcome.resolution, m_resolution );
MemWrite( &welcome.epoch, m_epoch );
MemWrite( &welcome.pid, pid );
MemWrite( &welcome.pid, m_pid );
MemWrite( &welcome.onDemand, onDemand );
MemWrite( &welcome.isApple, isApple );
memcpy( welcome.programName, procname, pnsz );
@@ -1811,6 +1810,22 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
MemWrite( &item->gpuTime.gpuTime, dt );
break;
}
case QueueType::SysCallEnter:
{
int64_t t = MemRead<int64_t>( &item->sysCallEnter.time );
int64_t dt = t - m_refTimeCtx;
m_refTimeCtx = t;
MemWrite( &item->sysCallEnter.time, dt );
break;
}
case QueueType::SysCallExit:
{
int64_t t = MemRead<int64_t>( &item->sysCallExit.time );
int64_t dt = t - m_refTimeCtx;
m_refTimeCtx = t;
MemWrite( &item->sysCallExit.time, dt );
break;
}
default:
assert( false );
break;
@@ -2039,7 +2054,9 @@ void Profiler::SendString( uint64_t str, const char* ptr, QueueType type )
type == QueueType::PlotName ||
type == QueueType::FrameName ||
type == QueueType::ExternalName ||
type == QueueType::ExternalThreadName );
type == QueueType::ExternalThreadName ||
type == QueueType::CodeLocationString
);
QueueItem item;
MemWrite( &item.hdr.type, type );
@@ -2243,6 +2260,16 @@ bool Profiler::HandleServerQuery()
SysTraceSendExternalName( ptr );
break;
#endif
case ServerQueryCodeLocationString:
{
#ifdef TRACY_HAS_CALLSTACK
const auto name = DecodeCallstackPtrFast( ptr );
#else
const auto name = "???";
#endif
SendString( ptr, name, QueueType::CodeLocationString );
break;
}
default:
assert( false );
break;
@@ -2482,6 +2509,11 @@ void Profiler::ProcessSysTime()
}
#endif
uint64_t Profiler::GetProfilerTid() const
{
return s_profilerThreadId;
}
}
#ifdef __cplusplus

View File

@@ -446,6 +446,9 @@ public:
void SendString( uint64_t ptr, const char* str, QueueType type );
uint64_t Pid() const { return m_pid; }
uint64_t GetProfilerTid() const;
private:
enum class DequeueStatus { Success, ConnectionLost, QueueEmpty };
@@ -541,6 +544,7 @@ private:
UdpBroadcast* m_broadcast;
bool m_noExit;
std::atomic<uint32_t> m_zoneId;
uint64_t m_pid;
uint64_t m_threadCtx;
int64_t m_refTimeThread;

View File

@@ -54,6 +54,47 @@ struct ReadyThread
int8_t reserverd;
};
#ifdef TRACY_SYSCALLS
struct SysCallEnter
{
// Official MSDN documentation is bullshitting here
// This is corroborated by tracerpt displaying address as 64-bit
#if defined __x86_64__ || defined _M_X64
uint64_t sysCallAddress;
#else
uint32_t sysCallAddress;
#endif
};
struct TraceThread
{
uint32_t ProcessId;
uint32_t TThreadId;
uint32_t StackBase;
uint32_t StackLimit;
uint32_t UserStackBase;
uint32_t UserStackLimit;
uint32_t Affinity;
uint32_t Win32StartAddr;
uint32_t TebBase;
uint32_t SubProcessTag;
uint8_t BasePriority;
uint8_t PagePriority;
uint8_t IoPriority;
uint8_t ThreadFlags;
};
uint64_t coreThread[256] = {};
// We need to know which thread ids are in our process. A hash map would be the best solution here,
// but there is no solution ready to use right now. Instead, let's store a thread bit map. Note that
// this is only an approximation, there are no guarantees about thread ids returned by the kernel, as
// Raymond Chen said on multiple occasions. In the false positive case, we will be reporting system
// calls belonging to some other process, and that's no big deal.
enum { TidCapacity = 8192 };
uint64_t processThreads[TidCapacity] = {};
#endif
void WINAPI EventRecordCallback( PEVENT_RECORD record )
{
#ifdef TRACY_ON_DEMAND
@@ -64,6 +105,7 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
if( hdr.EventDescriptor.Opcode == 36 )
{
const auto cswitch = (const CSwitch*)record->UserData;
const auto cpu = record->BufferContext.ProcessorNumber;
Magic magic;
auto token = GetToken();
@@ -75,10 +117,14 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
memcpy( &item->contextSwitch.newThread, &cswitch->newThreadId, sizeof( cswitch->newThreadId ) );
memset( ((char*)&item->contextSwitch.oldThread)+4, 0, 4 );
memset( ((char*)&item->contextSwitch.newThread)+4, 0, 4 );
MemWrite( &item->contextSwitch.cpu, record->BufferContext.ProcessorNumber );
MemWrite( &item->contextSwitch.cpu, cpu );
MemWrite( &item->contextSwitch.reason, cswitch->oldThreadWaitReason );
MemWrite( &item->contextSwitch.state, cswitch->oldThreadState );
tail.store( magic + 1, std::memory_order_release );
#ifdef TRACY_SYSCALLS
coreThread[cpu] = cswitch->newThreadId;
#endif
}
else if( hdr.EventDescriptor.Opcode == 50 )
{
@@ -94,6 +140,70 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
memset( ((char*)&item->threadWakeup.thread)+4, 0, 4 );
tail.store( magic + 1, std::memory_order_release );
}
#ifdef TRACY_SYSCALLS
else if( hdr.EventDescriptor.Opcode == 51 )
{
const auto cpu = record->BufferContext.ProcessorNumber;
const auto thread = coreThread[cpu];
if( thread != 0 )
{
const auto tid = thread / 4;
const auto entry = ( tid / 64 ) % TidCapacity;
const auto bit = tid % 64;
if( processThreads[entry] & ( 1ull << bit ) )
{
const auto syscall = (const SysCallEnter*)record->UserData;
uint64_t addr = syscall->sysCallAddress;
Magic magic;
auto token = GetToken();
auto& tail = token->get_tail_index();
auto item = token->enqueue_begin( magic );
MemWrite( &item->hdr.type, QueueType::SysCallEnter );
MemWrite( &item->sysCallEnter.time, hdr.TimeStamp.QuadPart );
MemWrite( &item->sysCallEnter.thread, thread );
MemWrite( &item->sysCallEnter.address, addr );
tail.store( magic + 1, std::memory_order_release );
}
}
}
else if( hdr.EventDescriptor.Opcode == 52 )
{
const auto cpu = record->BufferContext.ProcessorNumber;
const auto thread = coreThread[cpu];
if( thread != 0 )
{
const auto tid = thread / 4;
const auto entry = ( tid / 64 ) % TidCapacity;
const auto bit = tid % 64;
if( processThreads[entry] & ( 1ull << bit ) )
{
Magic magic;
auto token = GetToken();
auto& tail = token->get_tail_index();
auto item = token->enqueue_begin( magic );
MemWrite( &item->hdr.type, QueueType::SysCallExit );
MemWrite( &item->sysCallExit.time, hdr.TimeStamp.QuadPart );
MemWrite( &item->sysCallExit.thread, thread );
tail.store( magic + 1, std::memory_order_release );
}
}
}
else if( hdr.EventDescriptor.Opcode == 1 || hdr.EventDescriptor.Opcode == 3 )
{
const auto tt = (const TraceThread*)record->UserData;
if( tt->ProcessId == GetProfiler().Pid() && tt->TThreadId != GetProfiler().GetProfilerTid() && tt->TThreadId != GetCurrentThreadId() )
{
// Thread ids are (currently) multiples of 4, but its not a part of the contract.
const uint32_t tid = tt->TThreadId / 4;
const auto entry = ( tid / 64 ) % TidCapacity;
const auto bit = tid % 64;
processThreads[entry] |= ( 1ull << bit );
}
}
#endif
}
bool SysTraceStart()
@@ -114,7 +224,11 @@ bool SysTraceStart()
const auto psz = sizeof( EVENT_TRACE_PROPERTIES ) + sizeof( KERNEL_LOGGER_NAME );
s_prop = (EVENT_TRACE_PROPERTIES*)tracy_malloc( psz );
memset( s_prop, 0, sizeof( EVENT_TRACE_PROPERTIES ) );
#ifdef TRACY_SYSCALLS
s_prop->EnableFlags = EVENT_TRACE_FLAG_CSWITCH | EVENT_TRACE_FLAG_DISPATCHER | EVENT_TRACE_FLAG_SYSTEMCALL | EVENT_TRACE_FLAG_THREAD;
#else
s_prop->EnableFlags = EVENT_TRACE_FLAG_CSWITCH | EVENT_TRACE_FLAG_DISPATCHER;
#endif
s_prop->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
s_prop->Wnode.BufferSize = psz;
s_prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID;

View File

@@ -9,7 +9,7 @@
namespace tracy
{
enum : uint32_t { ProtocolVersion = 21 };
enum : uint32_t { ProtocolVersion = 22 };
enum : uint32_t { BroadcastVersion = 0 };
using lz4sz_t = uint32_t;
@@ -46,7 +46,8 @@ enum ServerQuery : uint8_t
ServerQueryCallstackFrame,
ServerQueryFrameName,
ServerQueryDisconnect,
ServerQueryExternalName
ServerQueryExternalName,
ServerQueryCodeLocationString
};
struct ServerQueryPacket

View File

@@ -42,6 +42,8 @@ enum class QueueType : uint8_t
ContextSwitch,
ThreadWakeup,
GpuTime,
SysCallEnter,
SysCallExit,
Terminate,
KeepAlive,
ThreadContext,
@@ -73,6 +75,7 @@ enum class QueueType : uint8_t
FrameImageData,
ExternalName,
ExternalThreadName,
CodeLocationString,
NUM_TYPES
};
@@ -330,6 +333,19 @@ struct QueueTidToPid
uint64_t pid;
};
struct QueueSysCallEnter
{
int64_t time;
uint64_t thread;
uint64_t address;
};
struct QueueSysCallExit
{
int64_t time;
uint64_t thread;
};
struct QueueHeader
{
union
@@ -378,6 +394,8 @@ struct QueueItem
QueueContextSwitch contextSwitch;
QueueThreadWakeup threadWakeup;
QueueTidToPid tidToPid;
QueueSysCallEnter sysCallEnter;
QueueSysCallExit sysCallExit;
};
};
#pragma pack()
@@ -420,6 +438,8 @@ static const size_t QueueDataSize[] = {
sizeof( QueueHeader ) + sizeof( QueueContextSwitch ),
sizeof( QueueHeader ) + sizeof( QueueThreadWakeup ),
sizeof( QueueHeader ) + sizeof( QueueGpuTime ),
sizeof( QueueHeader ) + sizeof( QueueSysCallEnter ),
sizeof( QueueHeader ) + sizeof( QueueSysCallExit ),
// above items must be first
sizeof( QueueHeader ), // terminate
sizeof( QueueHeader ), // keep alive
@@ -453,6 +473,7 @@ static const size_t QueueDataSize[] = {
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // frame image data
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // external name
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // external thread name
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // code location string
};
static_assert( QueueItemSize == 32, "Queue item size not 32 bytes" );

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.15
# Visual Studio Version 16
VisualStudioVersion = 16.0.29411.108
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tracy", "Tracy.vcxproj", "{1C736F84-08DF-4A7A-A7FB-7BA3412B8C97}"
EndProject

View File

@@ -339,6 +339,16 @@ struct MessageData
enum { MessageDataSize = sizeof( MessageData ) };
struct SysCall
{
int64_t start;
int64_t end;
uint64_t ptr;
};
enum { SysCallSize = sizeof( SysCall ) };
#pragma pack()
@@ -354,6 +364,7 @@ struct ThreadData
#ifndef TRACY_NO_STATISTICS
Vector<int64_t> childTimeStack;
#endif
Vector<SysCall> sysCalls;
};
struct GpuCtxThreadData

View File

@@ -2337,6 +2337,13 @@ void View::DrawZones()
offset += ostep * lockDepth;
depth += lockDepth;
}
if( !v->sysCalls.empty() )
{
const auto sysCallDepth = DrawSysCalls( v->sysCalls, hover, pxns, nspx, wpos, offset, yMin, yMax );
offset += ostep * sysCallDepth;
depth += sysCallDepth;
}
}
offset += ostep * 0.2f;
@@ -4371,6 +4378,119 @@ int View::DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos,
return cnt;
}
int View::DrawSysCalls( const Vector<SysCall>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, float yMin, float yMax )
{
auto it = std::lower_bound( vec.begin(), vec.end(), std::max<int64_t>( 0, m_vd.zvStart ), [] ( const auto& l, const auto& r ) { return (uint64_t)l.end < (uint64_t)r; } );
if( it == vec.end() ) return 0;
const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd, [] ( const auto& l, const auto& r ) { return l.start < r; } );
if( it == zitend ) return 0;
if( it->end < 0 ) return 0;
const auto w = ImGui::GetWindowContentRegionWidth() - 1;
const auto ty = ImGui::GetFontSize();
const auto ostep = ty + 1;
auto draw = ImGui::GetWindowDrawList();
while( it < zitend )
{
const auto color = 0xFFDD7777;
const auto zsz = std::max( ( it->end - it->start ) * pxns, pxns * 0.5 );
if( zsz < MinVisSize )
{
const auto origStart = it->start;
int num = 0;
const auto px0 = ( it->start - m_vd.zvStart ) * pxns;
auto px1 = ( it->end - m_vd.zvStart ) * pxns;
auto rend = it->end;
auto nextTime = it->end + MinVisSize;
for(;;)
{
const auto prevIt = it;
it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { return (uint64_t)l.end < (uint64_t)r; } );
if( it == prevIt ) ++it;
num += std::distance( prevIt, it );
if( it == zitend ) break;
const auto nend = it->end < 0 ? m_worker.GetLastTime() : it->end;
const auto pxnext = ( nend - m_vd.zvStart ) * pxns;
if( pxnext - px1 >= MinVisSize * 2 ) break;
px1 = pxnext;
rend = nend;
nextTime = nend + nspx;
}
draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ) ) )
{
if( num > 1 )
{
ImGui::BeginTooltip();
TextFocused( "Sys calls too small to display:", RealToString( num, true ) );
ImGui::Separator();
TextFocused( "Execution time:", TimeToString( rend - origStart ) );
ImGui::EndTooltip();
if( ImGui::IsMouseClicked( 2 ) && rend - origStart > 0 )
{
ZoomToRange( origStart, rend );
}
}
else if( ImGui::IsMouseClicked( 2 ) && rend - origStart > 0 )
{
ZoomToRange( origStart, it->end );
}
}
}
else
{
const auto name = m_worker.GetCodeLocationName( it->ptr );
const auto tsz = ImGui::CalcTextSize( name );
const auto pr0 = ( it->start - m_vd.zvStart ) * pxns;
const auto pr1 = ( it->end - m_vd.zvStart ) * pxns;
const auto px0 = std::max( pr0, -10.0 );
const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), color );
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), HighlightColor( color ) );
if( tsz.x < zsz )
{
const auto x = ( it->start - m_vd.zvStart ) * pxns + ( ( it->end - it->start ) * pxns - tsz.x ) / 2;
if( x < 0 || x > w - tsz.x )
{
ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, name );
ImGui::PopClipRect();
}
else if( it->start == it->end )
{
DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, name );
}
else
{
DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, name );
}
}
else
{
ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
DrawTextContrast( draw, wpos + ImVec2( ( it->start - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, name );
ImGui::PopClipRect();
}
if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ) ) )
{
if( !m_zoomAnim.active && ImGui::IsMouseClicked( 2 ) )
{
ZoomToRange( it->start, it->end );
}
}
++it;
}
}
return 1;
}
int View::DrawCpuData( int offset, double pxns, const ImVec2& wpos, bool hover, float yMin, float yMax )
{
const auto w = ImGui::GetWindowContentRegionWidth() - 1;
@@ -10508,6 +10628,7 @@ void View::DrawInfo()
TextFocused( "Context switch regions:", RealToString( m_worker.GetContextSwitchCount(), true ) );
ImGui::SameLine();
TextFocused( "+", RealToString( m_worker.GetContextSwitchPerCpuCount(), true ) );
TextFocused( "System calls:", RealToString( m_worker.GetSysCallCount(), true ) );
ImGui::TreePop();
}

View File

@@ -126,6 +126,7 @@ private:
int SkipGpuZoneLevel( const Vector<GpuEvent*>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift );
void DrawLockHeader( uint32_t id, const LockMap& lockmap, const SourceLocation& srcloc, bool hover, ImDrawList* draw, const ImVec2& wpos, float w, float ty, float offset, uint8_t tid );
int DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos, int offset, LockHighlight& highlight, float yMin, float yMax );
int DrawSysCalls( const Vector<SysCall>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, float yMin, float yMax );
int DrawPlots( int offset, double pxns, const ImVec2& wpos, bool hover, float yMin, float yMax );
void DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, const PlotItem* item, double prev, bool merged, PlotType type, float PlotHeight );
void DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, double val, double prev, bool merged, PlotType type, float PlotHeight );

View File

@@ -247,6 +247,7 @@ Worker::Worker( const char* addr, int port )
, m_pendingSourceLocation( 0 )
, m_pendingCallstackFrames( 0 )
, m_pendingCallstackSubframes( 0 )
, m_pendingCodeLocationStrings( 0 )
, m_callstackFrameStaging( nullptr )
, m_traceVersion( CurrentVersion )
, m_loadTime( 0 )
@@ -1840,6 +1841,16 @@ uint64_t Worker::GetContextSwitchPerCpuCount() const
return cnt;
}
uint64_t Worker::GetSysCallCount() const
{
uint64_t cnt = 0;
for( auto& td : m_data.threads )
{
cnt += td->sysCalls.size();
}
return cnt;
}
uint64_t Worker::GetPidFromTid( uint64_t tid ) const
{
auto it = m_data.tidToPid.find( tid );
@@ -2148,6 +2159,19 @@ std::pair<const char*, const char*> Worker::GetExternalName( uint64_t id ) const
}
}
const char* Worker::GetCodeLocationName( uint64_t ptr ) const
{
const auto it = m_data.codeLocationNames.find( ptr );
if( it == m_data.codeLocationNames.end() )
{
return "???";
}
else
{
return it->second;
}
}
const char* Worker::GetZoneName( const SourceLocation& srcloc ) const
{
if( srcloc.name.active )
@@ -2448,7 +2472,8 @@ void Worker::Exec()
{
if( m_pendingStrings != 0 || m_pendingThreads != 0 || m_pendingSourceLocation != 0 || m_pendingCallstackFrames != 0 ||
!m_pendingCustomStrings.empty() || m_data.plots.IsPending() || m_pendingCallstackPtr != 0 ||
m_pendingExternalNames != 0 || m_pendingCallstackSubframes != 0 || !m_pendingFrameImageData.empty() )
m_pendingExternalNames != 0 || m_pendingCallstackSubframes != 0 || !m_pendingFrameImageData.empty() ||
m_pendingCodeLocationStrings != 0 )
{
continue;
}
@@ -2549,6 +2574,9 @@ bool Worker::DispatchProcess( const QueueItem& ev, char*& ptr )
case QueueType::ExternalThreadName:
AddExternalThreadName( ev.stringTransfer.ptr, ptr, sz );
break;
case QueueType::CodeLocationString:
AddCodeLocationString( ev.stringTransfer.ptr, ptr, sz );
break;
default:
assert( false );
break;
@@ -2846,6 +2874,16 @@ void Worker::CheckExternalName( uint64_t id )
Query( ServerQueryExternalName, id );
}
void Worker::CheckCodeLocationString( uint64_t id )
{
if( m_data.codeLocationNames.find( id ) != m_data.codeLocationNames.end() ) return;
m_data.codeLocationNames.emplace( id, "???" );
m_pendingCodeLocationStrings++;
Query( ServerQueryCodeLocationString, id );
}
void Worker::AddSourceLocation( const QueueSourceLocation& srcloc )
{
assert( m_pendingSourceLocation > 0 );
@@ -2964,6 +3002,16 @@ void Worker::AddExternalThreadName( uint64_t ptr, char* str, size_t sz )
it->second.second = sl.ptr;
}
void Worker::AddCodeLocationString( uint64_t ptr, char* str, size_t sz )
{
assert( m_pendingCodeLocationStrings > 0 );
m_pendingCodeLocationStrings--;
auto it = m_data.codeLocationNames.find( ptr );
assert( it != m_data.codeLocationNames.end() && strcmp( it->second, "???" ) == 0 );
const auto sl = StoreString( str, sz );
it->second = sl.ptr;
}
static const uint8_t DxtcIndexTable[256] = {
85, 87, 86, 84, 93, 95, 94, 92, 89, 91, 90, 88, 81, 83, 82, 80,
117, 119, 118, 116, 125, 127, 126, 124, 121, 123, 122, 120, 113, 115, 114, 112,
@@ -3399,6 +3447,12 @@ bool Worker::Process( const QueueItem& ev )
case QueueType::TidToPid:
ProcessTidToPid( ev.tidToPid );
break;
case QueueType::SysCallEnter:
ProcessSysCallEnter( ev.sysCallEnter );
break;
case QueueType::SysCallExit:
ProcessSysCallExit( ev.sysCallExit );
break;
default:
assert( false );
break;
@@ -4641,6 +4695,40 @@ void Worker::ProcessTidToPid( const QueueTidToPid& ev )
m_data.tidToPid.emplace( ev.tid, ev.pid );
}
void Worker::ProcessSysCallEnter( const QueueSysCallEnter& ev )
{
auto td = RetrieveThread( ev.thread );
if( !td ) return;
if( td->sysCalls.empty() )
{
CheckCodeLocationString( ev.address );
const auto refTime = m_refTimeCtx + ev.time;
m_refTimeCtx = refTime;
const auto time = TscTime( refTime - m_data.baseTime );
td->sysCalls.push_back( SysCall { time, -1, ev.address } );
}
else
{
if( td->sysCalls.back().end < 0 ) return;
CheckCodeLocationString( ev.address );
const auto refTime = m_refTimeCtx + ev.time;
m_refTimeCtx = refTime;
const auto time = TscTime( refTime - m_data.baseTime );
td->sysCalls.push_back_non_empty( SysCall { time, -1, ev.address } );
}
}
void Worker::ProcessSysCallExit( const QueueSysCallExit& ev )
{
auto td = RetrieveThread( ev.thread );
if( !td || td->sysCalls.empty() ) return;
if( td->sysCalls.back().end >= 0 ) return;
const auto refTime = m_refTimeCtx + ev.time;
m_refTimeCtx = refTime;
const auto time = TscTime( refTime - m_data.baseTime );
td->sysCalls.back().end = time;
}
void Worker::MemAllocChanged( int64_t time )
{
const auto val = (double)m_data.memory.usage;

View File

@@ -173,6 +173,7 @@ private:
flat_hash_map<charutil::StringKey, uint32_t, charutil::StringKey::HasherPOT, charutil::StringKey::Comparator> stringMap;
flat_hash_map<uint64_t, const char*, nohash<uint64_t>> threadNames;
flat_hash_map<uint64_t, std::pair<const char*, const char*>, nohash<uint64_t>> externalNames;
flat_hash_map<uint64_t, const char*, nohash<uint64_t>> codeLocationNames;
flat_hash_map<uint64_t, SourceLocation, nohash<uint64_t>> sourceLocation;
Vector<SourceLocation*> sourceLocationPayload;
@@ -298,6 +299,7 @@ public:
uint64_t GetContextSwitchPerCpuCount() const;
bool HasContextSwitches() const { return !m_data.ctxSwitch.empty(); }
uint64_t GetSrcLocCount() const { return m_data.sourceLocationPayload.size() + m_data.sourceLocation.size(); }
uint64_t GetSysCallCount() const;
uint64_t GetCallstackPayloadCount() const { return m_data.callstackPayload.size() - 1; }
uint64_t GetCallstackFrameCount() const { return m_data.callstackFrameMap.size(); }
uint32_t GetFrameImageCount() const { return (uint32_t)m_data.frameImage.size(); }
@@ -354,6 +356,7 @@ public:
bool IsThreadLocal( uint64_t id ) const;
const SourceLocation& GetSourceLocation( int16_t srcloc ) const;
std::pair<const char*, const char*> GetExternalName( uint64_t id ) const;
const char* GetCodeLocationName( uint64_t ptr ) const;
const char* GetZoneName( const SourceLocation& srcloc ) const;
const char* GetZoneName( const ZoneEvent& ev ) const;
@@ -459,6 +462,8 @@ private:
tracy_force_inline void ProcessContextSwitch( const QueueContextSwitch& ev );
tracy_force_inline void ProcessThreadWakeup( const QueueThreadWakeup& ev );
tracy_force_inline void ProcessTidToPid( const QueueTidToPid& ev );
tracy_force_inline void ProcessSysCallEnter( const QueueSysCallEnter& ev );
tracy_force_inline void ProcessSysCallExit( const QueueSysCallExit& ev );
tracy_force_inline void ProcessZoneBeginImpl( ZoneEvent* zone, const QueueZoneBegin& ev );
tracy_force_inline void ProcessZoneBeginAllocSrcLocImpl( ZoneEvent* zone, const QueueZoneBegin& ev );
@@ -526,6 +531,7 @@ private:
void CheckString( uint64_t ptr );
void CheckThreadString( uint64_t id );
void CheckExternalName( uint64_t id );
void CheckCodeLocationString( uint64_t id );
void AddSourceLocation( const QueueSourceLocation& srcloc );
void AddSourceLocationPayload( uint64_t ptr, char* data, size_t sz );
@@ -535,6 +541,7 @@ private:
void AddCustomString( uint64_t ptr, char* str, size_t sz );
void AddExternalName( uint64_t ptr, char* str, size_t sz );
void AddExternalThreadName( uint64_t ptr, char* str, size_t sz );
void AddCodeLocationString( uint64_t ptr, char* str, size_t sz );
void AddFrameImageData( uint64_t ptr, char* data, size_t sz );
tracy_force_inline void AddCallstackPayload( uint64_t ptr, char* data, size_t sz );
@@ -615,6 +622,7 @@ private:
uint32_t m_pendingSourceLocation;
uint32_t m_pendingCallstackFrames;
uint8_t m_pendingCallstackSubframes;
uint32_t m_pendingCodeLocationStrings;
CallstackFrameData* m_callstackFrameStaging;
uint64_t m_callstackFrameStagingPtr;