Code to click a function in the blueprint editor and pop up the C++

This commit is contained in:
2026-03-02 16:26:19 -05:00
parent 2765ea3f07
commit e491c9db4e
3 changed files with 135 additions and 197 deletions

View File

@@ -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<FString> 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<ISourceCodeAccessModule>("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 <link.h>
@@ -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<FModuleStatus> 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" <debug_file>
+ 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<FString> 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;

View File

@@ -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);
}

View File

@@ -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();
};