1use std::fmt;
5
6use cfg_if::cfg_if;
7
8#[derive(Copy, Clone)]
9pub struct MemoryUsage {
10 pub allocated: Bytes,
11}
12
13impl fmt::Display for MemoryUsage {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 self.allocated.fmt(f)
16 }
17}
18
19impl std::ops::Sub for MemoryUsage {
20 type Output = MemoryUsage;
21 fn sub(self, rhs: MemoryUsage) -> MemoryUsage {
22 MemoryUsage { allocated: self.allocated - rhs.allocated }
23 }
24}
25
26impl MemoryUsage {
27 pub fn now() -> MemoryUsage {
28 cfg_if! {
29 if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] {
30 jemalloc_ctl::epoch::advance().unwrap();
31 MemoryUsage {
32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
33 }
34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
35 memusage_linux()
36 } else if #[cfg(windows)] {
37 use windows_sys::Win32::System::{Threading::*, ProcessStatus::*};
41 use std::mem::MaybeUninit;
42
43 let proc = unsafe { GetCurrentProcess() };
44 let mut mem_counters = MaybeUninit::uninit();
45 let cb = size_of::<PROCESS_MEMORY_COUNTERS>();
46 let ret = unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) };
47 assert!(ret != 0);
48
49 let usage = unsafe { mem_counters.assume_init().PagefileUsage };
50 MemoryUsage { allocated: Bytes(usage as isize) }
51 } else {
52 MemoryUsage { allocated: Bytes(0) }
53 }
54 }
55 }
56}
57
58#[cfg(all(target_os = "linux", target_env = "gnu", not(feature = "jemalloc")))]
59fn memusage_linux() -> MemoryUsage {
60 use std::sync::atomic::{AtomicUsize, Ordering};
66
67 static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
68
69 let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
70 if mallinfo2 == 1 {
71 mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"mallinfo2".as_ptr()) } as usize;
72 MALLINFO2.store(mallinfo2, Ordering::Relaxed);
74 }
75
76 if mallinfo2 == 0 {
77 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
79 MemoryUsage { allocated: Bytes(alloc) }
80 } else {
81 let mallinfo2: extern "C" fn() -> libc::mallinfo2 =
82 unsafe { std::mem::transmute(mallinfo2) };
83 let alloc = mallinfo2().uordblks as isize;
84 MemoryUsage { allocated: Bytes(alloc) }
85 }
86}
87
88#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
89pub struct Bytes(isize);
90
91impl Bytes {
92 pub fn new(bytes: isize) -> Bytes {
93 Bytes(bytes)
94 }
95}
96
97impl Bytes {
98 pub fn megabytes(self) -> isize {
99 self.0 / 1024 / 1024
100 }
101}
102
103impl fmt::Display for Bytes {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 let bytes = self.0;
106 let mut value = bytes;
107 let mut suffix = "b";
108 if value.abs() > 4096 {
109 value /= 1024;
110 suffix = "kb";
111 if value.abs() > 4096 {
112 value /= 1024;
113 suffix = "mb";
114 }
115 }
116 f.pad(&format!("{value}{suffix}"))
117 }
118}
119
120impl std::ops::AddAssign<usize> for Bytes {
121 fn add_assign(&mut self, x: usize) {
122 self.0 += x as isize;
123 }
124}
125
126impl std::ops::Sub for Bytes {
127 type Output = Bytes;
128 fn sub(self, rhs: Bytes) -> Bytes {
129 Bytes(self.0 - rhs.0)
130 }
131}