#[cfg(test)]
mod tests;
use crate::{db::memory_runtime, error::InternalError};
use candid::CandidType;
use ic_memory::MemoryAllocationSummary;
use serde::Deserialize;
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct MemoryAllocations {
pub current_generation: Option<u64>,
pub manager_layout_version: u8,
pub bucket_size_pages: u16,
pub bucket_size_bytes: u64,
pub bucket_capacity: u32,
pub allocated_buckets: u16,
pub remaining_buckets: u32,
pub maximum_bucket_bytes: u64,
pub physical_extent: MemoryExtent,
pub virtual_extent: MemoryExtent,
pub manager_metadata_bytes: u64,
pub manager_header_bytes: u64,
pub manager_bucket_table_bytes: u64,
pub manager_padding_bytes: u64,
pub allocated_bucket_bytes: u64,
pub bucket_slack_bytes: u64,
pub known_binding_bytes: u64,
pub unknown_binding_bytes: u64,
pub unmanaged_bytes: u64,
pub metadata_bytes_read: u64,
pub memories: Vec<MemoryAllocation>,
}
impl From<ic_memory::MemoryAllocations> for MemoryAllocations {
fn from(report: ic_memory::MemoryAllocations) -> Self {
Self {
current_generation: report.summary.current_generation,
manager_layout_version: MemoryAllocationSummary::MANAGER_LAYOUT_VERSION,
bucket_size_pages: report.summary.bucket_size_pages,
bucket_size_bytes: report.summary.bucket_size_bytes(),
bucket_capacity: MemoryAllocationSummary::BUCKET_CAPACITY,
allocated_buckets: report.summary.allocated_buckets,
remaining_buckets: report.summary.remaining_buckets(),
maximum_bucket_bytes: report.summary.maximum_bucket_bytes(),
physical_extent: report.summary.physical_extent.into(),
virtual_extent: report.summary.virtual_extent.into(),
manager_metadata_bytes: MemoryAllocationSummary::MANAGER_METADATA_BYTES,
manager_header_bytes: MemoryAllocationSummary::MANAGER_HEADER_BYTES,
manager_bucket_table_bytes: MemoryAllocationSummary::MANAGER_BUCKET_TABLE_BYTES,
manager_padding_bytes: MemoryAllocationSummary::MANAGER_PADDING_BYTES,
allocated_bucket_bytes: report.summary.allocated_bucket_bytes(),
bucket_slack_bytes: report.summary.bucket_slack_bytes(),
known_binding_bytes: report.summary.known_binding_bytes(),
unknown_binding_bytes: report.summary.unknown_binding_bytes(),
unmanaged_bytes: report.summary.unmanaged_bytes(),
metadata_bytes_read: MemoryAllocationSummary::METADATA_BYTES_READ,
memories: report
.memories
.into_iter()
.map(|memory| {
MemoryAllocation::from_runtime(memory, report.summary.bucket_size_pages)
})
.collect(),
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
pub struct MemoryExtent {
pub wasm_pages: u64,
pub bytes: u64,
}
impl From<ic_memory::DiagnosticMemorySize> for MemoryExtent {
fn from(extent: ic_memory::DiagnosticMemorySize) -> Self {
Self {
wasm_pages: extent.wasm_pages,
bytes: extent.bytes(),
}
}
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
pub enum MemoryAllocationBinding {
Current {
stable_key: String,
owner: String,
},
Ledger {
stable_key: String,
owner: String,
},
Unknown,
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct MemoryAllocation {
pub memory_manager_id: u8,
pub binding: MemoryAllocationBinding,
pub pool_eligible: Option<bool>,
pub virtual_extent: MemoryExtent,
pub allocated_buckets: u16,
pub allocated_bytes: u64,
pub bucket_slack_bytes: u64,
pub payload_bytes: Option<u64>,
}
impl MemoryAllocation {
fn from_runtime(memory: ic_memory::MemoryAllocation, bucket_size_pages: u16) -> Self {
let allocated_bytes = memory.allocated_bytes(bucket_size_pages);
let bucket_slack_bytes = memory.bucket_slack_bytes(bucket_size_pages);
Self {
memory_manager_id: memory.memory_manager_id,
binding: match memory.binding {
ic_memory::AllocationBinding::Current { stable_key, owner } => {
MemoryAllocationBinding::Current { stable_key, owner }
}
ic_memory::AllocationBinding::Ledger => MemoryAllocationBinding::Ledger {
stable_key: ic_memory::IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
owner: ic_memory::IC_MEMORY_AUTHORITY_OWNER.to_string(),
},
ic_memory::AllocationBinding::Unknown => MemoryAllocationBinding::Unknown,
},
pool_eligible: memory.pool_eligible,
virtual_extent: memory.virtual_extent.into(),
allocated_buckets: memory.allocated_buckets,
allocated_bytes,
bucket_slack_bytes,
payload_bytes: None,
}
}
}
pub(super) fn collect_memory_allocations() -> Result<Option<MemoryAllocations>, InternalError> {
let Some(runtime) = memory_runtime() else {
return Ok(None);
};
match runtime.memory_allocations() {
Ok(report) => Ok(Some(report.into())),
Err(_) => Err(InternalError::store_internal()),
}
}