From 0870416ddaecd496cd40ccbca6818f300db473d1 Mon Sep 17 00:00:00 2001 From: BlueRose <378100977@qq.com> Date: Mon, 4 May 2026 23:28:37 +0800 Subject: [PATCH] vault backup: 2026-05-04 23:28:37 --- .../various-complements/histories.json | 2 +- .../Gameplay/Debug/UE5编辑器开启ASAN.md | 304 ++++++++++++++++++ 07-Other/AI/AI Agent/Renderdoc/Renderdoc.md | 15 + .../UnrealEngine/Hardness Game Development.md | 50 ++- 4 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 03-UnrealEngine/Gameplay/Debug/UE5编辑器开启ASAN.md create mode 100644 07-Other/AI/AI Agent/Renderdoc/Renderdoc.md diff --git a/.obsidian/plugins/various-complements/histories.json b/.obsidian/plugins/various-complements/histories.json index 30cd633..2e072af 100644 --- a/.obsidian/plugins/various-complements/histories.json +++ b/.obsidian/plugins/various-complements/histories.json @@ -1 +1 @@ -{"要解决这个问题,得对记忆进行合理规划与维护:":{"要解决这个问题,得对记忆进行合理规划与维护:":{"currentFile":{"count":1,"lastUpdated":1774938677236}}},"Openclaw-workspace":{"Openclaw-workspace":{"currentFile":{"count":1,"lastUpdated":1774938879095}}},"workflow":{"workflow":{"currentFile":{"count":1,"lastUpdated":1775020352083}}},"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"currentFile":{"count":1,"lastUpdated":1776242568663}}},"推荐加在仓库内已有的标准元数据文件中,例如:":{"推荐加在仓库内已有的标准元数据文件中,例如:":{"currentFile":{"count":1,"lastUpdated":1776242909075}}},"推荐加在仓库内已有的标准元数据文件中,":{"推荐加在仓库内已有的标准元数据文件中,":{"currentFile":{"count":1,"lastUpdated":1776242914868}}},"Screenshots":{"Screenshots":{"currentFile":{"count":1,"lastUpdated":1776309732196}}},"WorkSpace":{"WorkSpace":{"currentFile":{"count":1,"lastUpdated":1776315471938}}},"VPS账号 以及PS4 DNS":{"VPS账号 以及PS4 DNS":{"internalLink":{"count":1,"lastUpdated":1776322449307}}},"G37、G37":{"G37、G37":{"currentFile":{"count":1,"lastUpdated":1776416826672}}},"GIt&SVN简报":{"GIt&SVN简报":{"internalLink":{"count":1,"lastUpdated":1776661549625}}},"redoc":{"redoc":{"currentFile":{"count":1,"lastUpdated":1776854054282}}},"的访问权限":{"的访问权限":{"currentFile":{"count":1,"lastUpdated":1776939597742}}}} \ No newline at end of file +{"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"currentFile":{"count":1,"lastUpdated":1776242568663}}},"推荐加在仓库内已有的标准元数据文件中,例如:":{"推荐加在仓库内已有的标准元数据文件中,例如:":{"currentFile":{"count":1,"lastUpdated":1776242909075}}},"推荐加在仓库内已有的标准元数据文件中,":{"推荐加在仓库内已有的标准元数据文件中,":{"currentFile":{"count":1,"lastUpdated":1776242914868}}},"Screenshots":{"Screenshots":{"currentFile":{"count":1,"lastUpdated":1776309732196}}},"WorkSpace":{"WorkSpace":{"currentFile":{"count":1,"lastUpdated":1776315471938}}},"VPS账号 以及PS4 DNS":{"VPS账号 以及PS4 DNS":{"internalLink":{"count":1,"lastUpdated":1776322449307}}},"G37、G37":{"G37、G37":{"currentFile":{"count":1,"lastUpdated":1776416826672}}},"GIt&SVN简报":{"GIt&SVN简报":{"internalLink":{"count":1,"lastUpdated":1776661549625}}},"redoc":{"redoc":{"currentFile":{"count":1,"lastUpdated":1776854054282}}},"的访问权限":{"的访问权限":{"currentFile":{"count":1,"lastUpdated":1776939597742}}},"SDK\\bin\\amd64\\msdia140.dll":{"SDK\\bin\\amd64\\msdia140.dll":{"currentFile":{"count":1,"lastUpdated":1777906125383}}}} \ No newline at end of file diff --git a/03-UnrealEngine/Gameplay/Debug/UE5编辑器开启ASAN.md b/03-UnrealEngine/Gameplay/Debug/UE5编辑器开启ASAN.md new file mode 100644 index 0000000..9183f5c --- /dev/null +++ b/03-UnrealEngine/Gameplay/Debug/UE5编辑器开启ASAN.md @@ -0,0 +1,304 @@ +--- +title: UE5编辑器开启ASAN +date: 2026-05-04 22:45:02 +excerpt: +tags: +rating: ⭐ +--- +UE5的编辑器直接开启ASAN会因为有全局变量初始化依赖问题导致误报阻塞。 + +虽然网上以及有人探究过底层原因以及解决方式:[https://zhuanlan.zhihu.com/p/14576791261](https://zhuanlan.zhihu.com/p/14576791261) +但是此解法需要修改大量代码而且容易因为UE的升级导致有新代码需要修改,非常不灵活。 +后面公司的UE交流群里有同事列出更好的解决方式,就是在引擎启动后主动调用DLL的ASAN初始化函数。 +以下是此修改方式是具体落地修改: +## 1. 修改Core.Build.cs + +Core.Build.cs添加DIA SDK依赖 +```cpp +// DIA SDK +if (Target.Platform == UnrealTargetPlatform.Win64) +{ + string DiaSdkDir = Target.WindowsPlatform.DiaSdkDir; + if (DiaSdkDir == null) + { + throw new System.Exception("Unable to find DIA SDK directory"); + } + + string ToolChainDir = Target.WindowsPlatform.ToolChainDir; + string ToolChainArch = (Target.Architecture == UnrealArch.Arm64) ? "arm64" : "x64"; + PrivateIncludePaths.Add(Path.Combine(ToolChainDir, "atlmfc", "include")); + PublicAdditionalLibraries.Add(Path.Combine(ToolChainDir, "atlmfc", "lib", ToolChainArch, "atls.lib")); + + string DiaArch = (Target.Architecture == UnrealArch.Arm64) ? "arm64" : "amd64"; + PrivateIncludePaths.Add(Path.Combine(DiaSdkDir, "include")); + PublicAdditionalLibraries.Add(Path.Combine(DiaSdkDir, "lib", DiaArch, "diaguids.lib")); + RuntimeDependencies.Add("$(TargetOutputDir)/msdia140.dll", Path.Combine(DiaSdkDir, "bin", DiaArch, "msdia140.dll")); +} +``` + +## 2.新建下面文件到此路径里编译 [Engine/Source/Runtime/Core/Private/Windows/](https://km.netease.com/v4/detail/blog/254722),命名随意,如AsanInitializer.cpp +```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================================================ +// Debug Print Utility +// ============================================================================ + +template +static void DebugPrint(const TCHAR(&format)[size], TArgs&&... args) +{ + TCHAR msg[1024] = {}; + int written = _stprintf_s(msg, format, std::forward(args)...); + if (written <= 0) + return; + OutputDebugString(msg); +} + +namespace fs = std::filesystem; + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +// ============================================================================ +// Symbol Resolution using DIA SDK +// ============================================================================ + +class SimpleDiaCallback : public IDiaLoadCallback2 +{ + LONG m_refCount = 0; + +public: + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID rid, void** ppUnk) override + { + if (!ppUnk) return E_INVALIDARG; + + if (rid == __uuidof(IDiaLoadCallback2) || + rid == __uuidof(IDiaLoadCallback) || + rid == __uuidof(IUnknown)) + { + *ppUnk = this; + AddRef(); + return S_OK; + } + *ppUnk = nullptr; + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() override { return ++m_refCount; } + ULONG STDMETHODCALLTYPE Release() override + { + auto count = --m_refCount; + if (count == 0) delete this; + return count; + } + + // 实现必要的回调方法 + HRESULT STDMETHODCALLTYPE NotifyDebugDir(BOOL, DWORD, BYTE*) override { return S_OK; } + HRESULT STDMETHODCALLTYPE NotifyOpenDBG(LPCOLESTR, HRESULT) override { return S_OK; } + HRESULT STDMETHODCALLTYPE NotifyOpenPDB(LPCOLESTR, HRESULT) override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictRegistryAccess() override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictSymbolServerAccess() override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictOriginalPathAccess() override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictReferencePathAccess() override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictDBGAccess() override { return S_OK; } + HRESULT STDMETHODCALLTYPE RestrictSystemRootAccess() override { return S_OK; } +}; + +static DWORD GetSymbolOffset(HMODULE hModule, const wchar_t* name) +{ + wchar_t moduleName[MAX_PATH] = {}; + GetModuleFileNameW(hModule, moduleName, MAX_PATH); + + HRESULT hr = CoInitialize(nullptr); + bool needUninitialize = SUCCEEDED(hr); + + IDiaDataSource* diaDataSource = nullptr; + hr = CoCreateInstance(__uuidof(DiaSource), nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IDiaDataSource), (void**)&diaDataSource); + if (FAILED(hr)) + { + if (needUninitialize) CoUninitialize(); + return 0; + } + + SimpleDiaCallback* callback = new SimpleDiaCallback(); + callback->AddRef(); + + hr = diaDataSource->loadDataForExe(moduleName, nullptr, callback); + if (FAILED(hr)) + { + callback->Release(); + diaDataSource->Release(); + if (needUninitialize) CoUninitialize(); + return 0; + } + + IDiaSession* session = nullptr; + hr = diaDataSource->openSession(&session); + if (FAILED(hr)) + { + callback->Release(); + diaDataSource->Release(); + if (needUninitialize) CoUninitialize(); + return 0; + } + + IDiaSymbol* globalSymbol = nullptr; + hr = session->get_globalScope(&globalSymbol); + + DWORD result = 0; + if (SUCCEEDED(hr)) + { + IDiaEnumSymbols* enumSymbols = nullptr; + if (SUCCEEDED(globalSymbol->findChildren(SymTagFunction, name, nsRegularExpression, &enumSymbols))) + { + IDiaSymbol* symbol = nullptr; + ULONG count = 0; + while (SUCCEEDED(enumSymbols->Next(1, &symbol, &count)) && count == 1) + { + wchar_t* symbolName = nullptr; + if (SUCCEEDED(symbol->get_name(&symbolName)) && wcscmp(symbolName, name) == 0) + { + symbol->get_relativeVirtualAddress(&result); + } + symbol->Release(); + } + enumSymbols->Release(); + } + globalSymbol->Release(); + } + + session->Release(); + callback->Release(); + diaDataSource->Release(); + if (needUninitialize) CoUninitialize(); + + return result; +} + +static FARPROC MyGetSymbolAddress(HMODULE hModule, const wchar_t* name) +{ + DWORD offset = GetSymbolOffset(hModule, name); + if (offset == 0) + { + return nullptr; + } + return (FARPROC)((char*)hModule + offset); +} + +// ============================================================================ +// ASAN Pre-Initialization +// ============================================================================ + +static int ExecInitializeClonedVariables(HMODULE hModule) +{ + using InitializeClonedVariablesFunc = int(); + auto InitializeClonedVariables = (InitializeClonedVariablesFunc*)MyGetSymbolAddress(hModule, L"__asan_initialize_cloned_variables"); + if (!InitializeClonedVariables) + { + return -1; + } + + TCHAR moduleName[MAX_PATH] = {}; + if (GetModuleFileName(hModule, moduleName, MAX_PATH) > 0) + { + DebugPrint(_T("AsanPreInit: Call InitializeClonedVariables on %s\n"), moduleName); + } + + return InitializeClonedVariables(); +} + +static std::vector GetModules(HANDLE hProcess) +{ + size_t count = 2048; + std::vector modHandles; + DWORD cbNeeded; + size_t loadedCount = 0; + do + { + modHandles.resize(count); + size_t size = modHandles.size() * sizeof(HMODULE); + assert(size < MAXDWORD); + if (!EnumProcessModules(hProcess, modHandles.data(), (DWORD)size, &cbNeeded)) + { + DebugPrint(_T("EnumProcessModules failed: %d\n"), GetLastError()); + } + loadedCount = cbNeeded / sizeof(HMODULE); + if (loadedCount < modHandles.size()) + { + modHandles.resize(loadedCount); + break; + } + count *= 2; + } while (loadedCount <= 10000); + return modHandles; +} + +static std::unordered_set GetMainBinaries() +{ + TCHAR executable[1024] = {}; + GetModuleFileName(nullptr, executable, sizeof(executable)); + fs::path execPath(executable); + std::unordered_set binaries; + for (auto& entry : fs::directory_iterator(execPath.parent_path())) + { + auto filename = entry.path().filename(); + if (filename.extension() == ".dll") + { + binaries.emplace(filename.string()); + } + } + return binaries; +} + +__declspec(dllexport) bool AsanPreInit_Setup() +{ + auto modules = GetModules(GetCurrentProcess()); + auto binaries = GetMainBinaries(); + HANDLE process = GetCurrentProcess(); + for (auto& mod : modules) + { + if (!mod) + continue; + if (mod == (HMODULE)&__ImageBase) + continue; + char baseName[MAX_PATH] = {}; + GetModuleBaseNameA(process, mod, baseName, sizeof(baseName)); + // 简单过滤下除当前执行文件目录外的动态库 + if (!binaries.count(baseName)) + continue; + ExecInitializeClonedVariables(mod); + } + return true; +} + +class AsanPreInit +{ +public: + AsanPreInit() { AsanPreInit_Setup(); } +}; +static AsanPreInit g_Init; +``` + +## 3.正常运行 +首次运行可能会有DIA报错,按照下图方式修复 + +根本原因 +系统注册表中找不到 DiaSource 的 CLSID,即msdia140.d11(或对应版本)没有被注册到 Windows COM 注册表中。 + +解决方案一:注册msdia*.dll (需要管理员权限) +以管理员身份运行 CMD,执行以下命令(根据你的 VS 版本选择): +1. VS2022 + 1. regsvr32 "C:\Program Files\Microsoft Visual Studio\2022\Professional\DIA SDK\bin\amd64\msdia140.dll" +2. VS2019 + 1. regsvr32 "C:\Program Files (x86) \Microsoft Visual Studio\2019\Professional\DIA SDK\bin\amd64\msdia140.dll" +3. VS2017 + 1. regsvr32 "C:\Program Files (x86) \Microsoft Visual Studio\2017\Professional\DIA SDK\bin\amd64\msdia140.dll" +注册成功后再运行代码即可 \ No newline at end of file diff --git a/07-Other/AI/AI Agent/Renderdoc/Renderdoc.md b/07-Other/AI/AI Agent/Renderdoc/Renderdoc.md new file mode 100644 index 0000000..2b499d6 --- /dev/null +++ b/07-Other/AI/AI Agent/Renderdoc/Renderdoc.md @@ -0,0 +1,15 @@ +--- +title: "AI一键生成完整截帧分析 RenderDocWorkflow-网易KM" +source: "https://km.netease.com/v4/detail/blog/264805" +author: +published: +created: 2026-05-04 +description: +tags: + - "clippings" +--- +- 文章 + - AI一键生成完整截帧分析 RenderDocWorkflow https://km.netease.com/v4/detail/blog/264805 +- 相关仓库 + - RenderDocMCP:http://git-internal.nie.netease.com/mcpanalyzer/RenderDocMCP + - RenderDocWorkflow:http://git-internal.nie.netease.com/mcpanalyzer/RenderDocWorkflow \ No newline at end of file diff --git a/07-Other/AI/AI Agent/UnrealEngine/Hardness Game Development.md b/07-Other/AI/AI Agent/UnrealEngine/Hardness Game Development.md index 65557cd..948da18 100644 --- a/07-Other/AI/AI Agent/UnrealEngine/Hardness Game Development.md +++ b/07-Other/AI/AI Agent/UnrealEngine/Hardness Game Development.md @@ -10,16 +10,56 @@ - UnrealMcp - Puerts - Puerts Editor - - uecli + - **uecli** - Readme的材质都是agent调用uecli做的 帮我生产材质 排版材质节点 帮我场景截图,帮我材质蓝图截图 帮我写readme 帮我提交仓库。 https://github.com/wlxklyh/UECLI - - 通过蓝图转c++功能,让AI读懂蓝图 - - IDE Debug MCP + - **通过蓝图转c++功能,让AI读懂蓝图** + +- Debug + - 雷火MCP**IDE Debug MCP** - https://km.netease.com/v4/section/aigc/detail/blog/263683 - cpp-debugger-cli + - 互娱 + - **从 ASAN 到 AI 的“接力排查”** https://km.netease.com/v4/detail/blog/256465 + - **1. 借助 ASAN 把“指针变野”的时间点钉住** + - 第一步是启用 ASAN(AddressSanitizer)来辅助定位: + - 在 UE5 编辑器环境启用 ASAN,本身需要做一些额外工作:[https://km.netease.com/v4/detail/blog/254722](https://km.netease.com/v4/detail/blog/254722) + - 这些环境搭建细节本文不展开,只强调结论:ASAN 成功启用,可以看到完整的分配 / 释放栈。 + - https://km.netease.com/v4/detail/blog/260127 + - [RiderDebugMcp](http://git-internal.nie.netease.com/mcpanalyzer/RiderDebugMcp) + - [VSDebugMCP](http://git-internal.nie.netease.com/mcpanalyzer/VSDebugMCP) + - [UnrealMCP](http://git-internal.nie.netease.com/mcpanalyzer/UnrealMCP) + - [ue-reference-diagnostic-mcp](http://git-internal.nie.netease.com/mcpanalyzer/ue-reference-diagnostic-mcp) + - LLDB MCP https://lldb.llvm.org/use/mcp.html + - 其他仓库 + - https://github.com/akiselev/debugger-cli - Docker - Gitea:工单以及版本管理。 - - OpenClaw:子节点部署,通过父节点进行控制。 + - ~~OpenClaw:子节点部署,通过父节点进行控制。~~ - SMB服务。 - Obsidian Cli:文档管理。 -## UE测试技术 \ No newline at end of file +## UE测试技术 +- 可视化日志 +- 自动测试框架 + +## LLDB +#### 1. 安装与环境配置 +首先,你需要确保本地安装了 LLDB,并为 Claude Code 配置 MCP 服务。 +- **方案 A:使用官方/社区 LLDB-MCP (推荐)** 在终端中运行以下命令添加 MCP 调试服务: +``` +# 使用 uvx 或 npx 自动安装并运行 lldb-mcp-server +claude mcp add lldb-debugger -- command "uvx" --args "lldb-mcp-server" +``` +可能是https://github.com/stass/lldb-mcp + +- **方案 B:开启 LLDB 2026 原生 MCP 支持** 现代版本的 LLDB(v19+)已内置 MCP 协议支持。你可以在项目目录下启动它: +``` +lldb --protocol-server start --port 59999 +``` + +#### 2. 安装调试“技能” (Skill) +为了让 Claude 具备系统化的调试思维(而不只是乱试命令),你需要从 **Skills.sh** 或 GitHub 安装技能包: +``` +# 安装通用的调试技能包 +claude skill install AlmogBaku/debug-skill +``` \ No newline at end of file