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

217 lines
8.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CpuTimingTrack.h"
// TraceServices
#include "TraceServices/Model/AnalysisSession.h"
#include "TraceServices/Model/TasksProfiler.h"
// TraceInsightsCore
#include "InsightsCore/Common/TimeUtils.h"
// TraceInsights
#include "Insights/TimingProfiler/ViewModels/ThreadTimingSharedState.h"
#include "Insights/ViewModels/ThreadTrackEvent.h"
#include "Insights/ViewModels/TooltipDrawState.h"
#define LOCTEXT_NAMESPACE "UE::Insights::TimingProfiler::CpuTimingTrack"
namespace UE::Insights::TimingProfiler
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// FCpuTimingTrack
////////////////////////////////////////////////////////////////////////////////////////////////////
INSIGHTS_IMPLEMENT_RTTI(FCpuTimingTrack)
////////////////////////////////////////////////////////////////////////////////////////////////////
void FCpuTimingTrack::AddTaskInfo(FTooltipDrawState& InOutTooltip, const TraceServices::FTaskInfo& Task) const
{
InOutTooltip.AddTextLine(FString::Printf(TEXT("-------- Task %" UINT64_FMT "%s --------"), Task.Id, Task.bTracked ? TEXT("") : TEXT(" (not tracked)")), FLinearColor::Green);
if (Task.DebugName != nullptr)
{
InOutTooltip.AddTextLine(FString::Printf(TEXT("%s"), Task.DebugName), FLinearColor::Green);
}
ENamedThreads::Type ThreadInfo = (ENamedThreads::Type)Task.ThreadToExecuteOn;
ENamedThreads::Type ThreadIndex = ENamedThreads::GetThreadIndex(ThreadInfo);
auto FormatTaskTimestamp = [](double Timestamp) -> FString
{
return (Timestamp != TraceServices::FTaskInfo::InvalidTimestamp) ? FString::SanitizeFloat(Timestamp) : TEXT("[not set]");
};
auto FormatTaskTime = [](double Time) -> FString
{
return FormatTimeAuto(Time, 2);
};
auto GetTrackName = [this](uint32 InThreadId) -> FString
{
TSharedPtr<FCpuTimingTrack> Track = GetSharedState().GetCpuTrack(InThreadId);
return Track.IsValid() ? Track->GetName() : TEXT("Unknown");
};
if (ThreadIndex == ENamedThreads::AnyThread)
{
const TCHAR* TaskPri = ENamedThreads::GetTaskPriority(ThreadInfo) == ENamedThreads::NormalTaskPriority ? TEXT("Normal") : TEXT("High");
int32 ThreadPriIndex = ENamedThreads::GetThreadPriorityIndex(ThreadInfo);
const TCHAR* ThreadPriStrs[] = { TEXT("Normal"), TEXT("High"), TEXT("Low") };
const TCHAR* ThreadPri = ensure(ThreadPriIndex >= 0 && ThreadPriIndex < 3) ? ThreadPriStrs[ThreadPriIndex] : TEXT("Unknown");
InOutTooltip.AddTextLine(
FString::Printf(TEXT("%s Pri task on %s Pri worker (%s)"), TaskPri, ThreadPri, *GetTrackName(Task.StartedThreadId)),
FLinearColor::Green);
}
else
{
const TCHAR* QueueStr = ENamedThreads::GetQueueIndex(ThreadInfo) == ENamedThreads::MainQueue ? TEXT("Main") : TEXT("Local");
InOutTooltip.AddTextLine(
FString::Printf(TEXT("%s (%s queue)"), *GetTrackName(Task.StartedThreadId), QueueStr),
FLinearColor::Green);
}
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Created:"), FString::Printf(TEXT("%s on %s"),
*FormatTaskTimestamp(Task.CreatedTimestamp),
*GetTrackName(Task.CreatedThreadId)));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Launched:"), FString::Printf(TEXT("%s (+%s) on %s"),
*FormatTaskTimestamp(Task.LaunchedTimestamp),
*FormatTaskTime(Task.LaunchedTimestamp - Task.CreatedTimestamp),
*GetTrackName(Task.LaunchedThreadId)));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Scheduled:"), FString::Printf(TEXT("%s (+%s) on %s"),
*FormatTaskTimestamp(Task.ScheduledTimestamp),
*FormatTaskTime(Task.ScheduledTimestamp - Task.LaunchedTimestamp),
*GetTrackName(Task.ScheduledThreadId)));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Started:"), FString::Printf(TEXT("%s (+%s)"),
*FormatTaskTimestamp(Task.StartedTimestamp),
*FormatTaskTime(Task.StartedTimestamp - Task.ScheduledTimestamp)));
if (Task.FinishedTimestamp != TraceServices::FTaskInfo::InvalidTimestamp)
{
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Finished:"), FString::Printf(TEXT("%s (+%s)"),
*FormatTaskTimestamp(Task.FinishedTimestamp),
*FormatTaskTime(Task.FinishedTimestamp - Task.StartedTimestamp)));
if (Task.CompletedTimestamp != TraceServices::FTaskInfo::InvalidTimestamp)
{
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Completed:"), FString::Printf(TEXT("%s (+%s) on %s"),
*FormatTaskTimestamp(Task.CompletedTimestamp),
*FormatTaskTime(Task.CompletedTimestamp - Task.FinishedTimestamp),
*GetTrackName(Task.CompletedThreadId)));
if (Task.DestroyedTimestamp != TraceServices::FTaskInfo::InvalidTimestamp)
{
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Destroyed:"), FString::Printf(TEXT("%s (+%s) on %s"),
*FormatTaskTimestamp(Task.DestroyedTimestamp),
*FormatTaskTime(Task.DestroyedTimestamp - Task.CompletedTimestamp),
*GetTrackName(Task.DestroyedThreadId)));
}
}
}
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Prerequisite tasks:"), FString::Printf(TEXT("%d"), Task.Prerequisites.Num()));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Subsequent tasks:"), FString::Printf(TEXT("%d"), Task.Subsequents.Num()));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Parent tasks:"), FString::Printf(TEXT("%d"), Task.ParentTasks.Num()));
InOutTooltip.AddNameValueTextLine(TEXTVIEW("Nested tasks:"), FString::Printf(TEXT("%d"), Task.NestedTasks.Num()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FCpuTimingTrack::PostInitTooltip(FTooltipDrawState& InOutTooltip, const FThreadTrackEvent& TooltipEvent, const TraceServices::IAnalysisSession& Session, const TCHAR* TimerName) const
{
// tasks
const TraceServices::ITasksProvider* TasksProvider = TraceServices::ReadTasksProvider(Session);
if (TasksProvider != nullptr)
{
// info about a task
const TraceServices::FTaskInfo* Task = TasksProvider->TryGetTask(GetThreadId(), TooltipEvent.GetStartTime());
if (Task != nullptr && Task->FinishedTimestamp >= TooltipEvent.GetEndTime())
{
AddTaskInfo(InOutTooltip, *Task);
}
// info about blocking
const TraceServices::FWaitingForTasks* Waiting = TasksProvider->TryGetWaiting(TimerName, GetThreadId(), TooltipEvent.GetStartTime());
if (Waiting != nullptr && Waiting->Tasks.Num() > 0)
{
InOutTooltip.AddTextLine(TEXT("-------- Waiting for tasks --------"), FLinearColor::Red);
constexpr int32 NumIdsOnRow = 4;
TStringBuilder<1024> StringBuilder;
// Add the first line of Task Id values.
for (int32 Index = 0; Index < Waiting->Tasks.Num() && Index < NumIdsOnRow; ++Index)
{
StringBuilder.Appendf(TEXT("%d, "), Waiting->Tasks[Index]);
}
// Remove separators from last entry.
if (Waiting->Tasks.Num() <= NumIdsOnRow)
{
StringBuilder.RemoveSuffix(2);
}
InOutTooltip.AddNameValueTextLine(FString::Printf(TEXT("Tasks[%d]:"), Waiting->Tasks.Num()), StringBuilder.ToView());
StringBuilder.Reset();
// Add the rest of the lines with an empty name so they appear as a multi line value.
for (int32 Index = NumIdsOnRow; Index < Waiting->Tasks.Num(); ++Index)
{
StringBuilder.Appendf(TEXT("%d, "), Waiting->Tasks[Index]);
if ((Index + 1) % NumIdsOnRow == 0)
{
InOutTooltip.AddNameValueTextLine(TEXT(""), StringBuilder.ToView());
StringBuilder.Reset();
}
}
if (StringBuilder.Len() > 1)
{
StringBuilder.RemoveSuffix(2);
InOutTooltip.AddNameValueTextLine(TEXT(""), StringBuilder.ToView());
}
InOutTooltip.AddNameValueTextLine(TEXT("Started waiting:"),
FString::Printf(TEXT("%s"), *FString::SanitizeFloat(Waiting->StartedTimestamp)));
if (Waiting->FinishedTimestamp != TraceServices::FTaskInfo::InvalidTimestamp)
{
InOutTooltip.AddNameValueTextLine(TEXT("Finished waiting:"),
FString::Printf(TEXT("%s (+%s)"),
*FString::SanitizeFloat(Waiting->FinishedTimestamp),
*FormatTimeAuto(Waiting->FinishedTimestamp - Waiting->StartedTimestamp, 2)));
}
else
{
InOutTooltip.AddNameValueTextLine(TEXT("Finished waiting:"), TEXT("[not set]"));
}
const int32 MaxWaitedTasksToList = 5;
int32 NumTasksToList = FMath::Min(Waiting->Tasks.Num(), MaxWaitedTasksToList);
for (int32 TaskIndex = 0; TaskIndex != NumTasksToList; ++TaskIndex)
{
const TraceServices::FTaskInfo* WaitedTask = TasksProvider->TryGetTask(Waiting->Tasks[TaskIndex]);
if (WaitedTask != nullptr)
{
AddTaskInfo(InOutTooltip , *WaitedTask);
}
}
if (NumTasksToList < Waiting->Tasks.Num())
{
InOutTooltip.AddTextLine(TEXT("[...]"), FLinearColor::Green);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace UE::Insights::TimingProfiler
#undef LOCTEXT_NAMESPACE