Skip to content

Commit

Permalink
✨ Add MmapStats
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurlm committed Sep 11, 2023
1 parent cf2d32d commit 8358fb8
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,14 @@ use std::{

pub use segment::Segment;
pub use segment_builder::{DefaultSegmentBuilder, SegmentBuilder};
pub use stats::MmapStats;
pub use vec_builder::MmapVecBuilder;

use crate::utils::page_size;

mod segment;
mod segment_builder;
mod stats;
mod utils;
mod vec_builder;

Expand Down
17 changes: 10 additions & 7 deletions src/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ use std::{
os::{fd::AsRawFd, unix::prelude::FileExt},
path::{Path, PathBuf},
ptr, slice,
sync::atomic::Ordering,
};

use crate::stats::{COUNT_ACTIVE_SEGMENT, COUNT_MMAP_FAILED, COUNT_MUNMAP_FAILED};

/// Segment is a constant slice of type T that is memory mapped to disk.
///
/// It is the basic building block of memory mapped data structure.
Expand Down Expand Up @@ -66,8 +69,10 @@ impl<T> Segment<T> {
};

if addr == libc::MAP_FAILED {
COUNT_MMAP_FAILED.fetch_add(1, Ordering::Relaxed);
Err(io::Error::last_os_error())
} else {
COUNT_ACTIVE_SEGMENT.fetch_add(1, Ordering::Relaxed);
Ok(Self {
addr: addr.cast(),
len: 0,
Expand Down Expand Up @@ -194,13 +199,11 @@ impl<T> Drop for Segment<T> {
let unmap_code =
unsafe { libc::munmap(self.addr.cast(), self.capacity * mem::size_of::<T>()) };

// Assert that munmap has not failed to force use it's return value and do not optimize out
// above call
assert!(
unmap_code == 0,
"munmap failed: {}",
io::Error::last_os_error()
);
if unmap_code == 0 {
COUNT_ACTIVE_SEGMENT.fetch_sub(1, Ordering::Relaxed);
} else {
COUNT_MUNMAP_FAILED.fetch_add(1, Ordering::Relaxed);
}
}

if let Some(path) = &self.path {
Expand Down
34 changes: 34 additions & 0 deletions src/stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::sync::atomic::{AtomicU64, Ordering};

pub(crate) static COUNT_ACTIVE_SEGMENT: AtomicU64 = AtomicU64::new(0);
pub(crate) static COUNT_MMAP_FAILED: AtomicU64 = AtomicU64::new(0);
pub(crate) static COUNT_MUNMAP_FAILED: AtomicU64 = AtomicU64::new(0);

/// Provides few statistics about low level segment allocation.
///
/// This stats can be useful to debug or to export in various monitoring
/// systems.
#[derive(Debug, Default)]
pub struct MmapStats;

impl MmapStats {
/// Get number of current segment mounted by this library.
///
/// On linux there is a `systctl` limit you can access with:
/// ```shell
/// sysctl vm.max_map_count
/// ```
pub fn active_segment(&self) -> u64 {
COUNT_ACTIVE_SEGMENT.load(Ordering::Relaxed)
}

/// Get number of segment creation failed.
pub fn map_failed(&self) -> u64 {
COUNT_MMAP_FAILED.load(Ordering::Relaxed)
}

/// Get number of segment deletion failed.
pub fn unmap_failed(&self) -> u64 {
COUNT_MUNMAP_FAILED.load(Ordering::Relaxed)
}
}
18 changes: 18 additions & 0 deletions tests/test_stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use mmap_vec::{MmapStats, MmapVec};

#[test]
fn test_stats() {
assert_eq!(MmapStats.active_segment(), 0);
assert_eq!(MmapStats.map_failed(), 0);
assert_eq!(MmapStats.unmap_failed(), 0);

let v = MmapVec::<u8>::with_capacity(500).unwrap();
assert_eq!(MmapStats.active_segment(), 1);
assert_eq!(MmapStats.map_failed(), 0);
assert_eq!(MmapStats.unmap_failed(), 0);

drop(v);
assert_eq!(MmapStats.active_segment(), 0);
assert_eq!(MmapStats.map_failed(), 0);
assert_eq!(MmapStats.unmap_failed(), 0);
}

0 comments on commit 8358fb8

Please sign in to comment.