1use super::{
2 MemoryManagerLayoutError, MemoryRuntime, RuntimeDiagnosticError, RuntimeLifecycle, layout,
3};
4use crate::{
5 DiagnosticMemorySize, IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY,
6 MEMORY_MANAGER_LEDGER_ID, WASM_PAGE_SIZE_BYTES,
7};
8use ic_stable_structures::Memory;
9use serde::Serialize;
10
11#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
20pub enum AllocationBinding {
21 Current { stable_key: String, owner: String },
23 Ledger { stable_key: String, owner: String },
25 Unknown,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
39pub struct MemoryAllocation {
40 pub memory_manager_id: u8,
41 pub binding: AllocationBinding,
42 pub pool_eligible: Option<bool>,
45 pub virtual_extent: DiagnosticMemorySize,
46 pub allocated_buckets: u16,
47 pub allocated_bytes: u64,
48 pub bucket_slack_bytes: u64,
49 pub payload_bytes: Option<u64>,
51}
52
53#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
70pub struct MemoryAllocations {
71 pub current_generation: Option<u64>,
73 pub manager_layout_version: u8,
74 pub bucket_size_pages: u16,
76 pub bucket_size_bytes: u64,
77 pub bucket_capacity: u32,
78 pub allocated_buckets: u16,
79 pub remaining_buckets: u32,
80 pub maximum_bucket_bytes: u64,
82 pub physical_extent: DiagnosticMemorySize,
83 pub virtual_extent: DiagnosticMemorySize,
84 pub manager_metadata_bytes: u64,
86 pub manager_header_bytes: u64,
87 pub manager_bucket_table_bytes: u64,
88 pub manager_padding_bytes: u64,
89 pub allocated_bucket_bytes: u64,
90 pub bucket_slack_bytes: u64,
91 pub known_binding_bytes: u64,
92 pub unknown_binding_bytes: u64,
93 pub unmanaged_bytes: u64,
95 pub metadata_bytes_read: u64,
97 pub memories: Vec<MemoryAllocation>,
98}
99
100#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
108pub struct MemoryBindingSummary {
109 pub allocated_bytes: u64,
110 pub bucket_slack_bytes: u64,
111}
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
124pub struct MemoryAllocationSummary {
125 pub current_generation: Option<u64>,
127 pub manager_layout_version: u8,
128 pub bucket_size_pages: u16,
130 pub bucket_size_bytes: u64,
131 pub bucket_capacity: u32,
132 pub allocated_buckets: u16,
133 pub remaining_buckets: u32,
134 pub maximum_bucket_bytes: u64,
136 pub physical_extent: DiagnosticMemorySize,
137 pub virtual_extent: DiagnosticMemorySize,
138 pub manager_metadata_bytes: u64,
140 pub manager_header_bytes: u64,
141 pub manager_bucket_table_bytes: u64,
142 pub manager_padding_bytes: u64,
143 pub allocated_bucket_bytes: u64,
144 pub bucket_slack_bytes: u64,
145 pub known_binding_bytes: u64,
146 pub unknown_binding_bytes: u64,
147 pub unmanaged_bytes: u64,
149 pub metadata_bytes_read: u64,
151 pub memories_measured: u16,
153 pub current_binding: MemoryBindingSummary,
154 pub ledger_binding: MemoryBindingSummary,
155 pub unknown_binding: MemoryBindingSummary,
156}
157
158impl<M: Memory> MemoryRuntime<M> {
159 pub fn memory_allocations(&self) -> Result<MemoryAllocations, RuntimeDiagnosticError> {
169 let (measured, summary) = self.measure_allocations()?;
170 let mut memories = Vec::with_capacity(layout::IDS);
171 for id in 0..255_u8 {
172 let index = usize::from(id);
173 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(measured.pages[index]);
174 let allocated_bytes = u64::from(measured.buckets[index]) * summary.bucket_size_bytes;
175 memories.push(MemoryAllocation {
176 memory_manager_id: id,
177 binding: AllocationBinding::Unknown,
178 pool_eligible: (id <= crate::MEMORY_MANAGER_GOVERNANCE_MAX_ID).then_some(false),
179 virtual_extent,
180 allocated_buckets: measured.buckets[index],
181 allocated_bytes,
182 bucket_slack_bytes: allocated_bytes - virtual_extent.bytes,
183 payload_bytes: None,
184 });
185 }
186 if let RuntimeLifecycle::Bootstrapped {
189 binding,
190 committed_allocations,
191 } = &self.lifecycle
192 {
193 for declaration in committed_allocations.declarations() {
194 let id = declaration.slot().id();
195 memories[usize::from(id)].binding = AllocationBinding::Current {
196 stable_key: declaration.stable_key().as_str().to_string(),
197 owner: binding
198 .pool
199 .authority_for_key(declaration.stable_key())
200 .expect("committed key has an admitted owner")
201 .to_string(),
202 };
203 }
204 for memory in &mut memories {
205 memory.pool_eligible = Some(binding.pool.contains(memory.memory_manager_id));
206 }
207 }
208 memories[usize::from(MEMORY_MANAGER_LEDGER_ID)].binding = AllocationBinding::Ledger {
209 stable_key: IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
210 owner: IC_MEMORY_AUTHORITY_OWNER.to_string(),
211 };
212 Ok(MemoryAllocations {
213 current_generation: summary.current_generation,
214 manager_layout_version: summary.manager_layout_version,
215 bucket_size_pages: summary.bucket_size_pages,
216 bucket_size_bytes: summary.bucket_size_bytes,
217 bucket_capacity: summary.bucket_capacity,
218 allocated_buckets: summary.allocated_buckets,
219 remaining_buckets: summary.remaining_buckets,
220 maximum_bucket_bytes: summary.maximum_bucket_bytes,
221 physical_extent: summary.physical_extent,
222 virtual_extent: summary.virtual_extent,
223 manager_metadata_bytes: summary.manager_metadata_bytes,
224 manager_header_bytes: summary.manager_header_bytes,
225 manager_bucket_table_bytes: summary.manager_bucket_table_bytes,
226 manager_padding_bytes: summary.manager_padding_bytes,
227 allocated_bucket_bytes: summary.allocated_bucket_bytes,
228 bucket_slack_bytes: summary.bucket_slack_bytes,
229 known_binding_bytes: summary.known_binding_bytes,
230 unknown_binding_bytes: summary.unknown_binding_bytes,
231 unmanaged_bytes: summary.unmanaged_bytes,
232 metadata_bytes_read: summary.metadata_bytes_read,
233 memories,
234 })
235 }
236
237 pub fn memory_allocation_summary(
241 &self,
242 ) -> Result<MemoryAllocationSummary, RuntimeDiagnosticError> {
243 self.measure_allocations().map(|(_, summary)| summary)
244 }
245
246 fn allocation_declarations(&self) -> Option<&[crate::AllocationDeclaration]> {
247 match &self.lifecycle {
249 RuntimeLifecycle::Unbootstrapped => None,
250 RuntimeLifecycle::Bootstrapped {
251 committed_allocations,
252 ..
253 } => Some(committed_allocations.declarations()),
254 }
255 }
256
257 fn measure_allocations(
258 &self,
259 ) -> Result<(layout::Layout, MemoryAllocationSummary), RuntimeDiagnosticError> {
260 let declarations = self.allocation_declarations();
261 let measured = layout::read(self.growth.backing.as_ref())?;
262 let live_buckets = self
263 .growth
264 .allocated_buckets
265 .try_borrow()
266 .map_err(|_| super::RuntimeStateError::ReentrantAccess)?;
267 if measured.bucket_pages != self.growth.bucket_size_pages
268 || measured.allocated_buckets != *live_buckets
269 {
270 return Err(super::RuntimeConstructionError::Layout(
271 MemoryManagerLayoutError::RuntimeMismatch,
272 )
273 .into());
274 }
275 let bucket_size_bytes = u64::from(measured.bucket_pages) * WASM_PAGE_SIZE_BYTES;
276 let mut current = [false; layout::IDS];
277 if let Some(snapshot) = declarations {
278 for declaration in snapshot {
279 let id = declaration.slot().id();
280 current[usize::from(id)] = true;
281 }
282 }
283 let mut groups = [MemoryBindingSummary::default(); 3];
284 let mut total_pages = 0;
285 for id in 0..255_u8 {
286 let index = usize::from(id);
287 if self.memory_size_pages(id) != measured.pages[index] {
288 return Err(super::RuntimeConstructionError::Layout(
289 MemoryManagerLayoutError::RuntimeMismatch,
290 )
291 .into());
292 }
293 let group = if id == MEMORY_MANAGER_LEDGER_ID {
294 1
295 } else if current[index] {
296 0
297 } else {
298 2
299 };
300 let allocated_bytes = u64::from(measured.buckets[index]) * bucket_size_bytes;
301 groups[group].allocated_bytes += allocated_bytes;
302 groups[group].bucket_slack_bytes +=
303 allocated_bytes - measured.pages[index] * WASM_PAGE_SIZE_BYTES;
304 total_pages += measured.pages[index];
305 }
306 let allocated_bucket_bytes = u64::from(measured.allocated_buckets) * bucket_size_bytes;
307 let physical_extent = DiagnosticMemorySize::from_wasm_pages(measured.physical_pages);
308 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(total_pages);
309 let summary = MemoryAllocationSummary {
310 current_generation: self
311 .committed_allocations()
312 .ok()
313 .map(crate::CommittedAllocations::generation),
314 manager_layout_version: 1,
315 bucket_size_pages: measured.bucket_pages,
316 bucket_size_bytes,
317 bucket_capacity: u32::from(layout::BUCKET_CAPACITY),
318 allocated_buckets: measured.allocated_buckets,
319 remaining_buckets: u32::from(layout::BUCKET_CAPACITY)
320 - u32::from(measured.allocated_buckets),
321 maximum_bucket_bytes: u64::from(layout::BUCKET_CAPACITY) * bucket_size_bytes,
322 physical_extent,
323 virtual_extent,
324 manager_metadata_bytes: WASM_PAGE_SIZE_BYTES,
325 manager_header_bytes: layout::HEADER_BYTES as u64,
326 manager_bucket_table_bytes: layout::BUCKETS as u64,
327 manager_padding_bytes: WASM_PAGE_SIZE_BYTES - layout::METADATA_BYTES as u64,
328 allocated_bucket_bytes,
329 bucket_slack_bytes: allocated_bucket_bytes - virtual_extent.bytes,
330 known_binding_bytes: groups[0].allocated_bytes + groups[1].allocated_bytes,
331 unknown_binding_bytes: groups[2].allocated_bytes,
332 unmanaged_bytes: physical_extent.bytes - WASM_PAGE_SIZE_BYTES - allocated_bucket_bytes,
333 metadata_bytes_read: layout::METADATA_BYTES as u64,
334 memories_measured: u16::from(crate::MEMORY_MANAGER_INVALID_ID),
335 current_binding: groups[0],
336 ledger_binding: groups[1],
337 unknown_binding: groups[2],
338 };
339 Ok((measured, summary))
340 }
341}