icydb_core/db/diagnostics/
memory_allocations.rs1#[cfg(test)]
7mod tests;
8
9use crate::{db::memory_runtime, error::InternalError};
10
11use ic_memory::MemoryAllocationSummary;
12use serde::Deserialize;
13
14#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
19#[cfg_attr(not(target_family = "wasm"), derive(ic_wire::Wire))]
20pub struct MemoryAllocations {
21 pub current_generation: Option<u64>,
23 pub manager_layout_version: u8,
25 pub bucket_size_pages: u16,
27 pub bucket_size_bytes: u64,
29 pub bucket_capacity: u32,
31 pub allocated_buckets: u16,
33 pub remaining_buckets: u32,
35 pub maximum_bucket_bytes: u64,
37 pub physical_extent: MemoryExtent,
39 pub virtual_extent: MemoryExtent,
41 pub manager_metadata_bytes: u64,
43 pub manager_header_bytes: u64,
45 pub manager_bucket_table_bytes: u64,
47 pub manager_padding_bytes: u64,
49 pub allocated_bucket_bytes: u64,
51 pub bucket_slack_bytes: u64,
53 pub known_binding_bytes: u64,
55 pub unknown_binding_bytes: u64,
57 pub unmanaged_bytes: u64,
59 pub metadata_bytes_read: u64,
61 pub memories: Vec<MemoryAllocation>,
63}
64
65impl MemoryAllocations {
66 fn from_runtime<M: ic_memory::Memory>(
69 runtime: &ic_memory::MemoryRuntime<M>,
70 ) -> Result<Self, InternalError> {
71 let report = runtime
72 .memory_allocations()
73 .map_err(|_| InternalError::store_internal())?;
74 let (bindings, pool) = if runtime.is_bootstrapped() {
75 (
76 runtime
77 .committed_allocations()
78 .map_err(|_| InternalError::store_internal())?,
79 Some(
80 runtime
81 .allocation_pool()
82 .map_err(|_| InternalError::store_internal())?,
83 ),
84 )
85 } else {
86 (&[][..], None)
87 };
88 Ok(Self {
89 current_generation: report.summary.current_generation,
90 manager_layout_version: MemoryAllocationSummary::MANAGER_LAYOUT_VERSION,
91 bucket_size_pages: report.summary.bucket_size_pages,
92 bucket_size_bytes: report.summary.bucket_size_bytes(),
93 bucket_capacity: MemoryAllocationSummary::BUCKET_CAPACITY,
94 allocated_buckets: report.summary.allocated_buckets,
95 remaining_buckets: report.summary.remaining_buckets(),
96 maximum_bucket_bytes: report.summary.maximum_bucket_bytes(),
97 physical_extent: report.summary.physical_extent.into(),
98 virtual_extent: report.summary.virtual_extent.into(),
99 manager_metadata_bytes: MemoryAllocationSummary::MANAGER_METADATA_BYTES,
100 manager_header_bytes: MemoryAllocationSummary::MANAGER_HEADER_BYTES,
101 manager_bucket_table_bytes: MemoryAllocationSummary::MANAGER_BUCKET_TABLE_BYTES,
102 manager_padding_bytes: MemoryAllocationSummary::MANAGER_PADDING_BYTES,
103 allocated_bucket_bytes: report.summary.allocated_bucket_bytes(),
104 bucket_slack_bytes: report.summary.bucket_slack_bytes(),
105 known_binding_bytes: report.summary.known_binding_bytes(),
106 unknown_binding_bytes: report.summary.unknown_binding_bytes(),
107 unmanaged_bytes: report.summary.unmanaged_bytes(),
108 metadata_bytes_read: MemoryAllocationSummary::METADATA_BYTES_READ,
109 memories: report
110 .memories
111 .into_iter()
112 .map(|memory| {
113 MemoryAllocation::from_runtime(
114 memory,
115 report.summary.bucket_size_pages,
116 bindings,
117 pool,
118 )
119 })
120 .collect::<Result<_, _>>()?,
121 })
122 }
123}
124
125#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
127#[cfg_attr(not(target_family = "wasm"), derive(ic_wire::Wire))]
128pub struct MemoryExtent {
129 pub wasm_pages: u64,
131 pub bytes: u64,
133}
134
135impl From<ic_memory::DiagnosticMemorySize> for MemoryExtent {
136 fn from(extent: ic_memory::DiagnosticMemorySize) -> Self {
137 Self {
138 wasm_pages: extent.wasm_pages,
139 bytes: extent.bytes(),
140 }
141 }
142}
143
144#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
146#[cfg_attr(not(target_family = "wasm"), derive(ic_wire::Wire))]
147pub enum MemoryAllocationBinding {
148 Current {
150 stable_key: String,
152 owner: String,
154 },
155 Ledger {
157 stable_key: String,
159 owner: String,
161 },
162 Unknown,
164}
165
166#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
168#[cfg_attr(not(target_family = "wasm"), derive(ic_wire::Wire))]
169pub struct MemoryAllocation {
170 pub memory_manager_id: u8,
172 pub binding: MemoryAllocationBinding,
174 pub pool_eligible: Option<bool>,
176 pub virtual_extent: MemoryExtent,
178 pub allocated_buckets: u16,
180 pub allocated_bytes: u64,
182 pub bucket_slack_bytes: u64,
184}
185
186impl MemoryAllocation {
187 fn from_runtime(
190 memory: ic_memory::MemoryAllocation,
191 bucket_size_pages: u16,
192 bindings: &[ic_memory::AllocationDeclaration],
193 pool: Option<&ic_memory::MemoryAllocationPool>,
194 ) -> Result<Self, InternalError> {
195 let allocated_bytes = memory.allocated_bytes(bucket_size_pages);
196 let bucket_slack_bytes = memory.bucket_slack_bytes(bucket_size_pages);
197 let binding = if memory.memory_manager_id == ic_memory::MEMORY_MANAGER_LEDGER_ID {
198 MemoryAllocationBinding::Ledger {
199 stable_key: ic_memory::IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
200 owner: ic_memory::IC_MEMORY_AUTHORITY_OWNER.to_string(),
201 }
202 } else if let Some(declaration) = bindings
203 .iter()
204 .find(|binding| binding.slot().id() == memory.memory_manager_id)
205 {
206 let owner = pool
207 .and_then(|pool| pool.authority_for_key(declaration.stable_key()))
208 .ok_or_else(InternalError::store_invariant)?;
209 MemoryAllocationBinding::Current {
210 stable_key: declaration.stable_key().to_string(),
211 owner: owner.to_string(),
212 }
213 } else {
214 MemoryAllocationBinding::Unknown
215 };
216 Ok(Self {
217 memory_manager_id: memory.memory_manager_id,
218 binding,
219 pool_eligible: pool.map(|pool| pool.contains(memory.memory_manager_id)),
220 virtual_extent: memory.virtual_extent.into(),
221 allocated_buckets: memory.allocated_buckets,
222 allocated_bytes,
223 bucket_slack_bytes,
224 })
225 }
226}
227
228pub(super) fn collect_memory_allocations() -> Result<Option<MemoryAllocations>, InternalError> {
231 let Some(runtime) = memory_runtime() else {
232 return Ok(None);
233 };
234 MemoryAllocations::from_runtime(runtime.as_ref()).map(Some)
235}