使用Asan定位内存泄漏问题
更新时间: 2026/04/17
在Gitcode上查看源码

Asan是一种内存错误检测工具,可以帮助我们定位内存泄漏问题。本文以devmon组件为例,介绍如何使用Asan工具定位内存泄漏问题

Asan简介

关于Asan工具,可以查看ASAN使用教程进行初步了解

使用Asan定位内存泄漏问题流程

以devmon为例(devmon示例),使用Asan的流程为:加上Asan相关编译选项、打包Asan相关的库、修改systemd配置,通过log_path指定Asan日志输出位置,其他组件也可以参考做类似修改集成Asan。下面分步骤进行介绍

加上Asan相关编译选项

可以参考devmon示例中conanfile.py添加的代码,进行添加

python
# 如果启用了 ASan,添加编译和链接选项
# if self.options.get_safe("asan", False):
print("Enable ASan flags for Meson build")
# 将 ASan 标志分开添加,避免 Meson 将它们当作单个参数
asan_compile_flags = [
    "-fsanitize=address",
    "-fsanitize-recover=address,all",
    "-fno-omit-frame-pointer",
    "-fno-stack-protector",
    "-O1"
]
for flag in asan_compile_flags:
    tc.extra_cflags.append(flag)
    tc.extra_cxxflags.append(flag)
tc.extra_ldflags.append("-fsanitize=address")
    # 设置 ASan 相关环境变量
    if self.settings.arch == "armv8":
        # 交叉编译环境,设置 ASan 库路径
        toolchain_libdir = "/opt/hcc_arm64le/aarch64-target-linux-gnu/lib64"
        if os.path.exists(toolchain_libdir):
            tc.extra_ldflags.append(f"-Wl,-rpath-link,{toolchain_libdir}")

打包Asan相关的库

可以参考devmon示例中conanfile.py的_package_asan_libs方法的修改,进行添加

def _package_asan_libs(self):
    """打包 AddressSanitizer 运行时库"""
    import subprocess

    print("Packaging AddressSanitizer runtime libraries...")

    # 确定目标库目录
    if self.settings.arch == "armv8" or self.settings.arch == "x86_64":
        target_libdir = os.path.join(self.package_folder, "usr/lib64")
    else:
        target_libdir = os.path.join(self.package_folder, "usr/lib")

    os.makedirs(target_libdir, exist_ok=True)

    # 根据架构查找 ASan 库
    asan_lib_paths = []

    if self.settings.arch == "armv8":
        # 交叉编译环境
        toolchain_libdir = "/opt/hcc_arm64le/aarch64-target-linux-gnu/lib64"
        if os.path.exists(toolchain_libdir):
            # 查找 libasan.so*
            for file in os.listdir(toolchain_libdir):
                if file.startswith("libasan.so"):
                    asan_lib_paths.append(os.path.join(toolchain_libdir, file))
    elif self.settings.arch == "x86_64":
        # 本地编译环境
        # 尝试通过 gcc 查找 libasan 路径
        try:
            result = subprocess.run(
                ["gcc", "-print-file-name=libasan.so"],
                capture_output=True,
                text=True,
                timeout=10
            )
            if result.returncode == 0 and result.stdout.strip():
                libasan_path = result.stdout.strip()
                if os.path.exists(libasan_path):
                    # 获取目录并查找所有相关文件
                    libasan_dir = os.path.dirname(libasan_path)
                    for file in os.listdir(libasan_dir):
                        if file.startswith("libasan.so"):
                            asan_lib_paths.append(os.path.join(libasan_dir, file))
        except Exception as e:
            print(f"Warning: Failed to find libasan: {e}")

    # 复制 ASan 库到包中
    if asan_lib_paths:
        for lib_path in asan_lib_paths:
            if os.path.isfile(lib_path):
                print(f"  Copying {lib_path} to {target_libdir}")
                copy(self, os.path.basename(lib_path),
                     src=os.path.dirname(lib_path),
                     dst=target_libdir)
            elif os.path.islink(lib_path):
                # 处理符号链接
                link_target = os.readlink(lib_path)
                link_name = os.path.basename(lib_path)
                target_link = os.path.join(target_libdir, link_name)
                if not os.path.exists(target_link):
                    os.symlink(link_target, target_link)
                    print(f"  Created symlink {target_link} -> {link_target}")
        print(f"Successfully packaged {len(asan_lib_paths)} ASan library files")
    else:
        print("Warning: No ASan libraries found to package")

修改systemd配置,通过log_path指定Asan日志输出位置

可以参考devmon示例中devmon.service中进行的修改,进行添加

shell
Environment="ASAN_OPTIONS=abort_on_error=1:detect_leaks=1:verbosity=1:log_path=/data/asan.log"
Environment="LD_PRELOAD=/usr/lib64/libasan.so.4"