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

304 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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"
注册成功后再运行代码即可