Files
UnrealEngine/Engine/Source/Runtime/SessionServices/Private/TraceController.cpp
2025-05-18 13:04:45 +08:00

241 lines
8.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TraceController.h"
#include "IMessageBus.h"
#include "MessageEndpoint.h"
#include "MessageEndpointBuilder.h"
#include "TraceControlMessages.h"
FTraceController::FTraceController(const TSharedRef<IMessageBus>& InMessageBus)
: MessageBus(InMessageBus)
{
MessageEndpoint = FMessageEndpoint::Builder("FTraceController", InMessageBus)
.Handling<FTraceControlDiscovery>(this, &FTraceController::OnDiscoveryResponse)
.Handling<FTraceControlStatus>(this, &FTraceController::OnStatus)
.Handling<FTraceControlSettings>(this, &FTraceController::OnSettings)
.Handling<FTraceControlChannelsDesc>(this, &FTraceController::OnChannelsDesc)
.Handling<FTraceControlChannelsStatus>(this, &FTraceController::OnChannelsStatus)
.NotificationHandling(FOnBusNotification::CreateRaw(this, &FTraceController::OnNotification));
}
FTraceController::~FTraceController()
{
}
void FTraceController::SendDiscoveryRequest(const FGuid& SessionId, const FGuid& InstanceId) const
{
const auto Message = FMessageEndpoint::MakeMessage<FTraceControlDiscoveryPing>();
Message->SessionId = SessionId;
Message->InstanceId = InstanceId;
MessageEndpoint->Publish<FTraceControlDiscoveryPing>(Message);
}
void FTraceController::SendDiscoveryRequest()
{
const auto Message = FMessageEndpoint::MakeMessage<FTraceControlDiscoveryPing>();
MessageEndpoint->Publish<FTraceControlDiscoveryPing>(Message);
}
void FTraceController::SendStatusUpdateRequest()
{
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FTraceControlStatusPing>());
}
void FTraceController::SendChannelUpdateRequest()
{
FReadScopeLock _(InstancesLock);
for (const auto& Instance : Instances)
{
const auto Message = FMessageEndpoint::MakeMessage<FTraceControlChannelsPing>();
Message->KnownChannelCount = uint32(Instance.Value.Status.Channels.Num());
MessageEndpoint->Send(Message, Instance.Key);
}
}
void FTraceController::SendSettingsUpdateRequest()
{
for (const auto& Instance : Instances)
{
MessageEndpoint->Send(FMessageEndpoint::MakeMessage<FTraceControlSettingsPing>(), Instance.Key);
}
}
void FTraceController::OnNotification(const FMessageBusNotification& Event)
{
if (Event.NotificationType == EMessageBusNotification::Unregistered)
{
// Many endpoints may be removed from one instance, look for the
// one we have registered.
FWriteScopeLock _(InstancesLock);
if(Instances.Remove(Event.RegistrationAddress) > 0)
{
InstanceToAddress = InstanceToAddress.FilterByPredicate([&](auto It) { return It.Value != Event.RegistrationAddress; } );
}
}
}
void FTraceController::OnDiscoveryResponse(const FTraceControlDiscovery& Message, const TSharedRef<IMessageContext>& Context)
{
FWriteScopeLock _(InstancesLock);
// If the message bus is no longer available there is no point in registering
// more sessions.
TSharedPtr<IMessageBus> Bus = MessageBus.Pin();
if (!Bus)
{
return;
}
FTracingInstance* Instance = Instances.Find(Context->GetSender());
if (!Instance)
{
// Create a new instance with default status
Instance = &Instances.Emplace(Context->GetSender(), {Bus.ToSharedRef(), Context->GetSender()});
InstanceToAddress.Add(Message.InstanceId, Context->GetSender());
}
UpdateStatus(Message, Instance->Status);
// This is the application session id, which is not necessarily the
// same as trace session (maybe overridden by commandline)
Instance->Status.SessionId = Message.SessionId;
Instance->Status.InstanceId = Message.InstanceId;
FTraceStatus::EUpdateType UpdateType = FTraceStatus::EUpdateType::ChannelsDesc | FTraceStatus::EUpdateType::ChannelsStatus | FTraceStatus::EUpdateType::Status;
StatusReceivedEvent.Broadcast(Instance->Status, UpdateType, Instance->Commands);
}
void FTraceController::OnStatus(const FTraceControlStatus& Message, const TSharedRef<IMessageContext>& Context)
{
FWriteScopeLock _(InstancesLock);
if (FTracingInstance* Instance = Instances.Find(Context->GetSender()))
{
FTraceStatus& Status = Instance->Status;
UpdateStatus(Message, Status);
StatusReceivedEvent.Broadcast(Status, FTraceStatus::EUpdateType::Status, Instance->Commands);
}
}
void FTraceController::OnChannelsDesc(const FTraceControlChannelsDesc& Message, const TSharedRef<IMessageContext>& Context)
{
FWriteScopeLock _(InstancesLock);
const auto Instance = Instances.Find(Context->GetSender());
if (!Instance)
{
return;
}
FTraceStatus& Status = Instance->Status;
check(Message.Channels.Num() == Message.Ids.Num() && Message.Channels.Num() == Message.Descriptions.Num());
const int32 Count = Message.Channels.Num();
for(int32 Index = 0; Index < Count; ++Index)
{
const uint32 Id = Message.Ids[Index];
if (Status.Channels.Contains(Id))
{
continue;
}
FTraceStatus::FChannel& NewChannel = Status.Channels.Add(Id);
NewChannel.Name = Message.Channels[Index];
NewChannel.Description = Message.Descriptions[Index];
NewChannel.Id = Id;
NewChannel.bEnabled = false;
NewChannel.bReadOnly = Message.ReadOnlyIds.Contains(Id);
StatusReceivedEvent.Broadcast(Status, FTraceStatus::EUpdateType::ChannelsDesc, Instance->Commands);
}
// Allow the commands instance to update its list of channels
Instance->Commands.OnChannelsDesc(Message);
}
void FTraceController::OnChannelsStatus(const FTraceControlChannelsStatus& Message, const TSharedRef<IMessageContext>& Context)
{
FWriteScopeLock _(InstancesLock);
if (const auto Instance = Instances.Find(Context->GetSender()))
{
FTraceStatus& Status = Instance->Status;
for (auto& [Id, Channel] : Status.Channels)
{
Channel.bEnabled = Message.EnabledIds.Contains(Id);
}
StatusReceivedEvent.Broadcast(Status, FTraceStatus::EUpdateType::ChannelsStatus, Instance->Commands);
}
}
void FTraceController::OnSettings(const FTraceControlSettings& Message, const TSharedRef<IMessageContext>& Context)
{
FWriteScopeLock _(InstancesLock);
if (const auto Instance = Instances.Find(Context->GetSender()))
{
FTraceStatus& Status = Instance->Status;
FTraceStatus::FSettings& Settings = Status.Settings;
Settings.bUseImportantCache = Message.bUseImportantCache;
Settings.bUseWorkerThread = Message.bUseWorkerThread;
Settings.TailSizeBytes = Message.TailSizeBytes;
Settings.ChannelPresets.Empty();
for (const FTraceChannelPreset& Preset : Message.ChannelPresets)
{
Settings.ChannelPresets.Add(FTraceStatus::FChannelPreset(Preset.Name, Preset.ChannelList, Preset.bIsReadOnly));
}
StatusReceivedEvent.Broadcast(Status, FTraceStatus::EUpdateType::Settings, Instance->Commands);
}
}
bool FTraceController::HasAvailableInstance(const FGuid& InstanceId)
{
return InstanceToAddress.Contains(InstanceId);
}
void FTraceController::UpdateStatus(const FTraceControlStatus& Message, FTraceStatus& Status)
{
Status.TraceSystemStatus = static_cast<FTraceStatus::ETraceSystemStatus>(Message.TraceSystemStatus);
Status.StatusTimestamp = Message.StatusTimestamp;
Status.bIsTracing = Message.bIsTracing;
Status.Endpoint = Message.Endpoint;
Status.SessionGuid = Message.SessionGuid;
Status.TraceGuid = Message.TraceGuid;
Status.bIsPaused = Message.bIsPaused;
Status.bAreStatNamedEventsEnabled = Message.bAreStatNamedEventsEnabled;
Status.Stats.BytesSent = Message.BytesSent;
Status.Stats.MemoryUsed = Message.MemoryUsed;
Status.Stats.BytesTraced = Message.BytesTraced;
Status.Stats.CacheAllocated = Message.CacheAllocated;
Status.Stats.CacheUsed = Message.CacheUsed;
Status.Stats.CacheWaste = Message.CacheWaste;
}
void FTraceController::WithInstance(FGuid InstanceId, FCallback Func)
{
FReadScopeLock _(InstancesLock);
if(const auto& InstanceAddress = InstanceToAddress.Find(InstanceId))
{
if(auto* Instance = Instances.Find(*InstanceAddress))
{
Func(Instance->Status, Instance->Commands);
}
}
}
FTraceController::FTracingInstance::FTracingInstance(const TSharedRef<IMessageBus>& InMessageBus, FMessageAddress InService)
: Status()
, Commands(InMessageBus, InService)
{
}
TSharedPtr<ITraceController> ITraceController::Create(TSharedPtr<IMessageBus>& InBus)
{
return MakeShareable(new FTraceController(InBus.ToSharedRef()));
}