IntialCommit
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#include "VisualStudioBlueprintDebuggerHelperModule.h"
|
||||
#include <Modules/ModuleManager.h>
|
||||
#include <UObject/Script.h>
|
||||
#include <UObject/Stack.h>
|
||||
#include <UObject/Object.h>
|
||||
#include <Templates/Casts.h>
|
||||
#include <Kismet2/KismetDebugUtilities.h>
|
||||
#include <Containers/Array.h>
|
||||
#include <UObject/Class.h>
|
||||
#include <Engine/BlueprintGeneratedClass.h>
|
||||
#include <Engine/Blueprint.h>
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
|
||||
#include <Blueprint/BlueprintExceptionInfo.h>
|
||||
#endif
|
||||
#include <Internationalization/Text.h>
|
||||
#include <HAL/Platform.h>
|
||||
#include <EdGraph/EdGraphNode.h>
|
||||
#include <EdGraph/EdGraphPin.h>
|
||||
#include <Templates/SharedPointer.h>
|
||||
#include <Templates/Tuple.h>
|
||||
#include <CoreGlobals.h>
|
||||
#include <map>
|
||||
|
||||
IMPLEMENT_MODULE(FVisualStudioBlueprintDebuggerHelper, VisualStudioBlueprintDebuggerHelper);
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogVisualStudioBlueprintDebuggerHelper);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
#define FCustomBlueprintPropertyInfo TSharedPtr<FPropertyInstanceInfo>
|
||||
#else
|
||||
#define FCustomBlueprintPropertyInfo FDebugInfo
|
||||
#endif
|
||||
|
||||
struct FVSNodePinRuntimeInformation
|
||||
{
|
||||
UEdGraphPin* Pin;
|
||||
FCustomBlueprintPropertyInfo Property;
|
||||
|
||||
FVSNodePinRuntimeInformation(UEdGraphPin* InPin, FCustomBlueprintPropertyInfo InProperty)
|
||||
: Pin(InPin)
|
||||
, Property(InProperty)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct FVSNodeData
|
||||
{
|
||||
FText NodeName;
|
||||
TArray<TSharedPtr<FVSNodePinRuntimeInformation>> Properties;
|
||||
int32 ScriptEntryTag;
|
||||
const UEdGraphNode* Node;
|
||||
};
|
||||
|
||||
struct FVSNodesRuntimeInformation
|
||||
{
|
||||
TArray<TSharedPtr<FVSNodeData>> Nodes;
|
||||
};
|
||||
|
||||
struct FVSBlueprintRuntimeInformation
|
||||
{
|
||||
TArray<TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>> RunningBlueprints;
|
||||
};
|
||||
|
||||
struct StackTraceHelper
|
||||
{
|
||||
int32 ScriptEntryTag;
|
||||
FString NodeName;
|
||||
};
|
||||
|
||||
// Keep exported so we can read it.
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API FVSBlueprintRuntimeInformation BlueprintsRuntimeInformation;
|
||||
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API std::map<void*, StackTraceHelper> StackFrameInformation;
|
||||
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API const char* DebuggerHelperVersion = "1.0.0";
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::StartupModule()
|
||||
{
|
||||
CurrentScriptEntryTag = 0;
|
||||
|
||||
FBlueprintContextTracker::OnEnterScriptContext.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnEnterScriptContext);
|
||||
|
||||
FBlueprintContextTracker::OnExitScriptContext.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnExitScriptContext);
|
||||
|
||||
FBlueprintCoreDelegates::OnScriptException.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnScriptException);
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::ShutdownModule()
|
||||
{
|
||||
FBlueprintCoreDelegates::OnScriptException.RemoveAll(this);
|
||||
FBlueprintContextTracker::OnExitScriptContext.RemoveAll(this);
|
||||
FBlueprintContextTracker::OnEnterScriptContext.RemoveAll(this);
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnEnterScriptContext(
|
||||
const struct FBlueprintContextTracker& Context,
|
||||
const UObject* SourceObject,
|
||||
const UFunction* Function)
|
||||
{
|
||||
if (!IsInGameThread())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentScriptEntryTag = Context.GetScriptEntryTag();
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnExitScriptContext(const struct FBlueprintContextTracker& Context)
|
||||
{
|
||||
if (!IsInGameThread())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto ItRunningBlueprints = BlueprintsRuntimeInformation.RunningBlueprints.CreateIterator(); ItRunningBlueprints; ++ItRunningBlueprints)
|
||||
{
|
||||
auto& RunningBlueprint = ItRunningBlueprints->Value;
|
||||
for (auto ItNodeData = RunningBlueprint->Nodes.CreateIterator(); ItNodeData; ++ItNodeData)
|
||||
{
|
||||
if ((*ItNodeData)->ScriptEntryTag == Context.GetScriptEntryTag())
|
||||
{
|
||||
ItNodeData.RemoveCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!RunningBlueprint->Nodes.Num())
|
||||
{
|
||||
ItRunningBlueprints.RemoveCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ItStackFrameInfo = StackFrameInformation.begin(); ItStackFrameInfo != StackFrameInformation.end();)
|
||||
{
|
||||
if (ItStackFrameInfo->second.ScriptEntryTag == Context.GetScriptEntryTag())
|
||||
{
|
||||
ItStackFrameInfo = StackFrameInformation.erase(ItStackFrameInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
++ItStackFrameInfo;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentScriptEntryTag--;
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnScriptException(
|
||||
const UObject* Owner,
|
||||
const struct FFrame& Stack,
|
||||
const FBlueprintExceptionInfo& ExceptionInfo)
|
||||
{
|
||||
EBlueprintExceptionType::Type ExceptionType = ExceptionInfo.GetType();
|
||||
if (ExceptionType != EBlueprintExceptionType::Type::Tracepoint &&
|
||||
ExceptionType != EBlueprintExceptionType::Type::WireTracepoint &&
|
||||
ExceptionType != EBlueprintExceptionType::Type::Breakpoint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UFunction* NodeFunction = Cast<UFunction>(Stack.Node);
|
||||
if (!NodeFunction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBlueprintGeneratedClass* BlueprintGeneratedClass = Cast<UBlueprintGeneratedClass>(NodeFunction->GetOuter());
|
||||
if (!BlueprintGeneratedClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBlueprint* Blueprint = Cast<UBlueprint>(BlueprintGeneratedClass->ClassGeneratedBy);
|
||||
if (!Blueprint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 BreakpointOffset = Stack.Code - Stack.Node->Script.GetData() - 1;
|
||||
const UEdGraphNode* NodeStoppedAt = FKismetDebugUtilities::FindSourceNodeForCodeLocation(Owner, Stack.Node, BreakpointOffset, /*bAllowImpreciseHit=*/ true);
|
||||
if (!NodeStoppedAt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StackFrameInformation[NodeFunction] = { CurrentScriptEntryTag, FString::Printf(TEXT("%s::%s"), *Blueprint->GetFriendlyName(), *NodeStoppedAt->GetNodeTitle(ENodeTitleType::Type::ListView).ToString()) };
|
||||
TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>* ExistingNodesRuntimeInformationTuple = BlueprintsRuntimeInformation.RunningBlueprints.FindByPredicate([&Blueprint](const TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>& Tuple) {
|
||||
return Tuple.Key == Blueprint;
|
||||
});
|
||||
|
||||
TSharedPtr<FVSNodesRuntimeInformation> NodesRuntimeInformation;
|
||||
if (!ExistingNodesRuntimeInformationTuple)
|
||||
{
|
||||
NodesRuntimeInformation = MakeShared<FVSNodesRuntimeInformation>();
|
||||
BlueprintsRuntimeInformation.RunningBlueprints.Add(MakeTuple(Blueprint, NodesRuntimeInformation));
|
||||
}
|
||||
else
|
||||
{
|
||||
NodesRuntimeInformation = ExistingNodesRuntimeInformationTuple->Value;
|
||||
}
|
||||
|
||||
TSharedPtr<FVSNodeData> CurrentNodeData;
|
||||
if (NodesRuntimeInformation->Nodes.Num() == 0 || NodeStoppedAt != NodesRuntimeInformation->Nodes.Top()->Node)
|
||||
{
|
||||
CurrentNodeData = MakeShared<FVSNodeData>();
|
||||
CurrentNodeData->Node = NodeStoppedAt;
|
||||
CurrentNodeData->NodeName = NodeStoppedAt->GetNodeTitle(ENodeTitleType::Type::ListView);
|
||||
CurrentNodeData->ScriptEntryTag = CurrentScriptEntryTag;
|
||||
NodesRuntimeInformation->Nodes.Push(CurrentNodeData);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentNodeData = NodesRuntimeInformation->Nodes.Top();
|
||||
}
|
||||
|
||||
FCustomBlueprintPropertyInfo PinInstanceInfo;
|
||||
for (auto GraphPin : NodeStoppedAt->Pins)
|
||||
{
|
||||
FKismetDebugUtilities::EWatchTextResult DebugResult = FKismetDebugUtilities::GetDebugInfo(PinInstanceInfo, Blueprint, (UObject*)Owner, GraphPin);
|
||||
if (DebugResult != FKismetDebugUtilities::EWTR_Valid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TSharedPtr<FVSNodePinRuntimeInformation>* Existing = CurrentNodeData->Properties.FindByPredicate([&GraphPin](TSharedPtr<FVSNodePinRuntimeInformation>& PinInfo) {
|
||||
return PinInfo->Pin == GraphPin;
|
||||
});
|
||||
|
||||
if (!Existing)
|
||||
{
|
||||
CurrentNodeData->Properties.Add(MakeShared<FVSNodePinRuntimeInformation>(GraphPin, PinInstanceInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
(*Existing)->Property = PinInstanceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
#include <Modules/ModuleInterface.h>
|
||||
#include <Modules/ModuleManager.h>
|
||||
#include <UObject/Script.h>
|
||||
#include <UObject/Stack.h>
|
||||
#include <UObject/Object.h>
|
||||
#include <Logging/LogMacros.h>
|
||||
#include <UObject/Class.h>
|
||||
#include <HAL/Platform.h>
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogVisualStudioBlueprintDebuggerHelper, Log, All);
|
||||
|
||||
class FVisualStudioBlueprintDebuggerHelper : public FDefaultModuleImpl
|
||||
{
|
||||
private:
|
||||
void OnScriptException(const UObject* Owner, const struct FFrame& Stack, const FBlueprintExceptionInfo& ExceptionInfo);
|
||||
void OnEnterScriptContext(const struct FBlueprintContextTracker& Context, const UObject* SourceObject, const UFunction* Function);
|
||||
void OnExitScriptContext(const struct FBlueprintContextTracker& Context);
|
||||
|
||||
int32 CurrentScriptEntryTag;
|
||||
|
||||
public:
|
||||
void StartupModule() override;
|
||||
void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class VisualStudioBlueprintDebuggerHelper: ModuleRules
|
||||
{
|
||||
public VisualStudioBlueprintDebuggerHelper(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
OptimizeCode = CodeOptimization.Never;
|
||||
PrivateDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"ApplicationCore",
|
||||
"AssetRegistry",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"Kismet",
|
||||
"UnrealEd",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"ToolMenus",
|
||||
"EditorSubsystem",
|
||||
"MainFrame",
|
||||
"BlueprintGraph",
|
||||
"VisualStudioDTE",
|
||||
"EditorStyle",
|
||||
"Projects"
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user