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
113impl<M: Memory> MemoryRuntime<M> {
114 pub fn memory_allocations(&self) -> Result<MemoryAllocations, RuntimeDiagnosticError> {
120 let declarations = match &self.lifecycle {
121 RuntimeLifecycle::Unbootstrapped => None,
122 RuntimeLifecycle::Bootstrapped { binding, .. } => Some(&binding.declarations),
123 };
124 if declarations.is_some_and(|snapshot| {
126 snapshot.registered_declarations().len() >= layout::IDS
127 || snapshot.range_authority().authorities().len() > layout::IDS
128 }) {
129 return Err(RuntimeDiagnosticError::AllocationBound);
130 }
131 let measured = layout::read(self.backing.as_ref())?;
132 if measured.bucket_pages != self.bucket_size_pages {
133 return Err(super::RuntimeConstructionError::Layout(
134 MemoryManagerLayoutError::RuntimeMismatch,
135 )
136 .into());
137 }
138 let bucket_size_bytes = u64::from(measured.bucket_pages) * WASM_PAGE_SIZE_BYTES;
139 let mut memories = Vec::with_capacity(layout::IDS);
140 let mut total_pages = 0;
141 let mut known_binding_bytes = 0;
142 for id in 0..255_u8 {
143 let index = usize::from(id);
144 if self.memory(id).size() != measured.pages[index] {
145 return Err(super::RuntimeConstructionError::Layout(
146 MemoryManagerLayoutError::RuntimeMismatch,
147 )
148 .into());
149 }
150 let (binding, range_claim) = current_binding(id, declarations);
151 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(measured.pages[index]);
152 let allocated_bytes = u64::from(measured.buckets[index]) * bucket_size_bytes;
153 if !matches!(binding, AllocationBinding::Unknown) {
154 known_binding_bytes += allocated_bytes;
155 }
156 total_pages += measured.pages[index];
157 memories.push(MemoryAllocation {
158 memory_manager_id: id,
159 binding,
160 range_claim,
161 virtual_extent,
162 allocated_buckets: measured.buckets[index],
163 allocated_bytes,
164 bucket_slack_bytes: allocated_bytes - virtual_extent.bytes,
165 payload_bytes: None,
166 });
167 }
168 let allocated_bucket_bytes = u64::from(measured.allocated_buckets) * bucket_size_bytes;
169 let physical_extent = DiagnosticMemorySize::from_wasm_pages(measured.physical_pages);
170 let virtual_extent = DiagnosticMemorySize::from_wasm_pages(total_pages);
171 Ok(MemoryAllocations {
172 current_generation: self
173 .committed_allocations()
174 .ok()
175 .map(crate::CommittedAllocations::generation),
176 manager_layout_version: 1,
177 bucket_size_pages: measured.bucket_pages,
178 bucket_size_bytes,
179 bucket_capacity: 32_768,
180 allocated_buckets: measured.allocated_buckets,
181 remaining_buckets: 32_768 - u32::from(measured.allocated_buckets),
182 maximum_bucket_bytes: 32_768 * bucket_size_bytes,
183 physical_extent,
184 virtual_extent,
185 manager_metadata_bytes: WASM_PAGE_SIZE_BYTES,
186 manager_header_bytes: layout::HEADER_BYTES as u64,
187 manager_bucket_table_bytes: layout::BUCKETS as u64,
188 manager_padding_bytes: WASM_PAGE_SIZE_BYTES - layout::METADATA_BYTES as u64,
189 allocated_bucket_bytes,
190 bucket_slack_bytes: allocated_bucket_bytes - virtual_extent.bytes,
191 known_binding_bytes,
192 unknown_binding_bytes: allocated_bucket_bytes - known_binding_bytes,
193 unmanaged_bytes: physical_extent.bytes - WASM_PAGE_SIZE_BYTES - allocated_bucket_bytes,
194 metadata_bytes_read: layout::METADATA_BYTES as u64,
195 memories,
196 })
197 }
198}
199
200fn current_binding(
201 id: u8,
202 declarations: Option<&crate::SealedDeclarationSnapshot>,
203) -> (AllocationBinding, Option<AllocationRangeClaim>) {
204 let mut binding = AllocationBinding::Unknown;
205 let mut range_claim = None;
206 if let Some(snapshot) = declarations {
207 if let Some(registration) = snapshot
208 .registered_declarations()
209 .iter()
210 .find(|registration| registration.declaration().slot().memory_manager_id() == Ok(id))
211 {
212 binding = AllocationBinding::Current {
213 stable_key: registration.declaration().stable_key().as_str().to_string(),
214 owner: registration.authority().to_string(),
215 };
216 }
217 if let Some(claim) = snapshot
218 .range_authority()
219 .authorities()
220 .iter()
221 .find(|claim| claim.range().contains(id))
222 {
223 range_claim = Some(AllocationRangeClaim {
224 authority: claim.authority().to_string(),
225 mode: claim.mode(),
226 });
227 }
228 }
229 if id == MEMORY_MANAGER_LEDGER_ID {
230 binding = AllocationBinding::Ledger {
231 stable_key: IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
232 owner: IC_MEMORY_AUTHORITY_OWNER.to_string(),
233 };
234 }
235 (binding, range_claim)
236}