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)]
19pub struct MemoryAllocations {
20 pub current_generation: Option<u64>,
22 pub manager_layout_version: u8,
24 pub bucket_size_pages: u16,
26 pub bucket_size_bytes: u64,
28 pub bucket_capacity: u32,
30 pub allocated_buckets: u16,
32 pub remaining_buckets: u32,
34 pub maximum_bucket_bytes: u64,
36 pub physical_extent: MemoryExtent,
38 pub virtual_extent: MemoryExtent,
40 pub manager_metadata_bytes: u64,
42 pub manager_header_bytes: u64,
44 pub manager_bucket_table_bytes: u64,
46 pub manager_padding_bytes: u64,
48 pub allocated_bucket_bytes: u64,
50 pub bucket_slack_bytes: u64,
52 pub known_binding_bytes: u64,
54 pub unknown_binding_bytes: u64,
56 pub unmanaged_bytes: u64,
58 pub metadata_bytes_read: u64,
60 pub memories: Vec<MemoryAllocation>,
62}
63
64impl MemoryAllocations {
65 fn from_runtime<M: ic_memory::Memory>(
68 runtime: &ic_memory::MemoryRuntime<M>,
69 ) -> Result<Self, InternalError> {
70 let report = runtime
71 .memory_allocations()
72 .map_err(|_| InternalError::store_internal())?;
73 let (bindings, pool) = if runtime.is_bootstrapped() {
74 (
75 runtime
76 .committed_allocations()
77 .map_err(|_| InternalError::store_internal())?,
78 Some(
79 runtime
80 .allocation_pool()
81 .map_err(|_| InternalError::store_internal())?,
82 ),
83 )
84 } else {
85 (&[][..], None)
86 };
87 Ok(Self {
88 current_generation: report.summary.current_generation,
89 manager_layout_version: MemoryAllocationSummary::MANAGER_LAYOUT_VERSION,
90 bucket_size_pages: report.summary.bucket_size_pages,
91 bucket_size_bytes: report.summary.bucket_size_bytes(),
92 bucket_capacity: MemoryAllocationSummary::BUCKET_CAPACITY,
93 allocated_buckets: report.summary.allocated_buckets,
94 remaining_buckets: report.summary.remaining_buckets(),
95 maximum_bucket_bytes: report.summary.maximum_bucket_bytes(),
96 physical_extent: report.summary.physical_extent.into(),
97 virtual_extent: report.summary.virtual_extent.into(),
98 manager_metadata_bytes: MemoryAllocationSummary::MANAGER_METADATA_BYTES,
99 manager_header_bytes: MemoryAllocationSummary::MANAGER_HEADER_BYTES,
100 manager_bucket_table_bytes: MemoryAllocationSummary::MANAGER_BUCKET_TABLE_BYTES,
101 manager_padding_bytes: MemoryAllocationSummary::MANAGER_PADDING_BYTES,
102 allocated_bucket_bytes: report.summary.allocated_bucket_bytes(),
103 bucket_slack_bytes: report.summary.bucket_slack_bytes(),
104 known_binding_bytes: report.summary.known_binding_bytes(),
105 unknown_binding_bytes: report.summary.unknown_binding_bytes(),
106 unmanaged_bytes: report.summary.unmanaged_bytes(),
107 metadata_bytes_read: MemoryAllocationSummary::METADATA_BYTES_READ,
108 memories: report
109 .memories
110 .into_iter()
111 .map(|memory| {
112 MemoryAllocation::from_runtime(
113 memory,
114 report.summary.bucket_size_pages,
115 bindings,
116 pool,
117 )
118 })
119 .collect::<Result<_, _>>()?,
120 })
121 }
122}
123
124#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
126pub struct MemoryExtent {
127 pub wasm_pages: u64,
129 pub bytes: u64,
131}
132
133impl From<ic_memory::DiagnosticMemorySize> for MemoryExtent {
134 fn from(extent: ic_memory::DiagnosticMemorySize) -> Self {
135 Self {
136 wasm_pages: extent.wasm_pages,
137 bytes: extent.bytes(),
138 }
139 }
140}
141
142#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
144pub enum MemoryAllocationBinding {
145 Current {
147 stable_key: String,
149 owner: String,
151 },
152 Ledger {
154 stable_key: String,
156 owner: String,
158 },
159 Unknown,
161}
162
163#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
165pub struct MemoryAllocation {
166 pub memory_manager_id: u8,
168 pub binding: MemoryAllocationBinding,
170 pub pool_eligible: Option<bool>,
172 pub virtual_extent: MemoryExtent,
174 pub allocated_buckets: u16,
176 pub allocated_bytes: u64,
178 pub bucket_slack_bytes: u64,
180 pub payload_bytes: Option<u64>,
182}
183
184impl MemoryAllocation {
185 fn from_runtime(
188 memory: ic_memory::MemoryAllocation,
189 bucket_size_pages: u16,
190 bindings: &[ic_memory::AllocationDeclaration],
191 pool: Option<&ic_memory::MemoryAllocationPool>,
192 ) -> Result<Self, InternalError> {
193 let allocated_bytes = memory.allocated_bytes(bucket_size_pages);
194 let bucket_slack_bytes = memory.bucket_slack_bytes(bucket_size_pages);
195 let binding = if memory.memory_manager_id == ic_memory::MEMORY_MANAGER_LEDGER_ID {
196 MemoryAllocationBinding::Ledger {
197 stable_key: ic_memory::IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
198 owner: ic_memory::IC_MEMORY_AUTHORITY_OWNER.to_string(),
199 }
200 } else if let Some(declaration) = bindings
201 .iter()
202 .find(|binding| binding.slot().id() == memory.memory_manager_id)
203 {
204 let owner = pool
205 .and_then(|pool| pool.authority_for_key(declaration.stable_key()))
206 .ok_or_else(InternalError::store_invariant)?;
207 MemoryAllocationBinding::Current {
208 stable_key: declaration.stable_key().to_string(),
209 owner: owner.to_string(),
210 }
211 } else {
212 MemoryAllocationBinding::Unknown
213 };
214 Ok(Self {
215 memory_manager_id: memory.memory_manager_id,
216 binding,
217 pool_eligible: pool.map(|pool| pool.contains(memory.memory_manager_id)),
218 virtual_extent: memory.virtual_extent.into(),
219 allocated_buckets: memory.allocated_buckets,
220 allocated_bytes,
221 bucket_slack_bytes,
222 payload_bytes: None,
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}