From 63e2c4f6b888f4965dcdf4869700e1c2fe1cc1fc Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Mon, 2 Jan 2023 20:49:43 +0000 Subject: [PATCH] add system information plugin and update relevant examples (#5911) # Objective Solve #5464 ## Solution Adds a `SystemInformationDiagnosticsPlugin` to add diagnostics. Adds `Cargo.toml` flags to fix building on different platforms. --- ## Changelog Adds `sysinfo` crate to `bevy-diagnostics`. Changes in import order are due to clippy. Co-authored-by: l1npengtul <35755164+l1npengtul@users.noreply.github.com> Co-authored-by: IceSentry --- crates/bevy_diagnostic/src/lib.rs | 60 +------ .../system_information_diagnostics_plugin.rs | 159 ++++++++++++++++++ examples/diagnostics/log_diagnostics.rs | 2 + 3 files changed, 166 insertions(+), 55 deletions(-) create mode 100644 crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index 5cef8389ac092..b2d127f115600 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -2,13 +2,14 @@ mod diagnostic; mod entity_count_diagnostics_plugin; mod frame_time_diagnostics_plugin; mod log_diagnostics_plugin; -use bevy_log::info; +mod system_information_diagnostics_plugin; + +use bevy_app::prelude::*; pub use diagnostic::*; pub use entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin; pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin; pub use log_diagnostics_plugin::LogDiagnosticsPlugin; - -use bevy_app::prelude::*; +pub use system_information_diagnostics_plugin::SystemInformationDiagnosticsPlugin; /// Adds core diagnostics resources to an App. #[derive(Default)] @@ -17,61 +18,10 @@ pub struct DiagnosticsPlugin; impl Plugin for DiagnosticsPlugin { fn build(&self, app: &mut App) { app.init_resource::() - .add_startup_system(log_system_info); + .add_startup_system(system_information_diagnostics_plugin::internal::log_system_info); } } /// The width which diagnostic names will be printed as /// Plugin names should not be longer than this value pub const MAX_DIAGNOSTIC_NAME_WIDTH: usize = 32; - -#[derive(Debug)] -// This is required because the Debug trait doesn't detect it's used when it's only used in a print :( -#[allow(dead_code)] -struct SystemInfo { - os: String, - kernel: String, - cpu: String, - core_count: String, - memory: String, -} - -const BYTES_TO_GIB: f64 = 1.0 / 1024.0 / 1024.0 / 1024.0; - -fn log_system_info() { - // NOTE: sysinfo fails to compile when using bevy dynamic or on iOS and does nothing on wasm - #[cfg(all( - any( - target_os = "linux", - target_os = "windows", - target_os = "android", - target_os = "macos" - ), - not(feature = "bevy_dynamic_plugin") - ))] - { - use sysinfo::{CpuExt, SystemExt}; - - let mut sys = sysinfo::System::new(); - sys.refresh_cpu(); - sys.refresh_memory(); - - let info = SystemInfo { - os: sys - .long_os_version() - .unwrap_or_else(|| String::from("not available")), - kernel: sys - .kernel_version() - .unwrap_or_else(|| String::from("not available")), - cpu: sys.global_cpu_info().brand().trim().to_string(), - core_count: sys - .physical_core_count() - .map(|x| x.to_string()) - .unwrap_or_else(|| String::from("not available")), - // Convert from Bytes to GibiBytes since it's probably what people expect most of the time - memory: format!("{:.1} GiB", sys.total_memory() as f64 * BYTES_TO_GIB), - }; - - info!("{:?}", info); - } -} diff --git a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs new file mode 100644 index 0000000000000..464fc06693da0 --- /dev/null +++ b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs @@ -0,0 +1,159 @@ +use crate::DiagnosticId; +use bevy_app::{App, Plugin}; + +/// Adds a System Information Diagnostic, specifically `cpu_usage` (in %) and `mem_usage` (in %) +/// +/// Supported targets: +/// * linux, +/// * windows, +/// * android, +/// * macos +/// +/// NOT supported when using the `bevy/dynamic` feature even when using previously mentioned targets +#[derive(Default)] +pub struct SystemInformationDiagnosticsPlugin; +impl Plugin for SystemInformationDiagnosticsPlugin { + fn build(&self, app: &mut App) { + app.add_startup_system(internal::setup_system) + .add_system(internal::diagnostic_system); + } +} + +impl SystemInformationDiagnosticsPlugin { + pub const CPU_USAGE: DiagnosticId = + DiagnosticId::from_u128(78494871623549551581510633532637320956); + pub const MEM_USAGE: DiagnosticId = + DiagnosticId::from_u128(42846254859293759601295317811892519825); +} + +// NOTE: sysinfo fails to compile when using bevy dynamic or on iOS and does nothing on wasm +#[cfg(all( + any( + target_os = "linux", + target_os = "windows", + target_os = "android", + target_os = "macos" + ), + not(feature = "bevy_dynamic_plugin") +))] +pub mod internal { + use bevy_ecs::{prelude::ResMut, system::Local}; + use bevy_log::info; + use sysinfo::{CpuExt, System, SystemExt}; + + use crate::{Diagnostic, Diagnostics}; + + const BYTES_TO_GIB: f64 = 1.0 / 1024.0 / 1024.0 / 1024.0; + + pub(crate) fn setup_system(mut diagnostics: ResMut) { + diagnostics.add( + Diagnostic::new( + super::SystemInformationDiagnosticsPlugin::CPU_USAGE, + "cpu_usage", + 20, + ) + .with_suffix("%"), + ); + diagnostics.add( + Diagnostic::new( + super::SystemInformationDiagnosticsPlugin::MEM_USAGE, + "mem_usage", + 20, + ) + .with_suffix("%"), + ); + } + + pub(crate) fn diagnostic_system( + mut diagnostics: ResMut, + mut sysinfo: Local>, + ) { + if sysinfo.is_none() { + *sysinfo = Some(System::new_all()); + } + let Some(sys) = sysinfo.as_mut() else { + return; + }; + + sys.refresh_cpu(); + sys.refresh_memory(); + let current_cpu_usage = { + let mut usage = 0.0; + let cpus = sys.cpus(); + for cpu in cpus { + usage += cpu.cpu_usage(); // NOTE: this returns a value from 0.0 to 100.0 + } + // average + usage / cpus.len() as f32 + }; + // `memory()` fns return a value in bytes + let total_mem = sys.total_memory() as f64 / BYTES_TO_GIB; + let used_mem = sys.used_memory() as f64 / BYTES_TO_GIB; + let current_used_mem = used_mem / total_mem * 100.0; + + diagnostics.add_measurement(super::SystemInformationDiagnosticsPlugin::CPU_USAGE, || { + current_cpu_usage as f64 + }); + diagnostics.add_measurement(super::SystemInformationDiagnosticsPlugin::MEM_USAGE, || { + current_used_mem + }); + } + + #[derive(Debug)] + // This is required because the Debug trait doesn't detect it's used when it's only used in a print :( + #[allow(dead_code)] + struct SystemInfo { + os: String, + kernel: String, + cpu: String, + core_count: String, + memory: String, + } + + pub(crate) fn log_system_info() { + let mut sys = sysinfo::System::new(); + sys.refresh_cpu(); + sys.refresh_memory(); + + let info = SystemInfo { + os: sys + .long_os_version() + .unwrap_or_else(|| String::from("not available")), + kernel: sys + .kernel_version() + .unwrap_or_else(|| String::from("not available")), + cpu: sys.global_cpu_info().brand().trim().to_string(), + core_count: sys + .physical_core_count() + .map(|x| x.to_string()) + .unwrap_or_else(|| String::from("not available")), + // Convert from Bytes to GibiBytes since it's probably what people expect most of the time + memory: format!("{:.1} GiB", sys.total_memory() as f64 * BYTES_TO_GIB), + }; + + info!("{:?}", info); + } +} + +#[cfg(not(all( + any( + target_os = "linux", + target_os = "windows", + target_os = "android", + target_os = "macos" + ), + not(feature = "bevy_dynamic_plugin") +)))] +pub mod internal { + pub(crate) fn setup_system() { + bevy_log::warn!("This platform and/or configuration is not supported!"); + } + + pub(crate) fn diagnostic_system() { + // no-op + } + + pub(crate) fn log_system_info() { + // no-op + } +} diff --git a/examples/diagnostics/log_diagnostics.rs b/examples/diagnostics/log_diagnostics.rs index 3af2af71df870..436bba684f78e 100644 --- a/examples/diagnostics/log_diagnostics.rs +++ b/examples/diagnostics/log_diagnostics.rs @@ -17,5 +17,7 @@ fn main() { // .add_plugin(bevy::diagnostic::EntityCountDiagnosticsPlugin::default()) // Uncomment this to add an asset count diagnostics: // .add_plugin(bevy::asset::diagnostic::AssetCountDiagnosticsPlugin::::default()) + // Uncomment this to add system info diagnostics: + // .add_plugin(bevy::diagnostic::SystemInformationDiagnosticsPlugin::default()) .run(); }