1
Fork 0
mirror of https://github.com/RGBCube/superfreq synced 2025-07-27 17:07:44 +00:00

cli: more command logic to a dedicated module; add debug cmd

This commit is contained in:
NotAShelf 2025-05-14 00:15:18 +03:00
parent ea84a2997b
commit dde938b638
No known key found for this signature in database
GPG key ID: 29D95B64378DB4BF
6 changed files with 254 additions and 3 deletions

166
src/cli/debug.rs Normal file
View file

@ -0,0 +1,166 @@
use crate::config::AppConfig;
use crate::conflict;
use crate::cpu;
use crate::monitor;
use std::error::Error;
use std::fs;
/// Prints comprehensive debug information about the system
pub fn run_debug(config: &AppConfig) -> Result<(), Box<dyn Error>> {
println!("=== SUPERFREQ DEBUG INFORMATION ===");
println!("Version: {}", env!("CARGO_PKG_VERSION"));
// Current date and time
let now = std::time::SystemTime::now();
println!("Timestamp: {now:?}");
// Get system information and conflicts
match monitor::collect_system_report(config) {
Ok(report) => {
println!("\n--- SYSTEM INFORMATION ---");
println!("CPU Model: {}", report.system_info.cpu_model);
println!("Architecture: {}", report.system_info.architecture);
println!(
"Linux Distribution: {}",
report.system_info.linux_distribution
);
println!("\n--- CONFIGURATION ---");
println!("Current Configuration: {config:#?}");
println!("\n--- CPU INFORMATION ---");
println!("Current Governor: {:?}", report.cpu_global.current_governor);
println!(
"Available Governors: {}",
report.cpu_global.available_governors.join(", ")
);
println!("Turbo Status: {:?}", report.cpu_global.turbo_status);
println!(
"Energy Performance Preference (EPP): {:?}",
report.cpu_global.epp
);
println!("Energy Performance Bias (EPB): {:?}", report.cpu_global.epb);
// Add governor override information
if let Some(override_governor) = cpu::get_governor_override() {
println!("Governor Override: {}", override_governor.trim());
} else {
println!("Governor Override: None");
}
println!("\n--- PLATFORM PROFILE ---");
println!(
"Current Platform Profile: {:?}",
report.cpu_global.platform_profile
);
match cpu::get_platform_profiles() {
Ok(profiles) => println!("Available Platform Profiles: {}", profiles.join(", ")),
Err(_) => println!("Available Platform Profiles: Not supported on this system"),
}
println!("\n--- CPU CORES DETAIL ---");
println!("Total CPU Cores: {}", report.cpu_cores.len());
for core in &report.cpu_cores {
println!("Core {}:", core.core_id);
println!(
" Current Frequency: {} MHz",
core.current_frequency_mhz
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
);
println!(
" Min Frequency: {} MHz",
core.min_frequency_mhz
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
);
println!(
" Max Frequency: {} MHz",
core.max_frequency_mhz
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
);
println!(
" Usage: {}%",
core.usage_percent
.map_or_else(|| "N/A".to_string(), |u| format!("{u:.1}"))
);
println!(
" Temperature: {}°C",
core.temperature_celsius
.map_or_else(|| "N/A".to_string(), |t| format!("{t:.1}"))
);
}
println!("\n--- TEMPERATURE INFORMATION ---");
println!(
"Average CPU Temperature: {}",
report.cpu_global.average_temperature_celsius.map_or_else(
|| "N/A (CPU temperature sensor not detected)".to_string(),
|t| format!("{t:.1}°C")
)
);
println!("\n--- BATTERY INFORMATION ---");
if report.batteries.is_empty() {
println!("No batteries found or all are ignored.");
} else {
for battery in &report.batteries {
println!("Battery: {}", battery.name);
println!(" AC Connected: {}", battery.ac_connected);
println!(
" Charging State: {}",
battery.charging_state.as_deref().unwrap_or("N/A")
);
println!(
" Capacity: {}%",
battery
.capacity_percent
.map_or_else(|| "N/A".to_string(), |c| c.to_string())
);
println!(
" Power Rate: {} W",
battery
.power_rate_watts
.map_or_else(|| "N/A".to_string(), |p| format!("{p:.2}"))
);
println!(
" Charge Start Threshold: {}",
battery
.charge_start_threshold
.map_or_else(|| "N/A".to_string(), |t| t.to_string())
);
println!(
" Charge Stop Threshold: {}",
battery
.charge_stop_threshold
.map_or_else(|| "N/A".to_string(), |t| t.to_string())
);
}
}
println!("\n--- SYSTEM LOAD ---");
println!(
"Load Average (1 min): {:.2}",
report.system_load.load_avg_1min
);
println!(
"Load Average (5 min): {:.2}",
report.system_load.load_avg_5min
);
println!(
"Load Average (15 min): {:.2}",
report.system_load.load_avg_15min
);
println!("\n--- CONFLICT DETECTION ---");
let conflicts = conflict::detect_conflicts();
println!("{}", conflicts.get_conflict_message());
println!("\n--- DAEMON STATUS ---");
// Simple check for daemon status - can be expanded later
let daemon_status = fs::metadata("/var/run/superfreq.pid").is_ok();
println!("Daemon Running: {daemon_status}");
Ok(())
}
Err(e) => Err(Box::new(e) as Box<dyn Error>),
}
}

1
src/cli/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod debug;

73
src/config/watcher.rs Normal file
View file

@ -0,0 +1,73 @@
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::path::Path;
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use std::thread;
use std::error::Error;
use crate::config::{load_config, AppConfig};
/// Watches a configuration file for changes and reloads it when modified
pub struct ConfigWatcher {
rx: Receiver<Result<Event, notify::Error>>,
_watcher: RecommendedWatcher, // keep watcher alive while watching
config_path: String,
}
impl ConfigWatcher {
/// Initialize a new config watcher for the given path
pub fn new(config_path: &str) -> Result<Self, notify::Error> {
let (tx, rx) = channel();
// Create a watcher with default config
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
// Start watching the config file
watcher.watch(Path::new(config_path), RecursiveMode::NonRecursive)?;
Ok(Self {
rx,
_watcher: watcher,
config_path: config_path.to_string(),
})
}
/// Check for config file changes and reload if necessary
///
/// # Returns
///
/// `Some(AppConfig)` if the config was reloaded, `None`` otherwise
pub fn check_for_changes(&self) -> Option<Result<AppConfig, Box<dyn Error>>> {
// Non-blocking check for file events
match self.rx.try_recv() {
Ok(Ok(event)) => {
// Only process write/modify events
if matches!(event.kind, EventKind::Modify(_)) {
// Add a small delay to ensure the file write is complete
thread::sleep(Duration::from_millis(100));
// Attempt to reload the config
match load_config() {
Ok(config) => {
println!("Configuration file changed. Reloaded configuration.");
Some(Ok(config))
}
Err(e) => {
eprintln!("Error reloading configuration: {e}");
Some(Err(Box::new(e)))
}
}
} else {
None
}
}
// No events or channel errors
_ => None,
}
}
/// Get the path of the config file being watched
pub const fn config_path(&self) -> &String {
&self.config_path
}
}

View file

@ -67,8 +67,8 @@ pub fn determine_and_apply_settings(
// If no batteries, assume AC power (desktop).
// Otherwise, check the ac_connected status from the (first) battery.
// XXX: This relies on the setting ac_connected in BatteryInfo being set correctly.
let on_ac_power = report.batteries.is_empty()
|| report.batteries.first().map_or(false, |b| b.ac_connected);
let on_ac_power =
report.batteries.is_empty() || report.batteries.first().is_some_and(|b| b.ac_connected);
if on_ac_power {
println!("Engine: On AC power, selecting Charger profile.");

View file

@ -1,3 +1,4 @@
mod cli;
mod config;
mod conflict;
mod core;
@ -45,6 +46,8 @@ enum Commands {
#[clap(value_enum)]
setting: TurboSetting,
},
/// Display comprehensive debug information
Debug,
/// Set Energy Performance Preference (EPP)
SetEpp {
epp: String,
@ -202,6 +205,7 @@ fn main() {
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
Some(Commands::Debug) => cli::debug::run_debug(&config),
None => {
println!("Welcome to superfreq! Use --help for commands.");
println!("Current effective configuration: {config:?}");

View file

@ -417,7 +417,14 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
None
};
let available_governors = get_platform_profiles().unwrap_or_else(|_| vec![]);
let available_governors = if cpufreq_base.join("scaling_available_governors").exists() {
read_sysfs_file_trimmed(cpufreq_base.join("scaling_available_governors")).map_or_else(
|_| vec![],
|s| s.split_whitespace().map(String::from).collect(),
)
} else {
vec![]
};
let turbo_status = if turbo_status_path.exists() {
// 0 means turbo enabled, 1 means disabled for intel_pstate