mirror of
https://github.com/wolfpld/tracy.git
synced 2026-06-09 17:13:49 +00:00
Compare commits
13 Commits
slomp/appl
...
syscalls
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ee42a723a | ||
|
|
154a3ead5f | ||
|
|
bbdbf2bc78 | ||
|
|
1fdb45979c | ||
|
|
359bcee690 | ||
|
|
8a6f4258e9 | ||
|
|
102c835b4c | ||
|
|
68cffc8525 | ||
|
|
14af14999e | ||
|
|
0610c73be3 | ||
|
|
17165102fc | ||
|
|
975207a2ad | ||
|
|
394ad35eb2 |
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user