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}
50
51#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
68pub struct MemoryAllocations {
69 pub summary: MemoryAllocationSummary,
71 pub memories: Vec<MemoryAllocation>,
72}
73
74#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
82pub struct MemoryBindingSummary {
83 pub allocated_bytes: u64,
84 pub bucket_slack_bytes: u64,
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
98pub struct MemoryAllocationSummary {
99 pub current_generation: Option<u64>,
101 pub manager_layout_version: u8,
102 pub bucket_size_pages: u16,
104 pub bucket_size_bytes: u64,
105 pub bucket_capacity: u32,
106 pub allocated_buckets: u16,
107 pub remaining_buckets: u32,
108 pub maximum_bucket_bytes: u64,
110 pub physical_extent: DiagnosticMemorySize,
111 pub virtual_extent: DiagnosticMemorySize,
112 pub manager_metadata_bytes: u64,
114 pub manager_header_bytes: u64,
115 pub manager_bucket_table_bytes: u64,
116 pub manager_padding_bytes: u64,
117 pub allocated_bucket_bytes: u64,
118 pub bucket_slack_bytes: u64,
119 pub known_binding_bytes: u64,
120 pub unknown_binding_bytes: u64,
121 pub unmanaged_bytes: u64,
123 pub metadata_bytes_read: u64,
125 pub memories_measured: u16,
127 pub current_binding: MemoryBindingSummary,
128 pub ledger_binding: MemoryBindingSummary,
129 pub unknown_binding: MemoryBindingSummary,
130}
131
132impl<M: Memory> MemoryRuntime<M> {
133 pub fn memory_allocations(&self) -> Result<MemoryAllocations, RuntimeDiagnosticError> {
143 let (measured, summary) = self.measure_allocations()?;
144 let mut memories = Vec::with_capacity(layout::IDS);
145 for id in 0..255_u8 {
146 let index = usize::from(id);
147 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(measured.pages[index]);
148 let allocated_bytes = u64::from(measured.buckets[index]) * summary.bucket_size_bytes;
149 memories.push(MemoryAllocation {
150 memory_manager_id: id,
151 binding: AllocationBinding::Unknown,
152 pool_eligible: (id <= crate::MEMORY_MANAGER_GOVERNANCE_MAX_ID).then_some(false),
153 virtual_extent,
154 allocated_buckets: measured.buckets[index],
155 allocated_bytes,
156 bucket_slack_bytes: allocated_bytes - virtual_extent.bytes,
157 });
158 }
159 if let RuntimeLifecycle::Bootstrapped {
162 binding,
163 committed_allocations,
164 } = &self.lifecycle
165 {
166 for declaration in committed_allocations.declarations() {
167 let id = declaration.slot().id();
168 memories[usize::from(id)].binding = AllocationBinding::Current {
169 stable_key: declaration.stable_key().as_str().to_string(),
170 owner: binding
171 .pool
172 .authority_for_key(declaration.stable_key())
173 .expect("committed key has an admitted owner")
174 .to_string(),
175 };
176 }
177 for memory in &mut memories {
178 memory.pool_eligible = Some(binding.pool.contains(memory.memory_manager_id));
179 }
180 }
181 memories[usize::from(MEMORY_MANAGER_LEDGER_ID)].binding = AllocationBinding::Ledger {
182 stable_key: IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
183 owner: IC_MEMORY_AUTHORITY_OWNER.to_string(),
184 };
185 Ok(MemoryAllocations { summary, memories })
186 }
187
188 pub fn memory_allocation_summary(
192 &self,
193 ) -> Result<MemoryAllocationSummary, RuntimeDiagnosticError> {
194 self.measure_allocations().map(|(_, summary)| summary)
195 }
196
197 fn allocation_declarations(&self) -> Option<&[crate::AllocationDeclaration]> {
198 match &self.lifecycle {
200 RuntimeLifecycle::Unbootstrapped => None,
201 RuntimeLifecycle::Bootstrapped {
202 committed_allocations,
203 ..
204 } => Some(committed_allocations.declarations()),
205 }
206 }
207
208 fn measure_allocations(
209 &self,
210 ) -> Result<(layout::Layout, MemoryAllocationSummary), RuntimeDiagnosticError> {
211 let declarations = self.allocation_declarations();
212 let measured = layout::read(self.growth.backing.as_ref())?;
213 let live_buckets = self
214 .growth
215 .allocated_buckets
216 .try_borrow()
217 .map_err(|_| super::RuntimeStateError::ReentrantAccess)?;
218 if measured.bucket_pages != self.growth.bucket_size_pages
219 || measured.allocated_buckets != *live_buckets
220 {
221 return Err(super::RuntimeConstructionError::Layout(
222 MemoryManagerLayoutError::RuntimeMismatch,
223 )
224 .into());
225 }
226 let bucket_size_bytes = u64::from(measured.bucket_pages) * WASM_PAGE_SIZE_BYTES;
227 let mut current = [false; layout::IDS];
228 if let Some(snapshot) = declarations {
229 for declaration in snapshot {
230 let id = declaration.slot().id();
231 current[usize::from(id)] = true;
232 }
233 }
234 let mut groups = [MemoryBindingSummary::default(); 3];
235 let mut total_pages = 0;
236 for id in 0..255_u8 {
237 let index = usize::from(id);
238 if self.memory_size_pages(id) != measured.pages[index] {
239 return Err(super::RuntimeConstructionError::Layout(
240 MemoryManagerLayoutError::RuntimeMismatch,
241 )
242 .into());
243 }
244 let group = if id == MEMORY_MANAGER_LEDGER_ID {
245 1
246 } else if current[index] {
247 0
248 } else {
249 2
250 };
251 let allocated_bytes = u64::from(measured.buckets[index]) * bucket_size_bytes;
252 groups[group].allocated_bytes += allocated_bytes;
253 groups[group].bucket_slack_bytes +=
254 allocated_bytes - measured.pages[index] * WASM_PAGE_SIZE_BYTES;
255 total_pages += measured.pages[index];
256 }
257 let allocated_bucket_bytes = u64::from(measured.allocated_buckets) * bucket_size_bytes;
258 let physical_extent = DiagnosticMemorySize::from_wasm_pages(measured.physical_pages);
259 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(total_pages);
260 let summary = MemoryAllocationSummary {
261 current_generation: self
262 .committed_allocations()
263 .ok()
264 .map(crate::CommittedAllocations::generation),
265 manager_layout_version: 1,
266 bucket_size_pages: measured.bucket_pages,
267 bucket_size_bytes,
268 bucket_capacity: u32::from(layout::BUCKET_CAPACITY),
269 allocated_buckets: measured.allocated_buckets,
270 remaining_buckets: u32::from(layout::BUCKET_CAPACITY)
271 - u32::from(measured.allocated_buckets),
272 maximum_bucket_bytes: u64::from(layout::BUCKET_CAPACITY) * bucket_size_bytes,
273 physical_extent,
274 virtual_extent,
275 manager_metadata_bytes: WASM_PAGE_SIZE_BYTES,
276 manager_header_bytes: layout::HEADER_BYTES as u64,
277 manager_bucket_table_bytes: layout::BUCKETS as u64,
278 manager_padding_bytes: WASM_PAGE_SIZE_BYTES - layout::METADATA_BYTES as u64,
279 allocated_bucket_bytes,
280 bucket_slack_bytes: allocated_bucket_bytes - virtual_extent.bytes,
281 known_binding_bytes: groups[0].allocated_bytes + groups[1].allocated_bytes,
282 unknown_binding_bytes: groups[2].allocated_bytes,
283 unmanaged_bytes: physical_extent.bytes - WASM_PAGE_SIZE_BYTES - allocated_bucket_bytes,
284 metadata_bytes_read: layout::METADATA_BYTES as u64,
285 memories_measured: u16::from(crate::MEMORY_MANAGER_INVALID_ID),
286 current_binding: groups[0],
287 ledger_binding: groups[1],
288 unknown_binding: groups[2],
289 };
290 Ok((measured, summary))
291 }
292}