You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何追踪Leak Sanitizer栈追踪中的未知模块?

解决Leak Sanitizer内存泄漏栈追踪中的识别问题

1. 强制ASAN启用完整符号解析

设置以下环境变量,禁用快速栈展开并指定符号化工具路径,确保ASAN能获取足够的模块与符号信息:

export ASAN_OPTIONS="symbolize=1:detect_leaks=1:fast_unwind_on_malloc=0"
export ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer"
  • fast_unwind_on_malloc=0:禁用快速栈展开逻辑,改用慢速展开保留更完整的调用链与模块信息
  • ASAN_SYMBOLIZER_PATH:指定Ubuntu 24.04系统自带的llvm-symbolizer路径,避免符号化工具缺失导致解析失败

2. 补充依赖库的调试符号表

系统自带OpenJDK 8

安装对应调试符号包,为JVM核心库及系统依赖补充完整符号:

apt update && apt install -y openjdk-8-dbg libc6-dbg libpthread-dbg libdl-dbg

自定义编译OpenJDK 8

编译时添加调试参数,保留全量符号信息:

./configure --with-debug-level=slowdebug --with-jvm-variants=server
make

slowdebug级别会保留最完整的调试符号,确保ASAN能解析到所有模块的函数信息。

3. 扩展dlopen拦截器记录库地址映射

在现有dlopen拦截逻辑中添加库基地址与路径的映射记录,方便后续匹配的内存地址:

拦截器代码示例

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef void* (*dlopen_t)(const char*, int);
static dlopen_t original_dlopen;

// 存储库基地址与路径的链表结构
typedef struct LibMap {
    void* base_addr;
    char* lib_path;
    struct LibMap* next;
} LibMap;
static LibMap* lib_map = NULL;

void* dlopen(const char* filename, int flags) {
    // 强制注入RTLD_NODELETE标记
    flags |= RTLD_NODELETE;
    void* handle = original_dlopen(filename, flags);

    if (handle && filename) {
        Dl_info dl_info;
        if (dladdr(handle, &dl_info)) {
            // 分配节点存储映射关系
            LibMap* entry = malloc(sizeof(LibMap));
            entry->base_addr = dl_info.dli_fbase;
            entry->lib_path = strdup(filename);
            entry->next = lib_map;
            lib_map = entry;

            // 写入日志文件,方便后续查询
            FILE* log = fopen("/tmp/loaded_libs.log", "a");
            if (log) {
                fprintf(log, "%p -> %s\n", entry->base_addr, entry->lib_path);
                fclose(log);
            }
        }
    }
    return handle;
}

// 初始化时获取原始dlopen函数
__attribute__((constructor)) void init_interceptor() {
    original_dlopen = (dlopen_t)dlsym(RTLD_NEXT, "dlopen");
}

编译与使用

gcc -shared -fPIC -ldl -o dlopen_track.so dlopen_track.c
# 预加载拦截器与ASAN库
export LD_PRELOAD="./dlopen_track.so:libasan.so.6"

4. 手动解析的内存地址

当LeakSanitizer输出类似#0 0x12345678 in <unknown module>时:

  1. 从/tmp/loaded_libs.log中找到最大的基地址base_addr,要求base_addr ≤ 0x12345678
  2. 计算地址偏移:offset = 0x12345678 - base_addr
  3. 使用llvm-symbolizer解析符号:
llvm-symbolizer --obj=/path/to/target/library --address=$offset

若符号表存在,会返回该偏移对应的函数名与代码位置。

5. 禁用JVM压缩指针

OpenJDK 8默认开启的Compressed Oops会压缩对象指针,可能导致ASAN对内存地址的解析异常,添加JVM参数关闭:

java -XX:-UseCompressedOops -jar your_application.jar

内容的提问来源于stack exchange,提问作者Xellos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 22:03:09