rs_malloc_tracker 1.0.0

Wraps LibC allocation calls to expose Prometheus memory statistics.
Documentation
use crate::{
    consumer::{MEMORY_ZONES, test_wait_processed},
    mmap::{MMAP_ZONES, test_wait_mmap_processed},
};
use ::backtrace::resolve as backtrace_resolve;

use super::*;

#[test]
fn test_simple_malloc() {
    let zone = unsafe { malloc(1024) };
    assert!(!zone.is_null());

    let buf: &mut [u8; 1024] = unsafe { std::mem::transmute(zone.as_mut().unwrap()) };
    for b in buf.as_mut_slice() {
        *b = 0xAB;
    }
    assert_eq!(buf, &[0xABu8; 1024]);

    unsafe { free(zone) };
}

#[test]
fn test_simple_realloc() {
    let zone = unsafe { malloc(1) };
    assert!(!zone.is_null());

    let zone = unsafe { realloc(zone, 1024) };
    assert!(!zone.is_null());

    let buf: &mut [u8; 1024] = unsafe { std::mem::transmute(zone.as_mut().unwrap()) };
    for b in buf.as_mut_slice() {
        *b = 0xAB;
    }
    assert_eq!(buf, &[0xABu8; 1024]);

    unsafe { free(zone) };
}

#[test]
fn test_zones() {
    let data = unsafe { malloc(0xCAFE) };
    assert!(!data.is_null());
    test_wait_processed();

    let guard = MEMORY_ZONES.read().unwrap();
    let zone = guard.get(&(data as *mut _ as usize)).expect("Missing zone");
    assert_eq!(zone.kind, ZoneKind::Malloc);
    assert_eq!(zone.size, 0xCAFE);
    assert!(zone.ip.is_some());
    assert!(zone.ip.unwrap() != 0);

    let mut resolved_fn = None;
    backtrace_resolve(zone.ip.unwrap() as *mut c_void, |sym| {
        resolved_fn = Some(sym.name().unwrap().as_str().unwrap().to_string())
    });
    assert!(resolved_fn.is_some());
    // println!("{:?}", resolved_fn);
    // println!("{:?}", ::backtrace::Backtrace::new());
    assert!(resolved_fn.unwrap().contains("test_zones")); // real name is something like _ZN17rs_malloc_tracker5tests10test_zones17h6ab0b7115b7adb9fE
    drop(guard);

    let new_data = unsafe { realloc(data, 0xCAFE2) };
    assert!(!data.is_null());
    test_wait_processed();

    let guard = MEMORY_ZONES.read().unwrap();
    assert!(
        MEMORY_ZONES
            .read()
            .unwrap()
            .get(&(data as *mut _ as usize))
            .is_none(),
        "old zone should not exist"
    );
    let new_zone = guard
        .get(&(new_data as *mut _ as usize))
        .expect("Missing zone");
    assert_eq!(new_zone.kind, ZoneKind::Realloc);
    assert_eq!(new_zone.size, 0xCAFE2);
    drop(guard);

    unsafe { free(new_data) };
    test_wait_processed();

    assert!(
        MEMORY_ZONES
            .read()
            .unwrap()
            .get(&(new_data as *mut _ as usize))
            .is_none(),
        "zone should not exist"
    );
}

#[test]
fn test_mmap() {
    let data = unsafe {
        libc::mmap(
            std::ptr::null_mut(),
            0xCAFE,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
            -1,
            0,
        )
    };
    assert!(!data.is_null());
    test_wait_mmap_processed();

    let guard = MMAP_ZONES.read().unwrap();
    let (ival, zone) = guard
        .get_key_value_at_point(data as *mut _ as usize)
        .expect("Missing zone");
    assert_eq!(ival.end() + 1 - ival.start(), 0xCAFE);
    assert!(zone.ip.is_some());
    assert!(zone.ip.unwrap() != 0);

    let mut resolved_fn = None;
    backtrace_resolve(zone.ip.unwrap() as *mut c_void, |sym| {
        resolved_fn = Some(sym.name().unwrap().as_str().unwrap().to_string())
    });
    assert!(resolved_fn.is_some());
    // println!("{:?}", ::backtrace::Backtrace::new());
    assert!(resolved_fn.unwrap().contains("test_mmap")); // real name is something like _ZN17rs_malloc_tracker5tests10test_zones17h6ab0b7115b7adb9fE
    drop(guard);
}