Capture sys calls on windows.

Only active if TRACY_SYSCALLS is defined, due to the volume of generated
data.
This commit is contained in:
Bartosz Taudul
2019-10-27 17:20:52 +01:00
parent 14af14999e
commit 68cffc8525
4 changed files with 145 additions and 1 deletions

View File

@@ -1810,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;

View File

@@ -54,8 +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
@@ -83,7 +122,9 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
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 )
{
@@ -99,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()
@@ -119,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;

View File

@@ -42,6 +42,8 @@ enum class QueueType : uint8_t
ContextSwitch,
ThreadWakeup,
GpuTime,
SysCallEnter,
SysCallExit,
Terminate,
KeepAlive,
ThreadContext,
@@ -330,6 +332,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 +393,8 @@ struct QueueItem
QueueContextSwitch contextSwitch;
QueueThreadWakeup threadWakeup;
QueueTidToPid tidToPid;
QueueSysCallEnter sysCallEnter;
QueueSysCallExit sysCallExit;
};
};
#pragma pack()
@@ -420,6 +437,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