Files
UnrealEngine/Engine/Source/Developer/TraceServices/Private/Model/ContextSwitches.cpp
2025-05-18 13:04:45 +08:00

406 lines
9.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TraceServices/Model/ContextSwitches.h"
#include "Model/ContextSwitchesPrivate.h"
#include "Algo/Sort.h"
#include "AnalysisServicePrivate.h"
namespace TraceServices
{
FContextSwitchesProvider::FContextSwitchesProvider(IAnalysisSession& InSession)
: Session(InSession)
, NumCpuCores(0)
{
}
FContextSwitchesProvider::~FContextSwitchesProvider()
{
for (const auto& ContextSwitches : Threads)
{
delete ContextSwitches.Value;
}
Threads.Reset();
for (const auto& CpuCoreEvents : CpuCores)
{
if (CpuCoreEvents)
{
delete CpuCoreEvents;
}
}
CpuCores.Reset();
}
bool FContextSwitchesProvider::HasData() const
{
Session.ReadAccessCheck();
return Threads.Num() > 0;
}
bool FContextSwitchesProvider::GetSystemThreadId(uint32 ThreadId, uint32& OutSystemThreadId) const
{
Session.ReadAccessCheck();
const uint32* SystemThreadIdPtr = TraceToSystemThreadIdMap.Find(ThreadId);
if (SystemThreadIdPtr)
{
OutSystemThreadId = *SystemThreadIdPtr;
return true;
}
return false;
}
bool FContextSwitchesProvider::GetThreadId(uint32 SystemThreadId, uint32& OutThreadId) const
{
Session.ReadAccessCheck();
const uint32* ThreadIdPtr = SystemToTraceThreadIdMap.Find(SystemThreadId);
if (ThreadIdPtr)
{
OutThreadId = *ThreadIdPtr;
return true;
}
return false;
}
bool FContextSwitchesProvider::GetSystemThreadId(uint32 CoreNumber, double Time, uint32& OutSystemThreadId) const
{
Session.ReadAccessCheck();
if (CoreNumber >= (uint32)CpuCores.Num())
{
return false;
}
const TPagedArray<FCpuCoreEvent>* CpuCoreEvents = CpuCores[CoreNumber];
if (!CpuCoreEvents)
{
return false;
}
uint64 PageIndex = Algo::LowerBoundBy(*CpuCoreEvents, Time, [](const TPagedArrayPage<FCpuCoreEvent>& In) -> double
{
return GetFirstItem(In)->Start;
});
if (PageIndex < GetNum(*CpuCoreEvents))
{
const TPagedArrayPage<FCpuCoreEvent>& Page = GetData(*CpuCoreEvents)[PageIndex];
uint64 Index = Algo::LowerBoundBy(Page, Time, [](const FCpuCoreEvent& In) -> double
{
return In.Start;
});
if (Index < GetNum(Page))
{
const FCpuCoreEvent& CpuCoreEvent = GetData(Page)[Index];
if (CpuCoreEvent.Start >= Time && CpuCoreEvent.End < Time)
{
OutSystemThreadId = CpuCoreEvent.SystemThreadId;
return true;
}
}
}
return false;
}
bool FContextSwitchesProvider::GetThreadId(uint32 CoreNumber, double Time, uint32& OutThreadId) const
{
Session.ReadAccessCheck();
uint32 SystemThreadId;
if (GetSystemThreadId(CoreNumber, Time, SystemThreadId))
{
return GetThreadId(SystemThreadId, OutThreadId);
}
return false;
}
bool FContextSwitchesProvider::GetCoreNumber(uint32 ThreadId, double Time, uint32& OutCoreNumber) const
{
Session.ReadAccessCheck();
const TPagedArray<FContextSwitch>* ContextSwitches = GetContextSwitches(ThreadId);
if (!ContextSwitches)
{
return false;
}
uint64 PageIndex = Algo::LowerBoundBy(*ContextSwitches, Time, [](const TPagedArrayPage<FContextSwitch>& In) -> double
{
return GetFirstItem(In)->Start;
});
if (PageIndex < GetNum(*ContextSwitches))
{
const TPagedArrayPage<FContextSwitch>& Page = GetData(*ContextSwitches)[PageIndex];
uint64 Index = Algo::LowerBoundBy(Page, Time, [](const FContextSwitch& In) -> double
{
return In.Start;
});
if (Index < GetNum(Page))
{
const FContextSwitch& ContextSwitch = GetData(Page)[Index];
if (ContextSwitch.Start >= Time && ContextSwitch.End < Time)
{
OutCoreNumber = ContextSwitch.CoreNumber;
return true;
}
}
}
return false;
}
void FContextSwitchesProvider::EnumerateCpuCores(CpuCoreCallback Callback) const
{
Session.ReadAccessCheck();
const uint32 CoreCount = (uint32)CpuCores.Num();
for (uint32 CoreNumber = 0; CoreNumber < CoreCount; ++CoreNumber)
{
if (CpuCores[CoreNumber] != nullptr)
{
FCpuCoreInfo CpuCoreInfo;
CpuCoreInfo.CoreNumber = CoreNumber;
Callback(CpuCoreInfo);
}
}
}
void FContextSwitchesProvider::EnumerateCpuCoreEvents(uint32 CoreNumber, double StartTime, double EndTime, CpuCoreEventCallback Callback) const
{
Session.ReadAccessCheck();
if (CoreNumber >= (uint32)CpuCores.Num())
{
return;
}
const TPagedArray<FCpuCoreEvent>* CpuCoreEvents = CpuCores[CoreNumber];
if (CpuCoreEvents == nullptr)
{
return;
}
uint64 PageIndex = Algo::UpperBoundBy(*CpuCoreEvents, StartTime, [](const TPagedArrayPage<FCpuCoreEvent>& Page)
{
return Page.Items[0].Start;
});
if (PageIndex > 0)
{
--PageIndex;
}
auto Iterator = CpuCoreEvents->GetIteratorFromPage(PageIndex);
const FCpuCoreEvent* CurrentCpuCoreEvent = Iterator.GetCurrentItem();
while (CurrentCpuCoreEvent && CurrentCpuCoreEvent->Start < EndTime)
{
if (CurrentCpuCoreEvent->End > StartTime)
{
if (Callback(*CurrentCpuCoreEvent) == EContextSwitchEnumerationResult::Stop)
{
break;
}
}
CurrentCpuCoreEvent = Iterator.NextItem();
}
}
void FContextSwitchesProvider::EnumerateCpuCoreEventsBackwards(uint32 CoreNumber, double EndTime, double StartTime, CpuCoreEventCallback Callback) const
{
Session.ReadAccessCheck();
if (CoreNumber >= (uint32)CpuCores.Num())
{
return;
}
const TPagedArray<FCpuCoreEvent>* CpuCoreEvents = CpuCores[CoreNumber];
if (CpuCoreEvents == nullptr)
{
return;
}
uint64 PageIndex = Algo::UpperBoundBy(*CpuCoreEvents, EndTime, [](const TPagedArrayPage<FCpuCoreEvent>& Page)
{
return Page.Items[0].Start;
});
if (PageIndex == 0)
{
return;
}
auto Iterator = PageIndex < CpuCoreEvents->NumPages() ?
CpuCoreEvents->GetIteratorFromPage(PageIndex) :
CpuCoreEvents->GetIteratorFromItem(CpuCoreEvents->Num() - 1);
const FCpuCoreEvent* CurrentCpuCoreEvent = Iterator.GetCurrentItem();
while (CurrentCpuCoreEvent && CurrentCpuCoreEvent->End > StartTime)
{
if (CurrentCpuCoreEvent->Start < EndTime)
{
if (Callback(*CurrentCpuCoreEvent) == EContextSwitchEnumerationResult::Stop)
{
break;
}
}
CurrentCpuCoreEvent = Iterator.PrevItem();
}
}
const TPagedArray<FContextSwitch>* FContextSwitchesProvider::GetContextSwitches(uint32 ThreadId) const
{
Session.ReadAccessCheck();
if (TraceToSystemThreadIdMap.Contains(ThreadId))
{
auto ContextSwitches = Threads.Find(TraceToSystemThreadIdMap[ThreadId]);
return ContextSwitches ? *ContextSwitches : nullptr;
}
return nullptr;
}
void FContextSwitchesProvider::EnumerateContextSwitches(uint32 ThreadId, double StartTime, double EndTime, ContextSwitchCallback Callback) const
{
Session.ReadAccessCheck();
const TPagedArray<FContextSwitch>* ContextSwitches = GetContextSwitches(ThreadId);
if (ContextSwitches == nullptr)
{
return;
}
uint64 PageIndex = Algo::UpperBoundBy(*ContextSwitches, StartTime, [](const TPagedArrayPage<FContextSwitch>& Page)
{
return Page.Items[0].Start;
});
if (PageIndex > 0)
{
--PageIndex;
}
auto Iterator = ContextSwitches->GetIteratorFromPage(PageIndex);
const FContextSwitch* CurrentContextSwitch = Iterator.NextItem();
while (CurrentContextSwitch && CurrentContextSwitch->Start < EndTime)
{
if (CurrentContextSwitch->End > StartTime)
{
if (Callback(*CurrentContextSwitch) == EContextSwitchEnumerationResult::Stop)
{
break;
}
}
CurrentContextSwitch = Iterator.NextItem();
}
}
void FContextSwitchesProvider::Add(uint32 SystemThreadId, double Start, double End, uint32 CoreNumber)
{
Session.WriteAccessCheck();
//////////////////////////////////////////////////
// Context Switch events
TPagedArray<FContextSwitch>** ContextSwitches = Threads.Find(SystemThreadId);
if (!ContextSwitches)
{
ContextSwitches = &Threads.Add(SystemThreadId, new TPagedArray<FContextSwitch>(Session.GetLinearAllocator(), 4096));
}
FContextSwitch& ContextSwitch = (*ContextSwitches)->PushBack();
ContextSwitch.Start = Start;
ContextSwitch.End = End;
ContextSwitch.CoreNumber = CoreNumber;
//////////////////////////////////////////////////
// Cpu Core events
if (CoreNumber >= (uint32)CpuCores.Num())
{
CpuCores.AddDefaulted(CoreNumber - CpuCores.Num() + 1);
}
TPagedArray<FCpuCoreEvent>* CpuCoreEvents = CpuCores[CoreNumber];
if (!CpuCoreEvents)
{
CpuCoreEvents = new TPagedArray<FCpuCoreEvent>(Session.GetLinearAllocator(), 4096);
++NumCpuCores;
CpuCores[CoreNumber] = CpuCoreEvents;
}
#if 0 // for debugging
auto Page = CpuCoreEvents->GetLastPage();
if (Page)
{
const FCpuCoreEvent* LastCpuCoreEvent = TraceServices::GetLastItem(*Page);
if (LastCpuCoreEvent)
{
check(LastCpuCoreEvent->End <= Start);
}
}
#endif
FCpuCoreEvent& CpuCoreEvent = CpuCoreEvents->PushBack();
CpuCoreEvent.Start = Start;
CpuCoreEvent.End = End;
CpuCoreEvent.SystemThreadId = SystemThreadId;
}
void FContextSwitchesProvider::AddThreadInfo(uint32 ThreadId, uint32 SystemThreadId)
{
Session.WriteAccessCheck();
if (SystemThreadId == 0)
{
// At the start of a session some threads might be received with a SystemThreadId of 0.
return;
}
uint32* OldSystemThreadIdPtr = TraceToSystemThreadIdMap.Find(ThreadId);
if (OldSystemThreadIdPtr)
{
uint32 OldSystemThreadId = *OldSystemThreadIdPtr;
if (ThreadId == 2 /*GameThread*/ && OldSystemThreadId != SystemThreadId)
{
// When -nothreading is used, multiple fake threads (FFakeThread) are created from the game thread.
// These will have same trace ThreadId == 2 and different SystemThreadId.
SystemToTraceThreadIdMap.Add(SystemThreadId, ThreadId);
return;
}
ensure(OldSystemThreadId == SystemThreadId);
}
TraceToSystemThreadIdMap.Add(ThreadId, SystemThreadId);
SystemToTraceThreadIdMap.Add(SystemThreadId, ThreadId);
}
void FContextSwitchesProvider::AddThreadName(uint32 SystemTreadId, uint32 SystemProcessId, FStringView Name)
{
//TODO
}
FName GetContextSwitchesProviderName()
{
static const FName Name("ContextSwitchesProvider");
return Name;
}
const IContextSwitchesProvider* ReadContextSwitchesProvider(const IAnalysisSession& Session)
{
return Session.ReadProvider<IContextSwitchesProvider>(GetContextSwitchesProviderName());
}
} // namespace TraceServices