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, MemoryManagerRangeMode, 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)]
37pub struct AllocationRangeClaim {
38 pub authority: String,
40 pub mode: MemoryManagerRangeMode,
42}
43
44#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
54pub struct MemoryAllocation {
55 pub memory_manager_id: u8,
56 pub binding: AllocationBinding,
57 pub range_claim: Option<AllocationRangeClaim>,
58 pub virtual_extent: DiagnosticMemorySize,
59 pub allocated_buckets: u16,
60 pub allocated_bytes: u64,
61 pub bucket_slack_bytes: u64,
62 pub payload_bytes: Option<u64>,
64}
65
66#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
83pub struct MemoryAllocations {
84 pub current_generation: Option<u64>,
86 pub manager_layout_version: u8,
87 pub bucket_size_pages: u16,
89 pub bucket_size_bytes: u64,
90 pub bucket_capacity: u32,
91 pub allocated_buckets: u16,
92 pub remaining_buckets: u32,
93 pub maximum_bucket_bytes: u64,
95 pub physical_extent: DiagnosticMemorySize,
96 pub virtual_extent: DiagnosticMemorySize,
97 pub manager_metadata_bytes: u64,
99 pub manager_header_bytes: u64,
100 pub manager_bucket_table_bytes: u64,
101 pub manager_padding_bytes: u64,
102 pub allocated_bucket_bytes: u64,
103 pub bucket_slack_bytes: u64,
104 pub known_binding_bytes: u64,
105 pub unknown_binding_bytes: u64,
106 pub unmanaged_bytes: u64,
108 pub metadata_bytes_read: u64,
110 pub memories: Vec<MemoryAllocation>,
111}
112
113#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
121pub struct MemoryBindingSummary {
122 pub allocated_bytes: u64,
123 pub bucket_slack_bytes: u64,
124}
125
126#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
137pub struct MemoryAllocationSummary {
138 pub current_generation: Option<u64>,
140 pub manager_layout_version: u8,
141 pub bucket_size_pages: u16,
143 pub bucket_size_bytes: u64,
144 pub bucket_capacity: u32,
145 pub allocated_buckets: u16,
146 pub remaining_buckets: u32,
147 pub maximum_bucket_bytes: u64,
149 pub physical_extent: DiagnosticMemorySize,
150 pub virtual_extent: DiagnosticMemorySize,
151 pub manager_metadata_bytes: u64,
153 pub manager_header_bytes: u64,
154 pub manager_bucket_table_bytes: u64,
155 pub manager_padding_bytes: u64,
156 pub allocated_bucket_bytes: u64,
157 pub bucket_slack_bytes: u64,
158 pub known_binding_bytes: u64,
159 pub unknown_binding_bytes: u64,
160 pub unmanaged_bytes: u64,
162 pub metadata_bytes_read: u64,
164 pub memories_measured: u16,
166 pub current_binding: MemoryBindingSummary,
167 pub ledger_binding: MemoryBindingSummary,
168 pub unknown_binding: MemoryBindingSummary,
169}
170
171impl<M: Memory> MemoryRuntime<M> {
172 pub fn memory_allocations(&self) -> Result<MemoryAllocations, RuntimeDiagnosticError> {
183 let (measured, summary) = self.measure_allocations()?;
184 let mut memories = Vec::with_capacity(layout::IDS);
185 for id in 0..255_u8 {
186 let index = usize::from(id);
187 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(measured.pages[index]);
188 let allocated_bytes = u64::from(measured.buckets[index]) * summary.bucket_size_bytes;
189 memories.push(MemoryAllocation {
190 memory_manager_id: id,
191 binding: AllocationBinding::Unknown,
192 range_claim: None,
193 virtual_extent,
194 allocated_buckets: measured.buckets[index],
195 allocated_bytes,
196 bucket_slack_bytes: allocated_bytes - virtual_extent.bytes,
197 payload_bytes: None,
198 });
199 }
200 if let Some(snapshot) = self.allocation_declarations() {
203 for registration in snapshot.registered_declarations() {
204 let declaration = registration.declaration();
205 let id = declaration
206 .slot()
207 .memory_manager_id()
208 .expect("sealed declaration slot");
209 memories[usize::from(id)].binding = AllocationBinding::Current {
210 stable_key: declaration.stable_key().as_str().to_string(),
211 owner: registration.authority().to_string(),
212 };
213 }
214 for claim in snapshot.range_authority().authorities() {
215 for id in claim.range().start()..=claim.range().end() {
216 memories[usize::from(id)].range_claim = Some(AllocationRangeClaim {
217 authority: claim.authority().to_string(),
218 mode: claim.mode(),
219 });
220 }
221 }
222 }
223 memories[usize::from(MEMORY_MANAGER_LEDGER_ID)].binding = AllocationBinding::Ledger {
224 stable_key: IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
225 owner: IC_MEMORY_AUTHORITY_OWNER.to_string(),
226 };
227 Ok(MemoryAllocations {
228 current_generation: summary.current_generation,
229 manager_layout_version: summary.manager_layout_version,
230 bucket_size_pages: summary.bucket_size_pages,
231 bucket_size_bytes: summary.bucket_size_bytes,
232 bucket_capacity: summary.bucket_capacity,
233 allocated_buckets: summary.allocated_buckets,
234 remaining_buckets: summary.remaining_buckets,
235 maximum_bucket_bytes: summary.maximum_bucket_bytes,
236 physical_extent: summary.physical_extent,
237 virtual_extent: summary.virtual_extent,
238 manager_metadata_bytes: summary.manager_metadata_bytes,
239 manager_header_bytes: summary.manager_header_bytes,
240 manager_bucket_table_bytes: summary.manager_bucket_table_bytes,
241 manager_padding_bytes: summary.manager_padding_bytes,
242 allocated_bucket_bytes: summary.allocated_bucket_bytes,
243 bucket_slack_bytes: summary.bucket_slack_bytes,
244 known_binding_bytes: summary.known_binding_bytes,
245 unknown_binding_bytes: summary.unknown_binding_bytes,
246 unmanaged_bytes: summary.unmanaged_bytes,
247 metadata_bytes_read: summary.metadata_bytes_read,
248 memories,
249 })
250 }
251
252 pub fn memory_allocation_summary(
256 &self,
257 ) -> Result<MemoryAllocationSummary, RuntimeDiagnosticError> {
258 self.measure_allocations().map(|(_, summary)| summary)
259 }
260
261 const fn allocation_declarations(&self) -> Option<&crate::SealedDeclarationSnapshot> {
262 match &self.lifecycle {
264 RuntimeLifecycle::Unbootstrapped => None,
265 RuntimeLifecycle::Bootstrapped { binding, .. } => Some(&binding.declarations),
266 }
267 }
268
269 fn measure_allocations(
270 &self,
271 ) -> Result<(layout::Layout, MemoryAllocationSummary), RuntimeDiagnosticError> {
272 let declarations = self.allocation_declarations();
273 let measured = layout::read(self.growth.backing.as_ref())?;
274 let live_buckets = self
275 .growth
276 .allocated_buckets
277 .try_borrow()
278 .map_err(|_| super::RuntimeStateError::ReentrantAccess)?;
279 if measured.bucket_pages != self.growth.bucket_size_pages
280 || measured.allocated_buckets != *live_buckets
281 {
282 return Err(super::RuntimeConstructionError::Layout(
283 MemoryManagerLayoutError::RuntimeMismatch,
284 )
285 .into());
286 }
287 let bucket_size_bytes = u64::from(measured.bucket_pages) * WASM_PAGE_SIZE_BYTES;
288 let mut current = [false; layout::IDS];
289 if let Some(snapshot) = declarations {
290 for registration in snapshot.registered_declarations() {
291 let id = registration
292 .declaration()
293 .slot()
294 .memory_manager_id()
295 .expect("sealed declaration slot");
296 current[usize::from(id)] = true;
297 }
298 }
299 let mut groups = [MemoryBindingSummary::default(); 3];
300 let mut total_pages = 0;
301 for id in 0..255_u8 {
302 let index = usize::from(id);
303 if self.memory(id).size() != measured.pages[index] {
304 return Err(super::RuntimeConstructionError::Layout(
305 MemoryManagerLayoutError::RuntimeMismatch,
306 )
307 .into());
308 }
309 let group = if id == MEMORY_MANAGER_LEDGER_ID {
310 1
311 } else if current[index] {
312 0
313 } else {
314 2
315 };
316 let allocated_bytes = u64::from(measured.buckets[index]) * bucket_size_bytes;
317 groups[group].allocated_bytes += allocated_bytes;
318 groups[group].bucket_slack_bytes +=
319 allocated_bytes - measured.pages[index] * WASM_PAGE_SIZE_BYTES;
320 total_pages += measured.pages[index];
321 }
322 let allocated_bucket_bytes = u64::from(measured.allocated_buckets) * bucket_size_bytes;
323 let physical_extent = DiagnosticMemorySize::from_wasm_pages(measured.physical_pages);
324 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(total_pages);
325 let summary = MemoryAllocationSummary {
326 current_generation: self
327 .committed_allocations()
328 .ok()
329 .map(crate::CommittedAllocations::generation),
330 manager_layout_version: 1,
331 bucket_size_pages: measured.bucket_pages,
332 bucket_size_bytes,
333 bucket_capacity: u32::from(layout::BUCKET_CAPACITY),
334 allocated_buckets: measured.allocated_buckets,
335 remaining_buckets: u32::from(layout::BUCKET_CAPACITY)
336 - u32::from(measured.allocated_buckets),
337 maximum_bucket_bytes: u64::from(layout::BUCKET_CAPACITY) * bucket_size_bytes,
338 physical_extent,
339 virtual_extent,
340 manager_metadata_bytes: WASM_PAGE_SIZE_BYTES,
341 manager_header_bytes: layout::HEADER_BYTES as u64,
342 manager_bucket_table_bytes: layout::BUCKETS as u64,
343 manager_padding_bytes: WASM_PAGE_SIZE_BYTES - layout::METADATA_BYTES as u64,
344 allocated_bucket_bytes,
345 bucket_slack_bytes: allocated_bucket_bytes - virtual_extent.bytes,
346 known_binding_bytes: groups[0].allocated_bytes + groups[1].allocated_bytes,
347 unknown_binding_bytes: groups[2].allocated_bytes,
348 unmanaged_bytes: physical_extent.bytes - WASM_PAGE_SIZE_BYTES - allocated_bucket_bytes,
349 metadata_bytes_read: layout::METADATA_BYTES as u64,
350 memories_measured: u16::from(crate::MEMORY_MANAGER_INVALID_ID),
351 current_binding: groups[0],
352 ledger_binding: groups[1],
353 unknown_binding: groups[2],
354 };
355 Ok((measured, summary))
356 }
357}