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

340 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PlatformFileTraceAnalysis.h"
#include "AnalysisServicePrivate.h"
#include "Common/Utils.h"
#include "HAL/LowLevelMemTracker.h"
#include "Model/FileActivity.h"
#include "TraceServices/Utils.h"
#define DEBUG_PLATFORMFILETRACE 0
#if DEBUG_PLATFORMFILETRACE
#define PLATFORMFILETRACE_WARNING(x) ensureMsgf(false, TEXT(x))
#else
#define PLATFORMFILETRACE_WARNING(x)
#endif
namespace TraceServices
{
FPlatformFileTraceAnalyzer::FPlatformFileTraceAnalyzer(IAnalysisSession& InSession, FFileActivityProvider& InFileActivityProvider)
: Session(InSession)
, FileActivityProvider(InFileActivityProvider)
{
}
void FPlatformFileTraceAnalyzer::OnAnalysisBegin(const FOnAnalysisContext& Context)
{
auto& Builder = Context.InterfaceBuilder;
Builder.RouteEvent(RouteId_BeginOpen, "PlatformFile", "BeginOpen");
Builder.RouteEvent(RouteId_EndOpen, "PlatformFile", "EndOpen");
Builder.RouteEvent(RouteId_BeginReOpen, "PlatformFile", "BeginReOpen");
Builder.RouteEvent(RouteId_EndReOpen, "PlatformFile", "EndReOpen");
Builder.RouteEvent(RouteId_BeginClose, "PlatformFile", "BeginClose");
Builder.RouteEvent(RouteId_EndClose, "PlatformFile", "EndClose");
Builder.RouteEvent(RouteId_BeginRead, "PlatformFile", "BeginRead");
Builder.RouteEvent(RouteId_EndRead, "PlatformFile", "EndRead");
Builder.RouteEvent(RouteId_BeginWrite, "PlatformFile", "BeginWrite");
Builder.RouteEvent(RouteId_EndWrite, "PlatformFile", "EndWrite");
}
bool FPlatformFileTraceAnalyzer::OnEvent(uint16 RouteId, EStyle Style, const FOnEventContext& Context)
{
LLM_SCOPE_BYNAME(TEXT("Insights/FPlatformFileTraceAnalyzer"));
FAnalysisSessionEditScope _(Session);
const auto& EventData = Context.EventData;
switch (RouteId)
{
case RouteId_BeginOpen:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
#if DEBUG_PLATFORMFILETRACE
if (PendingOpenMap.Contains(ThreadId))
{
PLATFORMFILETRACE_WARNING("BeginOpen: Duplicated event!?");
}
#endif
const FString FileName = FTraceAnalyzerUtils::LegacyAttachmentString<TCHAR>("Path", Context);
uint32 FileIndex = FileActivityProvider.GetFileIndex(*FileName);
FPendingActivity& Open = PendingOpenMap.Add(ThreadId);
Open.ActivityIndex = FileActivityProvider.BeginActivity(FileIndex, FileActivityType_Open, ThreadId, 0, 0, Time);
Open.FileIndex = FileIndex;
break;
}
case RouteId_EndOpen:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 FileHandle = EventData.GetValue<uint64>("FileHandle");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const FPendingActivity* FindOpen = PendingOpenMap.Find(ThreadId);
if (FindOpen)
{
if (FileHandle == uint64(-1)) // invalid file handle
{
FileActivityProvider.EndActivity(FindOpen->FileIndex, FindOpen->ActivityIndex, 0, Time, true);
}
else
{
#if DEBUG_PLATFORMFILETRACE
if (OpenFilesMap.Contains(FileHandle))
{
PLATFORMFILETRACE_WARNING("EndOpen: File already open!?");
}
#endif
OpenFilesMap.Add(FileHandle, FindOpen->FileIndex);
FileActivityProvider.EndActivity(FindOpen->FileIndex, FindOpen->ActivityIndex, 0, Time, false);
}
FileActivityProvider.SetActivityFileHandle(FindOpen->FileIndex, FindOpen->ActivityIndex, FileHandle);
PendingOpenMap.Remove(ThreadId);
}
else
{
PLATFORMFILETRACE_WARNING("EndOpen: BeginOpen event not traced!?");
}
break;
}
case RouteId_BeginReOpen:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 FileHandle = EventData.GetValue<uint64>("OldFileHandle");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
#if DEBUG_PLATFORMFILETRACE
if (PendingReOpenMap.Contains(ThreadId))
{
PLATFORMFILETRACE_WARNING("BeginReOpen: Duplicated event!?");
}
#endif
const uint32* FindFileIndex = OpenFilesMap.Find(FileHandle);
if (!FindFileIndex)
{
PLATFORMFILETRACE_WARNING("BeginReOpen: File is not open!?");
}
else
{
uint32 FileIndex = *FindFileIndex;
FPendingActivity& Open = PendingReOpenMap.Add(ThreadId);
Open.ActivityIndex = FileActivityProvider.BeginActivity(FileIndex, FileActivityType_ReOpen, ThreadId, 0, 0, Time);
Open.FileIndex = FileIndex;
}
break;
}
case RouteId_EndReOpen:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 NewFileHandle = EventData.GetValue<uint64>("NewFileHandle");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const FPendingActivity* FindOpen = PendingReOpenMap.Find(ThreadId);
if (FindOpen)
{
if (NewFileHandle == uint64(-1)) // invalid file handle
{
FileActivityProvider.EndActivity(FindOpen->FileIndex, FindOpen->ActivityIndex, 0, Time, true);
}
else
{
#if DEBUG_PLATFORMFILETRACE
if (OpenFilesMap.Contains(NewFileHandle))
{
PLATFORMFILETRACE_WARNING("EndReOpen: File already open!?");
}
#endif
OpenFilesMap.Add(NewFileHandle, FindOpen->FileIndex);
FileActivityProvider.EndActivity(FindOpen->FileIndex, FindOpen->ActivityIndex, 0, Time, false);
}
FileActivityProvider.SetActivityFileHandle(FindOpen->FileIndex, FindOpen->ActivityIndex, NewFileHandle);
PendingReOpenMap.Remove(ThreadId);
}
else
{
PLATFORMFILETRACE_WARNING("EndReOpen: BeginReOpen event not traced!?");
}
break;
}
case RouteId_BeginClose:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 FileHandle = EventData.GetValue<uint64>("FileHandle");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
#if DEBUG_PLATFORMFILETRACE
if (PendingCloseMap.Contains(ThreadId))
{
PLATFORMFILETRACE_WARNING("BeginClose: Duplicated event!?");
}
#endif
const uint32* FindFileIndex = OpenFilesMap.Find(FileHandle);
uint32 FileIndex;
if (FindFileIndex)
{
FileIndex = *FindFileIndex;
OpenFilesMap.Remove(FileHandle);
}
else
{
PLATFORMFILETRACE_WARNING("BeginClose: File is not open!?");
FileIndex = FileActivityProvider.GetUnknownFileIndex();
}
FPendingActivity& Close = PendingCloseMap.Add(ThreadId);
Close.ActivityIndex = FileActivityProvider.BeginActivity(FileIndex, FileActivityType_Close, ThreadId, 0, 0, Time);
FileActivityProvider.SetActivityFileHandle(FileIndex, Close.ActivityIndex, FileHandle);
Close.FileIndex = FileIndex;
break;
}
case RouteId_EndClose:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const FPendingActivity* FindClose = PendingCloseMap.Find(ThreadId);
if (FindClose)
{
FileActivityProvider.EndActivity(FindClose->FileIndex, FindClose->ActivityIndex, 0, Time, false);
PendingCloseMap.Remove(ThreadId);
}
else
{
PLATFORMFILETRACE_WARNING("EndClose: BeginClose event not traced!?");
}
break;
}
case RouteId_BeginRead:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 ReadHandle = EventData.GetValue<uint64>("ReadHandle");
uint64 FileHandle = EventData.GetValue<uint64>("FileHandle");
uint64 Offset = EventData.GetValue<uint64>("Offset");
uint64 Size = EventData.GetValue<uint64>("Size");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const uint32* FindFileIndex = OpenFilesMap.Find(FileHandle);
uint32 FileIndex;
if (FindFileIndex)
{
FileIndex = *FindFileIndex;
}
else
{
PLATFORMFILETRACE_WARNING("BeginRead: File is not open!?");
FileIndex = FileActivityProvider.GetUnknownFileIndex();
OpenFilesMap.Add(FileHandle, FileIndex);
}
uint64 ReadIndex = FileActivityProvider.BeginActivity(FileIndex, FileActivityType_Read, ThreadId, Offset, Size, Time);
FileActivityProvider.SetActivityFileHandle(FileIndex, ReadIndex, FileHandle);
FileActivityProvider.SetActivityReadWriteHandle(FileIndex, ReadIndex, ReadHandle);
#if DEBUG_PLATFORMFILETRACE
if (ActiveReadsMap.Contains(ReadHandle))
{
PLATFORMFILETRACE_WARNING("BeginRead: Duplicated ReadHandle!?");
}
#endif
FPendingActivity& Read = ActiveReadsMap.Add(ReadHandle);
Read.FileIndex = FileIndex;
Read.ActivityIndex = ReadIndex;
break;
}
case RouteId_EndRead:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 ReadHandle = EventData.GetValue<uint64>("ReadHandle");
uint64 SizeRead = EventData.GetValue<uint64>("SizeRead");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const FPendingActivity* FindRead = ActiveReadsMap.Find(ReadHandle);
if (FindRead)
{
FileActivityProvider.EndActivity(FindRead->FileIndex, FindRead->ActivityIndex, SizeRead, Time, false);
FileActivityProvider.CheckActivityReadWriteHandle(FindRead->FileIndex, FindRead->ActivityIndex, ReadHandle);
ActiveReadsMap.Remove(ReadHandle);
}
else
{
PLATFORMFILETRACE_WARNING("EndRead: BeginRead event not traced!?");
}
break;
}
case RouteId_BeginWrite:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 WriteHandle = EventData.GetValue<uint64>("WriteHandle");
uint64 FileHandle = EventData.GetValue<uint64>("FileHandle");
uint64 Offset = EventData.GetValue<uint64>("Offset");
uint64 Size = EventData.GetValue<uint64>("Size");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const uint32* FindFileIndex = OpenFilesMap.Find(FileHandle);
uint32 FileIndex;
if (FindFileIndex)
{
FileIndex = *FindFileIndex;
}
else
{
PLATFORMFILETRACE_WARNING("BeginWrite: File is not open!?");
FileIndex = FileActivityProvider.GetUnknownFileIndex();
OpenFilesMap.Add(FileHandle, FileIndex);
}
uint64 WriteIndex = FileActivityProvider.BeginActivity(FileIndex, FileActivityType_Write, ThreadId, Offset, Size, Time);
FileActivityProvider.SetActivityFileHandle(FileIndex, WriteIndex, FileHandle);
FileActivityProvider.SetActivityReadWriteHandle(FileIndex, WriteIndex, WriteHandle);
#if DEBUG_PLATFORMFILETRACE
if (ActiveWritesMap.Contains(WriteHandle))
{
PLATFORMFILETRACE_WARNING("BeginWrite: Duplicated WriteHandle!?");
}
#endif
FPendingActivity& Write = ActiveWritesMap.Add(WriteHandle);
Write.FileIndex = FileIndex;
Write.ActivityIndex = WriteIndex;
break;
}
case RouteId_EndWrite:
{
uint64 Cycle = EventData.GetValue<uint64>("Cycle");
double Time = Context.EventTime.AsSeconds(Cycle);
Session.UpdateDurationSeconds(Time);
uint64 WriteHandle = EventData.GetValue<uint64>("WriteHandle");
uint64 SizeWritten = EventData.GetValue<uint64>("SizeWritten");
uint32 ThreadId = FTraceAnalyzerUtils::GetThreadIdField(Context);
const FPendingActivity* FindWrite = ActiveWritesMap.Find(WriteHandle);
if (FindWrite)
{
FileActivityProvider.EndActivity(FindWrite->FileIndex, FindWrite->ActivityIndex, SizeWritten, Time, false);
FileActivityProvider.CheckActivityReadWriteHandle(FindWrite->FileIndex, FindWrite->ActivityIndex, WriteHandle);
ActiveWritesMap.Remove(WriteHandle);
}
else
{
PLATFORMFILETRACE_WARNING("EndWrite: BeginWrite event not traced!?");
}
break;
}
}
return true;
}
} // namespace TraceServices
#undef PLATFORMFILETRACE_WARNING
#undef DEBUG_PLATFORMFILETRACE