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

352 lines
9.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TraceServices/Model/Log.h"
#include "Model/LogPrivate.h"
#include "AnalysisServicePrivate.h"
#include "Common/FormatArgs.h"
namespace TraceServices
{
FLogProvider::FLogProvider(IAnalysisSession& InSession)
: Session(InSession)
, Categories(InSession.GetLinearAllocator(), 128)
, MessageSpecs(InSession.GetLinearAllocator(), 1024)
, Messages(InSession.GetLinearAllocator(), 1024)
, MessagesTable(Messages)
{
MessagesTable.EditLayout().
AddColumn(&FLogMessageInternal::Time, TEXT("Time")).
AddColumn<const TCHAR*>([](const FLogMessageInternal& Message)
{
return ToString(Message.Spec->Verbosity);
},
TEXT("Verbosity")).
AddColumn<const TCHAR*>([](const FLogMessageInternal& Message)
{
return Message.Spec->Category->Name;
},
TEXT("Category")).
AddColumn<const TCHAR*>([](const FLogMessageInternal& Message)
{
return Message.Spec->File;
},
TEXT("File")).
AddColumn<int32>([](const FLogMessageInternal& Message)
{
return Message.Spec->Line;
},
TEXT("Line")).
AddColumn(&FLogMessageInternal::Message, TEXT("Message"));
}
uint64 FLogProvider::RegisterCategory()
{
static uint64 IdGenerator = 0;
return IdGenerator++;
}
FLogCategoryInfo& FLogProvider::GetCategory(uint64 CategoryPointer)
{
Session.WriteAccessCheck();
if (CategoryMap.Contains(CategoryPointer))
{
return *CategoryMap[CategoryPointer];
}
else
{
FLogCategoryInfo& Category = Categories.PushBack();
Category.Name = TEXT("N/A");
Category.DefaultVerbosity = ELogVerbosity::All;
CategoryMap.Add(CategoryPointer, &Category);
return Category;
}
}
FLogMessageSpec& FLogProvider::GetMessageSpec(uint64 LogPoint)
{
Session.WriteAccessCheck();
if (SpecMap.Contains(LogPoint))
{
return *SpecMap[LogPoint];
}
else
{
FLogMessageSpec& Spec = MessageSpecs.PushBack();
SpecMap.Add(LogPoint, &Spec);
return Spec;
}
}
void FLogProvider::UpdateMessageCategory(uint64 LogPoint, uint64 InCategoryPointer)
{
Session.WriteAccessCheck();
FLogMessageSpec& LogMessageSpec = GetMessageSpec(LogPoint);
LogMessageSpec.Category = &GetCategory(InCategoryPointer);
}
void FLogProvider::UpdateMessageFormatString(uint64 LogPoint, const TCHAR* InFormatString)
{
Session.WriteAccessCheck();
FLogMessageSpec& LogMessageSpec = GetMessageSpec(LogPoint);
LogMessageSpec.FormatString = InFormatString;
}
void FLogProvider::UpdateMessageFile(uint64 LogPoint, const TCHAR* InFile, int32 InLine)
{
Session.WriteAccessCheck();
FLogMessageSpec& LogMessageSpec = GetMessageSpec(LogPoint);
LogMessageSpec.File = InFile;
LogMessageSpec.Line = InLine;
}
void FLogProvider::UpdateMessageVerbosity(uint64 LogPoint, ELogVerbosity::Type InVerbosity)
{
Session.WriteAccessCheck();
FLogMessageSpec& LogMessageSpec = GetMessageSpec(LogPoint);
LogMessageSpec.Verbosity = InVerbosity;
}
void FLogProvider::UpdateMessageSpec(uint64 LogPoint, uint64 InCategoryPointer, const TCHAR* InFormatString, const TCHAR* InFile, int32 InLine, ELogVerbosity::Type InVerbosity)
{
Session.WriteAccessCheck();
FLogMessageSpec& LogMessageSpec = GetMessageSpec(LogPoint);
LogMessageSpec.Category = &GetCategory(InCategoryPointer);
LogMessageSpec.FormatString = InFormatString;
LogMessageSpec.File = InFile;
LogMessageSpec.Line = InLine;
LogMessageSpec.Verbosity = InVerbosity;
}
FLogMessageInternal& FLogProvider::AppendMessageInternal(double Time)
{
// Performs binary search, resulting in position of the first log message with time > provided time value.
uint64 Index = TraceServices::PagedArrayAlgo::UpperBoundBy(Messages, Time,
[](const FLogMessageInternal& Item) { return Item.Time; });
if (Index < Messages.Num())
{
++NumInserts;
}
FLogMessageInternal& InternalMessage = Messages.Insert(Index);
InternalMessage.Time = Time;
return InternalMessage;
}
void FLogProvider::AppendUnknownMessageInternal(uint64 LogPoint, double Time, const FStringView Message)
{
FLogMessageSpec& Spec = GetMessageSpec(~0ull);
FLogCategoryInfo& Category = GetCategory(~0ull);
Spec.Category = &Category;
Spec.Line = 0;
Spec.Verbosity = ELogVerbosity::Error;
Spec.FormatString = TEXT("%s");
FLogMessageInternal& InternalMessage = AppendMessageInternal(Time);
InternalMessage.Spec = &Spec;
InternalMessage.Message = Session.StoreString(*FString::Printf(TEXT("Unknown log message spec (LogPoint=0x%llX)! Message: \"%.*s\""), LogPoint, Message.Len(), Message.GetData()));
}
void FLogProvider::AppendMessage(uint64 LogPoint, double Time, const uint8* FormatArgs)
{
Session.WriteAccessCheck();
FLogMessageSpec** FindSpec = SpecMap.Find(LogPoint);
if (FindSpec)
{
if ((*FindSpec)->Verbosity != ELogVerbosity::SetColor)
{
FLogMessageInternal& InternalMessage = AppendMessageInternal(Time);
InternalMessage.Spec = *FindSpec;
FFormatArgsHelper::Format(FormatBuffer, FormatBufferSize - 1, TempBuffer, FormatBufferSize - 1, InternalMessage.Spec->FormatString, FormatArgs);
InternalMessage.Message = Session.StoreString(FormatBuffer);
}
}
else
{
FFormatArgsHelper::Format(FormatBuffer, FormatBufferSize - 1, TempBuffer, FormatBufferSize - 1, TEXT("%s"), FormatArgs);
AppendUnknownMessageInternal(LogPoint, Time, FStringView(FormatBuffer));
}
Session.UpdateDurationSeconds(Time);
}
void FLogProvider::AppendMessage(uint64 LogPoint, double Time, const TCHAR* Text)
{
Session.WriteAccessCheck();
FLogMessageSpec** FindSpec = SpecMap.Find(LogPoint);
if (FindSpec)
{
if ((*FindSpec)->Verbosity != ELogVerbosity::SetColor)
{
FLogMessageInternal& InternalMessage = AppendMessageInternal(Time);
InternalMessage.Spec = *FindSpec;
InternalMessage.Message = Text;
}
else
{
AppendUnknownMessageInternal(LogPoint, Time, FStringView(Text));
}
}
Session.UpdateDurationSeconds(Time);
}
void FLogProvider::AppendMessage(uint64 LogPoint, double Time, const FStringView Message)
{
Session.WriteAccessCheck();
FLogMessageSpec** FindSpec = SpecMap.Find(LogPoint);
if (FindSpec)
{
if ((*FindSpec)->Verbosity != ELogVerbosity::SetColor)
{
FLogMessageInternal& InternalMessage = AppendMessageInternal(Time);
InternalMessage.Spec = *FindSpec;
InternalMessage.Message = Session.StoreString(Message);
}
}
else
{
AppendUnknownMessageInternal(LogPoint, Time, Message);
}
Session.UpdateDurationSeconds(Time);
}
bool FLogProvider::ReadMessage(uint64 Index, TFunctionRef<void(const FLogMessageInfo&)> Callback) const
{
Session.ReadAccessCheck();
if (Index >= Messages.Num())
{
return false;
}
ConstructMessage(Messages[Index], Index, Callback);
return true;
}
void FLogProvider::EnumerateMessagesByIndex(uint64 StartIndex, uint64 EndIndex, TFunctionRef<void(const FLogMessageInfo&)> Callback) const
{
Session.ReadAccessCheck();
uint64 Count = Messages.Num();
if (EndIndex > Count)
{
EndIndex = Count;
}
if (StartIndex >= EndIndex)
{
return;
}
for (auto It = Messages.GetIteratorFromItem(StartIndex); It && It.GetCurrentItemIndex() < EndIndex; ++It)
{
ConstructMessage(*It.GetCurrentItem(), It.GetCurrentItemIndex(), Callback);
}
}
void FLogProvider::EnumerateMessages(double StartTime, double EndTime, TFunctionRef<void(const FLogMessageInfo&)> Callback) const
{
Session.ReadAccessCheck();
if (StartTime > EndTime)
{
return;
}
uint64 MessageCount = Messages.Num();
if (MessageCount == 0)
{
return;
}
// Find the first log message with Time >= StartTime.
uint64 StartIndex = TraceServices::PagedArrayAlgo::LowerBoundBy(Messages, StartTime,
[](const FLogMessageInternal& Item) { return Item.Time; });
if (StartIndex >= Messages.Num())
{
return;
}
// Iterate from StartIndex and stop at first log message with Time > EndTime.
for (auto It = Messages.GetIteratorFromItem(StartIndex); It; ++It)
{
double Time = It.GetCurrentItem()->Time;
if (Time > EndTime)
{
break;
}
ConstructMessage(*It.GetCurrentItem(), It.GetCurrentItemIndex(), Callback);
}
}
uint64 FLogProvider::LowerBoundByTime(double Time) const
{
Session.ReadAccessCheck();
return TraceServices::PagedArrayAlgo::LowerBoundBy(Messages, Time,
[](const FLogMessageInternal& Item) { return Item.Time; });
}
uint64 FLogProvider::UpperBoundByTime(double Time) const
{
Session.ReadAccessCheck();
return TraceServices::PagedArrayAlgo::UpperBoundBy(Messages, Time,
[](const FLogMessageInternal& Item) { return Item.Time; });
}
uint64 FLogProvider::BinarySearchClosestByTime(double Time) const
{
Session.ReadAccessCheck();
return TraceServices::PagedArrayAlgo::BinarySearchClosestBy(Messages, Time,
[](const FLogMessageInternal& Item) { return Item.Time; });
}
void FLogProvider::ConstructMessage(const FLogMessageInternal& InternalMessage, uint64 Index, TFunctionRef<void(const FLogMessageInfo&)> Callback) const
{
FLogMessageInfo Message;
Message.Index = Index;
Message.Time = InternalMessage.Time;
Message.Category = InternalMessage.Spec->Category;
Message.File = InternalMessage.Spec->File;
Message.Line = InternalMessage.Spec->Line;
Message.Verbosity = InternalMessage.Spec->Verbosity;
Message.Message = InternalMessage.Message;
Callback(Message);
}
void FLogProvider::EnumerateCategories(TFunctionRef<void(const FLogCategoryInfo&)> Callback) const
{
Session.ReadAccessCheck();
for (auto Iterator = Categories.GetIteratorFromItem(0); Iterator; ++Iterator)
{
Callback(*Iterator);
}
}
FName GetLogProviderName()
{
static const FName Name("LogProvider");
return Name;
}
const ILogProvider& ReadLogProvider(const IAnalysisSession& Session)
{
return *Session.ReadProvider<ILogProvider>(GetLogProviderName());
}
IEditableLogProvider& EditLogProvider(IAnalysisSession& Session)
{
return *Session.EditProvider<IEditableLogProvider>(GetLogProviderName());
}
void FormatString(TCHAR* OutputString, uint32 OutputStringCount, const TCHAR* FormatString, const uint8* FormatArgs)
{
TCHAR* TempBuffer = (TCHAR*)FMemory_Alloca(OutputStringCount * sizeof(TCHAR));
FFormatArgsHelper::Format(OutputString, OutputStringCount - 1, TempBuffer, OutputStringCount - 1, FormatString, FormatArgs);
}
} // namespace TraceServices