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

307 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetadataAnalysis.h"
#include "Common/ProviderLock.h"
#include "Common/Utils.h"
#include "Containers/StringConv.h"
#include "CoreMinimal.h"
#include "Model/DefinitionProvider.h"
#include "Model/MetadataProvider.h"
#include "Trace/Analyzer.h"
#include "TraceServices/Model/Strings.h"
namespace TraceServices
{
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename ArrayType>
static void WriteData(ArrayType& OutData, const UE::Trace::IAnalyzer::FEventData& EventData, uint16 MetadataTypeId, const FMetadataProvider* MetadataProvider, IAnalysisSession& Session);
////////////////////////////////////////////////////////////////////////////////////////////////////
FMetadataAnalysis::FMetadataAnalysis(IAnalysisSession& InSession, FMetadataProvider* InProvider)
: Session(InSession)
, MetadataProvider(InProvider)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FMetadataAnalysis::~FMetadataAnalysis()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FMetadataAnalysis::OnAnalysisBegin(const FOnAnalysisContext& Context)
{
FInterfaceBuilder& Builder = Context.InterfaceBuilder;
// Some operations with the metadata stack
Builder.RouteEvent(RouteId_ClearScope, "MetadataStack", "ClearScope", true);
Builder.RouteEvent(RouteId_SaveStack, "MetadataStack", "SaveStack");
Builder.RouteEvent(RouteId_RestoreStack, "MetadataStack", "RestoreStack", true);
// Subscribe to all scopes on the "Metadata" logger. These are the actual meta data scopes.
Builder.RouteLoggerEvents(RouteId_Metascope, "Metadata", true);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FMetadataAnalysis::OnAnalysisEnd()
{
FProviderEditScopeLock _(*MetadataProvider);
MetadataProvider->OnAnalysisCompleted();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FMetadataAnalysis::OnEvent(uint16 RouteId, EStyle Style, const FOnEventContext& Context)
{
FProviderEditScopeLock _(*MetadataProvider);
switch (RouteId)
{
case RouteId_ClearScope:
{
const uint32 ThreadId = Context.ThreadInfo.GetId();
if (Style == EStyle::EnterScope)
{
MetadataProvider->BeginClearStackScope(ThreadId);
}
else if (ensure(Style == EStyle::LeaveScope))
{
MetadataProvider->EndClearStackScope(ThreadId);
}
}
break;
case RouteId_SaveStack:
{
const uint32 ThreadId = Context.ThreadInfo.GetId();
const uint32 RuntimeId = Context.EventData.GetValue<uint32>("Id");
MetadataProvider->SaveStack(ThreadId, RuntimeId);
}
break;
case RouteId_RestoreStack:
{
const uint32 ThreadId = Context.ThreadInfo.GetId();
if (Style == EStyle::EnterScope)
{
const uint32 RuntimeId = Context.EventData.GetValue<uint32>("Id");
MetadataProvider->BeginRestoreSavedStackScope(ThreadId, RuntimeId);
}
else if (ensure(Style == EStyle::LeaveScope))
{
MetadataProvider->EndRestoreSavedStackScope(ThreadId);
}
}
break;
case RouteId_Metascope:
{
const FEventData& EventData = Context.EventData;
const FEventTypeInfo& EventInfo = Context.EventData.GetTypeInfo();
// Check if we have already encountered and registered this meta data type. If not, build a schema
// for this event type and register it with the provider.
const uint16 MetadataTypeId = GetOrRegisterType(EventInfo);
const uint32 ThreadId = Context.ThreadInfo.GetId();
// Push the actual data of the scope to the provider or pop if we are leaving the scope.
if (Style == EStyle::EnterScope)
{
TArray<uint8, TInlineAllocator<32>> Data;
WriteData(Data, EventData, MetadataTypeId, MetadataProvider, Session);
MetadataProvider->PushScopedMetadata(ThreadId, MetadataTypeId, Data.GetData(), Data.Num());
}
else if (ensure(Style == EStyle::LeaveScope))
{
MetadataProvider->PopScopedMetadata(ThreadId, MetadataTypeId);
}
}
break;
default:
checkf(false, TEXT("Invalid route."));
break;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
uint16 FMetadataAnalysis::GetOrRegisterType(const FEventTypeInfo& EventInfo)
{
if (const uint16* CachedTypeId = EncounteredMetadataTypes.Find(EventInfo.GetId()))
{
return *CachedTypeId;
}
uint16 MetadataTypeId = 0;
FMetadataSchema Schema(static_cast<uint8>(EventInfo.GetFieldCount()));
FMetadataSchema::FBuilder Builder = Schema.Builder();
for (uint32 FieldIdx = 0; FieldIdx < EventInfo.GetFieldCount(); ++FieldIdx)
{
const FEventFieldInfo* FieldInfo = EventInfo.GetFieldInfo(FieldIdx);
const TCHAR* Name = Session.StoreString(ANSI_TO_TCHAR(FieldInfo->GetName()));
const uint8 Size = FieldInfo->GetSize();
switch (FieldInfo->GetType())
{
case FEventFieldInfo::EType::Integer:
{
if (FieldInfo->IsSigned())
{
constexpr auto FieldType = FMetadataSchema::EFieldType::SignedInteger;
if (Size == 1) Builder.AddField<int8>(Name, FieldType);
else if (Size == 2) Builder.AddField<int16>(Name, FieldType);
else if (Size == 4) Builder.AddField<int32>(Name, FieldType);
else if (Size == 8) Builder.AddField<int64>(Name, FieldType);
else { checkNoEntry(); }
}
else
{
constexpr auto FieldType = FMetadataSchema::EFieldType::Integer;
if (Size == 1) Builder.AddField<uint8>(Name, FieldType);
else if (Size == 2) Builder.AddField<uint16>(Name, FieldType);
else if (Size == 4) Builder.AddField<uint32>(Name, FieldType);
else if (Size == 8) Builder.AddField<uint64>(Name, FieldType);
else { checkNoEntry(); }
}
}
break;
case FEventFieldInfo::EType::Float:
{
constexpr auto FieldType = FMetadataSchema::EFieldType::FloatingPoint;
if (Size == 4) Builder.AddField<float>(Name, FieldType);
else if (Size == 8) Builder.AddField<double>(Name, FieldType);
}
break;
case FEventFieldInfo::EType::WideString:
case FEventFieldInfo::EType::AnsiString:
{
// Note that we don't have an ANSI string store yet, so we store any ANSI
// string as WIDE for metadata.
constexpr auto FieldType = FMetadataSchema::EFieldType::WideStringPtr;
Builder.AddField<TCHAR*>(Name, FieldType);
}
break;
case FEventFieldInfo::EType::Reference8:
case FEventFieldInfo::EType::Reference16:
case FEventFieldInfo::EType::Reference32:
case FEventFieldInfo::EType::Reference64:
{
constexpr auto FieldType = FMetadataSchema::EFieldType::Reference;
if (Size == 1) Builder.AddField<UE::Trace::FEventRef8>(Name, FieldType);
else if (Size == 2) Builder.AddField<UE::Trace::FEventRef16>(Name, FieldType);
else if (Size == 4) Builder.AddField<UE::Trace::FEventRef32>(Name, FieldType);
else if (Size == 8) Builder.AddField<UE::Trace::FEventRef64>(Name, FieldType);
else { checkNoEntry(); }
}
break;
default:
break;
}
}
Builder.Finish();
MetadataTypeId = MetadataProvider->RegisterMetadataType(ANSI_TO_TCHAR(EventInfo.GetName()), Schema);
EncounteredMetadataTypes.Emplace(EventInfo.GetId(), MetadataTypeId);
return MetadataTypeId;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename ArrayType>
static void WriteData(ArrayType& OutData, const UE::Trace::IAnalyzer::FEventData& EventData, uint16 MetadataTypeId, const FMetadataProvider* MetadataProvider, IAnalysisSession& Session)
{
using namespace UE::Trace;
const IAnalyzer::FEventTypeInfo& EventInfo = EventData.GetTypeInfo();
const FMetadataSchema* Schema = MetadataProvider->GetRegisteredMetadataSchema(MetadataTypeId);
check(Schema);
FMetadataSchema::FWriter Writer = Schema->Writer();
for (uint32 FieldIdx = 0; FieldIdx < EventInfo.GetFieldCount(); ++FieldIdx)
{
const IAnalyzer::FEventFieldInfo* FieldInfo = EventInfo.GetFieldInfo(FieldIdx);
switch (FieldInfo->GetType())
{
case IAnalyzer::FEventFieldInfo::EType::Reference8:
case IAnalyzer::FEventFieldInfo::EType::Reference16:
case IAnalyzer::FEventFieldInfo::EType::Reference32:
case IAnalyzer::FEventFieldInfo::EType::Reference64:
{
switch (FieldInfo->GetSize())
{
case 1:
{
auto RefValue = EventData.GetReferenceValue<uint8>(FieldIdx);
Writer.WriteField(static_cast<uint8>(FieldIdx), &RefValue, sizeof(UE::Trace::FEventRef8), OutData);
}
break;
case 2:
{
auto RefValue = EventData.GetReferenceValue<uint16>(FieldIdx);
Writer.WriteField(static_cast<uint8>(FieldIdx), &RefValue, sizeof(UE::Trace::FEventRef16), OutData);
}
break;
case 4:
{
auto RefValue = EventData.GetReferenceValue<uint32>(FieldIdx);
Writer.WriteField(static_cast<uint8>(FieldIdx), &RefValue, sizeof(UE::Trace::FEventRef32), OutData);
}
break;
case 8:
{
auto RefValue = EventData.GetReferenceValue<uint64>(FieldIdx);
Writer.WriteField(static_cast<uint8>(FieldIdx), &RefValue, sizeof(UE::Trace::FEventRef64), OutData);
}
break;
default:
checkNoEntry();
}
}
break;
case IAnalyzer::FEventFieldInfo::EType::WideString:
{
FWideStringView View;
EventData.GetString(FieldInfo->GetName(), View);
const TCHAR* Str = Session.StoreString(View);
Writer.WriteField(static_cast<uint8>(FieldIdx), Str, sizeof(TCHAR*), OutData);
}
break;
case IAnalyzer::FEventFieldInfo::EType::AnsiString:
{
FString String;
EventData.GetString(FieldInfo->GetName(), String);
const TCHAR* Str = Session.StoreString(String);
Writer.WriteField(static_cast<uint8>(FieldIdx), Str, sizeof(TCHAR*), OutData);
}
break;
default:
{
const IAnalyzer::FEventFieldHandle FieldHandle = EventInfo.GetFieldHandleUnchecked(FieldIdx);
const void* Value = EventData.GetValueRaw(FieldHandle);
const uint8 Size = FieldInfo->GetSize();
Writer.WriteField(static_cast<uint8>(FieldIdx), Value, Size, OutData);
}
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace TraceServices