// Copyright Epic Games, Inc. All Rights Reserved. #include "FileActivityTimingTrack.h" #include "Fonts/FontMeasure.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Styling/SlateBrush.h" #include "TraceServices/AnalysisService.h" // Insights #include "Insights/Common/PaintUtils.h" #include "Insights/Common/Stopwatch.h" #include "Insights/Common/TimeUtils.h" #include "Insights/InsightsManager.h" #include "Insights/ITimingViewSession.h" #include "Insights/TimingProfilerCommon.h" #include "Insights/ViewModels/TimingEvent.h" #include "Insights/ViewModels/TimingTrackViewport.h" #include "Insights/ViewModels/TooltipDrawState.h" #include "Insights/ViewModels/TimingEventSearch.h" #include "Insights/Widgets/STimingView.h" #include #define LOCTEXT_NAMESPACE "FileActivityTimingTrack" // The FileActivity (I/O) timelines are just prototypes for now. // Below code will be removed once the functionality is moved in analyzer. //////////////////////////////////////////////////////////////////////////////////////////////////// const TCHAR* GetFileActivityTypeName(Trace::EFileActivityType Type) { static_assert(Trace::FileActivityType_Open == 0, "Trace::EFileActivityType enum has changed!?"); static_assert(Trace::FileActivityType_Close == 1, "Trace::EFileActivityType enum has changed!?"); static_assert(Trace::FileActivityType_Read == 2, "Trace::EFileActivityType enum has changed!?"); static_assert(Trace::FileActivityType_Write == 3, "Trace::EFileActivityType enum has changed!?"); static_assert(Trace::FileActivityType_Count == 4, "Trace::EFileActivityType enum has changed!?"); static const TCHAR* GFileActivityTypeNames[] = { TEXT("Open"), TEXT("Close"), TEXT("Read"), TEXT("Write"), TEXT("Idle"), // virtual events added for cases where Close event is more than 1s away from last Open/Read/Write event. TEXT("NotClosed") // virtual events added when an Open activity never closes }; return GFileActivityTypeNames[Type]; } //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 GetFileActivityTypeColor(Trace::EFileActivityType Type) { static const uint32 GFileActivityTypeColors[] = { 0xFFCCAA33, // open 0xFF33AACC, // close 0xFF33AA33, // read 0xFFDD33CC, // write 0x55333333, // idle 0x55553333, // close }; return GFileActivityTypeColors[Type]; } //////////////////////////////////////////////////////////////////////////////////////////////////// // FFileActivitySharedState //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::OnBeginSession(Insights::ITimingViewSession& InSession) { if (&InSession != TimingView) { return; } IoOverviewTrack.Reset(); IoActivityTrack.Reset(); bShowHideAllIoTracks = false; bForceIoEventsUpdate = false; bMergeIoLanes = true; FileActivities.Reset(); FileActivityMap.Reset(); AllIoEvents.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::OnEndSession(Insights::ITimingViewSession& InSession) { if (&InSession != TimingView) { return; } IoOverviewTrack.Reset(); IoActivityTrack.Reset(); bShowHideAllIoTracks = false; bForceIoEventsUpdate = false; bMergeIoLanes = true; FileActivities.Reset(); FileActivityMap.Reset(); AllIoEvents.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::Tick(Insights::ITimingViewSession& InSession, const Trace::IAnalysisSession& InAnalysisSession) { if (&InSession != TimingView) { return; } if (!Trace::ReadFileActivityProvider(InAnalysisSession)) { return; } if (!IoOverviewTrack.IsValid()) { IoOverviewTrack = MakeShared(*this); IoOverviewTrack->SetOrder(FTimingTrackOrder::First); IoOverviewTrack->SetVisibilityFlag(bShowHideAllIoTracks); InSession.AddScrollableTrack(IoOverviewTrack); } if (!IoActivityTrack.IsValid()) { IoActivityTrack = MakeShared(*this); IoActivityTrack->SetOrder(FTimingTrackOrder::Last); IoActivityTrack->SetVisibilityFlag(bShowHideAllIoTracks); InSession.AddScrollableTrack(IoActivityTrack); } if (bForceIoEventsUpdate) { bForceIoEventsUpdate = false; FileActivities.Reset(); FileActivityMap.Reset(); AllIoEvents.Reset(); FStopwatch Stopwatch; Stopwatch.Start(); // Enumerate all IO events and cache them. { Trace::FAnalysisSessionReadScope SessionReadScope(InAnalysisSession); const Trace::IFileActivityProvider& FileActivityProvider = *Trace::ReadFileActivityProvider(InAnalysisSession); FileActivityProvider.EnumerateFileActivity([this](const Trace::FFileInfo& FileInfo, const Trace::IFileActivityProvider::Timeline& Timeline) { Timeline.EnumerateEvents(-std::numeric_limits::infinity(), +std::numeric_limits::infinity(), [this, &FileInfo, &Timeline](double EventStartTime, double EventEndTime, uint32 EventDepth, const Trace::FFileActivity* FileActivity) { // if (EventEndTime == std::numeric_limits::infinity()) //{ // EventEndTime = EventStartTime; // } TSharedPtr Activity = FileActivityMap.FindRef(FileInfo.Id); if (!Activity.IsValid()) { Activity = MakeShared(); Activity->Id = FileInfo.Id; Activity->Path = FileInfo.Path; Activity->StartTime = EventStartTime; Activity->EndTime = EventEndTime; Activity->CloseStartTime = EventStartTime; Activity->CloseEndTime = EventEndTime; Activity->EventCount = 1; Activity->Depth = -1; FileActivities.Add(Activity); FileActivityMap.Add(FileInfo.Id, Activity); } else { if (FileActivity->ActivityType != Trace::FileActivityType_Close) { ensure(EventStartTime >= Activity->StartTime); if (EventStartTime < Activity->StartTime) { Activity->StartTime = EventStartTime; } if (EventEndTime > Activity->EndTime) { Activity->EndTime = EventEndTime; } } else { // The time range for the Close event is stored separated; // for the purpose of avoiding lane collisions (overlaps) between activities. Activity->CloseStartTime = EventStartTime; Activity->CloseEndTime = EventEndTime; } Activity->EventCount++; } if (bMergeIoLanes) { EventDepth = 0; } else { EventDepth = FileInfo.Id % 32; // simple layout } uint32 Type = ((uint32)FileActivity->ActivityType & 0x0F) | (FileActivity->Failed ? 0x80 : 0); AllIoEvents.Add(FIoTimingEvent{EventStartTime, EventEndTime, EventDepth, Type, FileActivity->Offset, FileActivity->Size, FileActivity->ActualSize, Activity}); return Trace::EEventEnumerate::Continue; }); return true; }); } Stopwatch.Stop(); UE_LOG(TimingProfiler, Log, TEXT("[IO] Enumerated %s events (%s file activities) in %s."), *FText::AsNumber(AllIoEvents.Num()).ToString(), *FText::AsNumber(FileActivities.Num()).ToString(), *TimeUtils::FormatTimeAuto(Stopwatch.GetAccumulatedTime())); Stopwatch.Restart(); // Sort cached IO file activities by Start Time. FileActivities.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->StartTime < B->StartTime; }); // Sort cached IO events by Start Time. AllIoEvents.Sort([](const FIoTimingEvent& A, const FIoTimingEvent& B) { return A.StartTime < B.StartTime; }); Stopwatch.Stop(); UE_LOG(TimingProfiler, Log, TEXT("[IO] Sorted file activities and events in %s."), *TimeUtils::FormatTimeAuto(Stopwatch.GetAccumulatedTime())); if (bMergeIoLanes) { ////////////////////////////////////////////////// // Compute depth for file activities (avoids overlaps). Stopwatch.Restart(); TArray> ActivityLanes; for (TSharedPtr FileActivity: FileActivities) { // Find lane (avoiding overlaps with other file activities). for (int32 LaneIndex = 0; LaneIndex < ActivityLanes.Num(); ++LaneIndex) { TSharedPtr Lane = ActivityLanes[LaneIndex]; if (FileActivity->StartTime >= Lane->EndTime && (FileActivity->StartTime >= Lane->CloseEndTime || FileActivity->EndTime <= Lane->CloseStartTime)) // avoids overlaps with Close event { FileActivity->Depth = LaneIndex; ActivityLanes[LaneIndex] = FileActivity; break; } } if (FileActivity->Depth < 0) { const int32 MaxLanes = 10000; if (ActivityLanes.Num() < MaxLanes) { // Add new lane. FileActivity->Depth = ActivityLanes.Num(); ActivityLanes.Add(FileActivity); } else { int32 LaneIndex = ActivityLanes.Num() - 1; FileActivity->Depth = LaneIndex; TSharedPtr Lane = ActivityLanes[LaneIndex]; if (FileActivity->EndTime > Lane->EndTime) { ActivityLanes[LaneIndex] = FileActivity; } } } } Stopwatch.Stop(); UE_LOG(TimingProfiler, Log, TEXT("[IO] Computed layout for file activities in %s."), *TimeUtils::FormatTimeAuto(Stopwatch.GetAccumulatedTime())); ////////////////////////////////////////////////// Stopwatch.Restart(); for (FIoTimingEvent& Event: AllIoEvents) { Event.Depth = static_cast(Event.FileActivity->Depth); } Stopwatch.Stop(); UE_LOG(TimingProfiler, Log, TEXT("[IO] Updated depth for events in %s."), *TimeUtils::FormatTimeAuto(Stopwatch.GetAccumulatedTime())); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::ExtendFilterMenu(Insights::ITimingViewSession& InSession, FMenuBuilder& InOutMenuBuilder) { if (&InSession != TimingView) { return; } // InOutMenuBuilder.BeginSection("File Activity", LOCTEXT("FileActivityHeading", "File Activity")); //{ // InOutMenuBuilder.AddMenuEntry( // LOCTEXT("ShowAllIoTracks", "I/O Tracks - I"), // LOCTEXT("ShowAllIoTracks_Tooltip", "Show/hide the I/O (File Activity) tracks"), // FSlateIcon(), // FUIAction(FExecuteAction::CreateSP(this, &FFileActivitySharedState::ShowHideAllIoTracks), // FCanExecuteAction(), // FIsActionChecked::CreateSP(this, &FFileActivitySharedState::IsAllIoTracksToggleOn)), // NAME_None, //"QuickFilterSeparator", // EUserInterfaceActionType::ToggleButton // ); // } // InOutMenuBuilder.EndSection(); InOutMenuBuilder.BeginSection("File Activity"); { InOutMenuBuilder.AddSubMenu( LOCTEXT("FileActivity_SubMenu", "File Activity"), LOCTEXT("FileActivity_SubMenu_Desc", "File Activity track options"), FNewMenuDelegate::CreateSP(this, &FFileActivitySharedState::BuildSubMenu), false, FSlateIcon()); } InOutMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::BuildSubMenu(FMenuBuilder& InOutMenuBuilder) { InOutMenuBuilder.BeginSection("File Activity", LOCTEXT("FileActivityHeading", "File Activity")); { InOutMenuBuilder.AddMenuEntry( LOCTEXT("ShowIoOverviewTrack", "I/O Overview Track - I"), LOCTEXT("ShowIoOverviewTrack_Tooltip", "Show/hide the I/O Overview track"), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FFileActivitySharedState::ShowHideIoOverviewTrack), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FFileActivitySharedState::IsIoOverviewTrackVisible)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); InOutMenuBuilder.AddMenuEntry( LOCTEXT("ShowOnlyErrors", "Show Only Errors (I/O Overview Track)"), LOCTEXT("ShowOnlyErrors_Tooltip", "Show only the events with errors, in the I/O Overview track."), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FFileActivitySharedState::ToggleOnlyErrors), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FFileActivitySharedState::IsOnlyErrorsToggleOn)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); InOutMenuBuilder.AddMenuEntry( LOCTEXT("ShowIoActivityTrack", "I/O Activity Track - I"), LOCTEXT("ShowIoActivityTrack_Tooltip", "Show/hide the I/O Activity track"), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FFileActivitySharedState::ShowHideIoActivityTrack), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FFileActivitySharedState::IsIoActivityTrackVisible)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); InOutMenuBuilder.AddMenuEntry( LOCTEXT("ShowBackgroundEvents", "Show Background Events (I/O Activity Track) - O"), LOCTEXT("ShowBackgroundEvents_Tooltip", "Show background events for file activities, in the I/O Activity track."), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FFileActivitySharedState::ToggleBackgroundEvents), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FFileActivitySharedState::AreBackgroundEventsVisible)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); } InOutMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::SetAllIoTracksToggle(bool bOnOff) { bShowHideAllIoTracks = bOnOff; if (IoOverviewTrack.IsValid()) { IoOverviewTrack->SetVisibilityFlag(bShowHideAllIoTracks); } if (IoActivityTrack.IsValid()) { IoActivityTrack->SetVisibilityFlag(bShowHideAllIoTracks); } if (TimingView) { TimingView->OnTrackVisibilityChanged(); } if (bShowHideAllIoTracks) { RequestUpdate(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FFileActivitySharedState::IsIoOverviewTrackVisible() const { return IoOverviewTrack && IoOverviewTrack->IsVisible(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::ShowHideIoOverviewTrack() { if (IoOverviewTrack.IsValid()) { IoOverviewTrack->ToggleVisibility(); } if (TimingView) { TimingView->OnTrackVisibilityChanged(); } const bool bIsOverviewTrackVisible = IsIoOverviewTrackVisible(); const bool bIsActivityTrackVisible = IsIoActivityTrackVisible(); if (bIsOverviewTrackVisible == bIsActivityTrackVisible) { bShowHideAllIoTracks = bIsOverviewTrackVisible; } if (bIsOverviewTrackVisible) { RequestUpdate(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FFileActivitySharedState::IsIoActivityTrackVisible() const { return IoActivityTrack && IoActivityTrack->IsVisible(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::ShowHideIoActivityTrack() { if (IoActivityTrack.IsValid()) { IoActivityTrack->ToggleVisibility(); } if (TimingView) { TimingView->OnTrackVisibilityChanged(); } const bool bIsOverviewTrackVisible = IsIoOverviewTrackVisible(); const bool bIsActivityTrackVisible = IsIoActivityTrackVisible(); if (bIsOverviewTrackVisible == bIsActivityTrackVisible) { bShowHideAllIoTracks = bIsOverviewTrackVisible; } if (bIsActivityTrackVisible) { RequestUpdate(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FFileActivitySharedState::IsOnlyErrorsToggleOn() const { return IoOverviewTrack && IoOverviewTrack->IsOnlyErrorsToggleOn(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::ToggleOnlyErrors() { if (IoOverviewTrack) { IoOverviewTrack->ToggleOnlyErrors(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FFileActivitySharedState::AreBackgroundEventsVisible() const { return IoActivityTrack && IoActivityTrack->AreBackgroundEventsVisible(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivitySharedState::ToggleBackgroundEvents() { if (IoActivityTrack) { IoActivityTrack->ToggleBackgroundEvents(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // FFileActivityTimingTrack //////////////////////////////////////////////////////////////////////////////////////////////////// INSIGHTS_IMPLEMENT_RTTI(FFileActivityTimingTrack) //////////////////////////////////////////////////////////////////////////////////////////////////// void FFileActivityTimingTrack::InitTooltip(FTooltipDrawState& InOutTooltip, const ITimingEvent& InTooltipEvent) const { InOutTooltip.ResetContent(); if (InTooltipEvent.CheckTrack(this) && InTooltipEvent.Is()) { const FTimingEvent& TooltipEvent = InTooltipEvent.As(); auto MatchEvent = [this, &TooltipEvent](double InStartTime, double InEndTime, uint32 InDepth) { return InDepth == TooltipEvent.GetDepth() && InStartTime == TooltipEvent.GetStartTime() && InEndTime == TooltipEvent.GetEndTime(); }; FTimingEventSearchParameters SearchParameters(TooltipEvent.GetStartTime(), TooltipEvent.GetEndTime(), ETimingEventSearchFlags::StopAtFirstMatch, MatchEvent); FindIoTimingEvent(SearchParameters, [this, &InOutTooltip, &TooltipEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const FFileActivitySharedState::FIoTimingEvent& InEvent) { const Trace::EFileActivityType ActivityType = static_cast(InEvent.Type & 0x0F); const bool bHasFailed = ((InEvent.Type & 0xF0) != 0); FString TypeStr; uint32 TypeColor; if (bHasFailed) { TypeStr = TEXT("Failed "); TypeStr += GetFileActivityTypeName(ActivityType); TypeColor = 0xFFFF3333; } else { TypeStr = GetFileActivityTypeName(ActivityType); TypeColor = GetFileActivityTypeColor(ActivityType); } if (InEvent.ActualSize != InEvent.Size) { TypeStr += TEXT(" [!]"); } FLinearColor TypeLinearColor = FLinearColor(FColor(TypeColor)); TypeLinearColor.R *= 2.0f; TypeLinearColor.G *= 2.0f; TypeLinearColor.B *= 2.0f; InOutTooltip.AddTitle(TypeStr, TypeLinearColor); InOutTooltip.AddTitle(InEvent.FileActivity->Path); const double Duration = InEvent.EndTime - InEvent.StartTime; InOutTooltip.AddNameValueTextLine(TEXT("Duration:"), TimeUtils::FormatTimeAuto(Duration)); if (ActivityType == Trace::FileActivityType_Read || ActivityType == Trace::FileActivityType_Write) { InOutTooltip.AddNameValueTextLine(TEXT("Offset:"), FText::AsNumber(InEvent.Offset).ToString() + TEXT(" bytes")); InOutTooltip.AddNameValueTextLine(TEXT("Size:"), FText::AsNumber(InEvent.Size).ToString() + TEXT(" bytes")); FString ActualSizeStr = FText::AsNumber(InEvent.ActualSize).ToString() + TEXT(" bytes"); if (InEvent.ActualSize != InEvent.Size) { ActualSizeStr += TEXT(" [!]"); } InOutTooltip.AddNameValueTextLine(TEXT("Actual Size:"), ActualSizeStr); } if (!bIgnoreEventDepth) { InOutTooltip.AddNameValueTextLine(TEXT("Depth:"), FString::Printf(TEXT("%d"), InEvent.Depth)); } InOutTooltip.UpdateLayout(); }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FFileActivityTimingTrack::FindIoTimingEvent(const FTimingEventSearchParameters& InParameters, TFunctionRef InFoundPredicate) const { return TTimingEventSearch::Search( InParameters, // Search... [this](TTimingEventSearch::FContext& InContext) { if (bIgnoreDuration) { for (const FFileActivitySharedState::FIoTimingEvent& Event: SharedState.GetAllEvents()) { if (bShowOnlyErrors && ((Event.Type & 0xF0) == 0)) { continue; } if (Event.StartTime < InContext.GetParameters().StartTime) { continue; } if (!InContext.ShouldContinueSearching() || Event.StartTime > InContext.GetParameters().EndTime) { break; } InContext.Check(Event.StartTime, Event.StartTime, bIgnoreEventDepth ? 0 : Event.Depth, Event); } } else { for (const FFileActivitySharedState::FIoTimingEvent& Event: SharedState.GetAllEvents()) { if (bShowOnlyErrors && ((Event.Type & 0xF0) == 0)) { continue; } if (!bIgnoreDuration && Event.EndTime <= InContext.GetParameters().StartTime) { continue; } if (!InContext.ShouldContinueSearching() || Event.StartTime >= InContext.GetParameters().EndTime) { break; } InContext.Check(Event.StartTime, Event.EndTime, bIgnoreEventDepth ? 0 : Event.Depth, Event); } } }, // Found! [&InFoundPredicate](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const FFileActivitySharedState::FIoTimingEvent& InEvent) { InFoundPredicate(InFoundStartTime, InFoundEndTime, InFoundDepth, InEvent); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // FOverviewFileActivityTimingTrack //////////////////////////////////////////////////////////////////////////////////////////////////// void FOverviewFileActivityTimingTrack::BuildDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { const FTimingTrackViewport& Viewport = Context.GetViewport(); for (const FFileActivitySharedState::FIoTimingEvent& Event: SharedState.AllIoEvents) { const Trace::EFileActivityType ActivityType = static_cast(Event.Type & 0x0F); const uint64 EventType = static_cast(ActivityType); if (ActivityType >= Trace::FileActivityType_Count) { // Ignore "Idle" and "NotClosed" events. continue; } // const double EventEndTime = Event.EndTime; // keep duration of events const double EventEndTime = Event.StartTime; // make all 0 duration events if (EventEndTime <= Viewport.GetStartTime()) { continue; } if (Event.StartTime >= Viewport.GetEndTime()) { break; } const bool bHasFailed = ((Event.Type & 0xF0) != 0); if (bShowOnlyErrors && !bHasFailed) { continue; } uint32 Color = bHasFailed ? 0xFFAA0000 : GetFileActivityTypeColor(ActivityType); if (Event.ActualSize != Event.Size) { Color = (Color & 0xFF000000) | ((Color & 0xFEFEFE) >> 1); } Builder.AddEvent(Event.StartTime, EventEndTime, 0, Color, [&Event](float Width) { FString EventName; const bool bHasFailed = ((Event.Type & 0xF0) != 0); if (bHasFailed) { EventName += TEXT("Failed "); } const Trace::EFileActivityType ActivityType = static_cast(Event.Type & 0x0F); EventName += GetFileActivityTypeName(ActivityType); if (Event.ActualSize != Event.Size) { EventName += TEXT(" [!]"); } if (Width > EventName.Len() * 4.0f + 32.0f) { const double Duration = Event.EndTime - Event.StartTime; // actual event duration FTimingEventsTrackDrawStateBuilder::AppendDurationToEventName(EventName, Duration); } return EventName; }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// const TSharedPtr FOverviewFileActivityTimingTrack::SearchEvent(const FTimingEventSearchParameters& InSearchParameters) const { TSharedPtr FoundEvent; FindIoTimingEvent(InSearchParameters, [this, &FoundEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const FFileActivitySharedState::FIoTimingEvent& InEvent) { FoundEvent = MakeShared(SharedThis(this), InFoundStartTime, InFoundEndTime, InFoundDepth); }); return FoundEvent; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FOverviewFileActivityTimingTrack::BuildContextMenu(FMenuBuilder& InOutMenuBuilder) { InOutMenuBuilder.BeginSection(TEXT("Misc")); { InOutMenuBuilder.AddMenuEntry( LOCTEXT("OverviewTrack_ShowOnlyErrors", "Show Only Errors"), LOCTEXT("OverviewTrack_ShowOnlyErrors_Tooltip", "Show only the events with errors"), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FOverviewFileActivityTimingTrack::ToggleOnlyErrors), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FOverviewFileActivityTimingTrack::IsOnlyErrorsToggleOn)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); } InOutMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// // FDetailedFileActivityTimingTrack //////////////////////////////////////////////////////////////////////////////////////////////////// void FDetailedFileActivityTimingTrack::BuildDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { const FTimingTrackViewport& Viewport = Context.GetViewport(); // Add IO file activity background events. if (bShowBackgroundEvents) { for (const TSharedPtr& Activity: SharedState.FileActivities) { if (Activity->EndTime <= Viewport.GetStartTime()) { continue; } if (Activity->StartTime >= Viewport.GetEndTime()) { break; } ensure(Activity->Depth <= 10000); Builder.AddEvent(Activity->StartTime, Activity->EndTime, Activity->Depth, 0x55333333, [&Activity](float Width) { FString EventName = Activity->Path; if (Width > EventName.Len() * 4.0f + 32.0f) { const double Duration = Activity->EndTime - Activity->StartTime; FTimingEventsTrackDrawStateBuilder::AppendDurationToEventName(EventName, Duration); } return EventName; }); } } // Add IO file activity foreground events. for (const FFileActivitySharedState::FIoTimingEvent& Event: SharedState.AllIoEvents) { if (Event.EndTime <= Viewport.GetStartTime()) { continue; } if (Event.StartTime >= Viewport.GetEndTime()) { break; } ensure(Event.Depth <= 10000); const Trace::EFileActivityType ActivityType = static_cast(Event.Type & 0x0F); const bool bHasFailed = ((Event.Type & 0xF0) != 0); if (bShowOnlyErrors && !bHasFailed) { continue; } uint32 Color = bHasFailed ? 0xFFAA0000 : GetFileActivityTypeColor(ActivityType); if (Event.ActualSize != Event.Size) { Color = (Color & 0xFF000000) | ((Color & 0xFEFEFE) >> 1); } Builder.AddEvent(Event.StartTime, Event.EndTime, Event.Depth, Color, [&Event](float Width) { FString EventName; const bool bHasFailed = ((Event.Type & 0xF0) != 0); if (bHasFailed) { EventName += TEXT("Failed "); } const Trace::EFileActivityType ActivityType = static_cast(Event.Type & 0x0F); EventName += GetFileActivityTypeName(ActivityType); if (Event.ActualSize != Event.Size) { EventName += TEXT(" [!]"); } if (ActivityType >= Trace::FileActivityType_Count) { EventName += " ["; EventName += Event.FileActivity->Path; EventName += "]"; } if (Width > EventName.Len() * 4.0f + 32.0f) { const double Duration = Event.EndTime - Event.StartTime; FTimingEventsTrackDrawStateBuilder::AppendDurationToEventName(EventName, Duration); } return EventName; }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// const TSharedPtr FDetailedFileActivityTimingTrack::SearchEvent(const FTimingEventSearchParameters& InSearchParameters) const { TSharedPtr FoundEvent; FindIoTimingEvent(InSearchParameters, [this, &FoundEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const FFileActivitySharedState::FIoTimingEvent& InEvent) { FoundEvent = MakeShared(SharedThis(this), InFoundStartTime, InFoundEndTime, InFoundDepth); }); return FoundEvent; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FDetailedFileActivityTimingTrack::BuildContextMenu(FMenuBuilder& InOutMenuBuilder) { InOutMenuBuilder.BeginSection(TEXT("Misc")); { InOutMenuBuilder.AddMenuEntry( LOCTEXT("ActivityTrack_ShowOnlyErrors", "Show Only Errors"), LOCTEXT("ActivityTrack_ShowOnlyErrors_Tooltip", "Show only the events with errors"), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FDetailedFileActivityTimingTrack::ToggleOnlyErrors), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FDetailedFileActivityTimingTrack::IsOnlyErrorsToggleOn)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); InOutMenuBuilder.AddMenuEntry( LOCTEXT("ActivityTrack_ShowBackgroundEvents", "Show Background Events - O"), LOCTEXT("ActivityTrack_ShowBackgroundEvents_Tooltip", "Show background events for file activities."), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FDetailedFileActivityTimingTrack::ToggleBackgroundEvents), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FDetailedFileActivityTimingTrack::AreBackgroundEventsVisible)), NAME_None, //"QuickFilterSeparator", EUserInterfaceActionType::ToggleButton); } InOutMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE