Files
2025-05-18 13:04:45 +08:00

1128 lines
34 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InsightsManager.h"
#include "DesktopPlatformModule.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/Docking/TabManager.h"
#include "HAL/PlatformMemory.h"
#include "HAL/PlatformProcess.h"
#include "ISessionServicesModule.h"
#include "ISourceCodeAccessModule.h"
#include "ISourceCodeAccessor.h"
#include "Logging/MessageLog.h"
#include "MessageLogModule.h"
#include "Misc/CString.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "Templates/UniquePtr.h"
#include "WorkspaceMenuStructure.h"
#include "WorkspaceMenuStructureModule.h"
// TraceTools
#include "TraceTools/Interfaces/ITraceToolsModule.h"
// TraceAnalysis
#include "Trace/StoreClient.h"
// TraceServices
#include "TraceServices/AnalysisService.h"
#include "TraceServices/Model/Diagnostics.h"
#include "TraceServices/Model/NetProfiler.h"
// TraceInsightsCore
#include "InsightsCore/Common/MiscUtils.h"
#include "InsightsCore/Common/TimeUtils.h"
// TraceInsights
#include "Insights/Common/InsightsMenuBuilder.h"
#include "Insights/InsightsStyle.h"
#include "Insights/IUnrealInsightsModule.h"
#include "Insights/LoadingProfiler/LoadingProfilerManager.h"
#include "Insights/Log.h"
#include "Insights/NetworkingProfiler/NetworkingProfilerManager.h"
#include "Insights/Tests/InsightsTestRunner.h"
#include "Insights/TimingProfiler/TimingProfilerManager.h"
#include "Insights/TimingProfiler/Widgets/STimingProfilerWindow.h"
#include "Insights/Widgets/SSessionInfoWindow.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
#define LOCTEXT_NAMESPACE "InsightsManager"
const FName FInsightsManagerTabs::StartPageTabId(TEXT("TraceStore")); // DEPRECATED
const FName FInsightsManagerTabs::TraceStoreTabId(TEXT("TraceStore")); // DEPRECATED
const FName FInsightsManagerTabs::ConnectionTabId(TEXT("Connection")); // DEPRECATED
const FName FInsightsManagerTabs::LauncherTabId(TEXT("Launcher")); // DEPRECATED
const FName FInsightsManagerTabs::SessionInfoTabId(TEXT("SessionInfo"));
const FName FInsightsManagerTabs::TimingProfilerTabId(TEXT("TimingProfiler"));
const FName FInsightsManagerTabs::LoadingProfilerTabId(TEXT("LoadingProfiler"));
const FName FInsightsManagerTabs::MemoryProfilerTabId(TEXT("MemoryProfiler"));
const FName FInsightsManagerTabs::NetworkingProfilerTabId(TEXT("NetworkingProfiler"));
const FName FInsightsManagerTabs::AutomationWindowTabId(TEXT("AutomationWindow"));
const FName FInsightsManagerTabs::MessageLogTabId(TEXT("MessageLog"));
const FName FInsightsManagerTabs::TraceControlTabId(TEXT("TraceControl"));
namespace UE::Insights
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// FInsightsManager
////////////////////////////////////////////////////////////////////////////////////////////////////
const TCHAR* FInsightsManager::AutoQuitMsg = TEXT("Application is closing because it was started with the AutoQuit parameter and session analysis is complete.");
const TCHAR* FInsightsManager::AutoQuitMsgOnFail = TEXT("Application is closing because it was started with the AutoQuit parameter and session analysis failed to start.");
TSharedPtr<FInsightsManager> FInsightsManager::Instance = nullptr;
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedPtr<FInsightsManager> FInsightsManager::Get()
{
return FInsightsManager::Instance;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedPtr<FInsightsManager> FInsightsManager::CreateInstance(TSharedRef<TraceServices::IAnalysisService> TraceAnalysisService,
TSharedRef<TraceServices::IModuleService> TraceModuleService)
{
ensure(!FInsightsManager::Instance.IsValid());
if (FInsightsManager::Instance.IsValid())
{
FInsightsManager::Instance.Reset();
}
FInsightsManager::Instance = MakeShared<FInsightsManager>(TraceAnalysisService, TraceModuleService);
return FInsightsManager::Instance;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FInsightsManager::FInsightsManager(TSharedRef<TraceServices::IAnalysisService> InTraceAnalysisService,
TSharedRef<TraceServices::IModuleService> InTraceModuleService)
: LogListingName(TEXT("UnrealInsights"))
, AnalysisLogListingName(TEXT("TraceAnalysis"))
, AnalysisService(InTraceAnalysisService)
, ModuleService(InTraceModuleService)
, CommandList(new FUICommandList())
, ActionManager(this)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::Initialize(IUnrealInsightsModule& InsightsModule)
{
ensure(!bIsInitialized);
if (bIsInitialized)
{
return;
}
bIsInitialized = true;
InsightsMenuBuilder = MakeShared<FInsightsMenuBuilder>();
FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
MessageLogModule.RegisterLogListing(GetLogListingName(), LOCTEXT("UnrealInsights", "Unreal Insights"));
MessageLogModule.RegisterLogListing(AnalysisLogListingName, LOCTEXT("TraceAnalysis", "Trace Analysis"));
MessageLogModule.EnableMessageLogDisplay(true);
RegisterTraceControlTab();
// Register tick functions.
OnTick = FTickerDelegate::CreateSP(this, &FInsightsManager::Tick);
OnTickHandle = FTSTicker::GetCoreTicker().AddTicker(OnTick, 0.0f);
FInsightsCommands::Register();
BindCommands();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::Shutdown()
{
if (!bIsInitialized)
{
return;
}
bIsInitialized = false;
ResetSession(false);
FInsightsCommands::Unregister();
// Unregister tick function.
FTSTicker::GetCoreTicker().RemoveTicker(OnTickHandle);
// If the MessageLog module was already unloaded as part of the global Shutdown process, do not load it again.
if (FModuleManager::Get().IsModuleLoaded("MessageLog"))
{
FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
if (MessageLogModule.IsRegisteredLogListing(GetLogListingName()))
{
MessageLogModule.UnregisterLogListing(GetLogListingName());
}
}
FInsightsManager::Instance.Reset();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FInsightsManager::~FInsightsManager()
{
ensure(!bIsInitialized);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::BindCommands()
{
ActionManager.Map_InsightsManager_Load();
ActionManager.Map_ToggleDebugInfo_Global();
ActionManager.Map_OpenSettings_Global();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::RegisterMajorTabs(IUnrealInsightsModule& InsightsModule)
{
const FInsightsMajorTabConfig& SessionInfoConfig = InsightsModule.FindMajorTabConfig(FInsightsManagerTabs::SessionInfoTabId);
if (SessionInfoConfig.bIsAvailable)
{
// Register tab spawner for the Session Info.
FTabSpawnerEntry& TabSpawnerEntry = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(FInsightsManagerTabs::SessionInfoTabId,
FOnSpawnTab::CreateRaw(this, &FInsightsManager::SpawnSessionInfoTab))
.SetDisplayName(SessionInfoConfig.TabLabel.IsSet() ? SessionInfoConfig.TabLabel.GetValue() : LOCTEXT("SessionInfoTabTitle", "Session"))
.SetTooltipText(SessionInfoConfig.TabTooltip.IsSet() ? SessionInfoConfig.TabTooltip.GetValue() : LOCTEXT("SessionInfoTooltipText", "Open the Session tab."))
.SetIcon(SessionInfoConfig.TabIcon.IsSet() ? SessionInfoConfig.TabIcon.GetValue() : FSlateIcon(FInsightsStyle::GetStyleSetName(), "Icons.SessionInfo"));
TSharedRef<FWorkspaceItem> Group = SessionInfoConfig.WorkspaceGroup.IsValid() ? SessionInfoConfig.WorkspaceGroup.ToSharedRef() : GetInsightsMenuBuilder()->GetInsightsToolsGroup();
TabSpawnerEntry.SetGroup(Group);
}
#if !WITH_EDITOR
FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
MessageLogModule.RegisterMessageLogSpawner(GetInsightsMenuBuilder()->GetWindowsGroup());
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::UnregisterMajorTabs()
{
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(FInsightsManagerTabs::SessionInfoTabId);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SDockTab> FInsightsManager::SpawnSessionInfoTab(const FSpawnTabArgs& Args)
{
const TSharedRef<SDockTab> DockTab = SNew(SDockTab)
.TabRole(ETabRole::NomadTab);
DockTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &FInsightsManager::OnSessionInfoTabClosed));
// Create the SSessionInfoWindow widget.
TSharedRef<SSessionInfoWindow> Window = SNew(SSessionInfoWindow, DockTab, Args.GetOwnerWindow());
DockTab->SetContent(Window);
AssignSessionInfoWindow(Window);
if (!bIsMainTabSet)
{
FGlobalTabmanager::Get()->SetMainTab(DockTab);
bIsMainTabSet = true;
}
return DockTab;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OnSessionInfoTabClosed(TSharedRef<SDockTab> TabBeingClosed)
{
RemoveSessionInfoWindow();
// Disable TabClosed delegate.
TabBeingClosed->SetOnTabClosed(SDockTab::FOnTabClosedCallback());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedPtr<const TraceServices::IAnalysisSession> FInsightsManager::GetSession() const
{
return Session;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const TSharedRef<FUICommandList> FInsightsManager::GetCommandList() const
{
return CommandList;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const FInsightsCommands& FInsightsManager::GetCommands()
{
return FInsightsCommands::Get();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FInsightsActionManager& FInsightsManager::GetActionManager()
{
return FInsightsManager::Instance->ActionManager;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FInsightsSettings& FInsightsManager::GetSettings()
{
return FInsightsManager::Instance->Settings;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::Tick(float DeltaTime)
{
if (RetryLoadLastLiveSessionTimer > 0.0f)
{
LoadLastLiveSession(0.0f);
if (Session)
{
RetryLoadLastLiveSessionTimer = 0.0f;
}
else
{
RetryLoadLastLiveSessionTimer -= DeltaTime;
}
}
AutoLoadLiveSession();
UpdateSessionDuration();
PollAnalysisInfo();
#if !WITH_EDITOR
if (!bIsSessionInfoSet && Session.IsValid())
{
{
TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get());
const TraceServices::IDiagnosticsProvider* DiagnosticsProvider = TraceServices::ReadDiagnosticsProvider(*Session.Get());
if (DiagnosticsProvider && DiagnosticsProvider->IsSessionInfoAvailable())
{
bIsSessionInfoSet = true;
InstanceId = DiagnosticsProvider->GetSessionInfo().InstanceId;
TSharedPtr<SWidget> TraceControlPtr = TraceControl.Pin();
if (TraceControlPtr.IsValid())
{
FModuleManager::LoadModuleChecked<UE::TraceTools::ITraceToolsModule>("TraceTools").SetTraceControlWidgetInstanceId(TraceControlPtr.ToSharedRef(), InstanceId);
}
}
}
if (bIsSessionInfoSet)
{
UpdateAppTitle();
}
}
ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess");
ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor();
SourceCodeAccessor.Tick(DeltaTime);
CheckMemoryUsage();
#endif // !WITH_EDITOR
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::UpdateSessionDuration()
{
using namespace UE::Insights;
if (Session.IsValid())
{
if (!bIsAnalysisComplete)
{
AnalysisStopwatch.Update();
AnalysisDuration = AnalysisStopwatch.GetAccumulatedTime();
AnalysisSpeedFactor = SessionDuration / AnalysisDuration;
}
bool bLocalIsAnalysisComplete = false;
double LocalSessionDuration = 0.0;
{
TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get());
bLocalIsAnalysisComplete = Session->IsAnalysisComplete();
LocalSessionDuration = Session->GetDurationSeconds();
}
if (LocalSessionDuration != SessionDuration)
{
SessionDuration = LocalSessionDuration;
AnalysisSpeedFactor = SessionDuration / AnalysisDuration;
if (bIsAnalysisComplete)
{
UE_LOG(TraceInsights, Warning, TEXT("The session duration was updated (%s) after the analysis has been completed."),
*FormatTimeAuto(GetSessionDuration(), 2));
}
}
if (bLocalIsAnalysisComplete && !bIsAnalysisComplete)
{
bIsAnalysisComplete = true;
AnalysisStopwatch.Update();
AnalysisDuration = AnalysisStopwatch.GetAccumulatedTime();
AnalysisSpeedFactor = SessionDuration / AnalysisDuration;
SessionAnalysisCompletedEvent.Broadcast();
UE_LOG(TraceInsights, Log, TEXT("Analysis has completed in %s (%.1fX speed; session duration: %s)."),
*FormatTimeAuto(AnalysisDuration, 2),
AnalysisSpeedFactor,
*FormatTimeAuto(SessionDuration, 2));
OnSessionAnalysisCompleted();
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::CheckMemoryUsage()
{
if (Session.IsValid()) // only check if we are in "viewer mode"
{
constexpr double MemUsageLimitPercent = 80.0;
constexpr double MemUsageLimitHysteresisPercent = 50.0;
const uint64 Time = FPlatformTime::Cycles64();
const double DurationSeconds = static_cast<double>(Time - MemUsageLimitLastTimestamp) * FPlatformTime::GetSecondsPerCycle64();
if (DurationSeconds > 1.0) // only check once per second
{
MemUsageLimitLastTimestamp = Time;
FPlatformMemoryStats Stats = FPlatformMemory::GetStats();
constexpr double GiB = 1024.0 * 1024.0 * 1024.0;
const double UsedGiB = (double)(Stats.TotalPhysical - Stats.AvailablePhysical) / GiB;
const double TotalGiB = (double)(Stats.TotalPhysical) / GiB;
const double UsedPercent = (UsedGiB * 100.0) / TotalGiB;
if (!bMemUsageLimitHysteresis)
{
if (UsedPercent >= MemUsageLimitPercent)
{
bMemUsageLimitHysteresis = true;
const FText MessageBoxTextFmt = LOCTEXT("MemUsageWarning_TextFmt", "High System Memory Usage Detected: {0} / {1} GiB ({2}%)!\nUnreal Insights might need more memory!");
const FText MessageBoxText = FText::Format(MessageBoxTextFmt,
FText::AsNumber((uint32)(UsedGiB + 0.5)),
FText::AsNumber((uint32)(TotalGiB + 0.5)),
FText::AsNumber((uint32)(UsedPercent + 0.5)));
FMessageLog ReportMessageLog(GetLogListingName());
TSharedRef<FTokenizedMessage> Message = FTokenizedMessage::Create(EMessageSeverity::Warning, MessageBoxText);
ReportMessageLog.AddMessage(Message);
ReportMessageLog.Notify();
}
}
else
{
if (UsedPercent <= MemUsageLimitHysteresisPercent)
{
bMemUsageLimitHysteresis = false;
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::ResetSession(bool bNotify)
{
if (Session.IsValid())
{
Session->Stop(true);
Session.Reset();
CurrentTraceId = 0;
CurrentTraceFilename.Reset();
if (bNotify)
{
OnSessionChanged();
}
}
bIsSessionInfoSet = false;
bIsAnalysisComplete = false;
SessionDuration = 0.0;
AnalysisStopwatch.Restart();
AnalysisDuration = 0.0;
AnalysisSpeedFactor = 0.0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::PollAnalysisInfo()
{
if (Session.IsValid())
{
if (Session->GetNumPendingMessages())
{
TSharedPtr<TraceServices::IAnalysisSession> EditableSession = ConstCastSharedPtr<TraceServices::IAnalysisSession>(Session);
TraceServices::FAnalysisSessionEditScope SessionEditScope(*EditableSession.Get());
const auto Messages = EditableSession->DrainPendingMessages();
FMessageLog ReportMessageLog(AnalysisLogListingName);
for (const auto& Message : Messages)
{
TSharedRef<FTokenizedMessage> LogMessage = FTokenizedMessage::Create(Message.Severity, FText::FromString(Message.Message));
ReportMessageLog.AddMessage(LogMessage);
if (Message.Severity == EMessageSeverity::Error)
{
ReportMessageLog.Notify();
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OnSessionChanged()
{
if (Session.IsValid())
{
FMessageLog(AnalysisLogListingName).NewPage(FText::FromString(Session->GetName()));
}
SessionChangedEvent.Broadcast();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::SpawnAndActivateTabs()
{
// Open Session Info tab.
if (FGlobalTabmanager::Get()->HasTabSpawner(FInsightsManagerTabs::SessionInfoTabId))
{
FGlobalTabmanager::Get()->TryInvokeTab(FInsightsManagerTabs::SessionInfoTabId);
}
// Open Timing Insights tab.
if (FGlobalTabmanager::Get()->HasTabSpawner(FInsightsManagerTabs::TimingProfilerTabId))
{
FGlobalTabmanager::Get()->TryInvokeTab(FInsightsManagerTabs::TimingProfilerTabId);
}
// Open Asset Loading Insights tab.
if (FGlobalTabmanager::Get()->HasTabSpawner(FInsightsManagerTabs::LoadingProfilerTabId))
{
FGlobalTabmanager::Get()->TryInvokeTab(FInsightsManagerTabs::LoadingProfilerTabId);
}
// Close the existing Networking Insights tabs.
//for (int32 ReservedId = 0; ReservedId < 10; ++ReservedId)
{
FName TabId = FInsightsManagerTabs::NetworkingProfilerTabId;
//TabId.SetNumber(ReservedId);
if (FGlobalTabmanager::Get()->HasTabSpawner(TabId))
{
TSharedPtr<SDockTab> NetworkingProfilerTab;
while ((NetworkingProfilerTab = FGlobalTabmanager::Get()->FindExistingLiveTab(TabId)).IsValid())
{
NetworkingProfilerTab->RequestCloseTab();
}
}
}
ActivateTimingInsightsTab();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::ActivateTimingInsightsTab()
{
// Ensure Timing Insights / Timing View is the active tab / view.
if (TSharedPtr<SDockTab> TimingInsightsTab = FGlobalTabmanager::Get()->FindExistingLiveTab(FInsightsManagerTabs::TimingProfilerTabId))
{
TimingInsightsTab->ActivateInParent(ETabActivationCause::SetDirectly);
using namespace UE::Insights::TimingProfiler;
//TODO: FTimingProfilerManager::Get()->ActivateWindow();
TSharedPtr<class STimingProfilerWindow> Wnd = FTimingProfilerManager::Get()->GetProfilerWindow();
if (Wnd)
{
TSharedPtr<FTabManager> TabManager = Wnd->GetTabManager();
if (TSharedPtr<SDockTab> TimingViewTab = TabManager->FindExistingLiveTab(FTimingProfilerTabs::TimingViewID))
{
TimingViewTab->ActivateInParent(ETabActivationCause::SetDirectly);
FSlateApplication::Get().SetKeyboardFocus(TimingViewTab->GetContent());
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::ShowOpenTraceFileDialog(FString& OutTraceFile) const
{
static FString DefaultDirectory(FPaths::ConvertRelativePathToFull(GetStoreDir()));
TArray<FString> OutFiles;
bool bOpened = false;
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
if (DesktopPlatform != nullptr)
{
FSlateApplication::Get().CloseToolTip();
bOpened = DesktopPlatform->OpenFileDialog
(
FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr),
LOCTEXT("LoadTrace_FileDesc", "Open trace file...").ToString(),
DefaultDirectory,
TEXT(""),
LOCTEXT("LoadTrace_FileFilter", "Trace files (*.utrace)|*.utrace|All files (*.*)|*.*").ToString(),
EFileDialogFlags::None,
OutFiles
);
}
if (bOpened == true && OutFiles.Num() == 1)
{
OutTraceFile = OutFiles[0];
DefaultDirectory = FPaths::GetPath(OutTraceFile);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OpenTraceFile() const
{
FString TraceFile;
if (ShowOpenTraceFileDialog(TraceFile))
{
OpenTraceFile(TraceFile);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OpenTraceFile(const FString& InTraceFile) const
{
FString CmdLine = TEXT("-OpenTraceFile=\"") + InTraceFile + TEXT("\"");
FMiscUtils::OpenUnrealInsights(*CmdLine);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::AutoLoadLiveSession()
{
if (!bIsAutoLoadLiveSessionEnabled)
{
return;
}
UE::Trace::FStoreClient* StoreClient = TraceStoreConnection.GetStoreClient();
if (!StoreClient)
{
return;
}
uint32 AutoLoadTraceId = 0;
{
FScopeLock _(&TraceStoreConnection.GetStoreClientCriticalSection());
const uint32 SessionCount = StoreClient->GetSessionCount();
for (uint32 SessionIndex = 0; SessionIndex < SessionCount; ++SessionIndex)
{
const UE::Trace::FStoreClient::FSessionInfo* SessionInfo = StoreClient->GetSessionInfo(SessionIndex);
if (SessionInfo)
{
const uint32 TraceId = SessionInfo->GetTraceId();
if (TraceId != CurrentTraceId && !AutoLoadedTraceIds.Contains(TraceId))
{
AutoLoadTraceId = TraceId;
break;
}
}
}
}
if (AutoLoadTraceId != 0)
{
AutoLoadedTraceIds.Add(AutoLoadTraceId);
LoadTrace(AutoLoadTraceId);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::LoadLastLiveSession(float RetryTime)
{
ResetSession();
UE::Trace::FStoreClient* StoreClient = TraceStoreConnection.GetStoreClient();
if (!StoreClient)
{
return;
}
uint32 LastLiveSessionTraceId = 0;
{
FScopeLock _(&TraceStoreConnection.GetStoreClientCriticalSection());
const uint32 SessionCount = StoreClient->GetSessionCount();
if (SessionCount != 0)
{
const UE::Trace::FStoreClient::FSessionInfo* SessionInfo = StoreClient->GetSessionInfo(SessionCount - 1);
if (SessionInfo)
{
LastLiveSessionTraceId = SessionInfo->GetTraceId();
}
}
}
if (LastLiveSessionTraceId)
{
LoadTrace(LastLiveSessionTraceId);
}
if (Session == nullptr && RetryTime > 0)
{
RetryLoadLastLiveSessionTimer = RetryTime;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::LoadTrace(uint32 InTraceId, bool InAutoQuit)
{
ResetSession();
UE::Trace::FStoreClient* StoreClient = TraceStoreConnection.GetStoreClient();
if (!StoreClient)
{
if (InAutoQuit)
{
RequestEngineExit(AutoQuitMsgOnFail);
}
return;
}
FScopeLock StoreClientLock(&TraceStoreConnection.GetStoreClientCriticalSection());
UE::Trace::FStoreClient::FTraceData TraceData = StoreClient->ReadTrace(InTraceId);
if (!TraceData)
{
if (InAutoQuit)
{
RequestEngineExit(AutoQuitMsgOnFail);
}
return;
}
FString TraceName;
const UE::Trace::FStoreClient::FTraceInfo* TraceInfo = StoreClient->GetTraceInfoById(InTraceId);
if (TraceInfo != nullptr)
{
const FUtf8StringView Utf8NameView = TraceInfo->GetName();
FString Name(Utf8NameView);
FUtf8StringView Uri = TraceInfo->GetUri();
if (Uri.Len() > 0)
{
TraceName = FString(Uri);
}
else
{
// Fallback for older versions of UTS which didn't write uri
FString StoreDirectory(StoreClient->GetStatus()->GetStoreDir());
TraceName = FPaths::SetExtension(FPaths::Combine(StoreDirectory, Name), TEXT(".utrace"));
FPaths::MakePlatformFilename(TraceName);
}
}
StoreClientLock.Unlock();
Session = AnalysisService->StartAnalysis(InTraceId, *TraceName, MoveTemp(TraceData));
if (Session)
{
CurrentTraceId = InTraceId;
CurrentTraceFilename = TraceName;
bIsSessionInfoSet = false;
OnSessionChanged();
bSessionAnalysisCompletedAutoQuit = InAutoQuit;
}
else if (InAutoQuit)
{
RequestEngineExit(AutoQuitMsgOnFail);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::LoadTraceFile()
{
FString TraceFile;
if (ShowOpenTraceFileDialog(TraceFile))
{
LoadTraceFile(TraceFile);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::LoadTraceFile(const FString& InTraceFilename, bool InAutoQuit)
{
IPlatformFile& PlatformFile = IPlatformFile::GetPlatformPhysical();
if (!PlatformFile.FileExists(*InTraceFilename))
{
const uint32 TraceId = uint32(FCString::Strtoui64(*InTraceFilename, nullptr, 10));
return LoadTrace(TraceId, InAutoQuit);
}
ResetSession();
Session = AnalysisService->StartAnalysis(*InTraceFilename);
if (Session)
{
CurrentTraceId = 0;
CurrentTraceFilename = InTraceFilename;
bIsSessionInfoSet = false;
OnSessionChanged();
bSessionAnalysisCompletedAutoQuit = InAutoQuit;
}
else if (InAutoQuit)
{
RequestEngineExit(AutoQuitMsgOnFail);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
uint16 FInsightsManager::ListenForDirectTrace()
{
constexpr uint32 TraceId = 0;
FString TraceName(TEXT("DirectTrace"));
TUniquePtr<UE::Trace::FDirectSocketStream> TraceData = MakeUnique<UE::Trace::FDirectSocketStream>();
const uint16 Port = TraceData->StartListening();
Session = AnalysisService->StartAnalysis(TraceId, *TraceName, MoveTemp(TraceData));
if (Session)
{
CurrentTraceId = TraceId;
CurrentTraceFilename = TraceName;
bIsSessionInfoSet = false;
OnSessionChanged();
}
return Port;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::OnDragOver(const FDragDropEvent& DragDropEvent)
{
TSharedPtr<FExternalDragOperation> DragDropOp = DragDropEvent.GetOperationAs<FExternalDragOperation>();
if (DragDropOp.IsValid())
{
if (DragDropOp->HasFiles())
{
const TArray<FString>& Files = DragDropOp->GetFiles();
if (Files.Num() == 1)
{
const FString DraggedFileExtension = FPaths::GetExtension(Files[0], true);
if (DraggedFileExtension == TEXT(".utrace"))
{
return true;
}
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::OnDrop(const FDragDropEvent& DragDropEvent)
{
TSharedPtr<FExternalDragOperation> DragDropOp = DragDropEvent.GetOperationAs<FExternalDragOperation>();
if (DragDropOp.IsValid())
{
if (DragDropOp->HasFiles())
{
// For now, only allow a single file.
const TArray<FString>& Files = DragDropOp->GetFiles();
if (Files.Num() == 1)
{
const FString DraggedFileExtension = FPaths::GetExtension(Files[0], true);
if (DraggedFileExtension == TEXT(".utrace"))
{
LoadTraceFile(Files[0]);
UpdateAppTitle();
return true;
}
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::UpdateAppTitle()
{
#if !WITH_EDITOR
TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow();
if (RootWindow)
{
if (CurrentTraceFilename.IsEmpty())
{
const FText AppTitle = LOCTEXT("UnrealInsightsAppName", "Unreal Insights");
RootWindow->SetTitle(AppTitle);
}
else
{
bool bWasAppTitleUpdated = false;
if (Session.IsValid())
{
TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get());
const TraceServices::IDiagnosticsProvider* DiagnosticsProvider = TraceServices::ReadDiagnosticsProvider(*Session.Get());
if (DiagnosticsProvider && DiagnosticsProvider->IsSessionInfoAvailable())
{
TraceServices::FSessionInfo SessionInfo = DiagnosticsProvider->GetSessionInfo();
const FString SessionName = FPaths::GetBaseFilename(CurrentTraceFilename);
const FText AppTitle = FText::Format(LOCTEXT("UnrealInsightsAppNameFmt2", "{0}{1} - {2} - {3} - {4} - {5} Unreal Insights"),
FText::FromString(SessionName),
!SessionInfo.Branch.IsEmpty() ? FText::FromString(TEXT(" - ") + SessionInfo.Branch) : FText::GetEmpty(),
FText::FromString(SessionInfo.Platform),
FText::FromString(SessionInfo.AppName),
FText::FromString(LexToString(SessionInfo.ConfigurationType)),
FText::FromString(LexToString(SessionInfo.TargetType)));
RootWindow->SetTitle(AppTitle);
bWasAppTitleUpdated = true;
}
}
if (!bWasAppTitleUpdated)
{
const FString SessionName = FPaths::GetBaseFilename(CurrentTraceFilename);
const FText AppTitle = FText::Format(LOCTEXT("UnrealInsightsAppNameFmt", "{0} - Unreal Insights"), FText::FromString(SessionName));
RootWindow->SetTitle(AppTitle);
}
}
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OpenSettings()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::ScheduleCommand(const FString& InCmd)
{
SessionAnalysisCompletedCmd = InCmd;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OnSessionAnalysisCompleted()
{
if (!SessionAnalysisCompletedCmd.IsEmpty())
{
FOutputDevice& Ar = *GLog;
Ar.Logf(TEXT("Executing commands on analysis completed..."));
UE::Insights::FStopwatch Stopwatch;
Stopwatch.Start();
IUnrealInsightsModule& TraceInsightsModule = FModuleManager::LoadModuleChecked<IUnrealInsightsModule>("TraceInsights");
TraceInsightsModule.Exec(*SessionAnalysisCompletedCmd, Ar);
Stopwatch.Stop();
Ar.Logf(TEXT("Commands executed in %.3fs."), Stopwatch.GetAccumulatedTime());
}
#if !UE_BUILD_SHIPPING && !WITH_EDITOR
if (FInsightsTestRunner::Get().IsValid())
{
// Don't quit now. Let the test runner to execute.
bSessionAnalysisCompletedAutoQuit = false;
}
#endif
if (bSessionAnalysisCompletedAutoQuit)
{
bSessionAnalysisCompletedAutoQuit = false;
RequestEngineExit(AutoQuitMsg);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::Exec(const TCHAR* Cmd, FOutputDevice& Ar)
{
// @=ResponseFile
if (Cmd != nullptr &&
Cmd[0] == TEXT('@') &&
Cmd[1] == TEXT('='))
{
HandleResponseFileCmd(Cmd + 2, Ar);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool FInsightsManager::HandleResponseFileCmd(const TCHAR* ResponseFile, FOutputDevice& Ar)
{
Ar.Logf(TEXT("Executing commands using response file (\"%s\")..."), ResponseFile);
FString Contents;
if (!FFileHelper::LoadFileToString(Contents, &IPlatformFile::GetPlatformPhysical(), ResponseFile))
{
Ar.Logf(ELogVerbosity::Error, TEXT("Failed to open the response file (\"%s\")."), ResponseFile);
return false;
}
if (Contents.IsEmpty())
{
return true;
}
IUnrealInsightsModule& TraceInsightsModule = FModuleManager::LoadModuleChecked<IUnrealInsightsModule>("TraceInsights");
TCHAR* StartPos = &Contents[0];
TCHAR* EndPos = StartPos + Contents.Len();
TCHAR* CrtPos = StartPos;
while (CrtPos != EndPos)
{
uint32 EndOfLine = 0;
if (*CrtPos == TEXT('\r'))
{
if (*(CrtPos + 1) == TEXT('\n'))
{
EndOfLine = 2;
}
else
{
EndOfLine = 1;
}
}
else if (*CrtPos == TEXT('\n'))
{
EndOfLine = 1;
}
if (EndOfLine > 0)
{
const uint32 LineLen = uint32(CrtPos - StartPos);
if (LineLen > 0 && *StartPos != TEXT('#'))
{
StartPos[LineLen] = TEXT('\0');
TraceInsightsModule.Exec(StartPos, Ar);
}
CrtPos += EndOfLine;
StartPos = CrtPos;
}
else
{
++CrtPos;
}
}
const uint32 LineLen = uint32(CrtPos - StartPos);
if (LineLen > 0 && *StartPos != TEXT('#'))
{
StartPos[LineLen] = TEXT('\0');
TraceInsightsModule.Exec(StartPos, Ar);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::RegisterTraceControlTab()
{
#if !WITH_EDITOR
FTabSpawnerEntry& TabSpawnerEntry = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(FInsightsManagerTabs::TraceControlTabId,
FOnSpawnTab::CreateRaw(this, &FInsightsManager::SpawnTraceControlTab))
.SetDisplayName(LOCTEXT("TraceControl", "Trace Control"))
.SetTooltipText(LOCTEXT("TraceControlTooltip", "Open the Trace Control tab."))
.SetIcon(FSlateIcon(FInsightsStyle::GetStyleSetName(), "Icons.TraceControl"))
.SetAutoGenerateMenuEntry(false);
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SDockTab> FInsightsManager::SpawnTraceControlTab(const FSpawnTabArgs& Args)
{
const TSharedRef<SDockTab> DockTab = SNew(SDockTab)
.TabRole(ETabRole::NomadTab);
DockTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &FInsightsManager::OnTraceControlTabClosed));
ISessionServicesModule& SessionServicesModule = FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices");
TSharedPtr<ITraceController> TraceController = SessionServicesModule.GetTraceController();
TSharedRef<SWidget> TraceControlRef = FModuleManager::LoadModuleChecked<UE::TraceTools::ITraceToolsModule>("TraceTools").CreateTraceControlWidget(TraceController, InstanceId);
DockTab->SetContent(TraceControlRef);
TraceControl = TraceControlRef;
return DockTab;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OnTraceControlTabClosed(TSharedRef<SDockTab> TabBeingClosed)
{
TraceControl.Reset();
// Disable TabClosed delegate.
TabBeingClosed->SetOnTabClosed(SDockTab::FOnTabClosedCallback());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FInsightsManager::OpenTraceControlWindow()
{
FGlobalTabmanager::Get()->TryInvokeTab(FInsightsManagerTabs::TraceControlTabId);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace UE::Insights
#undef LOCTEXT_NAMESPACE