From 3339b179e9fd5509bb074bf3735942afa0d62746 Mon Sep 17 00:00:00 2001 From: Gautham Varadarajan Date: Thu, 19 Dec 2024 15:21:41 -0500 Subject: [PATCH] [RSDK-9548] log storage usage on startup (#365) --- micro-rdk/src/common/conn/viam.rs | 24 ++++++++--- micro-rdk/src/common/credentials_storage.rs | 8 ++++ micro-rdk/src/esp32/nvs_storage.rs | 47 +++++++++++++++++++-- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/micro-rdk/src/common/conn/viam.rs b/micro-rdk/src/common/conn/viam.rs index b430a9d8f..75b72406b 100644 --- a/micro-rdk/src/common/conn/viam.rs +++ b/micro-rdk/src/common/conn/viam.rs @@ -13,7 +13,7 @@ use std::future::Future; use crate::common::app_client::{ AppClient, AppClientBuilder, AppClientError, PeriodicAppClientTask, }; -use crate::common::credentials_storage::TlsCertificate; +use crate::common::credentials_storage::{StorageDiagnostic, TlsCertificate}; use crate::common::webrtc::signaling_server::SignalingServer; use std::marker::PhantomData; use std::net::{SocketAddr, TcpListener}; @@ -91,23 +91,33 @@ impl From<&proto::app::v1::CloudConfig> for RobotCloudConfig { #[cfg(not(feature = "ota"))] pub trait ViamServerStorage: - RobotConfigurationStorage + WifiCredentialStorage + Clone + 'static + RobotConfigurationStorage + WifiCredentialStorage + StorageDiagnostic + Clone + 'static { } #[cfg(not(feature = "ota"))] impl ViamServerStorage for T where - T: RobotConfigurationStorage + WifiCredentialStorage + Clone + 'static + T: RobotConfigurationStorage + WifiCredentialStorage + StorageDiagnostic + Clone + 'static { } #[cfg(feature = "ota")] pub trait ViamServerStorage: - RobotConfigurationStorage + WifiCredentialStorage + OtaMetadataStorage + Clone + 'static + RobotConfigurationStorage + + WifiCredentialStorage + + OtaMetadataStorage + + StorageDiagnostic + + Clone + + 'static { } #[cfg(feature = "ota")] impl ViamServerStorage for T where - T: RobotConfigurationStorage + WifiCredentialStorage + OtaMetadataStorage + Clone + 'static + T: RobotConfigurationStorage + + WifiCredentialStorage + + OtaMetadataStorage + + StorageDiagnostic + + Clone + + 'static { } @@ -454,6 +464,7 @@ where } pub(crate) async fn run(&mut self) -> ! { + self.storage.log_space_diagnostic(); // The first step is to check whether or not credentials are populated in // storage. If not, we should go straight to provisioning. // @@ -643,6 +654,7 @@ where }) .ok() } else { + log::info!("Failed to obtain certificates from app, will attempt to load any stored certificates"); Some(self.storage.get_tls_certificate().ok()) } .flatten(); @@ -664,6 +676,8 @@ where } } + self.storage.log_space_diagnostic(); + let (tx, rx) = async_channel::bounded(1); let mut inner = RobotServer { diff --git a/micro-rdk/src/common/credentials_storage.rs b/micro-rdk/src/common/credentials_storage.rs index 93c428046..dda062c09 100644 --- a/micro-rdk/src/common/credentials_storage.rs +++ b/micro-rdk/src/common/credentials_storage.rs @@ -139,6 +139,10 @@ pub trait OtaMetadataStorage { fn reset_ota_metadata(&self) -> Result<(), Self::Error>; } +pub trait StorageDiagnostic { + fn log_space_diagnostic(&self); +} + #[derive(Default)] struct RAMCredentialStorageInner { robot_creds: Option, @@ -300,6 +304,10 @@ impl WifiCredentialStorage for RAMStorage { } } +impl StorageDiagnostic for RAMStorage { + fn log_space_diagnostic(&self) {} +} + impl From for ServerError { fn from(_: Infallible) -> Self { unreachable!() diff --git a/micro-rdk/src/esp32/nvs_storage.rs b/micro-rdk/src/esp32/nvs_storage.rs index 0653024be..46f4b6128 100644 --- a/micro-rdk/src/esp32/nvs_storage.rs +++ b/micro-rdk/src/esp32/nvs_storage.rs @@ -2,20 +2,20 @@ use bytes::Bytes; use hyper::{http::uri::InvalidUri, Uri}; use prost::{DecodeError, Message}; -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, ffi::CString, num::NonZeroI32, rc::Rc}; use thiserror::Error; use crate::{ common::{ credentials_storage::{ - RobotConfigurationStorage, RobotCredentials, TlsCertificate, WifiCredentialStorage, - WifiCredentials, + RobotConfigurationStorage, RobotCredentials, StorageDiagnostic, TlsCertificate, + WifiCredentialStorage, WifiCredentials, }, grpc::{GrpcError, ServerError}, }, esp32::esp_idf_svc::{ nvs::{EspCustomNvs, EspCustomNvsPartition, EspNvs}, - sys::EspError, + sys::{esp, nvs_get_stats, nvs_stats_t, EspError, ESP_ERR_INVALID_ARG}, }, proto::{app::v1::RobotConfig, provisioning::v1::CloudConfig}, }; @@ -41,6 +41,7 @@ pub struct NVSStorage { // esp-idf-svc partition driver ensures that only one handle of a type can be created // so inner mutability can be achieves safely with RefCell nvs: Rc>, + partition_name: CString, } impl NVSStorage { @@ -51,6 +52,9 @@ impl NVSStorage { Ok(Self { nvs: Rc::new(nvs.into()), + partition_name: CString::new(partition_name).map_err(|_| { + EspError::from_non_zero(NonZeroI32::new(ESP_ERR_INVALID_ARG).unwrap()) + })?, }) } @@ -126,6 +130,41 @@ impl NVSStorage { } } +const BYTES_PER_ENTRY: usize = 32; + +impl StorageDiagnostic for NVSStorage { + fn log_space_diagnostic(&self) { + let mut stats: nvs_stats_t = Default::default(); + if let Err(err) = + esp!(unsafe { nvs_get_stats(self.partition_name.as_ptr(), &mut stats as *mut _) }) + { + log::error!("could not acquire NVS stats: {:?}", err); + return; + } + + let used_entries = stats.used_entries; + let used_space = used_entries * BYTES_PER_ENTRY; + let total_space = stats.total_entries * BYTES_PER_ENTRY; + + // From experimentation we have found that NVS requires 4000 bytes of + // unused space for reasons unknown. The percentage portion of the calculation (0.976) + // comes from the blob size restriction as stated in the ESP32 documentation + // on NVS + let total_usable_space = (0.976 * (total_space as f64)) - 4000.0; + let fraction_used = (used_space as f64) / total_usable_space; + log::log!( + if fraction_used > 0.9 { + log::Level::Warn + } else { + log::Level::Info + }, + "NVS stats: {:?} bytes used of {:?} available", + used_space, + total_space + ); + } +} + const NVS_ROBOT_SECRET_KEY: &str = "ROBOT_SECRET"; const NVS_ROBOT_ID_KEY: &str = "ROBOT_ID"; const NVS_ROBOT_APP_ADDRESS: &str = "ROBOT_APP_ADDR";