#memory-leaks #call-stack #leak #nightly #allocator #stack-memory #toolchain

nightly leak-detect-allocator

nightly工具链的内存泄露检测器

3个版本

0.1.3 2020年11月10日
0.1.2 2020年11月9日
0.1.0 2020年4月30日

#337 in 内存管理

MIT许可证

15KB
216 代码行

想法

检测内存泄露很困难,使用全局分配器,我们可以跟踪 alloc 添加 dealloc,如果我们记录 alloc 操作的调用栈,那么我们可以看到代码在哪里导致内存泄露。这个工具不会记录所有的分配,但在 dealloc 时会删除记录。

global allocator + heapless + backtrace 驱动,它只支持nightly工具链,这是由于 new_uninitconst_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