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