1use core::fmt::Write as _;
4
5use mnemosyne_core::NUM_SIZE_CLASSES;
6
7use crate::SizeClassOccupancy;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct MemoryStats {
12 pub current_mapped_bytes: usize,
16 pub peak_mapped_bytes: usize,
18 pub map_calls: usize,
20 pub unmap_calls: usize,
22 pub page_reset_calls: usize,
25 pub page_reset_bytes: usize,
27 pub guard_install_calls: usize,
30 pub guard_install_bytes: usize,
32 pub retained_free_segments: usize,
34 pub max_retained_free_segments: usize,
37 pub retained_free_bytes: usize,
39 pub purged_segments: usize,
41 pub purge_calls: usize,
43 pub purged_bytes: usize,
45 pub reset_segments: usize,
49 pub reset_calls: usize,
51 pub retained_huge_blocks: usize,
54 pub retained_huge_bytes: usize,
57 pub current_thread_live_allocations: usize,
59 pub current_thread_owned_segments: usize,
62 pub cross_thread_reclaimed_blocks: usize,
65 pub page_refills: usize,
68 pub recycled_pages: usize,
71 pub fresh_pages: usize,
73 pub fresh_segments: usize,
76 pub orphan_segments_adopted: usize,
79 pub recycle_sweeps: usize,
84 pub size_class_occupancy: [SizeClassOccupancy; NUM_SIZE_CLASSES],
87}
88
89impl Default for MemoryStats {
90 fn default() -> Self {
91 Self {
92 current_mapped_bytes: 0,
93 peak_mapped_bytes: 0,
94 map_calls: 0,
95 unmap_calls: 0,
96 page_reset_calls: 0,
97 page_reset_bytes: 0,
98 guard_install_calls: 0,
99 guard_install_bytes: 0,
100 retained_free_segments: 0,
101 max_retained_free_segments: 0,
102 retained_free_bytes: 0,
103 purged_segments: 0,
104 purge_calls: 0,
105 purged_bytes: 0,
106 reset_segments: 0,
107 reset_calls: 0,
108 retained_huge_blocks: 0,
109 retained_huge_bytes: 0,
110 current_thread_live_allocations: 0,
111 current_thread_owned_segments: 0,
112 cross_thread_reclaimed_blocks: 0,
113 page_refills: 0,
114 recycled_pages: 0,
115 fresh_pages: 0,
116 fresh_segments: 0,
117 orphan_segments_adopted: 0,
118 recycle_sweeps: 0,
119 size_class_occupancy: [SizeClassOccupancy::default(); NUM_SIZE_CLASSES],
120 }
121 }
122}
123
124impl MemoryStats {
125 pub fn to_json_with_bins(
130 &self,
131 bins: &[mnemosyne_local::BinSnapshot],
132 ) -> alloc::string::String {
133 use alloc::format;
134 use alloc::string::String;
135
136 let mut out = String::with_capacity(4096);
137 out.push('{');
138 macro_rules! kv_usize {
139 ($key:expr, $val:expr, $comma:expr) => {
140 if $comma {
141 out.push(',');
142 }
143 out.push('"');
144 out.push_str($key);
145 out.push_str("\":");
146 out.push_str(&format!("{}", $val));
147 };
148 }
149 kv_usize!("current_mapped_bytes", self.current_mapped_bytes, false);
150 kv_usize!("peak_mapped_bytes", self.peak_mapped_bytes, true);
151 kv_usize!("map_calls", self.map_calls, true);
152 kv_usize!("unmap_calls", self.unmap_calls, true);
153 kv_usize!("page_reset_calls", self.page_reset_calls, true);
154 kv_usize!("page_reset_bytes", self.page_reset_bytes, true);
155 kv_usize!(
156 "decommit_bytes",
157 mnemosyne_backend::backend_memory_stats().decommit_bytes,
158 true
159 );
160 kv_usize!("purged_bytes", self.purged_bytes, true);
161 kv_usize!("retained_free_segments", self.retained_free_segments, true);
162 kv_usize!(
163 "max_retained_free_segments",
164 self.max_retained_free_segments,
165 true
166 );
167 kv_usize!("retained_free_bytes", self.retained_free_bytes, true);
168 kv_usize!("purged_segments", self.purged_segments, true);
169 kv_usize!("purge_calls", self.purge_calls, true);
170 kv_usize!("reset_segments", self.reset_segments, true);
171 kv_usize!("reset_calls", self.reset_calls, true);
172 kv_usize!("retained_huge_blocks", self.retained_huge_blocks, true);
173 kv_usize!("retained_huge_bytes", self.retained_huge_bytes, true);
174 kv_usize!(
175 "current_thread_live_allocations",
176 self.current_thread_live_allocations,
177 true
178 );
179 kv_usize!(
180 "current_thread_owned_segments",
181 self.current_thread_owned_segments,
182 true
183 );
184 kv_usize!(
185 "cross_thread_reclaimed_blocks",
186 self.cross_thread_reclaimed_blocks,
187 true
188 );
189 kv_usize!("page_refills", self.page_refills, true);
190 kv_usize!("recycled_pages", self.recycled_pages, true);
191 kv_usize!("fresh_pages", self.fresh_pages, true);
192 kv_usize!("fresh_segments", self.fresh_segments, true);
193 kv_usize!(
194 "orphan_segments_adopted",
195 self.orphan_segments_adopted,
196 true
197 );
198 kv_usize!("recycle_sweeps", self.recycle_sweeps, true);
199 out.push_str(",\"bins\":[");
201 for (i, bin) in bins.iter().enumerate() {
202 if i > 0 {
203 out.push(',');
204 }
205 let _ = write!(
208 out,
209 "{{\"block_size\":{},\"alloc_count\":{},\"dealloc_count\":{},\
210 \"live_estimate\":{},\"requested_bytes\":{},\
211 \"fragmentation\":{:.4},\"internal_fragmentation\":{:.4}}}",
212 bin.block_size,
213 bin.alloc_count,
214 bin.dealloc_count,
215 bin.live_estimate,
216 bin.requested_bytes,
217 bin.fragmentation_ratio(),
218 bin.internal_fragmentation_ratio()
219 );
220 }
221 out.push_str("]}");
222 out
223 }
224}