rust 程序内存 profling 之旅
首先如果你使用的 macos,那么要做的第一件事情是找个 linux 的机器,要么服务器,要么虚拟机。不要用 macos 进行下面的操作,因为各种有问题。。。
比如我尝试使用 macos 读取 jemalloc 生成的数据结果
jeprof --show_bytes /Users/zzou/Desktop/geektime/conviva_gateway/target/release/conviva_gateway jeprof.out.54755.0.m0.heap jeprof.out.54755.1.m1.heap jeprof.out.54755.2.m2.heap jeprof.out.54755.3.m3.heap jeprof.out.54755.4.m4.heap (jeprof) top Total: 20971520 B 20971520 100.0% 100.0% 20971520 100.0% 000000010fefd8b9 0 0.0% 100.0% 20971520 100.0% 000000010fcf43bf 0 0.0% 100.0% 20971520 100.0% 000000010fcf56e4 0 0.0% 100.0% 20971520 100.0% 000000010fcf7ff2 0 0.0% 100.0% 20971520 100.0% 000000010fd23328 0 0.0% 100.0% 20971520 100.0% 000000010fd4057f 0 0.0% 100.0% 20971520 100.0% 000000010fd51d5c 0 0.0% 100.0% 20971520 100.0% 000000010fd77452 0 0.0% 100.0% 20971520 100.0% 000000010fd79118 0 0.0% 100.0% 20971520 100.0% 000000010fd826af
基本上全都是内存地址,无法获得正确的函数路径。尝试重新检查是否打开
[profile.release] debug = true
似乎配置都没有问题。所以最后我怀疑应该是 macos 的某些机制的影响。因为切换到 ubuntu 之后就一切正常了。
另外再 macos 上 jemalloc 生效的环境变量是 _RJEM_MALLOC_CONF, 例如:
export _RJEM_MALLOC_CONF="prof:true,prof_prefix:/tmp/jeprof.out"
但是官方文档上为
export MALLOC_CONF="prof:true,prof_prefix:jeprof.out"
实测官方文档上的 MALLOC_CONF 在 ubuntu 环境下生效,而 _RJEM_MALLOC_CONF 则无效。
网上有介绍好几种 profiling 工具,但是我最后只跑通了 jemalloc, 所以这里把使用 jemalloc 进行 rust profiling 的流程一步一步重新描述一下。
1. 切换内存分配器 => jemalloc。这一部分比较简单就是装价格相关包并且在 main.rs 中申明一下全局静态变量。
在 cargo.toml 添加新的包
[profile.release] debug = true strip = "debuginfo" jemallocator = { version = "0.5.0", features = ["debug","profiling"] } jemalloc-sys = { version = "0.5.0+5.3.0", features = ["debug","stats", "profiling", "unprefixed_malloc_on_supported_platforms"] } jemalloc-ctl = "0.5.0"
在 main.rs 中添加
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
2. 在 ubuntu 机器上安装 libjemalloc-dev
sudo apt update
sudo apt install libjemalloc-dev
3. 因为我本身是测试 actix-web 性能,自带 web 框架,所以我 new 了一个 handler 来放一下面代码,并通过调用 xxxx/mem 来开启性能 profile 收集并得到对应 heap 文件
.route("/mem", to(memory_heap)) pub async fn memory_heap() -> impl Responder { let thread = std::thread::Builder::new().name("spawn_dump".to_owned()); thread .spawn(|| loop { let r = dump(); println!("---------------dump ret: {} -------------------", r); sleep(Duration::from_secs(10)); }) .unwrap(); HttpResponse::Ok() .content_type(ContentType::plaintext()) .body("ok 👋!") } fn dump() -> c_int { unsafe { mallctl( "prof.dump\0".as_bytes() as *const _ as *const c_char, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), 0, ) } }
我来详细介绍一下这一部分是在做什么,我们要获得内存使用情况首先我们就得获得一段时间内内存使用的情况。这一部分代码的 dump() 部分,是在实现文档中所说的这一部分
上面的循环,是每隔多久生成一个新的 heap 文件,环境变量 MALLOC_CONF 也有提供多大 heap 文件切一个的选项例如
export MALLOC_CONF="prof:true,prof_prefix:/tmp/jeprof.out,lg_prof_interval:25"
代表 2 的 25次方大 byte 切一个文件,因为我们数据量较大,使用时间会比较好控制,避免文件数过多影响分析。
4. 开始生成文件,请求 web 接口 http://xxxx.com/mem 系统开始生成,一般我生成3 4 个文件就会停下来,感觉够用了。
5. 生成 pdf 格式结果文件,并下载到本地看。
jeprof -pdf ~/conviva-gateway/target/debug/conviva_gateway jeprof.out.131222.0.m0.heap jeprof.out.131222.1.m1.heap jeprof.out.131222.2.m2.heap jeprof.out.131222.3.m3.heap > out.pdf
使用
curl --upload-file ./out.pdf https://transfer.sh/out.pdf
获得地址,并下载到本地方便图形化方式打开。
6. 获得性能分析的图
看到这个分析图真是亲切。
没想到的是 golang pprof 能在 macos 上轻松获得的东西,rust 搞起来这么费劲。 linux 生态下的东西,尽量还是在 linux 下用工具搞吧。
学习了。
Reference:
https://blog.csdn.net/weixin_37902491/article/details/128103738 如何使用jeprof分析rust的内存
https://tidb.net/blog/f1875f94#%E4%BB%80%E4%B9%88%E6%98%AF%20Heap%20Profiling 内存泄漏的定位与排查:Heap Profiling 原理解析
https://github.com/jemalloc/jemalloc/wiki/Use-Case%3A-Heap-Profiling Use Case: Heap Profiling
https://github.com/tikv/jemallocator/issues/33 Use jeprof to see only the address, not the function name