mirror of
https://github.com/wolfpld/tracy.git
synced 2026-08-19 17:49:45 +00:00
Capture sys calls on windows.
Only active if TRACY_SYSCALLS is defined, due to the volume of generated data.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user