3个版本
0.1.3 | 2020年11月10日 |
---|---|
0.1.2 | 2020年11月9日 |
0.1.0 | 2020年4月30日 |
#337 in 内存管理
15KB
216 代码行
想法
检测内存泄露很困难,使用全局分配器,我们可以跟踪 alloc
添加 dealloc
,如果我们记录 alloc
操作的调用栈,那么我们可以看到代码在哪里导致内存泄露。这个工具不会记录所有的分配,但在 dealloc
时会删除记录。
由 global allocator
+ heapless
+ backtrace
驱动,它只支持nightly工具链,这是由于 new_uninit
和 const_fn
功能造成的。
使用方法
将以下内容添加到您的 cargo.toml 中
leak-detect-allocator = {git = "https://github.com/lynnux/leak-detect-allocator.git"}
示例
#[global_allocator]
static LEAK_TRACER: LeakTracerDefault = LeakTracerDefault::new();
#[tokio::main]
async fn main() -> Result<(), BoxError> {
let lda_size = LEAK_TRACER.init();
tokio::spawn(async move {
loop {
tokio::signal::ctrl_c().await.ok();
let mut out = String::new();
let mut count = 0;
let mut count_size = 0;
LEAK_TRACER.now_leaks(|addr, frames| {
count += 1;
let mut it = frames.iter();
// first is the alloc size
let size = it.next().unwrap_or(&0);
if *size == lda_size {
return true;
}
count_size += size;
out += &format!("leak memory address: {:#x}, size: {}\r\n", addr, size);
for f in it {
// Resolve this instruction pointer to a symbol name
unsafe {
out += &format!(
"\t{}\r\n",
LEAK_TRACER.get_symbol_name(*f).unwrap_or("".to_owned())
);
}
}
true // continue until end
});
out += &format!("\r\ntotal address:{}, bytes:{}, internal use for leak-detect-allacator:{} bytes\r\n", count, count_size, lda_size*2);
std::fs::write("foo.txt", out.as_str().as_bytes()).ok();
}
});
}
当按下CTRL+C时,它将获取一个 "foo.txt",输出如下
leak memory address: 0x44e440, size: 10
backtrace::backtrace::trace_unsynchronized<closure-0>
leak_detect_allocator::{{impl}}::alloc<leak_detect_allocator::LeakData<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B1>, typenum::bit::B0>>,typenu
flashcore::_::__rg_alloc
alloc::alloc::alloc
alloc::alloc::{{impl}}::alloc
alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::allocate_in<u8,alloc::alloc::Global>
alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::with_capacity<u8>
alloc::vec::Vec<u8>::with_capacity<u8>
alloc::slice::hack::to_vec<u8>
leak memory address: 0x4508c0, size: 30
backtrace::backtrace::trace_unsynchronized<closure-0>
leak_detect_allocator::{{impl}}::alloc<leak_detect_allocator::LeakData<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B1>, typenum::bit::B0>>,typenu
flashcore::_::__rg_alloc
alloc::alloc::alloc
alloc::alloc::{{impl}}::alloc
alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::allocate_in<u8,alloc::alloc::Global>
alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::with_capacity<u8>
alloc::vec::Vec<u8>::with_capacity<u8>
alloc::slice::hack::to_vec<u8>
...
total address:38, bytes:6373, internal use for leak-detect-allacator:7077904 bytes
调试版本的堆栈调用似乎更好。
自定义
// change the vec size to bigger, so we can save more call stack
use crate::{
consts, ArrayLength, FnvIndexMap, HeaplessVec, LeakData, LeakDataTrait, LeakTracer,
};
let aa = LeakTracer::<LeakData<consts::U20>, _>::new();
println!("size: {}", aa.init());
// change the whole indexmap size, so we get more space to save
struct CustomData<VN: ArrayLength<usize>> {
inner: FnvIndexMap<usize, HeaplessVec<usize, VN>, consts::U16384>, // --> U16384 is customized
}
impl<VN: ArrayLength<usize>> LeakDataTrait<VN> for CustomData<VN> {
fn insert(&mut self, key: usize, value: HeaplessVec<usize, VN>) {
self.inner.insert(key, value).ok();
}
fn contains_key(&self, key: usize) -> bool {
self.inner.contains_key(&key)
}
fn remove(&mut self, key: usize) {
self.inner.remove(&key);
}
fn iter_all<F>(&self, mut f: F)
where
F: FnMut(usize, &HeaplessVec<usize, VN>) -> bool,
{
for (addr, symbol_address) in self.inner.iter() {
if !f(*addr, symbol_address) {
break;
}
}
}
}
let bb = LeakTracer::<CustomData<consts::U12>, _>::new();
println!("size: {}", bb.init());
已知问题
在Win7 64上,如果您遇到死锁,您可以尝试将较新版本的dbghelp.dll放置在您的bin目录中。
依赖关系
~3.5–4.5MB
~91K SLoC