// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "UObject/Class.h" #include "Engine/Selection.h" static_assert(DO_BLUEPRINT_GUARD, "KismetDebugUtilities assumes BP exception tracking is enabled"); class UBlueprint; class UBreakpoint; template class TSimpleRingBuffer; DECLARE_LOG_CATEGORY_EXTERN(LogBlueprintDebug, Log, All); ////////////////////////////////////////////////////////////////////////// // FKismetTraceSample struct FKismetTraceSample { TWeakObjectPtr Context; TWeakObjectPtr Function; int32 Offset; double ObservationTime; }; ////////////////////////////////////////////////////////////////////////// // FDebugInfo struct FDebugInfo { FText DisplayName; FText Value; FText Type; TArray Children; }; ////////////////////////////////////////////////////////////////////////// // FObjectsBeingDebuggedIterator // Helper class to iterate over all objects that should be visible in the debugger struct UNREALED_API FObjectsBeingDebuggedIterator { public: FObjectsBeingDebuggedIterator(); /** @name Element access */ //@{ UObject* operator*() const; UObject* operator->() const; //@} /** Advances iterator to the next element in the container. */ FObjectsBeingDebuggedIterator& operator++(); /** conversion to "bool" returning true if the iterator has not reached the last element. */ FORCEINLINE explicit operator bool() const { return IsValid(); } /** inverse of the "bool" operator */ FORCEINLINE bool operator!() const { return !(bool)*this; } private: FSelectionIterator SelectedActorsIter; int32 LevelScriptActorIndex; private: void FindNextLevelScriptActor(); bool IsValid() const; UWorld* GetWorld() const; }; ////////////////////////////////////////////////////////////////////////// // FObjectsBeingDebuggedIterator // Helper class to iterate over all objects that should be visible in the debugger struct UNREALED_API FBlueprintObjectsBeingDebuggedIterator { public: FBlueprintObjectsBeingDebuggedIterator(UBlueprint* InBlueprint); /** @name Element access */ //@{ UObject* operator*() const; UObject* operator->() const; //@} /** Advances iterator to the next element in the container. */ FBlueprintObjectsBeingDebuggedIterator& operator++(); /** conversion to "bool" returning true if the iterator has not reached the last element. */ FORCEINLINE explicit operator bool() const { return IsValid(); } /** inverse of the "bool" operator */ FORCEINLINE bool operator!() const { return !(bool)*this; } private: UBlueprint* Blueprint; private: bool IsValid() const; }; ////////////////////////////////////////////////////////////////////////// // FKismetDebugUtilities class UNREALED_API FKismetDebugUtilities { public: // Delegate for when pins are added or removed from the watchlist DECLARE_MULTICAST_DELEGATE_OneParam(FOnWatchedPinsListChanged, class UBlueprint*); static void OnScriptException(const UObject* ActiveObject, const FFrame& StackFrame, const FBlueprintExceptionInfo& Info); /** Returns the current instruction; if a PIE/SIE session is started but paused; otherwise NULL */ static class UEdGraphNode* GetCurrentInstruction(); /** Returns the most recent hit breakpoint; if a PIE/SIE session is started but paused; otherwise NULL */ static class UEdGraphNode* GetMostRecentBreakpointHit(); /** Request an attempt to single-step to the next node */ static void RequestSingleStepIn(); /** Request an attempt to step over to the next node in this graph or the calling graph */ static void RequestStepOver(); /** Request an attempt to step out of the current graph */ static void RequestStepOut(); /** Called on terminatation of the current script execution so we can reset any break conditions */ static void EndOfScriptExecution(const FBlueprintContextTracker& BlueprintContext); // The maximum number of trace samples to gather before overwriting old ones enum { MAX_TRACE_STACK_SAMPLES = 1024 }; // Get the trace stack static const TSimpleRingBuffer& GetTraceStack(); // Find the node that resulted in code at the specified location in the Object, or NULL if there was a problem (e.g., no debugging information was generated) static class UEdGraphNode* FindSourceNodeForCodeLocation(const UObject* Object, UFunction* Function, int32 DebugOpcodeOffset, bool bAllowImpreciseHit = false); // Return proper class for breakpoint static UClass* FindClassForNode(const UObject* Object, UFunction* Function); // Notify the debugger of the start of the game frame static void NotifyDebuggerOfStartOfGameFrame(UWorld* CurrentWorld); // Notify the debugger of the end of the game frame static void NotifyDebuggerOfEndOfGameFrame(UWorld* CurrentWorld); // Whether or not we are single stepping static bool IsSingleStepping(); // Breakpoint utils /** Is the node a valid breakpoint target? (i.e., the node is impure and ended up generating code) */ static bool IsBreakpointValid(UBreakpoint* Breakpoint); /** Set the node that the breakpoint should focus on */ static void SetBreakpointLocation(UBreakpoint* Breakpoint, UEdGraphNode* NewNode); /** Set or clear the enabled flag for the breakpoint */ static void SetBreakpointEnabled(UBreakpoint* Breakpoint, bool bIsEnabled); /** Sets this breakpoint up as a single-step breakpoint (will disable or delete itself after one go if the breakpoint wasn't already enabled) */ static void SetBreakpointEnabledForSingleStep(UBreakpoint* Breakpoint, bool bDeleteAfterStep); /** Reapplies the breakpoint (used after recompiling to ensure it is set if needed) */ static void ReapplyBreakpoint(UBreakpoint* Breakpoint); /** Start the process of deleting this breakpoint */ static void StartDeletingBreakpoint(UBreakpoint* Breakpoint, UBlueprint* OwnerBlueprint); /** Update the internal state of the breakpoint when it got hit */ static void UpdateBreakpointStateWhenHit(UBreakpoint* Breakpoint, UBlueprint* OwnerBlueprint); /** Returns the installation site(s); don't cache these pointers! */ static void GetBreakpointInstallationSites(UBreakpoint* Breakpoint, TArray& InstallSites); /** Install/uninstall the breakpoint into/from the script code for the generated class that contains the node */ static void SetBreakpointInternal(UBreakpoint* Breakpoint, bool bShouldBeEnabled); /** Returns the set of valid macro source node breakpoint location(s) for the given macro instance node. The set may be empty. */ static void GetValidBreakpointLocations(const class UK2Node_MacroInstance* MacroInstanceNode, TArray& BreakpointLocations); // Blueprint utils // Looks thru the debugging data for any class variables associated with the pin static class FProperty* FindClassPropertyForPin(UBlueprint* Blueprint, const UEdGraphPin* Pin); // Looks thru the debugging data for any class variables associated with the node (e.g., temporary variables or timelines) static class FProperty* FindClassPropertyForNode(UBlueprint* Blueprint, const UEdGraphNode* Node); // Is there debugging data available for this blueprint? static bool HasDebuggingData(const UBlueprint* Blueprint); /** Returns the breakpoint associated with a node, or NULL */ static UBreakpoint* FindBreakpointForNode(UBlueprint* Blueprint, const UEdGraphNode* Node, bool bCheckSubLocations = false); /** Deletes all breakpoints in this blueprint */ static void ClearBreakpoints(UBlueprint* Blueprint); // Notifies listeners when a watched pin is added or removed static FOnWatchedPinsListChanged WatchedPinsListChangedEvent; static bool CanWatchPin(const UBlueprint* Blueprint, const UEdGraphPin* Pin); static bool IsPinBeingWatched(const UBlueprint* Blueprint, const UEdGraphPin* Pin); static void TogglePinWatch(UBlueprint* Blueprint, const UEdGraphPin* Pin); static void RemovePinWatch(UBlueprint* Blueprint, const UEdGraphPin* Pin); static void ClearPinWatches(UBlueprint* Blueprint); enum EWatchTextResult { // The property was valid and the value has been returned as a string EWTR_Valid, // The property is a local of a function that is not on the current stack EWTR_NotInScope, // There is no debug object selected EWTR_NoDebugObject, // There is no property related to the pin EWTR_NoProperty }; // Gets the watched tooltip for a specified site static EWatchTextResult GetWatchText(FString& OutWatchText, UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin); // Gets the debug info for a specified site static EWatchTextResult GetDebugInfo(FDebugInfo& OutDebugInfo, UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin); //@TODO: Pretty lame way to handle this messaging, ideally the entire Info object gets pushed into the editor when intraframe debugging is triggered! // This doesn't work properly if there is more than one blueprint editor open at once either (one will consume it, the others will be left in the cold) static FText GetAndClearLastExceptionMessage(); protected: static void CheckBreakConditions(UEdGraphNode* NodeStoppedAt, bool bHitBreakpoint, int32 BreakpointOffset, bool& InOutBreakExecution); static void AttemptToBreakExecution(UBlueprint* BlueprintObj, const UObject* ActiveObject, const FFrame& StackFrame, const FBlueprintExceptionInfo& Info, UEdGraphNode* NodeStoppedAt, int32 DebugOpcodeOffset); static void GetDebugInfo_InContainer(int32 Index, FDebugInfo& DebugInfo, FProperty* Property, const void* Data); static void GetDebugInfoInternal(FDebugInfo& DebugInfo, FProperty* Property, const void* PropertyValue); /** * @brief Helper function for converting between blueprint and debuggable data * output params are only valid if the return result is EWatchTextResult::EWTR_Valid * * @param Blueprint Active blueprint that is being debugged * @param ActiveObject Instance of the object that is being debugged * @param WatchPin The pin where this debug breakpoint is from * @param OutProperty Property of interest * @param OutData Populated with the property address of interest * @param OutDelta Populated with the same thing as OutData * @param OutParent Populated with the active object * @param SeenObjects Used to track what objects have been traversed to find the OutProperty address * @return EWTR_Valid if the debug data could be found, otherwise an appropriate error code */ static EWatchTextResult FindDebuggingData(UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin, FProperty*& OutProperty, void*& OutData, void*& OutDelta, UObject*& OutParent, TArray& SeenObjects, bool* OutbShouldUseContainerOffset = nullptr); private: FKismetDebugUtilities() {} }; //////////////////////////////////////////////////////////////////////////