From e491c9db4e3021b11f99996899561d426b5bb705 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 2 Mar 2026 16:26:19 -0500 Subject: [PATCH] Code to click a function in the blueprint editor and pop up the C++ --- EnginePatches/EnginePatch | 142 +++++++++++++++++++++++++-- Source/Integration/TriggeredTask.cpp | 70 ------------- Source/Integration/TriggeredTask.h | 120 ---------------------- 3 files changed, 135 insertions(+), 197 deletions(-) delete mode 100644 Source/Integration/TriggeredTask.cpp delete mode 100644 Source/Integration/TriggeredTask.h diff --git a/EnginePatches/EnginePatch b/EnginePatches/EnginePatch index d4b786b7..b3781f9b 100644 --- a/EnginePatches/EnginePatch +++ b/EnginePatches/EnginePatch @@ -1,6 +1,6 @@ -diff -u --recursive UnrealEngine-5.3.1-release/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py UnrealEngine/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py ---- UnrealEngine-5.3.1-release/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2023-09-27 09:48:07.000000000 -0400 -+++ UnrealEngine/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-06-19 17:47:01.619969745 -0400 +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py /home/jyelon/integration.UE/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py +--- /home/jyelon/integration.UE.orig/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-09-08 15:15:35.242987722 -0400 @@ -32,7 +32,7 @@ if DataVal == 0: Val = 'NULL' @@ -22,11 +22,46 @@ diff -u --recursive UnrealEngine-5.3.1-release/Engine/Extras/LLDBDataFormatters/ return Val def UESignedCharSummaryProvider(valobj,dict): -diff -u --recursive UnrealEngine-5.3.1-release/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp UnrealEngine/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp ---- UnrealEngine-5.3.1-release/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2023-09-27 09:48:07.000000000 -0400 -+++ UnrealEngine/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-06-19 17:47:01.621969761 -0400 +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp /home/jyelon/integration.UE/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp +--- /home/jyelon/integration.UE.orig/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2026-03-02 15:28:34.593675093 -0500 +@@ -149,7 +149,7 @@ + FString SolutionDir = GetSolutionPath(); + TArray Args; + Args.Add(MakePath(SolutionDir)); +- Args.Add(TEXT("-g ") + MakePath(FullPath) + FString::Printf(TEXT(":%d:%d"), LineNumber, ColumnNumber)); ++ Args.Add(TEXT("-g ") + MakePath(FullPath + FString::Printf(TEXT(":%d:%d"), LineNumber, ColumnNumber))); + return Launch(Args); + } + +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp /home/jyelon/integration.UE/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp +--- /home/jyelon/integration.UE.orig/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp 2026-03-02 15:37:59.934459864 -0500 +@@ -463,7 +463,7 @@ + ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked("SourceCodeAccess"); + ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor(); + +-#if PLATFORM_WINDOWS ++#if PLATFORM_WINDOWS || PLATFORM_LINUX + FString SourceFileName; + uint32 SourceLineNumber = 1; + uint32 SourceColumnNumber = 0; +@@ -622,8 +622,8 @@ + } + + UE_LOG(LogSelectionDetails, Warning, TEXT("NavigateToFunctionSource: Unable to look up symbol: %s in module:%s"), *FunctionSymbolName, *FunctionModuleName); +- +-#endif // PLATFORM_WINDOWS ++ ++#endif // PLATFORM_WINDOWS || PLATFORM_LINUX + } + + +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp /home/jyelon/integration.UE/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp +--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-09-08 15:15:35.242987722 -0400 @@ -299,6 +299,9 @@ - SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_SHOW_CURSOR, "1"); // When relative mouse mode is acive, don't hide cursor. + SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // When relative mouse mode is active, don't hide cursor. SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0"); // Don't warp the cursor to the center in relative mouse mode. + // Unreal does its own dynamic capturing, we don't need SDL to do it. @@ -35,3 +70,96 @@ diff -u --recursive UnrealEngine-5.3.1-release/Engine/Source/Runtime/Application // If we're rendering offscreen, use the "dummy" SDL video driver if (FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen")) && !getenv("SDL_VIDEODRIVER")) { +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp +--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp 2026-03-02 15:37:24.464156072 -0500 +@@ -15,6 +15,7 @@ + #include "HAL/ExceptionHandling.h" + #include "HAL/PlatformProcess.h" + #include "HAL/PlatformTime.h" ++#include "Modules/ModuleManager.h" + #include "AutoRTFM/AutoRTFM.h" + + #include +@@ -1060,3 +1061,69 @@ + } + ReportLock.Unlock(); + } ++ ++bool FUnixPlatformStackWalk::GetFunctionDefinitionLocation(const FString& FunctionSymbolName, const FString& FunctionModuleName, FString& OutPathname, uint32& OutLineNumber, uint32& OutColumnNumber) ++{ ++ // Find the .so path for this module. ++ FString ModulePath; ++ TArray AllModules; ++ FModuleManager::Get().QueryModules(AllModules); ++ for (const FModuleStatus& Status : AllModules) ++ { ++ if (FPaths::GetBaseFilename(Status.FilePath) == FunctionModuleName) ++ { ++ ModulePath = Status.FilePath; ++ break; ++ } ++ } ++ if (ModulePath.IsEmpty()) ++ { ++ return false; ++ } ++ ++ // Debug symbols are in a separate .debug file alongside the .so. ++ FString DebugPath = FPaths::ChangeExtension(ModulePath, TEXT("debug")); ++ if (!FPaths::FileExists(DebugPath)) ++ { ++ return false; ++ } ++ ++ // Use lldb to look up the source file and line number. ++ // Run: lldb -b -o "image lookup -v -n ClassName::FuncName" ++ FString LldbParams = FString::Printf(TEXT("-b -o \"image lookup -v -n %s\" \"%s\""), *FunctionSymbolName, *DebugPath); ++ int32 ReturnCode = 0; ++ FString AllOutput; ++ FString Errors; ++ FPlatformProcess::ExecProcess(TEXT("/usr/bin/lldb"), *LldbParams, &ReturnCode, &AllOutput, &Errors); ++ if (ReturnCode != 0) ++ { ++ return false; ++ } ++ ++ // Parse the LineEntry from lldb verbose output. ++ // Format: "LineEntry: [0x...-0x...): /path/to/file.cpp:132" ++ TArray Lines; ++ AllOutput.ParseIntoArrayLines(Lines); ++ for (const FString& Line : Lines) ++ { ++ FString Trimmed = Line.TrimStartAndEnd(); ++ if (!Trimmed.StartsWith(TEXT("LineEntry:"))) ++ continue; ++ ++ int32 ParenIndex = Trimmed.Find(TEXT("): ")); ++ if (ParenIndex == INDEX_NONE) ++ continue; ++ FString FileAndLine = Trimmed.Mid(ParenIndex + 3); ++ ++ int32 ColonIndex; ++ if (!FileAndLine.FindLastChar(TCHAR(':'), ColonIndex)) ++ continue; ++ ++ OutPathname = FileAndLine.Left(ColonIndex); ++ OutLineNumber = FCString::Atoi(*FileAndLine.Mid(ColonIndex + 1)); ++ OutColumnNumber = 0; ++ return true; ++ } ++ ++ return false; ++} +diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h +--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h 2025-03-11 10:33:14.000000000 -0400 ++++ /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h 2026-03-02 15:36:22.690627000 -0500 +@@ -24,6 +24,8 @@ + static CORE_API void ThreadStackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, int32 IgnoreCount, uint32 ThreadId); + static CORE_API int32 GetProcessModuleCount(); + static CORE_API int32 GetProcessModuleSignatures(FStackWalkModuleInfo *ModuleSignatures, const int32 ModuleSignaturesSize); ++ ++ static CORE_API bool GetFunctionDefinitionLocation(const FString& FunctionSymbolName, const FString& FunctionModuleName, FString& OutPathname, uint32& OutLineNumber, uint32& OutColumnNumber); + }; + + typedef FUnixPlatformStackWalk FPlatformStackWalk; diff --git a/Source/Integration/TriggeredTask.cpp b/Source/Integration/TriggeredTask.cpp deleted file mode 100644 index b1d371e1..00000000 --- a/Source/Integration/TriggeredTask.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "TriggeredTask.h" - -FTriggeredTask::FTriggeredTask() { - Client = nullptr; - Thread = nullptr; - ThreadStopRequested = false; - CallEvent = nullptr; - ReturnEvent = nullptr; -} - -uint32 FTriggeredTask::Run() { - while (true) - { - CallEvent->Wait(); - if (ThreadStopRequested) { - break; - } - // The payload. - Client->Run(); - ReturnEvent->Trigger(); - } - return 0; -} - -void FTriggeredTask::Startup(FRunnable *client) { - FScopeLock lock(&Mutex); - if (Thread == nullptr) { - Client = client; - CallEvent = FPlatformProcess::GetSynchEventFromPool(false); - ReturnEvent = FPlatformProcess::GetSynchEventFromPool(true); - ReturnEvent->Trigger(); - Thread = FRunnableThread::Create(this, TEXT("Worker Thread")); - } -} - -void FTriggeredTask::Shutdown() { - FScopeLock lock(&Mutex); - if (Thread != nullptr) { - ReturnEvent->Wait(); - ThreadStopRequested = true; - CallEvent->Trigger(); - delete Thread; // This waits for the thread to complete. - Thread = nullptr; - FPlatformProcess::ReturnSynchEventToPool(CallEvent); - FPlatformProcess::ReturnSynchEventToPool(ReturnEvent); - CallEvent = nullptr; - ReturnEvent = nullptr; - } - ThreadStopRequested = false; -} - -void FTriggeredTask::Trigger() { - FScopeLock lock(&Mutex); - if (Thread != nullptr) { - ReturnEvent->Reset(); - CallEvent->Trigger(); - } -} - -void FTriggeredTask::Wait() { - FScopeLock lock(&Mutex); - if (Thread != nullptr) { - ReturnEvent->Wait(); - } -} - -bool FTriggeredTask::IsRunning() { - FScopeLock lock(&Mutex); - return (Thread != nullptr); -} diff --git a/Source/Integration/TriggeredTask.h b/Source/Integration/TriggeredTask.h deleted file mode 100644 index 659149fd..00000000 --- a/Source/Integration/TriggeredTask.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "HAL/Runnable.h" - -/////////////////////////////////////////////// -// -// TRIGGERED TASKS -// -// This is the situation where this class is -// useful: -// -// * You have a function to run in the background. -// * It should start the instant a foreground thread says "NOW". -// * It should be run exactly once for each "NOW". -// * The foreground thread eventually waits for the background thread to finish. -// -// To use this class, construct an FTriggeredTask, -// and provide a runnable object. The runnable -// object's "Run" method will get executed in -// each time you call 'Trigger' on the FTriggeredTask. -// -/////////////////////////////////////////////// - -class FTriggeredTask : public FRunnable -{ -private: - // Mutex used by control routines. - // - FCriticalSection Mutex; - - // The worker thread. - // - FRunnableThread* Thread; - - // Used to tell the worker thread to stop. - // - bool ThreadStopRequested; - - // This event is used to wake up the thread. - // - // This is an auto-reset event, meaning that each time we - // call trigger, the background thread is gated exactly once. - // - // Normally, this means we want the worker to run the task - // once. But if ThreadStopRequested is true, it means we - // want the thread to exit. - // - FEvent* CallEvent; - - // This event is used when the thread is done with its work. - // - // This is not an auto-reset event. This event stays triggered - // whenever the background thread is not running. - // - FEvent* ReturnEvent; - - // The client whose task we're triggering. - // - FRunnable* Client; - -private: - ///////////////////////////////////////////// - // - // This section contains routines that are - // executed by the thread itself. - // - ///////////////////////////////////////////// - - // Method of FRunnable, called by the Luprex thread. - // - virtual uint32 Run() override; - -public: - ///////////////////////////////////////////// - // - // This section contains thread control routines. - // These are not invoked by the thread, but by the - // outside entity that started the thread. - // - ///////////////////////////////////////////// - - // Constructor. - // - // The client must outlive the FTriggeredTask. - // - FTriggeredTask(); - - // Startup. - // - // Bring the system online, put it in ready mode. - // - void Startup(FRunnable* client); - - // Shutdown. - // - // Bring the system down, deallocate all threads. - // - void Shutdown(); - - // Trigger - // - // Run the client's 'RUN' method once, in the - // background. - // - void Trigger(); - - // Wait - // - // Wait for the background task to finish its work. - // If the background thread isn't running, then this - // returns immediately. - // - void Wait(); - - // IsRunning - // - bool IsRunning(); -}; -