Files
BlueRoseNote/03-UnrealEngine/Gameplay/Debug/UE5编辑器开启ASAN.md

304 lines
9.1 KiB
Markdown
Raw Normal View History

2026-05-04 23:28:37 +08:00
---
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 <Windows.h>
#include <tchar.h>
#include <psapi.h>
#include <dia2.h>
#include <debugapi.h>
#include <cassert>
#include <filesystem>
#include <unordered_set>
#include <vector>
// ============================================================================
// Debug Print Utility
// ============================================================================
template<typename ...TArgs, size_t size>
static void DebugPrint(const TCHAR(&format)[size], TArgs&&... args)
{
TCHAR msg[1024] = {};
int written = _stprintf_s(msg, format, std::forward<TArgs>(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<HMODULE> GetModules(HANDLE hProcess)
{
size_t count = 2048;
std::vector<HMODULE> 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<std::string> GetMainBinaries()
{
TCHAR executable[1024] = {};
GetModuleFileName(nullptr, executable, sizeof(executable));
fs::path execPath(executable);
std::unordered_set<std::string> 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"
注册成功后再运行代码即可