如何追踪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>时:
- 从
/tmp/loaded_libs.log中找到最大的基地址base_addr,要求base_addr ≤ 0x12345678 - 计算地址偏移:
offset = 0x12345678 - base_addr - 使用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
相关产品推荐
相关产品推荐

