// Copyright Epic Games, Inc. All Rights Reserved. #include "ContextSwitchesSharedState.h" #include "Framework/Commands/Commands.h" #include "Framework/Commands/UICommandList.h" #include "Framework/MultiBox/MultiBoxBuilder.h" // TraceServices #include "TraceServices/Model/Threads.h" #include "TraceServices/Model/ContextSwitches.h" // TraceInsightsCore #include "InsightsCore/Filter/ViewModels/FilterConfigurator.h" // TraceInsights #include "Insights/ContextSwitches/ContextSwitchesProfilerManager.h" #include "Insights/ContextSwitches/ViewModels/ContextSwitchesFilterConverters.h" #include "Insights/ContextSwitches/ViewModels/ContextSwitchesTimingTrack.h" #include "Insights/ContextSwitches/ViewModels/ContextSwitchTimingEvent.h" #include "Insights/ContextSwitches/ViewModels/CpuCoreTimingTrack.h" #include "Insights/ITimingViewSession.h" #include "Insights/InsightsManager.h" #include "Insights/InsightsStyle.h" #include "Insights/TimingProfiler/TimingProfilerManager.h" #include "Insights/TimingProfiler/Tracks/CpuTimingTrack.h" #include "Insights/TimingProfiler/ViewModels/ThreadTimingSharedState.h" #include "Insights/TimingProfiler/Widgets/STimingProfilerWindow.h" #include "Insights/Widgets/STimingView.h" #define LOCTEXT_NAMESPACE "UE::Insights::ContextSwitches" namespace UE::Insights::ContextSwitches { //////////////////////////////////////////////////////////////////////////////////////////////////// // FContextSwitchesStateCommands //////////////////////////////////////////////////////////////////////////////////////////////////// FContextSwitchesStateCommands::FContextSwitchesStateCommands() : TCommands( TEXT("ContextSwitchesStateCommands"), NSLOCTEXT("Contexts", "ContextSwitchesStateCommands", "Insights - Context Switches"), NAME_None, FInsightsStyle::GetStyleSetName()) { } //////////////////////////////////////////////////////////////////////////////////////////////////// // UI_COMMAND takes long for the compiler to optimize UE_DISABLE_OPTIMIZATION_SHIP void FContextSwitchesStateCommands::RegisterCommands() { UI_COMMAND(Command_ShowCoreTracks, "CPU Core Tracks", "Shows/hides the CPU Core tracks.", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Alt, EKeys::C)); UI_COMMAND(Command_ShowContextSwitches, "Context Switches", "Shows/hides the context switches on top of CPU timing tracks.", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Shift, EKeys::C)); UI_COMMAND(Command_ShowOverlays, "Overlays", "Extends the visualization of context switches over the CPU timing tracks.", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Shift, EKeys::O)); UI_COMMAND(Command_ShowExtendedLines, "Extended Lines", "Shows/hides the extended vertical lines at edges of each context switch event.", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Shift, EKeys::L)); UI_COMMAND(Command_ShowNonTargetProcessEvents, "Non-Target Process Events", "Shows/hides the CPU Core events that do not belong to the target process.", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(Command_NavigateToCpuThreadEvent, "Go To CPU Thread Track", "Selects the context switch event in the corresponding CPU Thread track.", EUserInterfaceActionType::Button, FInputChord(/*EModifierKey::Control, EKeys::Tab*/)); UI_COMMAND(Command_DockCpuThreadTrackToBottom, "Dock CPU Thread Track To Bottom", "Docks the corresponding CPU Thread track to the bottom of the Timing view.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(Command_NavigateToCpuCoreEvent, "Go To CPU Core Track", "Selects the timing event in the corresponding CPU Core track.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::Tab)); UI_COMMAND(Command_DockCpuCoreTrackToTop, "Dock CPU Core Track To Top", "Docks the corresponding CPU Core track to the top of the Timing view.", EUserInterfaceActionType::Button, FInputChord()); } UE_ENABLE_OPTIMIZATION_SHIP //////////////////////////////////////////////////////////////////////////////////////////////////// // FContextSwitchesSharedState //////////////////////////////////////////////////////////////////////////////////////////////////// FContextSwitchesSharedState::FContextSwitchesSharedState(TimingProfiler::STimingView* InTimingView) : TimingViewSession(InTimingView) , ThreadsSerial(0) , CpuCoresSerial(0) , bAreCoreTracksVisible(true) , bAreContextSwitchesVisible(true) , bAreOverlaysVisible(true) , bAreExtendedLinesVisible(true) , bAreNonTargetProcessEventsVisible(true) , bSyncWithProviders(true) { } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::OnBeginSession(Timing::ITimingViewSession& InSession) { if (&InSession != TimingViewSession) { if (InSession.GetName() == FInsightsManagerTabs::TimingProfilerTabId) { TimingViewSession = &InSession; AddCommands(); } else { return; } } CpuCoreTimingTracks.Reset(); ContextSwitchesTimingTracks.Reset(); ThreadsSerial = 0; CpuCoresSerial = 0; bAreCoreTracksVisible = true; bAreContextSwitchesVisible = true; bAreOverlaysVisible = true; bAreExtendedLinesVisible = true; bAreNonTargetProcessEventsVisible = true; bSyncWithProviders = true; TargetTimingEvent = nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::OnEndSession(Timing::ITimingViewSession& InSession) { if (&InSession != TimingViewSession) { return; } CpuCoreTimingTracks.Reset(); ContextSwitchesTimingTracks.Reset(); ThreadsSerial = 0; CpuCoresSerial = 0; bAreCoreTracksVisible = true; bAreContextSwitchesVisible = true; bAreOverlaysVisible = true; bAreExtendedLinesVisible = true; bSyncWithProviders = false; TargetTimingEvent = nullptr; TimingViewSession = nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::Tick(Timing::ITimingViewSession& InSession, const TraceServices::IAnalysisSession& InAnalysisSession) { if (&InSession != TimingViewSession) { return; } if (bSyncWithProviders && AreContextSwitchesAvailable()) { if (bAreCoreTracksVisible) { AddCoreTracks(); } if (bAreContextSwitchesVisible) { AddContextSwitchesChildTracks(); } if (FInsightsManager::Get()->IsAnalysisComplete()) { // No need to sync anymore when analysis is completed. bSyncWithProviders = false; } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::ExtendCpuTracksFilterMenu(Timing::ITimingViewSession& InSession, FMenuBuilder& InMenuBuilder) { if (&InSession != TimingViewSession) { return; } BuildSubMenu(InMenuBuilder); //InMenuBuilder.BeginSection("ContextSwitches"); //{ // InMenuBuilder.AddSubMenu( // LOCTEXT("ContextSwitches_SubMenu", "Context Switches"), // LOCTEXT("ContextSwitches_SubMenu_Desc", "Context Switch track options"), // FNewMenuDelegate::CreateSP(this, &FContextSwitchesSharedState::BuildSubMenu), // false, // FSlateIcon() // ); //} //InMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::BuildSubMenu(FMenuBuilder& InMenuBuilder) { InMenuBuilder.BeginSection("ContextSwitches", LOCTEXT("ContextMenu_Section_ContextSwitches", "Context Switches")); { InMenuBuilder.AddMenuEntry(FContextSwitchesStateCommands::Get().Command_ShowCoreTracks); InMenuBuilder.AddMenuEntry(FContextSwitchesStateCommands::Get().Command_ShowNonTargetProcessEvents); InMenuBuilder.AddMenuEntry(FContextSwitchesStateCommands::Get().Command_ShowContextSwitches); InMenuBuilder.AddMenuEntry(FContextSwitchesStateCommands::Get().Command_ShowOverlays); InMenuBuilder.AddMenuEntry(FContextSwitchesStateCommands::Get().Command_ShowExtendedLines); } InMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::AddQuickFindFilters(TSharedPtr FilterConfigurator) { using namespace UE::Insights; TSharedPtr>> CoreEventNameFilterOperators = MakeShared>>(); CoreEventNameFilterOperators->Add(StaticCastSharedRef(MakeShared>(EFilterOperator::Eq, TEXT("Is"), [](int64 lhs, int64 rhs) { return lhs == rhs; }))); TSharedRef TimerNameFilter = MakeShared( static_cast(EFilterField::CoreEventName), LOCTEXT("CoreEventName", "Core Event Name"), LOCTEXT("CoreEventName", "Core Event Name"), EFilterDataType::StringInt64Pair, MakeShared(), CoreEventNameFilterOperators); TimerNameFilter->SetCallback([this](const FString& Text, TArray& OutSuggestions) { this->PopulateCoreEventNameSuggestionList(Text, OutSuggestions); }); FilterConfigurator->Add(TimerNameFilter); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::PopulateCoreEventNameSuggestionList(const FString& Text, TArray& OutSuggestions) { TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::IThreadProvider& ThreadProvider = TraceServices::ReadThreadProvider(*Session.Get()); ThreadProvider.EnumerateThreads([&Text, &OutSuggestions](const TraceServices::FThreadInfo& ThreadInfo) { if (FCString::Stristr(ThreadInfo.Name, *Text)) { OutSuggestions.Add(ThreadInfo.Name); } }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::AddCommands() { FContextSwitchesStateCommands::Register(); TSharedPtr TimingView = GetTimingView(); if (!TimingView.IsValid()) { return; } TSharedPtr CommandList = TimingView->GetCommandList(); ensure(CommandList.IsValid()); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_ShowCoreTracks, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowCoreTracks_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowCoreTracks_CanExecute), FIsActionChecked::CreateSP(this, &FContextSwitchesSharedState::Command_ShowCoreTracks_IsChecked)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_ShowNonTargetProcessEvents, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowNonTargetProcessEvents_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowNonTargetProcessEvents_CanExecute), FIsActionChecked::CreateSP(this, &FContextSwitchesSharedState::Command_ShowNonTargetProcessEvents_IsChecked)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_ShowContextSwitches, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowContextSwitches_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowContextSwitches_CanExecute), FIsActionChecked::CreateSP(this, &FContextSwitchesSharedState::Command_ShowContextSwitches_IsChecked)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_ShowOverlays, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowOverlays_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowOverlays_CanExecute), FIsActionChecked::CreateSP(this, &FContextSwitchesSharedState::Command_ShowOverlays_IsChecked)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_ShowExtendedLines, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowExtendedLines_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_ShowExtendedLines_CanExecute), FIsActionChecked::CreateSP(this, &FContextSwitchesSharedState::Command_ShowExtendedLines_IsChecked)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_NavigateToCpuThreadEvent, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_NavigateToCpuThreadEvent_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_NavigateToCpuThreadEvent_CanExecute)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_DockCpuThreadTrackToBottom, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_DockCpuThreadTrackToBottom_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_DockCpuThreadTrackToBottom_CanExecute)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_NavigateToCpuCoreEvent, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_NavigateToCpuCoreEvent_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_NavigateToCpuCoreEvent_CanExecute)); CommandList->MapAction( FContextSwitchesStateCommands::Get().Command_DockCpuCoreTrackToTop, FExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_DockCpuCoreTrackToTop_Execute), FCanExecuteAction::CreateSP(this, &FContextSwitchesSharedState::Command_DockCpuCoreTrackToTop_CanExecute)); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FContextSwitchesSharedState::AreContextSwitchesAvailable() const { return FContextSwitchesProfilerManager::Get()->IsAvailable(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::AddCoreTracks() { TSharedPtr TimingView = GetTimingView(); if (!TimingView.IsValid()) { return; } TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (!Session.IsValid()) { return; } TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::IContextSwitchesProvider* ContextSwitchesProvider = TraceServices::ReadContextSwitchesProvider(*Session.Get()); if (ContextSwitchesProvider == nullptr) { return; } const uint64 NewCpuCoresSerial = ContextSwitchesProvider->GetCpuCoresSerial(); if (NewCpuCoresSerial == CpuCoresSerial) { return; } CpuCoresSerial = NewCpuCoresSerial; ContextSwitchesProvider->EnumerateCpuCores([this, TimingView](const TraceServices::FCpuCoreInfo& CpuCoreInfo) { TSharedPtr* TrackPtrPtr = CpuCoreTimingTracks.Find(CpuCoreInfo.CoreNumber); if (TrackPtrPtr == nullptr) { const FString TrackName = FString::Printf(TEXT("Core %u"), CpuCoreInfo.CoreNumber); TSharedPtr Track = MakeShared(*this, TrackName, CpuCoreInfo.CoreNumber); const int32 Order = FTimingTrackOrder::Cpu - 1024 + CpuCoreInfo.CoreNumber; Track->SetOrder(Order); CpuCoreTimingTracks.Add(CpuCoreInfo.CoreNumber, Track); TimingView->AddScrollableTrack(Track); } }); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::RemoveCoreTracks() { TSharedPtr TimingView = GetTimingView(); CpuCoresSerial = 0; if (TimingView) { for (const auto& KV : CpuCoreTimingTracks) { TimingView->RemoveTrack(KV.Value); } } CpuCoreTimingTracks.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::AddContextSwitchesChildTracks() { #if 0 TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (!Session.IsValid()) { return; } TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::IContextSwitchesProvider* ContextSwitchesProvider = TraceServices::ReadContextSwitchesProvider(*Session.Get()); if (ContextSwitchesProvider == nullptr) { return; } const uint64 NewThreadsSerial = ContextSwitchesProvider->GetThreadsSerial(); if (NewThreadsSerial == ThreadsSerial) { return; } ThreadsSerial = NewThreadsSerial; //TODO: Create "CPU Thread" timing tracks also for threads without CPU timing events (i.e. only with context switch events). #endif TSharedPtr TimingView = GetTimingView(); if (!TimingView.IsValid()) { return; } TSharedPtr TimingSharedState = TimingView->GetThreadTimingSharedState(); if (!TimingSharedState.IsValid()) { return; } const TMap>& CpuTracks = TimingSharedState->GetAllCpuTracks(); for (const TPair>& MapEntry : CpuTracks) { const TSharedPtr& CpuTrack = MapEntry.Value; if (CpuTrack.IsValid() && !CpuTrack->FindChildTrackOfType().IsValid()) { TSharedRef ContextSwitchesTrack = MakeShared(*this, TEXT("Context Switches"), CpuTrack->GetTimelineIndex(), CpuTrack->GetThreadId()); ContextSwitchesTrack->SetParentTrack(CpuTrack); CpuTrack->AddChildTrack(ContextSwitchesTrack); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::RemoveContextSwitchesChildTracks() { ThreadsSerial = 0; TSharedPtr TimingView = GetTimingView(); if (!TimingView.IsValid()) { return; } TSharedPtr TimingSharedState = TimingView->GetThreadTimingSharedState(); if (!TimingSharedState.IsValid()) { return; } const TMap>& CpuTracks = TimingSharedState->GetAllCpuTracks(); for (const TPair>& MapEntry : CpuTracks) { const TSharedPtr& CpuTrack = MapEntry.Value; if (CpuTrack.IsValid()) { if (TSharedPtr ChildTrack = CpuTrack->FindChildTrackOfType()) { CpuTrack->RemoveChildTrack(ChildTrack.ToSharedRef()); } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::SetCoreTracksVisible(bool bOnOff) { if (bAreCoreTracksVisible != bOnOff) { bAreCoreTracksVisible = bOnOff; if (bAreCoreTracksVisible) { AddCoreTracks(); } else { RemoveCoreTracks(); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::SetContextSwitchesVisible(bool bOnOff) { if (bAreContextSwitchesVisible != bOnOff) { bAreContextSwitchesVisible = bOnOff; if (bAreContextSwitchesVisible) { AddContextSwitchesChildTracks(); } else { RemoveContextSwitchesChildTracks(); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::SetOverlaysVisible(bool bOnOff) { bAreOverlaysVisible = bOnOff; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::SetExtendedLinesVisible(bool bOnOff) { bAreExtendedLinesVisible = bOnOff; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::SetNonTargetProcessEventsVisible(bool bOnOff) { if (bAreNonTargetProcessEventsVisible != bOnOff) { TSharedPtr TimingView = GetTimingView(); const TSharedPtr EventFilter = TimingView->GetEventFilter(); for (auto Track : CpuCoreTimingTracks) { Track.Value->SetDirtyFlag(); if (!bOnOff && EventFilter) { if (EventFilter->FilterTrack(*Track.Value)) { TimingView->ResetEventFilter(); } } } bAreNonTargetProcessEventsVisible = bOnOff; } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FContextSwitchesSharedState::IsValidCpuCoreEventSelected() const { TSharedPtr TimingView = GetTimingView(); if (TimingView == nullptr) { return false; } if (TargetTimingEvent.IsValid()) { return TargetTimingEvent->Is(); } return TimingView->GetSelectedEvent().IsValid() && TimingView->GetSelectedEvent()->Is(); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FContextSwitchesSharedState::IsValidContextSwitchEventSelected() const { TSharedPtr TimingView = GetTimingView(); if (TimingView == nullptr) { return false; } if (TargetTimingEvent.IsValid()) { return TargetTimingEvent->Is(); } return TimingView->GetSelectedEvent().IsValid() && TimingView->GetSelectedEvent()->Is(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::Command_NavigateToCpuThreadEvent_Execute() { TSharedPtr TimingView = GetTimingView(); if (TimingView && ensure(IsValidCpuCoreEventSelected())) { if (!TargetTimingEvent.IsValid()) { TargetTimingEvent = TimingView->GetSelectedEvent(); } if (ensure(TargetTimingEvent.IsValid() && TargetTimingEvent->Is())) { const FCpuCoreTimingEvent& CpuCoreEvent = TargetTimingEvent->As(); const uint32 SystemThreadId = CpuCoreEvent.GetSystemThreadId(); uint32 ThreadId; const TCHAR* ThreadName; GetThreadInfo(SystemThreadId, ThreadId, ThreadName); if (ThreadId != ~0) { TSharedPtr ThreadTimingTrack = GetThreadTimingTrack(ThreadId); if (ThreadTimingTrack.IsValid() && ThreadTimingTrack->IsVisible()) { TimingView->SelectTimingTrack(ThreadTimingTrack, true); if (TSharedPtr ContextSwitchesTrack = ThreadTimingTrack->FindChildTrackOfType()) { TSharedPtr FoundEvent = ContextSwitchesTrack->SearchEvent( FTimingEventSearchParameters(CpuCoreEvent.GetStartTime(), CpuCoreEvent.GetEndTime(), ETimingEventSearchFlags::StopAtFirstMatch)); if (FoundEvent.IsValid()) { TimingView->SelectTimingEvent(FoundEvent, true); } } } } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FContextSwitchesSharedState::Command_NavigateToCpuThreadEvent_CanExecute() const { if (!AreContextSwitchesAvailable() || !AreContextSwitchesVisible() || !IsValidCpuCoreEventSelected()) { return false; } TSharedPtr TimingView = GetTimingView(); if (TimingView && IsValidCpuCoreEventSelected()) { if (TargetTimingEvent.IsValid() && TargetTimingEvent->Is()) { const FCpuCoreTimingEvent& CpuCoreEvent = TargetTimingEvent->As(); const uint32 SystemThreadId = CpuCoreEvent.GetSystemThreadId(); uint32 ThreadId; const TCHAR* ThreadName; GetThreadInfo(SystemThreadId, ThreadId, ThreadName); if (ThreadId != ~0) { TSharedPtr ThreadTimingTrack = GetThreadTimingTrack(ThreadId); if (ThreadTimingTrack.IsValid() && ThreadTimingTrack->IsVisible()) { return true; } } } } return false; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::Command_DockCpuThreadTrackToBottom_Execute() { TSharedPtr TimingView = GetTimingView(); if (TimingView && ensure(IsValidCpuCoreEventSelected())) { if (!TargetTimingEvent.IsValid()) { TargetTimingEvent = TimingView->GetSelectedEvent(); } if (ensure(TargetTimingEvent.IsValid() && TargetTimingEvent->Is())) { const FCpuCoreTimingEvent& CpuCoreEvent = TargetTimingEvent->As(); const uint32 SystemThreadId = CpuCoreEvent.GetSystemThreadId(); uint32 ThreadId; const TCHAR* ThreadName; GetThreadInfo(SystemThreadId, ThreadId, ThreadName); if (ThreadId != ~0) { TSharedPtr ThreadTimingTrack = GetThreadTimingTrack(ThreadId); if (ThreadTimingTrack.IsValid()) { TimingView->ChangeTrackLocation(ThreadTimingTrack.ToSharedRef(), ETimingTrackLocation::BottomDocked); if (!ThreadTimingTrack->IsVisible()) { ThreadTimingTrack->Show(); TimingView->HandleTrackVisibilityChanged(); } } } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::Command_NavigateToCpuCoreEvent_Execute() { TSharedPtr TimingView = GetTimingView(); if (TimingView && ensure(IsValidContextSwitchEventSelected())) { if (!TargetTimingEvent.IsValid()) { TargetTimingEvent = TimingView->GetSelectedEvent(); } if (ensure(TargetTimingEvent.IsValid() && TargetTimingEvent->Is())) { const FContextSwitchTimingEvent& ContextSwitchEvent = TargetTimingEvent->As(); const uint32 CoreNumber = ContextSwitchEvent.GetCoreNumber(); if (CoreNumber != ~0) { TSharedPtr CpuCoreTimingTrack = GetCpuCoreTimingTrack(CoreNumber); if (CpuCoreTimingTrack.IsValid() && CpuCoreTimingTrack->IsVisible()) { TimingView->SelectTimingTrack(CpuCoreTimingTrack, true); TSharedPtr FoundEvent = CpuCoreTimingTrack->SearchEvent( FTimingEventSearchParameters(ContextSwitchEvent.GetStartTime(), ContextSwitchEvent.GetEndTime(), ETimingEventSearchFlags::StopAtFirstMatch)); if (FoundEvent.IsValid()) { TimingView->SelectTimingEvent(FoundEvent, true); } } } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::Command_DockCpuCoreTrackToTop_Execute() { TSharedPtr TimingView = GetTimingView(); if (TimingView && ensure(IsValidContextSwitchEventSelected())) { if (!TargetTimingEvent.IsValid()) { TargetTimingEvent = TimingView->GetSelectedEvent(); } if (ensure(TargetTimingEvent.IsValid() && TargetTimingEvent->Is())) { const FContextSwitchTimingEvent& ContextSwitchEvent = TargetTimingEvent->As(); const uint32 CoreNumber = ContextSwitchEvent.GetCoreNumber(); if (CoreNumber != ~0) { TSharedPtr CpuCoreTimingTrack = GetCpuCoreTimingTrack(CoreNumber); if (CpuCoreTimingTrack.IsValid()) { if (TimingView->CanChangeTrackLocation(CpuCoreTimingTrack.ToSharedRef(), ETimingTrackLocation::TopDocked)) { TimingView->ChangeTrackLocation(CpuCoreTimingTrack.ToSharedRef(), ETimingTrackLocation::TopDocked); } if (!CpuCoreTimingTrack->IsVisible()) { CpuCoreTimingTrack->Show(); TimingView->HandleTrackVisibilityChanged(); } } } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FContextSwitchesSharedState::GetThreadInfo(uint32 InSystemThreadId, uint32& OutThreadId, const TCHAR*& OutThreadName) const { OutThreadId = ~0; OutThreadName = TEXT("Unknown Thread"); TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::IContextSwitchesProvider* ContextSwitchesProvider = TraceServices::ReadContextSwitchesProvider(*Session.Get()); if (ContextSwitchesProvider) { if (ContextSwitchesProvider->GetThreadId(InSystemThreadId, OutThreadId)) { const TraceServices::IThreadProvider& ThreadProvider = TraceServices::ReadThreadProvider(*Session.Get()); OutThreadName = ThreadProvider.GetThreadName(OutThreadId); } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FContextSwitchesSharedState::GetThreadTimingTrack(uint32 ThreadId) const { TSharedPtr FoundTrack; TSharedPtr TimingView = GetTimingView(); if (TimingView) { TimingView->EnumerateAllTracks([&FoundTrack, ThreadId](TSharedPtr& Track) -> bool { if (Track->Is() && Track->As().GetThreadId() == ThreadId) { FoundTrack = StaticCastSharedPtr(Track); return false; } return true; }); } return FoundTrack; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FContextSwitchesSharedState::GetCpuCoreTimingTrack(uint32 CoreNumber) const { TSharedPtr FoundTrack; TSharedPtr TimingView = GetTimingView(); if (TimingView) { TimingView->EnumerateAllTracks([&FoundTrack, CoreNumber](TSharedPtr& Track) -> bool { if (Track->Is() && Track->As().GetCoreNumber() == CoreNumber) { FoundTrack = StaticCastSharedPtr(Track); return false; } return true; }); } return FoundTrack; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FContextSwitchesSharedState::GetTimingView() { using namespace UE::Insights::TimingProfiler; TSharedPtr Window = FTimingProfilerManager::Get()->GetProfilerWindow(); return Window.IsValid() ? Window->GetTimingView() : nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace UE::Insights::ContextSwitches #undef LOCTEXT_NAMESPACE