// 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::Instance = nullptr; //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FInsightsManager::Get() { return FInsightsManager::Instance; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FInsightsManager::CreateInstance(TSharedRef TraceAnalysisService, TSharedRef TraceModuleService) { ensure(!FInsightsManager::Instance.IsValid()); if (FInsightsManager::Instance.IsValid()) { FInsightsManager::Instance.Reset(); } FInsightsManager::Instance = MakeShared(TraceAnalysisService, TraceModuleService); return FInsightsManager::Instance; } //////////////////////////////////////////////////////////////////////////////////////////////////// FInsightsManager::FInsightsManager(TSharedRef InTraceAnalysisService, TSharedRef 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(); FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("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("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 Group = SessionInfoConfig.WorkspaceGroup.IsValid() ? SessionInfoConfig.WorkspaceGroup.ToSharedRef() : GetInsightsMenuBuilder()->GetInsightsToolsGroup(); TabSpawnerEntry.SetGroup(Group); } #if !WITH_EDITOR FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); MessageLogModule.RegisterMessageLogSpawner(GetInsightsMenuBuilder()->GetWindowsGroup()); #endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void FInsightsManager::UnregisterMajorTabs() { FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(FInsightsManagerTabs::SessionInfoTabId); } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef FInsightsManager::SpawnSessionInfoTab(const FSpawnTabArgs& Args) { const TSharedRef DockTab = SNew(SDockTab) .TabRole(ETabRole::NomadTab); DockTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &FInsightsManager::OnSessionInfoTabClosed)); // Create the SSessionInfoWindow widget. TSharedRef 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 TabBeingClosed) { RemoveSessionInfoWindow(); // Disable TabClosed delegate. TabBeingClosed->SetOnTabClosed(SDockTab::FOnTabClosedCallback()); } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FInsightsManager::GetSession() const { return Session; } //////////////////////////////////////////////////////////////////////////////////////////////////// const TSharedRef 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 TraceControlPtr = TraceControl.Pin(); if (TraceControlPtr.IsValid()) { FModuleManager::LoadModuleChecked("TraceTools").SetTraceControlWidgetInstanceId(TraceControlPtr.ToSharedRef(), InstanceId); } } } if (bIsSessionInfoSet) { UpdateAppTitle(); } } ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked("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(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 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 EditableSession = ConstCastSharedPtr(Session); TraceServices::FAnalysisSessionEditScope SessionEditScope(*EditableSession.Get()); const auto Messages = EditableSession->DrainPendingMessages(); FMessageLog ReportMessageLog(AnalysisLogListingName); for (const auto& Message : Messages) { TSharedRef 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 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 TimingInsightsTab = FGlobalTabmanager::Get()->FindExistingLiveTab(FInsightsManagerTabs::TimingProfilerTabId)) { TimingInsightsTab->ActivateInParent(ETabActivationCause::SetDirectly); using namespace UE::Insights::TimingProfiler; //TODO: FTimingProfilerManager::Get()->ActivateWindow(); TSharedPtr Wnd = FTimingProfilerManager::Get()->GetProfilerWindow(); if (Wnd) { TSharedPtr TabManager = Wnd->GetTabManager(); if (TSharedPtr 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 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 TraceData = MakeUnique(); 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 DragDropOp = DragDropEvent.GetOperationAs(); if (DragDropOp.IsValid()) { if (DragDropOp->HasFiles()) { const TArray& 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 DragDropOp = DragDropEvent.GetOperationAs(); if (DragDropOp.IsValid()) { if (DragDropOp->HasFiles()) { // For now, only allow a single file. const TArray& 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 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("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("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 FInsightsManager::SpawnTraceControlTab(const FSpawnTabArgs& Args) { const TSharedRef DockTab = SNew(SDockTab) .TabRole(ETabRole::NomadTab); DockTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &FInsightsManager::OnTraceControlTabClosed)); ISessionServicesModule& SessionServicesModule = FModuleManager::LoadModuleChecked("SessionServices"); TSharedPtr TraceController = SessionServicesModule.GetTraceController(); TSharedRef TraceControlRef = FModuleManager::LoadModuleChecked("TraceTools").CreateTraceControlWidget(TraceController, InstanceId); DockTab->SetContent(TraceControlRef); TraceControl = TraceControlRef; return DockTab; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FInsightsManager::OnTraceControlTabClosed(TSharedRef TabBeingClosed) { TraceControl.Reset(); // Disable TabClosed delegate. TabBeingClosed->SetOnTabClosed(SDockTab::FOnTabClosedCallback()); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FInsightsManager::OpenTraceControlWindow() { FGlobalTabmanager::Get()->TryInvokeTab(FInsightsManagerTabs::TraceControlTabId); } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace UE::Insights #undef LOCTEXT_NAMESPACE