Files
UnrealEngineUWP/Engine/Plugins/Runtime/StateTree/Source/StateTreeModule/Private/Debugger/StateTreeTraceAnalyzer.cpp
yoan stamant 05bc614a8e [StateTreeDebugger]
- Added trace event for Phase (Push + Pop) to make it easier to recreate the event hierarchy when analyzing the traces.
- Merged enums EStateTreeTraceInstanceEventType and EStateTreeTraceNodeEventType to EStateTreeTraceEventType which is mainly a list of verbs that could be reused for different events (States, Instances, Tasks, etc.)
- EStateTreeUpdatePhase is no longer used as flags and reduced to uint8
- Phase events are stacked when producing traces and sent only if meaningful events (Task, State, Transition, etc.) are sent during their scope. This is to avoid sending useless events when the StateTree is ticked without any changes
#rnx
#rb mikko.mononen
#preflight 6474a5e62e05bcc3309d093e

[CL 25663847 by yoan stamant in ue5-main branch]
2023-05-29 10:13:21 -04:00

187 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#if WITH_STATETREE_DEBUGGER
#include "Debugger/StateTreeTraceAnalyzer.h"
#include "Debugger/StateTreeDebugger.h"
#include "Debugger/StateTreeTraceProvider.h"
#include "Debugger/StateTreeTraceTypes.h"
#include "Serialization/MemoryReader.h"
#include "TraceServices/Model/AnalysisSession.h"
FStateTreeTraceAnalyzer::FStateTreeTraceAnalyzer(TraceServices::IAnalysisSession& InSession, FStateTreeTraceProvider& InProvider)
: Session(InSession)
, Provider(InProvider)
{
}
void FStateTreeTraceAnalyzer::OnAnalysisBegin(const FOnAnalysisContext& Context)
{
auto& Builder = Context.InterfaceBuilder;
Builder.RouteEvent(RouteId_WorldTimestamp, "StateTreeDebugger", "WorldTimestampEvent");
Builder.RouteEvent(RouteId_Instance, "StateTreeDebugger", "InstanceEvent");
Builder.RouteEvent(RouteId_Phase, "StateTreeDebugger", "PhaseEvent");
Builder.RouteEvent(RouteId_LogMessage, "StateTreeDebugger", "LogEvent");
Builder.RouteEvent(RouteId_State, "StateTreeDebugger", "StateEvent");
Builder.RouteEvent(RouteId_Task, "StateTreeDebugger", "TaskEvent");
Builder.RouteEvent(RouteId_Transition, "StateTreeDebugger", "TransitionEvent");
Builder.RouteEvent(RouteId_Condition, "StateTreeDebugger", "ConditionEvent");
Builder.RouteEvent(RouteId_ActiveStates, "StateTreeDebugger", "ActiveStatesEvent");
}
bool FStateTreeTraceAnalyzer::OnEvent(const uint16 RouteId, EStyle Style, const FOnEventContext& Context)
{
LLM_SCOPE_BYNAME(TEXT("Insights/FStateTreeAnalyzer"));
TraceServices::FAnalysisSessionEditScope _(Session);
const auto& EventData = Context.EventData;
switch (RouteId)
{
case RouteId_WorldTimestamp:
{
WorldTime = EventData.GetValue<double>("WorldTime");
break;
}
case RouteId_Instance:
{
FString ObjectName, ObjectPathName;
EventData.GetString("TreeName", ObjectName);
EventData.GetString("TreePath", ObjectPathName);
const FTopLevelAssetPath Path((FName)ObjectPathName, (FName)ObjectName);
TWeakObjectPtr<const UStateTree> WeakStateTree;
{
// This might not work when using a debugger on a client but should be fine in Editor as long as
// we are not trying to find the object during GC. We might not currently be in the game thread.
// @todo STDBG: eventually errors should be reported in the UI
FGCScopeGuard Guard;
WeakStateTree = FindObject<UStateTree>(Path);
}
if (const UStateTree* StateTree = WeakStateTree.Get())
{
const uint32 CompiledDataHash = EventData.GetValue<uint32>("CompiledDataHash");
if (StateTree->LastCompiledEditorDataHash == CompiledDataHash)
{
FString InstanceName;
EventData.GetString("InstanceName", InstanceName);
Provider.AppendInstanceEvent(StateTree,
FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
*InstanceName,
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
WorldTime,
EventData.GetValue<EStateTreeTraceEventType>("EventType"));
}
else
{
UE_LOG(LogStateTree, Warning, TEXT("Traces are not using the same StateTree asset version as the current asset."));
}
}
else
{
UE_LOG(LogStateTree, Warning, TEXT("Unable to find StateTree asset: %s : %s"), *ObjectPathName, *ObjectName);
}
break;
}
case RouteId_Phase:
{
const FStateTreeTracePhaseEvent Event(WorldTime,
EventData.GetValue<EStateTreeUpdatePhase>("Phase"),
EventData.GetValue<EStateTreeTraceEventType>("EventType"));
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTracePhaseEvent>(), Event));
break;
}
case RouteId_LogMessage:
{
FString Message;
EventData.GetString("Message", Message);
const FStateTreeTraceLogEvent Event(WorldTime, Message);
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceLogEvent>(), Event));
break;
}
case RouteId_State:
{
const FStateTreeTraceStateEvent Event(WorldTime,
FStateTreeIndex16(EventData.GetValue<uint16>("StateIndex")),
EventData.GetValue<EStateTreeTraceEventType>("EventType"),
EventData.GetValue<EStateTreeStateSelectionBehavior>("SelectionBehavior"));
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceStateEvent>(), Event));
break;
}
case RouteId_Task:
{
FString TypePath, DataAsText;
FMemoryReaderView Archive(EventData.GetArrayView<uint8>("DataView"));
Archive << TypePath;
Archive << DataAsText;
const FStateTreeTraceTaskEvent Event(WorldTime,
FStateTreeIndex16(EventData.GetValue<uint16>("NodeIndex")),
EventData.GetValue<EStateTreeTraceEventType>("EventType"),
EventData.GetValue<EStateTreeRunStatus>("Status"),
TypePath, DataAsText);
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceTaskEvent>(), Event));
break;
}
case RouteId_Condition:
{
FString TypePath, DataAsText;
FMemoryReaderView Archive(EventData.GetArrayView<uint8>("DataView"));
Archive << TypePath;
Archive << DataAsText;
const FStateTreeTraceConditionEvent Event(WorldTime,
FStateTreeIndex16(EventData.GetValue<uint16>("NodeIndex")),
EventData.GetValue<EStateTreeTraceEventType>("EventType"),
TypePath, DataAsText);
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceConditionEvent>(), Event));
break;
}
case RouteId_Transition:
{
const FStateTreeTraceTransitionEvent Event(WorldTime,
FStateTreeIndex16(EventData.GetValue<uint16>("TransitionIndex")),
EventData.GetValue<EStateTreeTraceEventType>("EventType"));
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceTransitionEvent>(), Event));
break;
}
case RouteId_ActiveStates:
{
FStateTreeTraceActiveStatesEvent Event(WorldTime);
Event.ActiveStates = EventData.GetArrayView<uint16>("ActiveStates");
Provider.AppendEvent(FStateTreeInstanceDebugId(EventData.GetValue<uint32>("InstanceId"), EventData.GetValue<uint32>("InstanceSerial")),
Context.EventTime.AsSeconds(EventData.GetValue<uint64>("Cycle")),
FStateTreeTraceEventVariantType(TInPlaceType<FStateTreeTraceActiveStatesEvent>(), Event));
break;
}
default:
ensureMsgf(false, TEXT("Unhandle route id: %s"), RouteId);
}
return true;
}
#endif // WITH_STATETREE_DEBUGGER