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> {
178 let (measured, summary) = self.measure_allocations()?;
179 let mut memories = Vec::with_capacity(layout::IDS);
180 for id in 0..255_u8 {
181 let index = usize::from(id);
182 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(measured.pages[index]);
183 let allocated_bytes = u64::from(measured.buckets[index]) * summary.bucket_size_bytes;
184 memories.push(MemoryAllocation {
185 memory_manager_id: id,
186 binding: AllocationBinding::Unknown,
187 range_claim: None,
188 virtual_extent,
189 allocated_buckets: measured.buckets[index],
190 allocated_bytes,
191 bucket_slack_bytes: allocated_bytes - virtual_extent.bytes,
192 payload_bytes: None,
193 });
194 }
195 if let Some(snapshot) = self.allocation_declarations() {
198 for registration in snapshot.registered_declarations() {
199 let declaration = registration.declaration();
200 let id = declaration.slot().id();
201 memories[usize::from(id)].binding = AllocationBinding::Current {
202 stable_key: declaration.stable_key().as_str().to_string(),
203 owner: registration.authority().to_string(),
204 };
205 }
206 for claim in snapshot.range_authority().authorities() {
207 for id in claim.range().start()..=claim.range().end() {
208 memories[usize::from(id)].range_claim = Some(AllocationRangeClaim {
209 authority: claim.authority().to_string(),
210 mode: claim.mode(),
211 });
212 }
213 }
214 }
215 memories[usize::from(MEMORY_MANAGER_LEDGER_ID)].binding = AllocationBinding::Ledger {
216 stable_key: IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
217 owner: IC_MEMORY_AUTHORITY_OWNER.to_string(),
218 };
219 Ok(MemoryAllocations {
220 current_generation: summary.current_generation,
221 manager_layout_version: summary.manager_layout_version,
222 bucket_size_pages: summary.bucket_size_pages,
223 bucket_size_bytes: summary.bucket_size_bytes,
224 bucket_capacity: summary.bucket_capacity,
225 allocated_buckets: summary.allocated_buckets,
226 remaining_buckets: summary.remaining_buckets,
227 maximum_bucket_bytes: summary.maximum_bucket_bytes,
228 physical_extent: summary.physical_extent,
229 virtual_extent: summary.virtual_extent,
230 manager_metadata_bytes: summary.manager_metadata_bytes,
231 manager_header_bytes: summary.manager_header_bytes,
232 manager_bucket_table_bytes: summary.manager_bucket_table_bytes,
233 manager_padding_bytes: summary.manager_padding_bytes,
234 allocated_bucket_bytes: summary.allocated_bucket_bytes,
235 bucket_slack_bytes: summary.bucket_slack_bytes,
236 known_binding_bytes: summary.known_binding_bytes,
237 unknown_binding_bytes: summary.unknown_binding_bytes,
238 unmanaged_bytes: summary.unmanaged_bytes,
239 metadata_bytes_read: summary.metadata_bytes_read,
240 memories,
241 })
242 }
243
244 pub fn memory_allocation_summary(
248 &self,
249 ) -> Result<MemoryAllocationSummary, RuntimeDiagnosticError> {
250 self.measure_allocations().map(|(_, summary)| summary)
251 }
252
253 const fn allocation_declarations(&self) -> Option<&crate::SealedDeclarationSnapshot> {
254 match &self.lifecycle {
256 RuntimeLifecycle::Unbootstrapped => None,
257 RuntimeLifecycle::Bootstrapped { binding, .. } => Some(&binding.declarations),
258 }
259 }
260
261 fn measure_allocations(
262 &self,
263 ) -> Result<(layout::Layout, MemoryAllocationSummary), RuntimeDiagnosticError> {
264 let declarations = self.allocation_declarations();
265 let measured = layout::read(self.growth.backing.as_ref())?;
266 let live_buckets = self
267 .growth
268 .allocated_buckets
269 .try_borrow()
270 .map_err(|_| super::RuntimeStateError::ReentrantAccess)?;
271 if measured.bucket_pages != self.growth.bucket_size_pages
272 || measured.allocated_buckets != *live_buckets
273 {
274 return Err(super::RuntimeConstructionError::Layout(
275 MemoryManagerLayoutError::RuntimeMismatch,
276 )
277 .into());
278 }
279 let bucket_size_bytes = u64::from(measured.bucket_pages) * WASM_PAGE_SIZE_BYTES;
280 let mut current = [false; layout::IDS];
281 if let Some(snapshot) = declarations {
282 for registration in snapshot.registered_declarations() {
283 let id = registration.declaration().slot().id();
284 current[usize::from(id)] = true;
285 }
286 }
287 let mut groups = [MemoryBindingSummary::default(); 3];
288 let mut total_pages = 0;
289 for id in 0..255_u8 {
290 let index = usize::from(id);
291 if self.memory_size_pages(id) != measured.pages[index] {
292 return Err(super::RuntimeConstructionError::Layout(
293 MemoryManagerLayoutError::RuntimeMismatch,
294 )
295 .into());
296 }
297 let group = if id == MEMORY_MANAGER_LEDGER_ID {
298 1
299 } else if current[index] {
300 0
301 } else {
302 2
303 };
304 let allocated_bytes = u64::from(measured.buckets[index]) * bucket_size_bytes;
305 groups[group].allocated_bytes += allocated_bytes;
306 groups[group].bucket_slack_bytes +=
307 allocated_bytes - measured.pages[index] * WASM_PAGE_SIZE_BYTES;
308 total_pages += measured.pages[index];
309 }
310 let allocated_bucket_bytes = u64::from(measured.allocated_buckets) * bucket_size_bytes;
311 let physical_extent = DiagnosticMemorySize::from_wasm_pages(measured.physical_pages);
312 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(total_pages);
313 let summary = MemoryAllocationSummary {
314 current_generation: self
315 .committed_allocations()
316 .ok()
317 .map(crate::CommittedAllocations::generation),
318 manager_layout_version: 1,
319 bucket_size_pages: measured.bucket_pages,
320 bucket_size_bytes,
321 bucket_capacity: u32::from(layout::BUCKET_CAPACITY),
322 allocated_buckets: measured.allocated_buckets,
323 remaining_buckets: u32::from(layout::BUCKET_CAPACITY)
324 - u32::from(measured.allocated_buckets),
325 maximum_bucket_bytes: u64::from(layout::BUCKET_CAPACITY) * bucket_size_bytes,
326 physical_extent,
327 virtual_extent,
328 manager_metadata_bytes: WASM_PAGE_SIZE_BYTES,
329 manager_header_bytes: layout::HEADER_BYTES as u64,
330 manager_bucket_table_bytes: layout::BUCKETS as u64,
331 manager_padding_bytes: WASM_PAGE_SIZE_BYTES - layout::METADATA_BYTES as u64,
332 allocated_bucket_bytes,
333 bucket_slack_bytes: allocated_bucket_bytes - virtual_extent.bytes,
334 known_binding_bytes: groups[0].allocated_bytes + groups[1].allocated_bytes,
335 unknown_binding_bytes: groups[2].allocated_bytes,
336 unmanaged_bytes: physical_extent.bytes - WASM_PAGE_SIZE_BYTES - allocated_bucket_bytes,
337 metadata_bytes_read: layout::METADATA_BYTES as u64,
338 memories_measured: u16::from(crate::MEMORY_MANAGER_INVALID_ID),
339 current_binding: groups[0],
340 ledger_binding: groups[1],
341 unknown_binding: groups[2],
342 };
343 Ok((measured, summary))
344 }
345}